+++ /dev/null
-/* $Id: LICENSE,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (c) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+++ /dev/null
-$Id: README,v 1.7 2004/06/04 02:31:51 mmondor Exp $
-
-
-
-Xisop Copyright 2001-2003, Matthew Mondor,
-All rights reserved.
-
-Currently under development. The only current port is Amiga, and an .uaerc
-is provided for the UAE emulator to test it and possibly enhance it :)
- Xisop/src/ports/amiga/boot/DOTuaerc
-It is recommended to run the UAE emulator using the -T command line parameter
-when on unix systems (or setting the x11.use_mitshm=true in the ~/.uaerc file).
-Note that kickstart 3.0 or later is required, and that I will not provide this
-file to anyone, as it is copyrighted material. You can buy a kickstart kit, or
-mirror the one of your amiga to file. The models which used to ship with 3.0+
-natively were the A1200 and A4000. It is possible to make Xisop kickstart 1.3
-compatible if it was to be used to make games, since only the bootloader is
-AmigaOS dependent.
-
-The compiled kernel is 30k in size if compiled with statistics support, or
-19k otherwise.
-
-The source of the port-dependent initialization function (which currently
-launches tasks displaying colors for testing and demonstrating) is located at
- Xisop/src/ports/amiga/boot/init.c
-
-Some development notes are also being worked on according to the internal
-implementation details, programming style notes and how to port Xisop
-(also under development as the kernel interfaces are being stabilized).
- Xisop/doc/xisop.pdf
-This file is built from xisop.lyx which is maintained using the LyX utility.
-
-Almost all the current code and documentation were re-written from scratch in
-Febuary and March 2003. The old Xisop code somewhat served as reference
-when needed.
-
-Please read TODO on what currently needs to be worked on to continue the
-project, what was done and what needs debugging. The project is to eventually
-be fully released under BSD-style license when at least one port works fine.
-Make sure to also read the documentation in doc/.
-
-To build the amiga port you should specify the location of the cross building
-tools in Xisop/src/ports/amiga/makedefs.sh, or use the defaults if you have
-a NetBSD 1.6.1 source, in which case you can simply use the build.sh script
-provided in /usr/src to build the netbsdelf-m68k target toolchain (which only
-needs to be done once):
- cd /usr/src
- ./build.sh -a m68k -t
-Then:
- cd Xisop/src
- ./make.sh -t amiga
-
-Matthew Mondor
+++ /dev/null
-$Id: TODO,v 1.20 2004/10/14 15:13:20 mmondor Exp $
-
-
-- Import modifications from mmsoftware/mmlib:
- - New more efficient pool allocator, with enhanced debugging capability.
- This could imply reworking some of the whole system.
- - Maybe incorporate C byteorder library, so that less processor-specific
- code is required (of course, they can override C ones with asm ones).
-
-
-Working:
-=======
-- Memory management system and ANSI-C related functions
-- Syscalls, including one which allows to execute arbitrary code in supervisor
- mode
-- Exclusive, recursive and read/write locks
-- User interrupt facilities
-- Preemptive multitasking, _yield() and task_sleep()/task_wakeup()
-- CPU saving ideling
- When no tasks are on the ready queue to run, the _scontext _ctx_t
- defined in scheduler.c is resumed, which in fact returns to main.c's
- main() function, which sole purpose is to sys_idle(), in a loop,
- which suspends the processor until the next interrupt occurs.
-- Serious exceptions generate a display showing a number of lines which
- corresponds to the actual error (_ecatch() parameter), useful to debug
- the current Amiga port.
-- Useful statistics book-keeping of global counters
-- Inter-task signals
-- Inter-task communication reliable ports and messages
-- The init and task reaper system tasks
-- Operations which need fast lookup tables to use kernlib/hash.[ch].
- This is done for some system lists, such as for port_find().
-- Multiple tasks shareing a common mpool_t created with TF_SHARED
-
-
-Bugs to work out:
-================
-- There again is some problem with memory or stack sizes. When DEBUG and
- STATISTICS are enabled in config.h, things do not work as intended.
- This seems to simply arise now that the kernel image is about a k larger.
- For some reason, the ports/amiga/boot/config.h values do not seem to
- always generate proper kernel images, for instance when I increase the
- stack size to 16384 I get a guru meditation...
-- The scheduler which takes task priorities into account is currently not
- working. There is a temporary replacement scheduler which works now and
- only performs round-robin when preempting tasks. The old one which has
- some bugs is found in src/common/kernel/scheduler.c and is called
- old_schedule().
-- Signals and message ports
- Some hack should be found a more appropriate solution: signal_send()
- needs to _yield() twice (See src/common/kernel/signal.c).
- For some not yet determined reason, tasks would all eventually be
- stuck on the wait queue if this was not done.
-
-
-Remaining to complete:
-=====================
-- Add system similar to mmlib/mmalarm(3) to Xisop, to go with the existing
- MI interrupt hook management functions
-- Perhaps use hashtables in exceptions hooks management instead of linked
- lists? Is this needed or wanted? Running through a hash table is slower,
- but lookups are faster. Think about it and see which is best to use.
-- Fix issue with amiga port init stack size... m68k usermode() could
- be specified a stack size for instance, at least. We also could
- reserve some memory for the stack in advance and supply it...
-- Implement setjmp()/longjmp() for i386
-- CPU specific _bfill16 and _bfill32 etc (at least _bfillint or such),
- to efficiently fill a native word size with an 8-bit pattern. This
- could be used by the string library in memset() for instance...
-- Probably that the cases of usecount (devicenode_t, mpool_t) could use
- an _rlock_t for more efficiency. It is implemented in assembler, and
- is sure to be atomic.
-- Finish dprintf() FIFO operation optimizations, and write a generic
- vsnprintf() implementation with stdarg. We want to enhance DPRINTF()
- to print module and line numbers, etc.
-- _panic()
-- Console and printf() (along with corresponding console.device)
-- Map remaining exceptions to output messages on console
- For the console.device to eventually exist, the port primitives are
- essencial. So until they work reliably it's not really possible to
- implement right now (at least on Amiga).
- It would be easy to use the video hardware memory on a i386 port
- however, so a i386 port could perhaps actually help in development
- for the rest :)
- Ports implementation is now working reliably. Revise.
-- Probably that the statistics would also be nice to have on a per-task basis.
- Moreover, they are not as useful as they would if a console existed :<
- currently, the DPRINTF() system logs to a FIFO buffer.
-- Xisop shared libraries
-- Xisop devices as a synchronization abstraction over the message ports system
-- Xisop handlers as a higher-level (filesystem/file) abstraction over devices
- libraries and/or hardware
-- Relocatable binary loader
- This unfortunately either implies writing a full fledged ELF or a.out
- loader, or a BFD library for GCC ld to output in a new desired format.
- The ELF loader was started but was not completed (not included in the
- current sources). ELF unfortunately seems really bloated for the
- actual needs of this project, and it's state is uncertain.
- The loader would be used to load shared libraries, and executable
- tasks, including devices and handlers.
- For the moment, the system is monolithic and soon a simple interface
- will be provided to allow the port-specific code to provide a list
- of tasks to be launched by Xisop init.
- That alone, even monolithic, allows Xisop to stand as a nice C
- interface which most code is portable among 32-bit systems, to abstract
- interrupt facilities, shared memory access and multitasking for
- various applications. Very small, it can fit the application on a
- floppy (which includes the kernel).
-- timer.device
-- input.device
-- console.device
-- Add necessary functions to allow linking in the common/kernlib/hash.c module
-
-
-
-NEW MEMORY MANAGEMENT SYSTEM NOTES:
-
-As before, we need several types of memory.
-For each memory type, we should still attach/detach memory chunks, like now.
-
-For each memory chunk, a new system should be implemented for page-level
-contiguous memory management and freeing, as well as a new pool allocator,
-based on the idea of the new mmlib/mmpool(3) one.
-
-Because Xisop does not rely on MMU/PMMU, we can simplify the allocator to a
-simple block allocator, without even worrying about page boundaries, if we
-wanted. This would also mean that a pool page could be virtual, that is, could
-not be dependent on the system page size at all, if we wanted. If that was the
-case, the current mmlib/mmpool(3) allocator could be used as-is.
-
-Not to say that, even if we respected page alignment, we still could use a
-better allocator.
-
-A good idea would be to maintain a list of contiguous page (or memory bytes)
-chunks. Nodes of this list would be split as necessary to provide the
-requested size in the allocation functions. The freeing functions would need
-to attempt to coalesce the contigious chunks together when possible as well.
-
-Perhaps that we also would like a best-fit strategy rather than first-fit when
-allocating, so that we could favor contiguous memory chunks that are closest
-in size to the requested amount, rather than always splitting large chunks
-unnecessarily. This would reduce fragmentation, although being slower at
-allocation. However, because this would affect the page allocator, to which
-calls would be reduced by the pool allocators, this could be reasonable.
-
-Perhaps that to observe the best-fit strategy it would be possible to somewhat
-maintain a sorted index or such, of the smaller to larger available chunks.
-It needs to be verified that the code and memory overhead for such an index
-is negligeable, however. Moreover, would keeping a sorted list of free chunks
-useful for performance? How would the allocator efficiently perform jumps in
-the list? Wouldn't it need to be a tree, instead? If we used one, wouldn't we
-need recursion or special iteration for both node insertion and lookup? I will
-need to check that out. What I should do is count the iterations in my similar
-sorted tree system in dnamed, to get an idea of the actual code overhead
-involved.
-
-struct contiguous_chunk {
- node_t chunks_node; /* To link in free/allocated chunks list */
- node_t sorted_node; /* To link in free chunks sorted list */
- void *address; /* Starting address of free memory */
- size_t length; /* Size of contiguous free memory */
- u_int32_t pages; /* Or, could be number of free pages */
-};
+++ /dev/null
-# $Id: clean.sh,v 1.2 2004/06/06 04:21:45 mmondor Exp $
-
-rm -f xisop.dvi xisop.tex xisop.lyx~ xisop.pdf xisop.ps xisop.toc .log
+++ /dev/null
-# $Id: make.sh,v 1.3 2004/06/06 04:21:05 mmondor Exp $
-
-# First export LyX document to LaTeX then run this script
-
-lyx -e latex xisop.lyx
-latex xisop.tex
-latex xisop.tex
-rm -f xisop.ps xisop.aux xisop.log .log
-dvips -Pcmz -Pamz -o xisop.ps -t letter -D 300 -Z xisop.dvi
-ps2pdf xisop.ps
-rm -f *~
+++ /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
-\topmargin 0.5in
-\bottommargin 0.5in
-\secnumdepth 3
-\tocdepth 3
-\paragraph_separation indent
-\defskip medskip
-\quotes_language english
-\quotes_times 2
-\papercolumns 1
-\papersides 1
-\paperpagestyle default
-
-\layout Title
-
-Xisop kernel development notes
-\layout Author
-\pagebreak_bottom
-Copyright (c) 2001-2003, Matthew Mondor
-\newline
-All rights reserved.
-\layout Standard
-
-
-\begin_inset LatexCommand \tableofcontents{}
-
-\end_inset
-
-
-\layout Section
-\pagebreak_top
-General development notes
-\layout Standard
-
-Before attempting to write software for Xisop to expand it's funtionality,
- or before porting Xisop to a new architecture, it is recommended to carefully
- read this manual entirely.
- It attempts to answer all questions one could have about it's organization
- and build process, as well as style and conventions one must follow for
- code consistancy with the rest of the project.
-\layout Subsection
-
-Development software used
-\layout Standard
-
-To develop Xisop, the GNU GCC suite of compiler, assembler, linker and binutils
- software was chosen.
- The main reasons for this consists of cost savings, and portability.
- GCC has support for many different CPU types was a main factor.
- Moreover, the AT&T assembly syntax allows to somewhat obtain some consistency.
-\layout Standard
-
-An effort was made to try to not support GCC-specific functions however,
- for portability reasons.
- For instance, the various
-\emph on
-_spl
-\emph default
-*
-\emph on
-()
-\emph default
- functions are implemented as macros around a separate assembly function,
- located in the assembler
-\emph on
-.s
-\emph default
- files, rather than embedded inside the C code using inline assembly directives.
- (
-\emph on
-XXX
-\emph default
-
-\emph on
-actually they may be fixed to be GCC-dependent soon heh
-\emph default
-).
-\layout Standard
-
-A choice was made to not use GNU or BSD make.
- Instead,
-\emph on
-/bin/sh
-\emph default
- is assumed to exist.
- This usually consists of a POSIX compliant shell, on GNU systems (and Linux)
- the
-\emph on
-bash
-\emph default
- shell usually emulates it's behavior and
-\emph on
-/bin/sh
-\emph default
- then consists of a symbolic link to
-\emph on
-/bin/bash
-\emph default
-.
- On BSD systems only POSIX 1003.2 and 1003.2a functionality is usually present
- in their
-\emph on
-/bin/sh
-\emph default
-, which is what Xisop build scripts are making use of.
- As
-\emph on
-
-\emph default
-such,
-\emph on
- bash
-\emph default
- is therefore not a requirement.
-\layout Standard
-
-This document is written and maintained using LyX.
- The UAE (Amiga emulator) and Bochs (i386 emulator) utilities were useful
- to develop the current ports.
- The original host development system consists of an i386 compatible system
- running NetBSD 1.6_STABLE.
- This operating system is ideal for programming and cross compiling.
- The current Xisop building system was tuned to the NetBSD 1.6.1 toolchain.
- To compile the amiga port, for instance, you only should need to use the
- build.sh script to build the suite of netbsdelf-m68k tools.
- Using the Xisop
-\begin_inset Quotes eld
-\end_inset
-
-
-\emph on
-./make.sh -t amiga
-\emph default
-
-\begin_inset Quotes erd
-\end_inset
-
- command should then build the amiga target.
-\layout Standard
-
-The software was written using the VIm editor, with
-\emph on
-ts=8
-\emph default
-,
-\emph on
-sw=4
-\emph default
- and
-\emph on
-cindent
-\emph default
-.
-\layout Standard
-
-If you compile your own gcc with your intended compiling target, you simply
- need to change a file to tell the locations of the various building tools.
- See the section about
-\begin_inset Quotes eld
-\end_inset
-
-the build process
-\begin_inset Quotes erd
-\end_inset
-
- later on for more information.
-\layout Subsection
-
-Xisop compatibility with other systems, and where it fits best
-\layout Subsubsection
-
-UNIX, POSIX, BSD, Linux
-\layout Standard
-
-Xisop is definitely not POSIX, although it's functions are simpler than
- POSIX is, requireing a small learning curve only to use.
- It was not designed as a general-purpose operating system to replace Unix
- and provide all it's capabilities.
- It's an entity of it's own, simpler and smaller, mostly made to suit the
- requirements of restricted embedded systems.
- To implement most POSIX requirements a larger system is required, which
- generally also results in slower code.
- For instance, Xisop addresses ports and tasks as addresses, rather than
- IDs.
- This solves the problem of allocation and reuse of process IDs.
-\layout Standard
-
-A Xisop task also is lightweight compared to a UNIX-style process.
- Moreover, the need for more custom user signals than POSIX environment
- provides made it unsuitable to reserve almost all 32 signals to reserved
- semantics.
- The concept of file descriptors and select()/poll() is also different.
- However, SIGPOLL signal was reserved in Xisop for the implementation of
- message-based signals and events using a single message port and signal.
- This can be used when there can be a large number of entities a task may
- be monitoring, and the user signals would not be appropriate.
- It would be possible to work a system out using this facility for the 32
- standard Unix signals if the need existed.
-\layout Standard
-
-Although it would be possible to implement POSIX compatibility libraries,
- it was not a main goal in Xisop development.
-\layout Subsubsection
-
-Memory and process protection
-\layout Standard
-
-Xisop was not designed to support Memory Management Unit (MMU) coprocessors
- and provide page-grained memory protection.
- This helped to implement message passing in a very efficient manner, only
- passing pointers around, and reduced considerably kernel size.
- Moreover, it allows to provide most user-access system functions in a shared
- library, reducing considerably the amount of system calls an application
- needs to make (accessed through traps).
- The number of traps being reduced, the preemtive scheduler is less clobbered.
- Calling functions of a shared library happen entirely in the caller's task
- allocated CPU time slice, which means that the kernel load is not affected.
- Not requireing MMU is also an advantage with Xisop, as it can run with
- a single MC68000L8 for instance, and a little RAM.
-\layout Standard
-
-However, we all know that without appropriate memory protection, Xisop cannot
- efficiently protect the kernel from user tasks, which can take full control
- on the system at any time.
- Clean interfaces were however implemented, which internally perform various
- sanity checking to ensure the validity of the supplied objects and arguments,
- so that common software bugs do not automatically corrupt memory and the
- system.
- Xisop knows the difference between a valid task, port, device handle, etc,
- and invalid ones.
- It also knows when to not attempt to free memory if a wrong pointer is
- supplied, since
-\emph on
-mnode_t
-\emph default
- nodes are also validated using a unique magic cookie.
- Such steps permit to minimize system crashes in the event of a software
- bug.
-\layout Standard
-
-The kernel comports special macros to aid in realizing object validity sceals
- and dependancies checking, which are defined in <
-\emph on
-common/kernel/object.h
-\emph default
->:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-OBJECT_VALIDATE(objptr,\SpecialChar ~
-objtype)
-\emph default
- Sets the
-\emph on
-objptr
-\emph default
-->object_magic field to
-\emph on
-objtype
-\emph default
-.
- This type corresponds to one of the
-\emph on
-OBJECT_
-\emph default
-* names which are defined in the same headerfile.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-OBJECT_INVALIDATE(objptr)
-\emph default
- Sets the
-\emph on
-objptr
-\emph default
-->object_magic field to 0.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-OBJECT_REGISTER(objptr)
-\emph default
- Registers
-\emph on
-objptr
-\emph default
- object by setting the
-\emph on
-objptr
-\emph default
-->object_id field to a unique number.
- This number will be used for matching when verifying for proper dependancy
- link.
- Only objects which may be required as dependancy to others need to use
- this.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-OBJECT_SETDEP(objptr,\SpecialChar ~
-depobjptr)
-\emph default
- Associates
-\emph on
-objptr
-\emph default
- object as requireing the
-\emph on
-depobjptr
-\emph default
- object as dependancy for proper function.
- What this does is internally set
-\emph on
-objptr
-\emph default
-->objdep_magic to
-\emph on
-depobjptr
-\emph default
-->magic and
-\emph on
-objptr
-\emph default
-->objdep_id to
-\emph on
-depobjptr
-\emph default
-->object_id.
- As a result, it becomes possible to refuse to perform operations related
- to this object if the dependancy link ever dies.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-OBJECT_VALID(objptr,\SpecialChar ~
-objtype)
-\emph default
- First verifies if objptr is non-NULL, and then evaluates if the object
- associated with
-\emph on
-objptr
-\emph default
- truely corresponds to
-\emph on
-objtype
-\emph default
- (
-\emph on
-objptr
-\emph default
- != NULL &&
-\emph on
-objptr
-\emph default
-->object_magic ==
-\emph on
-objtype
-\emph default
-).
- This consists of the reason why the various
-\emph on
-OBJECT_
-\emph default
-* definitions in the headerfile should consist of unique values which are
- unlikely to occur randomly.
- Returns TRUE on success, or FALSE otherwise.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-OBJECT_DEPENDS(objptr,\SpecialChar ~
-depobjptr)
-\emph default
- Verifies the integrity of a dependancy link, which was previously linked
- using
-\emph on
-OBJECT_SETDEP()
-\emph default
- on
-\emph on
-objptr
-\emph default
-.
- This in fact makes sure that the object it should relate to (
-\emph on
-depobjptr
-\emph default
-) is still valid, and consists of the same one (
-\emph on
-depobjptr
-\emph default
- != NULL &&
-\emph on
-depobjptr
-\emph default
-->object_magic ==
-\emph on
-objptr
-\emph default
-->objdep_magic &&
-\emph on
-depobjptr
-\emph default
-->object_id ==
-\emph on
-objptr
-\emph default
-->objdep_id).
- Returns TRUE on success, or FALSE otherwise.
-\layout Standard
-
-Obviously, the structure of an object only requires to hold the fields which
- are required for the functionality it requires according to these macros.
- Those fields are all expected to be of type
-\emph on
-u_int32_t
-\emph default
-.
- This system allows relatively lightweight validity checking of object credentia
-ls, without the need for memory protection.
- The kernel uses them wherever needed for safety.
- Using this system also allows to use efficient dynamic memory allocation
- techniques (
-\emph on
-pool_t
-\emph default
- functions) to create and destroy critical system objects, and reference
- can be made using pointers through the system, rather than using less efficient
- or fixed-sized arrays and object ID allocation.
- Programming using these macros also yields in a clean interface to result
- in consistant, clean C code.
-\layout Subsubsection
-
-Multitasking
-\layout Standard
-
-While Xisop provides preemptive multitasking (more information provided
- in the next section), it can be disabled by user applications if need be.
- This means that Xisop can be used to create single-tasking applications
- where a multitasking environment is not wanted, while still taking advantage
- of the abstraction of CPU and hardware access Xisop allows.
- This for instance allows games to be written almost entirely in C, and
- be independent of an operating system such as Windows or AmigaOS, using
- Xisop instead.
- Such a small game can be booted from a single floppy, which would comport
- all necessary components of the game, including Xisop itself.
- Disabling multitasking also allows direct access to the hardware resources
- by the main application, in a safe manner.
- When multitasking is enabled, such safe access to hardware resources is
- also provided, although using the
-\emph on
-device
-\emph default
- concept is required to remain multitasking friendly and access those.
- More information about Xisop devices is given later on.
-\layout Standard
-
-This means that multitasking was provided as a useful optional facility
- rather than to force limits over the applications.
- It is great to develop some kinds of systems using multitasking, while
- some other applications are much better without it.
- This was an important consideration when designing Xisop.
- Whether multitasking is enabled or disabled, the public interrupt facilities
- interface works as expected.
- More information is provided on this interface in a later section.
-\layout Subsubsection
-
-AmigaOS
-\layout Standard
-
-Although some ideas were admitedly borrowed from the Amiga Operating System,
- the compatibility stops there.
- The AmigaOS headerfiles, although publically available, or source code
- (which is unavailable publically) were not used as a reference to write
- it, nor can Xisop run AmigaOS native object files and binaries.
- Xisop is a clean-room kernel implementation, written from scratch and uses
- it's own set of ideas and conventions, which are described in this document.
-\layout Standard
-
-The Xisop signals, message ports, devices and handlers concepts were however
- borrowed from AmigaOS, as it was a great proof of feasibility using a very
- simple underlaying structure.
- The interface and internals do not behave identically but anyone who programmed
- using the AmigaOS inter-task communication and synchronization features
- will feel at ease with Xisop.
-\layout Subsubsection
-
-Where Xisop fits best
-\layout Standard
-
-The best application Xisop suits for consists of an embedded system, dedicated
- to provide specific services or tasks, running with low-cost hardware and
- restricted physical memory.
- There are however a wide range of applications which fit in this category,
- cell phones, microwave ovens, PDAs, clocks, industrial microcontrollers
- or event loggers, alarm systems, etc.
- An example of low-cost hardware Xisop runs great on is a Motorola MC68000L8
- microprocessor based board with a restricted 512 kilobytes of memory, and
- an RS-232 interface with UART controler chip.
- Xisop can also be used to develop games on 32-bit consoles or arcade hardware.
-\layout Standard
-
-Xisop consists of a nice component when simplifying hardware design of a
- project, where a simple CPU unit, little RAM and Xisop-based software can
- provide the tasks of more complex hardware-only solutions.
- This also implies that the simpler hardware may then be reused, since it
- now basically consists of a general purpose programmable system rather
- than single-purpose hardware units which can only serve for a single project.
-\layout Standard
-
-It does not consist of a full multipurpose operating system but rather of
- a collection of primitives to aid a C programmer to write his applications
- on a variety of hardware, and benefit from memory management including
- support for runtime dynamic memory addition and removal, as well as multiple
- memory types, inter-task communication primitives, a fair number of user
- signals, abstracted interrupt facilities, and a microkernel design allowing
- to add future functionality modularized as userspace tasks, shared libraries,
- devices and handlers.
-\layout Standard
-
-It's weak license (BSDL-style) makes it especially suitable for commercial
- embedded applications when a technical team can build a custom application,
- where royalties for use, or redistribution of custom changes in the code
- are not required.
- An effort is provided to make the CPU-specific interface abstract so that
- most of the code can be written in C, for code clarity, consistency, developmen
-t speed and ease.
- The assembly code is restricted to the minimum, and always consists of
- a backend to use C.
- The same is true for architecture-specific assembly low-level code.
- Those sections are described later on.
-\layout Standard
-
-As such, very little time is generally required to port the basics of Xisop
- to a new architecture (other 32-bit ones, at least).
- Engineers can then develop custom low-cost hardware and a programmer can
- write most of the software for it using C and Xisop rather than having
- to write all of it in assembly, or starting from nothing and having to
- re-invent the assembly to C support primitives.
-\layout Standard
-
-Very low cost hardware helps when many units are being deployed.
- However, in the cases where the hardware costs are higher (i.e.
- microprosessor with MMU support and 8 or more megabytes of memory), Xisop
- may seem too rudimentary, but there are then alternatives such as NetBSD
- which can be ported quite fast (when required, as it already consists of
- the most portable OS), and POSIX functionalities, with full unix features,
- networking, protected memory, etc all become available.
- It is also released under the BSD unrestrictive license and is available
- at
-\emph on
-http://www.netbsd.org
-\emph default
-.
-\layout Subsection
-
-Xisop coding standards
-\layout Subsubsection
-
-Licencing issues and censorship
-\layout Standard
-
-All code which is to be publically released within the standard Xisop distributi
-on should be licensed under a BSD-style license, and should not be released
- under the GNU Public License (GPL) or GNU Lesser Public License (LGPL).
- It however is obvious that companion packages be released and distributed
- separately with the Xisop main archive, as long as it be clearly isolated
- and labeled as such, so that it can easily be stripped when code under
- such strict a license is not wanted within a project.
- It is allowed for authors to include their advertizing clause as wanted
- in the BSD license advertizing at the top of their files.
- A main file can then easily be created using a script of advertizement
- clauses and related files whcih one can append in the documentation of
- a commercial or closed source product using Xisop.
-\layout Standard
-
-This allows anyone to use Xisop for open source or closed source projects
- as wanted.
- Although it is encouraged to publically donate the new processor and architectu
-re specific modules one develops, as well as machine-independent new modules
- of interest for inclusion into the main Xisop tree, released under BSD
- license, noone is obliged to do so.
- This also allows the main tree to not become too tainted with alot of code
- everyone develops which does not serve the other Xisop users much and mostly
- bloat the project.
- The mirokernel design easy allows third party supplied libraries, devices
- and handlers to be developed, and these may also be closed source, and
- commercial.
- There of course is no problem to release such optional kernel-independent
- modules under one of the GNU licenses.
-\layout Standard
-
-If you port Xisop to a new processor or architecture, and are willing to
- contribute the code to the tree, I suggest that the source be obtained
- via CVS from the main Xisop source repository, and that
-\emph on
-cvs diff -u
-\emph default
- be used to create a patch.
- This patch should be sent via email to Matthew Modor, at
-\emph on
-mmondor@gobot.ca
-\emph default
-.
- I then will attempt to merge it into the main Xisop CVS tree, after performing
- basic sanity checking on the code, and fixing required bugs if possible
- (if any).
- I may also reply back if there is any reason why the changes cannot be
- applied, in which case I could help to make it conform with the requirements.
-\layout Subsubsection
-
-Standard data types and when to use them
-\layout Standard
-
-Xisop uses a defined set of data types in it's kernel, and attempts to remain
- consistant when using them for code clarity and obviousness.
- The following C standard data types are used:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-char
-\emph default
- The standard C character type, used for C strings only in Xisop.
-
-\emph on
-int8_t
-\emph default
-and
-\emph on
- u_int8_t
-\emph default
- should be used for other byte-related types.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-char\SpecialChar ~
-*
-\emph default
- Obvious, also only used in direct relation with C strings.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void
-\emph default
- When a function returns nothing, or has no arguments, for both prototype
- and function declaration.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*
-\emph default
- This consists as usual, of a pointer to an abstract type, and is used where
- required.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-int
-\emph default
- This is used as a convenient format for various return codes and variables
- where bit-size is not a concern, or should match the one which is assumed
- for the processor (although it is highly recommended to use the various
- defined types below for these).
-\layout Standard
-
-The other standard C data types are replaced by the following ones, which
- are defined in the Xisop machine-independent <
-\emph on
-common/types.h>
-\emph default
- headerfile:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool
-\emph default
- Corresponds to
-\emph on
-int
-\emph default
-, but specifically denotes that this variable is used for boolean operations
- and conditionals for code clarity it may only hold TRUE (1) or FALSE (0)
- values.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-int8_t
-\emph default
- Is used everywhere byte-sized signed elements are explicitely required,
- from -127 to +128, except for strings where the standard C
-\emph on
-char
-\emph default
- type should be used.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int8_t
-\emph default
- Should be used where unsigned byte-sized elements are required, for 0-255
- values.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-int16_t
-\emph default
- Is the data type used for 16-bit signed integers values, -32768 to +32767.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int16_t
-\emph default
- The unsigned 16-bit value data type which can old 0 to 65535.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-int32_t
-\emph default
- For use with 32-bit signed data types
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int32_t
-\emph default
- 32-bit unsigned data type
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-int64_t
-\emph default
- signed 64-bit data type (usually typedef with
-\emph on
-long long
-\emph default
-)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int64_t
-\emph default
- unsigned 64-bit data type.
- It is to be noted that operations on 64-bit objects is generally slow as
- it requires multiple instructions on 32-bit architectures.
- Currently, no 64-bit data types are beign used anymore, the few ones which
- used it were converted to use 32-bit ones instead.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-size_t
-\emph default
- Used to represent a size in bytes, this in fact is equivalent to
-\emph on
-u_int32_t
-\emph default
-.
-
-\emph on
-size_t
-\emph default
- should not be used to represent arbitary types amounts, but byte amounts
- only (corresponding to a number of
-\emph on
-char
-\emph default
-,
-\emph on
-u_int8_t
-\emph default
- or
-\emph on
-int8_t
-\emph default
- types).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-ssize_t
-\emph default
- Purpose is the same as for
-\emph on
-size_t
-\emph default
-, but this can hold -1 for error.
-\layout Standard
-
-And other frequently used standard Xisop types, which are still defined
- in <
-\emph on
-common/types.h
-\emph default
->, but have their corresponding structures defined at their respective locations
- (mentionned in parentheses):
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-node_t
-\emph default
- A doubly linked list node to be used in
-\emph on
-list_t
-\emph default
- type lists.
- (common/kernlib/list.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-list_t
-\emph default
- A doubly linked list (common/kernlib/list.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-mchunk_t
-\emph default
- A chunk of contiguous memory pages, part of a
-\emph on
-ppool_t
-\emph default
- (common/kernel/memory.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-page_t
-\emph default
- A physical memory page, or a number of contiguous physical memory pages,
- holding the necessary information for freeing back.
- (common/kernel/memory.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-ppool_t
-\emph default
- A system pool of pages, that is, what other pools allocate pages from.
- (common/kernel/memory.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-pool_t
-\emph default
- A efficient fixed sized objects (
-\emph on
-pnode_t
-\emph default
-) memory pool with internal statistical page cacheing, which can optionally
- dynamically grow and shrink, or be static in size (common/kernel/memory.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-pnode_t
-\emph default
- A pool object node, which must prefix any pool object.
- This type internally begins with a
-\emph on
-node_t
-\emph default
- and can therefore be used directly for queuing in
-\emph on
-list_t
-\emph default
-.
- (common/kernel/memory.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-mpool_t
-\emph default
- A multiple
-\emph on
-pool_t
-\emph default
- memory pool used to serve multiple sized blocks requests.
- Of various memory types.
- Bocks which are too large are rounded at page boundaries.
- (common/kernel/memory.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-mnode_t
-\emph default
- Internally used by the
-\emph on
-mpool_t
-\emph default
- related allocation primitives (common/kernel/memory.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-facility_t
-\emph default
- A public interrupt facility which the port-specific code initializes calling
- the common
-\emph on
-facility_init()
-\emph default
- function.
- (common/kernel/exception.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-hook_t
-\emph default
- An interrupt, trap or exception code vector attached to a
-\emph on
-facility_t
-\emph default
-.
- (common/kernel/exception.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-hookid_t
-\emph default
- An
-\emph on
-hook_t
-\emph default
- ID, internally a
-\emph on
-u_int32_t
-\emph default
-, which is guarranteed to be unique for the facility it belongs to.
- (common/kernel/exception.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-task_t
-\emph default
- Task structure (common/kernel/task.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-priority_t
-\emph default
- A task priority number (common/kernel/task.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-signum_t
-\emph default
- Signal number (common/kernel/signal.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-sigmask_t
-\emph default
- Signal mask which can represent one or more
-\emph on
-signum_t
-\emph default
-.
- (common/kernel/signal.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-port_t
-\emph default
- Message port (common/kernel/port.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-message_t
-\emph default
- A message header structure which comports the glue to queue messages in
-
-\emph on
-port_t
-\emph default
- (common/kernel/port.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-device_t
-\emph default
- An open device handler (common/kernel/device.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-devicenode_t
-\emph default
- A linked device hook (common/kernel/device.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-iorequest_t
-\emph default
- An I/O request message type used for
-\emph on
-device_t
-\emph default
- requests (common/kernel/device.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-fifo8_t
-\emph default
- An 8-bit elements FIFO buffer.
-
-\emph on
-fifo
-\emph default
-*
-\emph on
-_t
-\emph default
- types are implemented as a fixed bytes buffer once initialized, rather
- than using linked lists.
- It is possible to easily create new
-\emph on
-fifo
-\emph default
-*
-\emph on
-_t
-\emph default
- types of arbitrary object sizes using a macro.
- (common/kernlib/fifo.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-fifo16_t
-\emph default
- A 16-bit elements FIFO buffer (common/kernlib/fifo.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-fifo32_t
-\emph default
- A 32-bit elements FIFO buffer (common/kernlib/fifo.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-lifo8_t
-\emph default
- A 8-bit elements LIFO buffer.
-
-\emph on
-lifo
-\emph default
-*
-\emph on
-_t
-\emph default
- types operate like stacks, and are internally implemented as a fixed bytes
- buffer once initialized, rather than using linked lists.
- A macro is provided to easily create new types of
-\emph on
-lifo
-\emph default
-*
-\emph on
-_t
-\emph default
- for objects of arbitrary size.
- (common/kernlib/lifo.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-lifo16_t
-\emph default
- A 16-bit elements LIFO buffer (common/kernlib/lifo.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-lifo32_t
-\emph default
- A 32-bit elements LIFO buffer (common/kernlib/lifo.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-hashtable_t
-\emph default
- A hash lookup table (common/kernlib/hash.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-hashnode_t
-\emph default
- A hash lookup table node (common/kernlib/hash.h>
-\layout Standard
-
-And here are the processor and architecture specific opaque data types they
- provide:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_ipl_t
-\emph default
- Abstract Interrupt Priority Level type, associated with the
-\emph on
-_spl
-\emph default
-n
-\emph on
-()
-\emph default
- and
-\emph on
-_splx()
-\emph default
- functions.
- (processor/support.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_ctx_t
-\emph default
- Abstract processor-specific supplied context handle, associated to
-\emph on
-_ctx_init()
-\emph default
- function which is used by
-\emph on
-task_alloc()
-\emph default
- and internal kernel context switching.
- (processor/support.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_lock_t
-\emph default
- Abstract processor-specific supplied atomic simple lock handle.
- Associated to the
-\emph on
-_lock_
-\emph default
-*
-\emph on
-()
-\emph default
- functions.
- (processor/support.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_rlock_t
-\emph default
- An abstract atomic recursive lock handle (needs to be unlocked the same
- number of times it was locked to free the
-\emph on
-_rlock_t
-\emph default
-).
- Associated to the
-\emph on
-_rlock_
-\emph default
-*
-\emph on
-()
-\emph default
- functions.
- (processor/support.h)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-XXX
-\emph default
- The 64-bit related types are currently unused.
- On most processors the GCC compiler requires additional libraries to support
- these, which are currently not part of Xisop.
- There however exist such implementations fully written in C, like the one
- used on BSD systems which would not be hard to include if it was necessary.
- An effort was made to also not include software which requires the use
- of floating point, for code size and efficiency.
-\layout Subsubsection
-
-Programming style and conventions
-\layout Standard
-
-It is a fact that consistency is a must for readibility, especially in an
- open source project which many others may need to modify to suit their
- particular needs.
- The following programming conventions are used, and are defined for assembly
- and C code.
-\layout Paragraph
-
-Function names
-\layout Standard
-
-All functions which are not behaving identically to ANSI-C or POSIX or other
- widely standarized ones, but have the same name should have their name
- prefixed by an underscore ('_').
- Others which behave exactly like the standards should have the same name
- the standard expects.
- Other functions which have nothing in common with standard names should
- not be prefixed by underscores, except for the defined processor and port-speci
-fic functions, which names should also begin with an underscore.
- Of course, if a hardware-specific function is provided as a replacement
- to a common portable one for performance considerations, it obviously should
- bear the same name, however.
-\layout Paragraph
-
-Formatting
-\layout Standard
-
-Line termination should consist of
-\begin_inset Quotes eld
-\end_inset
-
-
-\backslash
-n
-\begin_inset Quotes erd
-\end_inset
-
- (linefeed, as is used on Amiga and Unix), rather than
-\begin_inset Quotes eld
-\end_inset
-
-
-\backslash
-r
-\backslash
-n
-\begin_inset Quotes erd
-\end_inset
-
- which is used on MS-DOS and Windows for text file storage.
- Other than tab (
-\begin_inset Quotes eld
-\end_inset
-
-
-\backslash
-t
-\begin_inset Quotes erd
-\end_inset
-
-) and linefeed (
-\begin_inset Quotes eld
-\end_inset
-
-
-\backslash
-n
-\begin_inset Quotes erd
-\end_inset
-
-), no other control characters should be found within the source files.
- Moreover, every line of the source files should not exceed 79 characters.
-\layout Paragraph
-
-C language sections
-\layout Standard
-
-It is important that C files be using the standard eight (8) tab size for
- real tabs.
- However, the software tab should consist of four (4) characters.
- Tabs should be used where 8 space tabs are needed, and spaces used to fill
- the 4 character tabs.
- When using the VIm editor, these should be used:
-\emph on
-ts=8
-\emph default
-,
-\emph on
-sw=4
-\emph default
-, and
-\emph on
-cindent
-\emph default
-.
- This allows the files to be printable using text file viewers properly
- at all times, and to also be printable as-is on most printers.
-
-\emph on
-XXX
-\emph default
- Add settings for emacs
-\layout Itemize
-
-C program suffix should consist of lower case
-\emph on
-.c
-\layout Itemize
-
-C headerfiles should use the lower case suffix
-\emph on
-.h
-\layout Itemize
-
-ANSI-C programming norms should be observed as much as possible.
-\layout Itemize
-
-ANSI convention for function arguments specification rather than K&R.
-\layout Itemize
-
-BSD programming style used rather than GNU style, especially for opening
- and closing braces convention.
- This corresponds to the formatting performed using GNU indent program with
- the following parameters:
-\emph on
-gindent -kr -ncs <file>.c
-\layout Itemize
-
-Functions and global variables which are specific to the current module
- should be declared
-\emph on
-static
-\emph default
-, and their prototypes should be in a private headerfile or in the current
- C source file, not in a public headerfile which is included by any other
- C source module.
-\layout Itemize
-
-Macros and variables directly refering to hardware architecture-specific
- registers should use the
-\emph on
-volatile
-\emph default
- keyword.
-\layout Itemize
-
-Every function must have a corresponding prototype, defined either at the
- top of the current C file (if static) or in a corresponding headerfile
- (public ones).
-\layout Itemize
-
-For kernel code, the conventions should be followed about the choice of
- variable types to use (described in the previous section).
-\layout Itemize
-
-No C++ or other language should be used within the kernel code.
- Of course there is no such restriction for any user program.
-\layout Itemize
-
-Code should obviously be commented as required for clarity, but
-\emph on
-
-\begin_inset Quotes eld
-\end_inset
-
-/*
-\begin_inset Quotes erd
-\end_inset
-
-
-\emph default
- and
-\emph on
-
-\begin_inset Quotes eld
-\end_inset
-
-*/
-\begin_inset Quotes erd
-\end_inset
-
-
-\emph default
- C-style delimited comments only are allowed, not
-\emph on
-
-\begin_inset Quotes eld
-\end_inset
-
-//
-\begin_inset Quotes erd
-\end_inset
-
-
-\emph default
- C++ ones.
-\layout Itemize
-
-Code should not invoke general purpose memory allocation routines when a
- special fast access pool was provided already to allocate and free these
- types of items.
- Where appropriate, the general kernel objects pool system should be expanded
- when a new object is frequently allocated and freed, rather than using
- the general purpose management functions.
- These obviously should be common, machine-independent objects (which can
- possibly use machine-specific definitions, if they become standard and
- documented in this document so all ports provide them).
- See the memory management section later on for more information.
-\layout Itemize
-
-General purpose libraries supplied in
-\emph on
-src/common/kernlib/
-\emph default
- should normally have each function implemented as a separate C file into
- the library's directory.
- During the build process they will get compiled independently, and then
- archived together using
-\emph on
-ar
-\emph default
-.
-
-\emph on
-ranlib
-\emph default
- will then be executed on the archive.
- This ensures that unused functions do not be included into the end kernel
- result, reducing kernel size considerably when full multipurpose libraries
- are used (such as complete string libraries).
-\layout Paragraph
-
-Assembly sections
-\layout Standard
-
-For assembly sections, the AT&T style should be observed, and the assembly
- should not be embedded into the C code using inline assembly routines.
- Often a library requireing both assembly and C will at build time assemble
- it's assembly section file (
-\emph on
-.s
-\emph default
-), compile it's C part (
-\emph on
-.c
-\emph default
-), and link the two together into an object file (
-\emph on
-.o
-\emph default
-) using the
-\emph on
--r
-\emph default
- linker switch, which then gets linked with the kernel.
- In other situations one may want to instead compile all modules to objects
- (
-\emph on
-.o
-\emph default
-), create an
-\emph on
-ar
-\emph default
- archive (
-\emph on
-.a
-\emph default
-), run
-\emph on
-ranlib
-\emph default
- on it, and link it to the kernel (in which case all unused functionality
- which was isolated into it's own module does not get included by the linker,
- reducing kernel size).
- This applies to both processor-specific and architecture-specific backends.
- The rest is written in C.
- It is encouraged to write most of the architecture-specific boot loader
- code in C and provide a companion assembly file to interface and call the
- C loader main function.
- This minimizes assembly code, and C is more consistant and readable, it
- can be more suitable to sometimes write tricky sections like relocators,
- etc.
-\layout Standard
-
-The assembly sections should ensure to be reeintrant, that is, using the
- stack to save registers rather than using temporary registers to save other
- registers, and using the stack as well for temporary variables for which
- registers cannot be used, rather than static memory locations.
- These are usually indended to be called from C, and because of the way
- Xisop shared libraries work, is a main reason to follow these directives.
- An assembly section which is known to execute uninterruptably may at it's
- discretion use static memory if needed.
-\layout Itemize
-
-Assembly files should have the following lowercase suffix:
-\emph on
-.s
-\layout Itemize
-
-Tabulation should be set to use real tabs (not spaces), with a standard
- tab width of 8.
-\layout Itemize
-
-Assembler opcodes should be located after the first tab.
-\layout Itemize
-
-Operands should be located after the second tabulation.
-\layout Itemize
-
-Where more than an operand is required and are separated by a comma, a space
- is required after the comma.
-\layout Itemize
-
-Opcode names should be lowercase, and must match those shown by objdump
- when dissassembling.
- This allows consistancy between all code for a particular processor.
- Although this may sound tidious at first, it is not as hard as it sounds
- to learn the specific mnemonic names objdump displays, with some little
- practice.
- Adventages results in consistency, where it also becomes possible to locate
- specific instructions in the code with regular expressions, etc.
-\layout Itemize
-
-
-\emph on
-.equ
-\emph default
- and
-\emph on
-=
-\emph default
- directives, if any, should be located at the top of the assembly file.
-\layout Itemize
-
-
-\emph on
-.globl
-\emph default
- directives to declare public functions should also be located at the top
- of the file.
-\layout Itemize
-
-All code should be within the
-\emph on
-.text
-\emph default
- section, along with any static memory variables if need be (although use
- of these is discouraged, except for instance in the kernel loader bootblock
- where they can be useful, or in special context code where one knows what
- he's doing rather than only using the stack).
-\layout Itemize
-
-A comment on a line or two should be placed before each function, especially
- the ones which are intended to be called from C, along with the C prototype.
-\layout Itemize
-
-Other comments should be found along the code where required for obviousness.
-\layout Itemize
-
-Obviously, registers which a function modifies should be saved in the stack,
- and restored upon function return, except (if any) the register corresponding
- to the return code expected in C for that processor.
-\layout Itemize
-
-Interrupt, trap and exception assembly sections should normally save all
- registers which are commonly used for the particular processor, and restore
- them before returning.
- It is wrong to assume that C compiled functions always save all registers
- they modify other than the expected result register.
- This was learned from experience using GCC.
-\layout Subsection
-
-Xisop scheduler and inter-task communication
-\layout Subsubsection
-
-The multitasking scheduler
-\layout Standard
-
-The scheduler currently allows to set priorities for each task, between
- -128 and 127.
- It is preemptive and will make sure to allow other tasks to run even when
- a particular task hugs the CPU by not going to sleep.
- A task can be in two main states: running, and sleeping.
-\layout Standard
-
-When a task sleeps, it has a mask of signals that will awake it as soon
- as possible if it receives any signal it waits for, and leaves more opportuniti
-es for other tasks to execute their shores when it is sleeping.
- When a task runs, it usually executes it's shores and goes back to sleep
- for the next event to process, when running in a multitasking system.
- However, if it does not return to sleep fast enough, the scheduler preemtive
- nature will suspend it and allow other tasks to also get a fair CPU time
- share, depending on the priority of the running tasks, unless the task
- has disabled scheduling, in which case it will remain running until it
- decides to delegate control to the system again.
-\layout Standard
-
-The task priority controls the speed and frequency at which a running task
- awakes, or runs.
- What the scheduler does is assign a set of credits to all tasks in the
- run queue, according to their priority one another.
- Then using round-robin at each scheduler round, the task with the highest
- amount of credits is given some CPU time, and a credit is taken out from
- it.
- If a task expired it's credits, it is no longer given a round, until all
- other tasks also expired their credits.
- When this happens, all tasks are given credits according to their priority
- again.
- Some care is taken to not constantly give a turn to the last task even
- if it has more credits, to allow to make the best out of Xisop asynchroneous
- facilities, but such a high priority task will have more turns within the
- run still, observing it's relative priority to the others.
-\layout Standard
-
-When a task goes to sleep, it is immediately taken out of the run queue
- and will not be awaken until a signal it awaits for is received.
- When it does, it will be moved in the running queue as fast as possible,
- at which point the time elapsing before it can act to the signal is directly
- dependent on the priority of the task related to the other ready to run
- tasks.
- However, when the task is moved in the run queue, it is immediately given
- it's credits at the maximum it's priority permits, thus increasing the
- chances it can answer in a fast manner among the other tasks in the run
- queue, which may already have been there before, and elapsed their credits.
-\layout Standard
-
-This means that if all tasks run with the default priority of 0, they all
- get a fair share of the CPU, and are naturally awaking very fast when one
- get a signal.
- In the case where one of the tasks remains running, the speed of signal
- responsiveness will ususally consist of the frequency of the scheduler.
- This frequency is usually configurable, and depends on the capacity of
- the timers available for the particular architecture (and of course on
- CPU speed).
-\layout Standard
-
-In the case where the scheduler detects that all tasks are sleeping, it
- attempts to reduce it's CPU usage by calling the
-\emph on
-_idle()
-\emph default
- processor-specific function, which will only awaken the scheduler again
- when the next interrupt, trap or exception occurs.
- It is possible in Xisop to have dead locked tasks, if they decide to wait
- for no signals, or if they wait for a signal to occur which never does.
-\layout Standard
-
-Following are described all functions which are related to tasks and scheduler,
- or which are useful to synchronize resources, other than the
-\emph on
-signal_t
-\emph default
- and
-\emph on
-port_t
-\emph default
- based systems, which are described in a further section.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-task_t\SpecialChar ~
-*task_alloc(int\SpecialChar ~
-(*start)(void\SpecialChar ~
-*,\SpecialChar ~
-void\SpecialChar ~
-*), void\SpecialChar ~
-*res,\SpecialChar ~
-void\SpecialChar ~
-*args,\SpecialChar ~
-priority_t\SpecialChar ~
-priorit
-y,\SpecialChar ~
-size_t\SpecialChar ~
-stacksize,\SpecialChar ~
-u_int8_t\SpecialChar ~
-flags)
-\emph default
- Allocates a task which can then be started or freed back.
-
-\emph on
-start
-\emph default
- specifies the entry point function,
-\emph on
-res
-\emph default
- an optional pointer to a buffer for results which the task may need to
- use or NULL, and
-\emph on
-args
-\emph default
- an optional pointer to a buffer which may be used by the task to obtain
- it's argument, or NULL.
- When
-\emph on
-start
-\emph default
- is called,
-\emph on
-res
-\emph default
- is passed as the first argument and
-\emph on
-args
-\emph default
- as the second one.
-
-\emph on
-priority
-\emph default
- consists of the task initial running priority which may be in the range
- of -128 to 127, 0 being the normal running priority.
-
-\emph on
-stacksize
-\emph default
- specifies the size of the stack in bytes which should be dedicated to the
- task, a common size being 4096 bytes.
-
-\emph on
-flags
-\emph default
- may be 0, or ORed
-\emph on
-TS_
-\emph default
-* flags which are defined in <
-\emph on
-src/common/kernel/task.h
-\emph default
->.
- The task is not yet launched by Xisop.
- If the
-\emph on
-TS_SHARED
-\emph default
- flag is supplied, this task will use the
-\emph on
-mpool_t
-\emph default
- of it's parent (the current task which allocates it).
- This means that all memory allocated by one of the tasks sharing a memory
- pool is inherently shared.
- One task ending will not cause the allocated regions to be freed unless
- explicitely freed.
- When all tasks shareing a memory pool exit, all allocated memory by any
- of them is automatically freed back.
- This feature can be used when memory should inherently be shared among
- the tasks, or when memory resources are very scarce.
- However, unlike using Xisop messages which provide synchronization to share
- arbitrary memory regions among tasks, implicit synchronization should be
- used by the tasks when accessing the same shared memory locations which
- they should access concurrently.
- For this,
-\emph on
-rwlock_t
-\emph default
- and
-\emph on
-_lock_t
-\emph default
- based systems can be used, or a custom system based around Xisop signals
- and/or messages.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-task_t\SpecialChar ~
-*task_free(task_t\SpecialChar ~
-*task)
-\emph default
- Allows to free
-\emph on
-task
-\emph default
- which was previously allocated by
-\emph on
-task_alloc()
-\emph default
-.
- Only tasks which have been allocated but which have not been launched may
- be freed using this function, or it internally does nothing.
- NULL is returned.
- The kernel automatically frees tasks which have been launched when they
- exit.
- When a task is freed, all the resources it allocated using standard Xisop
- functions are automatically freed back to the system as well.
- The exception is when the task was setup with the
-\emph on
-TS_SHARED
-\emph default
- flag, where the memory is only freed when all tasks sharing that memory
- pool ended.
- This does not affect message ports, signals, devices, libraries or handlers,
- however, which are released by each task, always, as they exit.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-task_start(task_t\SpecialChar ~
-*task)
-\emph default
- Launches
-\emph on
-task
-\emph default
- which should have previously been allocated using
-\emph on
-task_alloc()
-\emph default
-.
- The task is moved to the ready queue and will start executing as soon as
- the current task yields calling
-\emph on
-_yield()
-\emph default
- or is preempted by the scheduler.
- The delay for it to actually execute also depends on the relative priority
- of the tasks on the ready queue and their current credits.
- If synchronization is needed with it's startup, signals or messages can
- be used.
- TRUE is returned on success, or FALSE if the task is invalid, or was already
- launched.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-task_end(task_t\SpecialChar ~
-*task)
-\emph default
- Immediately forces termination of the specified
-\emph on
-task
-\emph default
-, which may consist of the current task, or of another task.
- The task is cleanly freed as if it exited itself.
- Can also be used on a task which is locked on the waiting queue, perhaps
- waiting for events it will never receive.
- When a task ends and needs to be freed, it is moved in a queue for a dedicated
- task called the reaper, which chores consist of freeing all resources related
- to each task and the tasks themselves on it's own scheduler CPU time slices.
- Other tasks and the kernel are then releaved from that work.
- When a task ends it will soon automatically be freed by Xisop, and the
- resources it allocated are automatically freed as well.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_yield(task_t\SpecialChar ~
-*task)
-\emph default
- Permits a currently running task to immediately yield control back to the
- scheduler to allow other tasks to have an immediate opportunity to execute,
- rather than leaving the preemtive scheduler interrupt it.
- The task is not moved to the wait queue, and will have an opportunity to
- resume again very soon.
-
-\emph on
-task
-\emph default
- consists of a suggestion to which task to delegate control to, which should
- also currently be in the ready queue.
- When
-\emph on
-task
-\emph default
- is invalid or NULL, the scheduler is left to decide which task to execute
- next.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-task_sleep(task_t\SpecialChar ~
-*task,\SpecialChar ~
-u_int32_t\SpecialChar ~
-flags,\SpecialChar ~
-task_t\SpecialChar ~
-*yield)
-\emph default
- Allows to immediately switch the specified task (which may also be the
- currently running task) to the waiting queue.
- The task will be unable to execute again until it is specifically moved
- back again on the ready queue by calling
-\emph on
-task_wakeup()
-\emph default
- on it with at least one of the same bits in
-\emph on
-flags
-\emph default
-.
- This can be useful when custom interrupt attached code hooks need to synchroniz
-e tasks and such and that signals and message ports are not desired.
- If the task being put to sleep consists of the current task,
-\emph on
-yield
-\emph default
- can specify another optional ready task to run immediately, if non-NULL.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-task_wakeup(task_t\SpecialChar ~
-*task,\SpecialChar ~
-u_int32_t\SpecialChar ~
-flags)
-\emph default
- Immediately awakes the specified task if it currently is sleeping in the
- waiting queue.
- This is normally used after a
-\emph on
-task_sleep()
-\emph default
- call was made on the task.
- However, it is possible to use this function to wake a task which is waiting
- for a signal as well if
-\emph on
-TSF_SIGNAL
-\emph default
- flag is specified.
- This is normally used by custom interrupt hooks which need to synchronize
- tasks, when they cannot use signals or message ports.
- They then can share a
-\emph on
-fifo_t
-\emph default
- buffer, or custom
-\emph on
-list_t
-\emph default
- queue, with the wanted tasks and cause them to awake and sleep at will
- for instance.
- The task is only awaken if at least one bit supplied in
-\emph on
-flags
-\emph default
- matches one of the bits of the
-\emph on
-flags
-\emph default
- which were used at
-\emph on
-task_sleep()
-\emph default
-.
- TRUE is returned if the task could be awaken, or FALSE if the flags do
- not permit to awake it or another problem occurs.
-\layout Standard
-
-The current sleeping flags are described as follows (as defined in <
-\emph on
-src/common/kernel/scheduler.h
-\emph default
->) :
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-TSF_KERNEL
-\emph default
- Kernel-reserved, used by the task reaper for instance.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-TSF_SIGNAL
-\emph default
- Task is waiting for the reception of at least of one of the signal bits
- in
-\emph on
-task_t->sigwait
-\emph default
-.
- Internally used bu the signal subsystem.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-TSF_CUSTOM
-\emph default
- As the name implies, may be used by user tasks.
-\layout Standard
-
-Here are then described the scheduler control functions:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-priority_t\SpecialChar ~
-task_getpriority(task_t\SpecialChar ~
-*task)
-\emph default
- Obtains the current running priority level of
-\emph on
-task
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-priority_t\SpecialChar ~
-task_setpriority(task_t\SpecialChar ~
-*task,\SpecialChar ~
-priority_t\SpecialChar ~
-p)
-\emph default
- Permits to change the running priority of
-\emph on
-task
-\emph default
- to
-\emph on
-p
-\emph default
-.
- Returned is the previous priority of
-\emph on
-task
-\emph default
- which could be used to restore it.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-sched_disable(void)
-\emph default
- Allows to disable the preemptive scheduler, which will only be re-enabled
- when a corresponding number of
-\emph on
-sched_enable()
-\emph default
- have been called, as the scheduler lock consists of a recursive
-\emph on
-_rlock_t
-\emph default
-.
- It is important to not call
-\emph on
-yield()
-\emph default
-,
-\emph on
-port_wait(), port_send(), signal_send()
-\emph default
- or
-\emph on
-signal_wait()
-\emph default
- when the scheduler is disabled, because it would prevent the task from
- ever being awaken again.
- Obviously, this call should be used with care, if any.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-sched_enable(void)
-\emph default
- Re-enables the scheduler if the same number of instances of this function
- matched with the previous
-\emph on
-sched_disable()
-\emph default
- calls.
-\layout Subsubsection
-
-Locks and synchronization primitives
-\layout Standard
-
-Other synchronization systems such as signals and message ports are later
- described in a next section.
- However, these are also very useful synchronization primitives which Xisop
- provides to both kernelspace and userspace software:
-\layout Paragraph
-
-Exclusive access locks
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_lock_init(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Initializes an exclusive access a lock.
- This has to be used before using other functions
-\emph on
-lock
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_lock_acquire(_lock_t\SpecialChar ~
-*lock)
-\emph default
- This function is not multitasking-friendly in that the current task loops
- in an endless loop until exclusive obtention of the
-\emph on
-_lock_t
-\emph default
- is acquired.
- Tasks should normally use
-\emph on
-lock_acquire()
-\emph default
- instead.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_lock_release(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Immediately releases the exclusive
-\emph on
-_lock_t
-\emph default
-
-\emph on
-lock
-\emph default
- to allow another locker to obtain it.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-_lock_try(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Attempts to exclusively acquire
-\emph on
-lock
-\emph default
-, but returns immediately with FALSE if it cannot.
- TRUE is returned on success.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-_lock_available(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Returns TRUE if there currently are no locker on
-\emph on
-lock
-\emph default
-, but does not attempt to lock it.
- It is unsafe to attempt to implement
-\emph on
-_lock_try()
-\emph default
- using this function, obviously.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-lock_acquire(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Similarily to
-\emph on
-_lock_acquire()
-\emph default
-, locks execution of the current task until
-\emph on
-lock
-\emph default
- is exclusively obtained.
- This function is however better according to multitasking as it immediately
- yields CPU time to allow the current locker to free it as soon as possible
- for the current task to own it.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-lock_release(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Identical to
-\emph on
-_lock_release()
-\emph default
-, made to match
-\emph on
-lock_acquire()
-\emph default
- for consistency.
-\layout Paragraph
-
-Recursive locks
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_rlock_init(_rlock_t\SpecialChar ~
-*lock)
-\emph default
- Initializes a recursive lock of type
-\emph on
-_rlock_t
-\emph default
-.
- This needs to be performed before calling other recursive lock operations
- on
-\emph on
-lock
-\emph default
-.
- Recursive locks are different from exclusive locks in that there can be
- any number of concurrent lockers at the same time.
- They are usually used for systems which execute at intervals, like the
- Xisop scheduler and interrupt facilities which also make use of this system.
- They can then make sure to be the only locker before performing their critical
- work.
- This way arbitrary tasks may disable the facilities safely by locking the
- lock, and unlocking it when they are done with their critical section.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_rlock_acquire(_rlock_t\SpecialChar ~
-*lock)
-\emph default
- Locks recusively the
-\emph on
-lock
-\emph default
-
-\emph on
-_rlock_t
-\emph default
-.
- This basically atomically increases the lockers counter of the lock, and
- never fails.
- It always returns immediately.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_rlock_release(_rlock_t\SpecialChar ~
-*lock)
-\emph default
- Releases the
-\emph on
-lock
-\emph default
-
-\emph on
-_rlock_t
-\emph default
-.
- Note that the same number of release operations must be performed on
-\emph on
-lock
-\emph default
- for it to become available again.
- This only atomically decreases the lockers counter of the lock, and returns
- immediately.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-_rlock_trylock(_rlock_t\SpecialChar ~
-*lock)
-\emph default
- If the lock is free/available, that is, no current lockers exist on
-\emph on
-lock
-\emph default
-, this function atomically locks it and returns TRUE immediately.
- Otherwise it returns FALSE and returns immediately, in which case the lock
- is left in it's original state before the call.
- This can be very useful for an event-driven system which needs to make
- sure that the lock is free to perform it's tasks, but also needs to prevent
- it's own recursion.
- Obviously, if it returned TRUE,
-\emph on
-_rlock_release()
-\emph default
- is expected to be called when done.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-_rlock_available(_rlock_t\SpecialChar ~
-*lock)
-\emph default
- Returns FALSE if there are any lockers on
-\emph on
-lock
-\emph default
-, or TRUE if the lock is currently free from any lockers.
- This however only performs the check, and does not change the lock state.
- It is unsafe to use this function along with
-\emph on
-_rlock_acquire()
-\emph default
- to implement a
-\emph on
-_rlock_trylock()
-\emph default
- replacement as it would not be atomic.
-\layout Paragraph
-
-Read/Write locks
-\layout Standard
-
-These locks internally consist of a combination of exclusive and recursive
- locks.
- They are ideal for instance to protect synchronization of resources where
- multiple readers are allowed but that exclusive access is required for
- write operations.
- There currently is provided no way to upgrade a currently held shared access
- lock to an exclusive lock, or to downgrade an exclusively held lock to
- a shared access lock, other than releasing the lock and re-locking it.
- However, if this proves necessary in the future, we shall implement these
- features.
- These locks are especially adequate to protect resources which are frequently
- accessed in read-only mode, but which occasionally need to be updated,
- which obviously requires write/exclusive access, which is the case with
- several Xisop system lists, and these locks are then used by the involved
- kernel functions as well.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-rwlock_init(rwlock_t\SpecialChar ~
-*lock)
-\emph default
- Initializes
-\emph on
-lock
-\emph default
-, which is necessary before using any other
-\emph on
-rwlock_
-\emph default
-*
-\emph on
-()
-\emph default
- function on it.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-rwlock_acquire(rwlock_t\SpecialChar ~
-*lock,\SpecialChar ~
-bool\SpecialChar ~
-exclusive)
-\emph default
- Locks the current task until the
-\emph on
-lock
-\emph default
-
-\emph on
-rwlock_t
-\emph default
- can be exclusively accessed (for read and/or writing access) if
-\emph on
-exclusive
-\emph default
- is TRUE, or until a shared recursive access is obtained (for read-only
- access) if
-\emph on
-exclusive
-\emph default
- is FALSE.
- Is multitasking-safe.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-rwlock_release(rwlock_t\SpecialChar ~
-*lock)
-\emph default
- Releases the access on
-\emph on
-lock
-\emph default
- which was obtained using
-\emph on
-rwlock_acquire()
-\emph default
- or
-\emph on
-rwlock_try()
-\emph default
-.
- Whether this access was exclusive or shared is internally remembered.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-rwlock_try(rwlock_t\SpecialChar ~
-*lock,\SpecialChar ~
-bool\SpecialChar ~
-exclusive)
-\emph default
- Very similar to
-\emph on
-rwlock_acquire()
-\emph default
- but always immediately returns with FALSE if the access cannot be obtained
- immediately.
- TRUE is returned with the lock held on success.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-rwlock_upgrade(rwlock_t\SpecialChar ~
-*lock)
-\emph default
- Permits to upgrade an already obtained shared lock to an exclusive lock.
- The current task may sleep as required until all shared lockers free their
- lock.
- The caller
-\emph on
-MUST
-\emph default
- already be holding the lock in shared mode.
- This function is most useful for operations such as check-and-modify, when
- read-only access is generally required to a resource, but that under certain
- conditions write access is needed to modify the resource after the check.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-rwlock_downgrade(rwlock_t\SpecialChar ~
-*lock)
-\emph default
- Conversely to
-\emph on
-rwlock_upgrade()
-\emph default
-, allows to downgrade an already obtained exclusive lock to a shared lock.
- The caller
-\emph on
-MUST
-\emph default
- already be holding this lock in exclusive mode.
- The usefulness of this function is questionable.
- Although provided, the kernel does not use it.
-\layout Paragraph
-
-System lists access
-\layout Standard
-
-Several system lists require internal
-\emph on
-rwlock_t
-\emph default
- for synchronization against corruption.
- In <
-\emph on
-common/kernel/main.h
-\emph default
-> are defined several macros which can be used to access those securely
- by kernel functions.
- The
-\emph on
-enum systables
-\emph default
- specifies various system lists which can be accessed using these methods,
- called
-\emph on
-SYSTABLE_
-\emph default
-*.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-SYSTABLE_RLOCK(enum\SpecialChar ~
-systables\SpecialChar ~
-table)
-\emph default
- Locks the
-\emph on
-rwlock_t
-\emph default
- of the specified
-\emph on
-table
-\emph default
-, in shared mode (read-only access).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-SYSTABLE_WLOCK(enum\SpecialChar ~
-systables\SpecialChar ~
-table)
-\emph default
- Locks the
-\emph on
-rwlock_t
-\emph default
- of the specified
-\emph on
-table
-\emph default
-, in exclusive mode (read/write access).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-SYSTABLE_UNLOCK(enum\SpecialChar ~
-systables\SpecialChar ~
-table)
-\emph default
- Unlocks the
-\emph on
-rwlock_t
-\emph default
- of the specified
-\emph on
-table
-\emph default
-, which should have previously been locked using
-\emph on
-SYSTABLE_RLOCK()
-\emph default
- or
-\emph on
-SYSTABLE_WLOCK()
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-SYSTABLE_UPGRADE(enum\SpecialChar ~
-systables\SpecialChar ~
-table)
-\emph default
- Allows a caller which
-\emph on
-MUST
-\emph default
- hold a shared read-only access lock (after using
-\emph on
-SYSTABLE_RLOCK()
-\emph default
-) to upgrade the lock to exclusive mode.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-hashtable_t\SpecialChar ~
-*SYSTABLE(enum\SpecialChar ~
-systables\SpecialChar ~
-table)
-\emph default
- Returns the
-\emph on
-hashtable_t
-\emph default
- pointer associated with the system list
-\emph on
-table
-\emph default
-.
- Obviously, this should only be used when the lock is held, and should only
- be used for the access corresponding to the currently obtained lock type.
-\layout Standard
-
-The following two functions, as opposed to macros, are implemented as C
- functions and perform boundary checking on the arguments, as they are provided
- for user tasks rather than for the kernel, for which the previous macros
- were designed:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-hashtable_t\SpecialChar ~
-*systable_lock(u_int32_t\SpecialChar ~
-systable,\SpecialChar ~
-bool\SpecialChar ~
-exclusive)
-\emph default
- Attempts to lock access to the system list
-\emph on
-systable
-\emph default
-, in exclusive or shared mode.
- A pointer to the
-\emph on
-hashtable_t
-\emph default
- is returned on success, or NULL otherwise.
- As usual, shared locks should be obtained when read-only access is performed
- on a list, but an exclusive lock is required if there is any need to modify
- a system list.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-systable_unlock(u_int32_t\SpecialChar ~
-systable)
-\emph default
- Unlocks a previously held lock for
-\emph on
-systable
-\emph default
-, which should have previously been obtained using
-\emph on
-systable_lock()
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-systable_upgrade(u_int32_t\SpecialChar ~
-systable)
-\emph default
- Upgrades a previously held lock for systable in shared mode to exclusive
- mode.
- The lock
-\emph on
-MUST
-\emph default
- previously be obtained in shared mode.
-\layout Subsubsection
-
-Signals
-\layout Standard
-
-Very few signals are reserved by the system, SIGTERM and SIGPOLL, respectively.
- As Xisop currently supports 32 different signals per task, this results
- in 30 user signals the applications programmer may play with.
- Signals are internally implemented as bits, and are not reliably queued,
- which means that although a task will always know that a particular signal
- was received, it cannot know if it occured more than once before it had
- the chance to process it.
- It ressembles alot to a hardware bus line, which can be activated by the
- other end, although our capacity to monitor a single signal state is dependent
- on the rate at which we run.
-\layout Standard
-
-Note that sending a SIGTERM signal to a task does not force it to exit.
- It merely consists of a request to exit to the task, and the task may ignore
- it or respect it.
-\layout Standard
-
-
-\emph on
-XXX 32 is no longer a fixed value now that signum_t and sigmask_t are abstracted
- and could be set by the port-specific code.
-\layout Standard
-
-Message ports are used when the need for reliable event queuing is met.
- The signals merely allow a task to sleep and be awaken on specified events,
- like a message arriving on a port, which internally uses a signal bit.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-signum_t\SpecialChar ~
-signal_alloc(void)
-\emph default
- Attempts to allocate an available signal bit from the current task.
- Returns the signal number allocated, or -1 if no more signals available.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-signal_free(sigmask_t\SpecialChar ~
-sigmask)
-\emph default
- Frees all specified signals in
-\emph on
-sigmask
-\emph default
-, which should previously have been obtained using
-\emph on
-signal_alloc()
-\emph default
-.
- If reserved signals are part of the supplied
-\emph on
-sigmask
-\emph default
-, those are ignored.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-sigmask_t\SpecialChar ~
-signal_wait(sigmask_t\SpecialChar ~
-sigmask,\SpecialChar ~
-task_t\SpecialChar ~
-*task)
-\emph default
- Suspends the current task until at least one of the supplied signals in
-
-\emph on
-sigmask
-\emph default
-, or a system reserved signal occurs.
- Returned is the mask of signals we received which caused an awakening.
- Multitasking-friendly applications usually spend most of their time suspended
- by this call, and are awakening to perform their operations asynchroneously,
- and go back to sleep as soon as possible, to allow other tasks to also
- respond and execute efficienty.
- However, because of the nature of the preemptive scheduler, a task which
- remains in running state will not prevent others from executing.
-
-\emph on
-task
-\emph default
-, which may be NULL, optionally specifies which task should be favored as
- a suggestion to the scheduler, which allows to implement cooperative tasks
- more efficiently.
- The current task is guaranteed to awake at the occurance of the specified
- signals, but will not be able to determine how many occurances of each
- signal occurred as no queueing is performed.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-signal_send(task_t\SpecialChar ~
-*task,\SpecialChar ~
-sigmask_t\SpecialChar ~
-sigmask)
-\emph default
- Atomically sends the supplied signals in
-\emph on
-sigmask
-\emph default
- to the specified
-\emph on
-task
-\emph default
-.
- The task, if suspended waiting for signals, is awaken if a signal it waits
- for is included in
-\emph on
-sigmask
-\emph default
-, and will have a chance to run.
- The delay elapsing between
-\emph on
-signal_send()
-\emph default
- and the task being able to process the signal depends on three factors:
-\begin_deeper
-\layout Itemize
-
-The speed at which the current task returns to sleep (using
-\emph on
-yield()
-\emph default
-,
-\emph on
-port_wait()
-\emph default
- or
-\emph on
-signal_wait()
-\emph default
-).
-\layout Itemize
-
-If it does not return to sleep, the current task will continue running until
- it is preempted by the scheduler, which is dependent on the scheduler timer
- interrupt frequency.
-\layout Itemize
-
-The priority of the tasks in the ready queue, that is, which are currently
- awake.
- This factor takes place after the other end was awaken, and before it gets
- an opportunity to actually execute.
-\end_deeper
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-sigmask_t\SpecialChar ~
-SIGMASK(signum_t\SpecialChar ~
-signum)
-\emph default
- Consists of a useful macro to convert a signal number (
-\emph on
-signum_t
-\emph default
-) to a signal mask (
-\emph on
-sigmask_t
-\emph default
-).
- Those may then be ORed together (using C '|' bitwise operator character)
- to represent multiple signals.
-\layout Subsubsection
-
-Message ports
-\layout Standard
-
-In the UNIX world, this is called Inter Process Communication (IPC).
- In the case of Xisop, we communicate between light-weight tasks, rather
- than between full fledged processes, using signals, datagram messages and
- connectionless, unidirectional ports.
- The Xisop port functions internally operate using signal primitives and
- message queues (in FIFO order).
- These are safe to call from userspace as well as kernelspace tasks.
- However, it is not safe to transfer messages among ports in an interrupt
- handler context without using
-\emph on
-_splhigh()
-\emph default
- for synchronization.
-
-\emph on
-XXX
-\layout Standard
-
-A Xisop message consists of an arbitrary sized object prefixed with a
-\emph on
-message_t
-\emph default
- structure, which is internally used for message queueing and replying informati
-on.
- Because the message is internally
-\begin_inset Quotes eld
-\end_inset
-
-moved
-\begin_inset Quotes erd
-\end_inset
-
- by only swapping pointers efficiently, memory copy operations are minimized.
- However, this also means that the original creator of the message, as well
- as the other end to which the message is passed, are responsible to synchronize
- operations properly among eachother on the message memory area (more informatio
-n on this below).
- The
-\emph on
-message_t
-\emph default
- header starts with a
-\emph on
-pnode_t
-\emph default
- internally, which means that the user can use
-\emph on
-pool_t
-\emph default
- primitives to efficiently and arbitrarily create and destroy messages.
- It also means that at their discretion the ends are able to internally
- queue the message in custom
-\emph on
-list_t
-\emph default
- lists when necessary (after
-\emph on
-port_get()
-\emph default
-, and taking care to unlink it before
-\emph on
-port_send()
-\emph default
-/
-\emph on
-port_reply()
-\emph default
-, obviously).
-\layout Standard
-
-Some care was made when implementing Xisop message ports to take in consideratio
-n tasks and or ports which may be destroyed before a message is replied
- back by the other end, or for cases where a public port address, after
- being obtained once, becomes invalid as the public service dies.
- Instead of implementing a higher overhead or less versatile unique port
- ID allocation, validation and lookup system, or using expensive string
- operations with a
-\emph on
-hashtable_t
-\emph default
- using
-\emph on
-hashtable_lookup()
-\emph default
- for every message send, the following techniques were implemented:
-\layout Itemize
-
-A special magic cookie is set on a valid, existing port.
- This means that when attempting to perform any operation on a
-\emph on
-port_t
-\emph default
- pointer which does not resolve to an actual, currently valid port, operation
- is refused to take place.
- When a message port is destroyed, that magic number is reset to zero, which
- renders it unusable.
-\layout Itemize
-
-Because an efficient
-\emph on
-pool_t
-\emph default
- is used to allocate and free back
-\emph on
-port_t
-\emph default
- objects, it would be possible for an intended reply port to be destroyed,
- and for another port, belonging to any task, to be created at the same
- address.
- To solve this issue, each port also comports an internal unique ID.
- When a message is sent to a port, from which a reply is expected, the
-\emph on
-message_t
-\emph default
- is made to remember both the reply
-\emph on
-port_t
-\emph default
- address and unique ID.
- When attempting to reply, the
-\emph on
-port_t
-\emph default
- validity is performed as usual via the magic number, and the unique ID
- is then evaluated for a match.
- This way, a reply message will never be queued back on an unexpected port,
- or to a nonexisting one which may just have died.
-\layout Itemize
-
-The unique ID supplied to a port only consists of an unsigned 32-bit integer,
- which is obtained from a global shared counter, which is incremented and
- used whenever required.
- This means that the odds of another
-\emph on
-port_t
-\emph default
- using the same address and ID within the delay of a send/reply are probably
- always none.
-\layout Itemize
-
-Using this method prevented restrictions on the maximum number of ports
- and tasks which can exist in Xisop.
- The overhead is also smaller than having to run through an array looking
- for an empty slot, and having to re-allocate the memory area to dynamically
- grow or shrink it, because without MMU support alot of memory copying would
- be required for those operations.
- Using a
-\emph on
-pool_t
-\emph default
- then is ideal for speed.
- Using a relatively fast lookup hash table would require a lookup to be
- performed before every message send, which was considered suboptimal when
- designing Xisop.
- Although
-\emph on
-port_find()
-\emph default
- uses this, it would have been absurd to need to perform this lookup before
- sending any message.
- Especially considering that locking must be used when accessing the system
- hash table for synchronization.
-\layout Standard
-
-Usually, the tasks should be written to be reliable and to synchronize well
- within eachother.
- However, with these precautions, supported by well written applications,
- the
-\emph on
-port_t
-\emph default
- system is always very reliable and predictable.
- Here are described the functions:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-port_t\SpecialChar ~
-*port_create(const\SpecialChar ~
-char\SpecialChar ~
-*name)
-\emph default
- Allows to create a message port on the current task.
- This internally also needs to allocate a signal bit using
-\emph on
-signal_alloc()
-\emph default
-, which is associated to the
-\emph on
-port_t
-\emph default
- for the
-\emph on
-port_wait()
-\emph default
- internals.
- NULL is returned on failure, or a pointer to the new
-\emph on
-port_t
-\emph default
- on success.
-
-\emph on
-name
-\emph default
- is optional and should be NULL if the port does not need to be advertized
- to the whole system.
- If other tasks need to find our port by name, then
-\emph on
-name
-\emph default
- will be usable with
-\emph on
-port_find()
-\emph default
- to locate it, and listing the system public ports would advertize it as
- well.
- Ports are generally private, except for special public services.
- The length of
-\emph on
-name
-\emph default
- will be truncated to 32 characters if it is longer.
- If a public port is created, but that another public port bears the same
- name alerady, the operation fails.
- The port name is case-sensitive.
- If the task exists without closing it's ports, they are automatically destroyed
- by the kernel.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-port_t\SpecialChar ~
-*port_destroy(port_t\SpecialChar ~
-*port)
-\emph default
- Destroys a message port of the current process, which was created using
-
-\emph on
-port_create()
-\emph default
-.
- The internally allocated signal bit and memory are released back to the
- system.
- If any queued messages exist, the queue is lost, but the
-\emph on
-message_t
-\emph default
- objects are untouched, as they remain the responsibility of the application
- who created them.
- NULL is returned.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-port_t\SpecialChar ~
-*port_find(const\SpecialChar ~
-char\SpecialChar ~
-*name)
-\emph default
- Allows to locate a public message port by name.
- If the port can be found, it's address is returned.
- NULL is returned otherwise.
- Because this needs to lookup through a system's public ports hash table
- it needs to lock it internally using a
-\emph on
-rwlock_t
-\emph default
- in shared access mode.
- The port name is case-sensitive.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-port_send(port_t\SpecialChar ~
-*port,\SpecialChar ~
-port_t\SpecialChar ~
-*replyport,\SpecialChar ~
-message_t\SpecialChar ~
-*message)
-\emph default
- Queues the supplied
-\emph on
-message_t
-\emph default
- to the specified
-\emph on
-port_t
-\emph default
- in FIFO order, and internally
-\emph on
-send_signal()
-\emph default
- the associated process with the internal port signal.
- This means that the other process could be sleeping using
-\emph on
-signal_wait()
-\emph default
- or
-\emph on
-port_wait()
-\emph default
- and will be awakened so that it may process the message.
- The supplied message then becomes owned by the other end, and should be
- left alone by the current task until a reply be obtained from the other
- side on the
-\emph on
-message_t
-\emph default
-'s
-\emph on
-replyport
-\emph default
-.
- This
-\emph on
-port_send()
-\emph default
- and
-\emph on
-port_reply()
-\emph default
- mechanism serves for synchronization.
- This means that normally,
-\emph on
-replyport
-\emph default
- is supplied the address of a local
-\emph on
-port_t
-\emph default
- used to receive results from the task we send messages to via it's own
-
-\emph on
-port_t
-\emph default
-.
- TRUE is returned on success, or FALSE if the message could not be delivered
- (in which case the supplied
-\emph on
-port_t
-\emph default
- address is probably invalid and a
-\emph on
-port_find()
-\emph default
- operation can be used again to attempt to locate the port, if it consists
- of a public port which should exist).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-message_t\SpecialChar ~
-*port_get(port_t\SpecialChar ~
-*port)
-\emph default
- If at least one message is available in the specified
-\emph on
-port_t
-\emph default
-, the first one is unqueued from it in FIFO order and the address to it
- is returned.
- Otherwise, NULL is returned.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-port_reply(message_t\SpecialChar ~
-*msg)
-\emph default
- Sends back the supplied
-\emph on
-message_t
-\emph default
- to the reply
-\emph on
-port_t
-\emph default
- of the task that previously sent it to us.
- This port corresponds to the
-\emph on
-replyport
-\emph default
- argument that was used at
-\emph on
-port_send()
-\emph default
-.
- Normally, the other end waits until we are done with the message, and we
- use this function to notify that it can safely continue to do whatever
- it wants with the
-\emph on
-message_t
-\emph default
- data.
- TRUE is returned on success, or FALSE if no
-\emph on
-replyport
-\emph default
- was set for the
-\emph on
-message_t
-\emph default
-, or that the reply port is no longer valid.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-port_t\SpecialChar ~
-*port_wait(port_t\SpecialChar ~
-**ports,\SpecialChar ~
-u_int32_t\SpecialChar ~
-num,\SpecialChar ~
-task_t\SpecialChar ~
-*task)
-\emph default
- Suspends the current task until a message is received through one of the
- supplied port(s) specified in the array of
-\emph on
-port_t
-\emph default
- pointers
-\emph on
-ports
-\emph default
-.
-
-\emph on
-num
-\emph default
- specifies the number of
-\emph on
-port_t
-\emph default
- pointers supplied in the ports array.
-
-\emph on
-task
-\emph default
- specifies a suggestion preference of which task to switch to, or NULL to
- let the scheduler choose.
- Returned is the pointer to the
-\emph on
-port_t
-\emph default
- which received the message, or NULL if a reserved signal was the awakening
- cause.
- If one of the ports already has queued messages the task will not be set
- to sleep and the aforementionned port will be returned immediately.
- If it is necessary to monitor other signals as well as port message arrivals,
- it is advised to manually assemble the
-\emph on
-sigmask_t
-\emph default
- from all monitored signals, including the ones associated to the monitored
- ports, and to use
-\emph on
-signal_wait()
-\emph default
- instead.
- Evaluating the resulting
-\emph on
-sigmask_t
-\emph default
- will permit to know which ports received messages, if any.
- Use of the
-\emph on
-PORT_SIGMASK()
-\emph default
- macro will then be useful.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-port_flush(port_t\SpecialChar ~
-*port)
-\emph default
- Unqueues all pending messages in the supplied
-\emph on
-port_t
-\emph default
-, if any.
- This can be useful to discard unused
-\emph on
-port_reply()
-\emph default
- results which are obtained where no result codes are needed, rather than
- using a
-\emph on
-while()
-\emph default
- loop of
-\emph on
-port_get()
-\emph default
- statements.
- Also useful before destroying ports when desired.
- Returns TRUE on success, or FALSE if the port is invalid.
- Should obviously not be used on other tasks' ports, only ours.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-sigmask_t\SpecialChar ~
-PORT_SIGMASK(port_t\SpecialChar ~
-*port)
-\emph default
- Is useful when it is necessary to know which signal bit is associated with
- a particular port.
- The returned
-\emph on
-sigmask_t
-\emph default
- can be ORed with the other masks to provide to
-\emph on
-signal_wait()
-\emph default
-.
- This way, it is possible for a task to monitor the state of other signals
- while at the same time monitoring message ports for events.
- This macro, just like
-\emph on
-SIGMASK()
-\emph default
-, should be used instead of expressions such as
-\emph on
-(1L << n)
-\emph default
- both for code obviousness and backwards compatibility, because the number
- of available signals could eventually grow.
-\layout Subsubsection
-
-The special SIGPOLL signal and the poll port
-\layout Standard
-
-
-\emph on
-XXX needs to be rethinked and re-written :)
-\layout Standard
-
-As there only are 30 user signals, and that a message port is usually associated
- with a signal, it may be useful to assign more than one message port to
- a single signal, or process more than one event using a single signal and
- message port.
-\layout Standard
-
-The SIGPOLL signal is reserved for just that, and each task always has a
- message port associated with this signal.
- Through this port can be sent various type of messages for more than one
- event the task may want to be awakened for.
- This provides message multiplexing, while the method for multiple message
- ports using the same signal would provide port multiplexing.
- The reason why message multiplexing was chosen as a standard in Xisop was
- because it requires less resources.
- The more ports there are on the system, the larger the system ports pool
- grows and it will never shrink for considerations described in the previous
- subsection.
-
-\emph on
-XXX
-\layout Standard
-
-This special system-reserved port can be used to transfer reliable signals,
- possibly emulating POSIX semantics, or to transfer many other types of
- events a task may all be waiting for.
- It also is useful to have a high number of concurrently opened high-level
- files, and implement filedescriptors.
- A special set of functions is provided by Xisop machine-independent layer
- to transfer messages through this port, and to evaluate the type of event
- that originated the message, along with message size.
- Because unlike for standard message ports where messages of a fixed size
- are usually transfered, the message size changes here, depending on the
- event type and message.
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsection
-
-Xisop memory management system
-\layout Standard
-
-Xisop provides three types of memory management primitives for the kernel.
- These are provided by the Xisop machine-independent layer.
- It also provides distinction between the various types of memory, when
- an architecture has more than one (i.e.
- Amiga CHIP and FAST RAM, peripheral memory, etc).
- Special care was used to prevent the memory management system from having
- to disable interrupts (which may be very useful when the
-\emph on
-_FACILITY_SCHEDTIMER
-\emph default
- consists of the only timing source for an architecture and needs to be
- as reliable as possible).
- A
-\emph on
-_lock_t
-\emph default
- is internally used by the system page primitives to sychronize access to
- the system page pools.
- This also avoids having to export them as system calls via traps.
-\layout Subsubsection
-
-Page primitives
-\layout Standard
-
-The first level system consists of page allocation and freeing.
- The page size is dependent on architecture when MMU is concerned, however
- within Xisop which provides it's own memory management, a page is typically
- 4096 bytes.
- Primitives are provided to allocate a single page, or number of physically
- contiguous pages, and to free back one page, or a number of contiguous
- pages.
- At system initialization, the page pools are initiated to provide access
- to the physical memory areas, and are classified by memory type.
- There are a few functions also defined by Xisop which allow to add physical
- memory areas to the page pools, and specify their type, so that the architectur
-e-dependent initialization code be simpler, and also that at runtime it
- be possible to attach new RAM which was mapped from external devices, possibly
- hot-pluggable ones such as PCMCIA memory cards, etc.
- Here are described the dynamic memory linking functions.
- Note that these functions internally use a
-\emph on
-_lock_t
-\emph default
- which is acquired to access the system pages pools.
- This means that they are generally safe to call anytime, but from interrupt
- handler context.
-
-\emph on
-XXX
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-mchunk_t\SpecialChar ~
-*mchunk_init(void\SpecialChar ~
-*mem,\SpecialChar ~
-size_t\SpecialChar ~
-size)
-\emph default
- Internally prepares the supplied memory block to pages which can later
- be linked into the system.
- The internal control structures are setup in hidden data which will not
- be added into the public pages.
- This is dependent on
-\emph on
-_PAGE_SIZE
-\emph default
-, which should be defined by the port-specific <
-\emph on
-port/support.h
-\emph default
-> headerfile.
- NULL is returned if the supplied memory area was too small to be split
- into pages and to store the necessary control data.
- Otherwise, a pointer to the
-\emph on
-mchunk_t
-\emph default
- is provided.
- After using this call, the memory area should never be manipulated anymore,
- other than using other Xisop standard calls, unless it is known that the
- area is not linked into the system pages.
- The supplied memory is expected to be physical contiguous memory.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-mchunk_attach(int\SpecialChar ~
-memtype,\SpecialChar ~
-mchunk_t\SpecialChar ~
-*mchunk)
-\emph default
- Allows to link an
-\emph on
-mchunk_t
-\emph default
- which was previously prepared using
-\emph on
-mchunk_init()
-\emph default
-, to the system
-\emph on
-ppool_t
-\emph default
- associated with the specified memory type.
- After this is performed, it is possible to allocate memory pages from this
- memory chunk until it be unlinked using
-\emph on
-mchunk_detach()
-\emph default
-.
- This depends on
-\emph on
-_MEM_MAX
-\emph default
-, which is defined by the port specific <
-\emph on
-port/support.h
-\emph default
-> headerfile.
- TRUE is normally returned, unless the chunk is seen to already have been
- attached, or that the supplied memory type is invalid, in which case FALSE
- is returned.
- It is possible to setup and append as many
-\emph on
-mchunk_t
-\emph default
- as wanted.
- For instance, the port-specific code which sets up the kernel pages for
- available memory may need to call
-\emph on
-mchunk_init()
-\emph default
- and
-\emph on
-mchunk_attach()
-\emph default
- multiple times, one for each contiguous memory block.
- It is safe to call this function to attach new memory at system runtime,
- anytime.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-mchunk_detach(int\SpecialChar ~
-memtype,\SpecialChar ~
-mchunk_t\SpecialChar ~
-*mchunk)
-\emph default
- Permits to safely detach from the system an
-\emph on
-mchunk_t
-\emph default
- which previously was attached using
-\emph on
-mchunk_attach()
-\emph default
-.
- TRUE is returned on success, or FALSE if the memory type is invalid, if
- the supplied
-\emph on
-mchunk_t
-\emph default
- was not currently attached, or if any memory from that
-\emph on
-mchunk_t
-\emph default
- is still currently allocated, in which case the chunk is not unliked.
-\layout Standard
-
-The first level of allocation primitives allow to allocate and free one
- or multiple contiguous pages of physical memory to and from the system
- pools (
-\emph on
-ppool_t
-\emph default
- of each memory type).
- Internally, the list of linked
-\emph on
-mchunk_t
-\emph default
- for a memory type is ran through.
- These functions also acquire the system pools safety lock while working
- on the system memory pools:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-page_t\SpecialChar ~
-*pages_alloc(int\SpecialChar ~
-memtype,\SpecialChar ~
-u_int32_t\SpecialChar ~
-many,\SpecialChar ~
-bool\SpecialChar ~
-zero)
-\emph default
- Allocates
-\emph on
-many
-\emph default
- contiguous memory pages of
-\emph on
-_PAGE_SIZE
-\emph default
- bytes, of the supplied
-\emph on
-memtype
-\emph default
-, which can be
-\emph on
-_MEM_ANY
-\emph default
- in which case all memory types are tried sequencially, favoring the first
- memory type to the last.
- A
-\emph on
-page_t
-\emph default
- pointer is returned which can be used to access the memory area via
-\emph on
-page_t->address
-\emph default
-, or to free back the memory using
-\emph on
-pages_free()
-\emph default
-.
- NULL is returned if not enough memory can be found to satisfy the request.
- If
-\emph on
-zero
-\emph default
- is TRUE and pages could be allocated, the memory area is zeroed using
-\emph on
-pageclr()
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-pages_free(page_t\SpecialChar ~
-*pages)
-\emph default
- Releases to the system one or more contiguous memory pages which previously
- were allocated using
-\emph on
-pages_alloc()
-\emph default
-.
- TRUE is returned on success, or FALSE if the supplied pointer was NULL,
- or if the
-\emph on
-page_t
-\emph default
- was already freed back.
-\layout Subsubsection
-
-Simple object pool primitives
-\layout Standard
-
-The second level consists of a pool of fixed sized entities, the
-\emph on
-pool_t
-\emph default
-.
- A pool can be pre-allocated once, with a fixed maximum number of elements,
- setup to grow automatically when new elements are needed, internally allocating
- and preparing new pages as required, and can optionally also shrink smaller
- automatically, releasing back to the system the unused pages.
- When a pool is about to shrink, it evaluates statistics about the number
- of pages the pool normally holds on average, so that primitives to free
- the page are not called too often unnecessarily.
- Such pages are buffered by the pool for future use, if they are expected
- to be reclaimed back soon.
- Using these functions is more efficient in speed and memory than using
- page allocation primitives, calls to which it attempts to minimize, while
- filling each page of memory with the most objects possible.
-\layout Standard
-
-A C structure should be defined for the type of object which is to be allocated,
- and the first element of that structure should consist of a
-\emph on
-pnode_t
-\emph default
- element.
- These functions, unlike page allocation ones, do not synchronize if multiple
- tasks or interrupt handlers share a
-\emph on
-pool_t
-\emph default
-.
- The caller should therefore provide synchronization whenever necessary.
- However, if the
-\emph on
-pool_t
-\emph default
- allocates or frees system pages, the
-\emph on
-pages_alloc()
-\emph default
- and
-\emph on
-pages_free()
-\emph default
- functions are used which internally use synchronization to ensure that
- the system pools do not corrupt.
- These functions are mostly for kernel use.
- If used from userspace, these pools will not be automatically freed back
- to the system unless the tasks specifically destroys them before exiting.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-pool_init(pool_t\SpecialChar ~
-*p, u_int32_t\SpecialChar ~
-stepp, u_int32_t\SpecialChar ~
-minp, u_int32_t\SpecialChar ~
-maxp, size_t\SpecialChar ~
-node
-size, int\SpecialChar ~
-memtype)
-\emph default
- Initializes the supplied
-\emph on
-pool_t
-\emph default
- object
-\emph on
-p
-\emph default
- to be used to allocate objects of
-\emph on
-nodesize
-\emph default
- length (size which should include the
-\emph on
-pnode_t
-\emph default
- element).
- This size should be equal or smaller than
-\emph on
-_PAGE_SIZE
-\emph default
- *
-\emph on
-stepp
-\emph default
- / 2.
-
-\emph on
-stepp
-\emph default
- specifies how many contiguous pages should be allocated and freed at once
- using
-\emph on
-pages_
-\emph default
-*
-\emph on
-()
-\emph default
- primitives, when required.
- If
-\emph on
-stepp
-\emph default
- appears too small to fit a useful number of nodes in a
-\emph on
-page_t
-\emph default
-, it will be automatically grown until it reaches a maximum of 8, after
- which FALSE will be returned for failure (although there is no such limit
- for the
-\emph on
-stepp
-\emph default
- argument itself when manually set high enough).
- Each
-\emph on
-page_t
-\emph default
- is then internally split into objects.
-
-\emph on
-minp
-\emph default
- tells the minimum number of
-\emph on
-page_t
-\emph default
- which should always remain into the
-\emph on
-pool_t
-\emph default
-, or 0 if it is allowed to shrink totally when it needs no memory.
- The pool will initially allocate those at initialization, and it will never
- shrink smaller during it's lifetime, if non-zero.
-
-\emph on
-maxp
-\emph default
- similarily specifies the maximum number of
-\emph on
-page_t
-\emph default
- which the
-\emph on
-pool_t
-\emph default
- should eventually grow to, or 0 for no limit.
- For instance, using
-\emph on
-minp
-\emph default
- and
-\emph on
-maxp
-\emph default
- of 0 allows the pool_t to dynamically grow and shrink as necessary and
- it could eventually use all available RAM.
- Using a
-\emph on
-minp
-\emph default
- and
-\emph on
-maxp
-\emph default
- of 2 ensures that at least two
-\emph on
-page_t
-\emph default
- be initially allocated, which will never be freed back unless the
-\emph on
-pool_t
-\emph default
- is destroyed, and also tells that the pool should never allocate more pages.
- This can be useful in interrupt context or some situations where we do
- not want the system pool pages to be accessed.
- A fixed number of maximum object nodes will be available to allocate and
- free efficiently then.
-
-\emph on
-memtype
-\emph default
- specifies the memory type which this pool will use to allocate the pages.
- It is invalid to specify
-\emph on
-_MEM_ANY
-\emph default
- here.
- TRUE is returned on success, or FALSE on failure (invalid memory type or
- not enough available memory).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-pool_destroy(pool_t\SpecialChar ~
-*pool)
-\emph default
- Frees all memory currently being used by the specified
-\emph on
-pool_t
-\emph default
- and destroys the pool object, which can no longer be used by pool object
- allocation primitives unless it is re-initialized.
- Any currently allocated objects from the
-\emph on
-pool_t
-\emph default
- become invalid immediately and should therefore not be accessed anymore,
- they get freed as well.
- TRUE is returned on success, or FALSE if the supplied
-\emph on
-pool_t
-\emph default
- was not previously initialized successfully.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-pnode_t\SpecialChar ~
-*pool_alloc(pool_t\SpecialChar ~
-*pool,\SpecialChar ~
-bool\SpecialChar ~
-zero)
-\emph default
- Attempts to allocate a single object from the specified
-\emph on
-pool_t
-\emph default
-.
- The object size depends on the
-\emph on
-nodesize
-\emph default
- parameter which was supplied at
-\emph on
-pool_init()
-\emph default
-.
- A pointer to the object is returned on success, or NULL if not enough memory
- is available, either for the
-\emph on
-pool_t
-\emph default
-, if
-\emph on
-maxp
-\emph default
- was non-zero, or system memory.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-pnode_t\SpecialChar ~
-*pool_free(pnode_t\SpecialChar ~
-*node)
-\emph default
- Releases the specified object
-\emph on
-node
-\emph default
- which was previously allocated using
-\emph on
-pool_alloc()
-\emph default
-, back to the
-\emph on
-pool_t
-\emph default
- it belongs to.
- NULL is returned.
-\layout Subsubsection
-
-General purpose memory pools
-\layout Standard
-
-The third level memory management system, working with
-\emph on
-mpool_t
-\emph default
-, consists of a number of
-\emph on
-pool_t
-\emph default
-, adapted to serve most byte requirement requests, through more standard
- malloc() and free() like functions.
- For a system with a
-\emph on
-_PAGE_SIZE
-\emph default
- of 4096,
-\emph on
-_MPOOLSTEP
-\emph default
- of 1 and
-\emph on
-_MPOOLS
-\emph default
- of 7 and
-\emph on
-_MPOOLSTART
-\emph default
- of 16, there are 7
-\emph on
-pool_t
-\emph default
-, deserved to serve element sized as closely possible to the requested number
- of bytes (smallest nodesize 16 + sizeof(mnode_t)), and a
-\emph on
-list_t
-\emph default
- designed to hold
-\emph on
-page_t
-\emph default
- pointers to satisfy larger requests, which get rounded on page boundaries.
- An
-\emph on
-mpool_t
-\emph default
- is capable of serving requests for various memory types, including
-\emph on
-_MEM_ANY
-\emph default
-, where the first memory types are privileged over the last ones.
- Like their
-\emph on
-pool_t
-\emph default
- counterparts, these functions do not perform any special synchronization
- which may be necessary if an
-\emph on
-mpool_t
-\emph default
- was shared among several tasks.
- Here are the functions related to the
-\emph on
-mpool_t
-\emph default
-:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-mpool_init(mpool_t\SpecialChar ~
-*mpool)
-\emph default
- Initializes an
-\emph on
-mpool_t
-\emph default
- to accept allocation requests.
- TRUE is returned on success, or FALSE if there is a problem initializing
- the
-\emph on
-pool_t
-\emph default
- elements, or if the supplied
-\emph on
-mpool
-\emph default
- pointer is NULL.
- This function is dependent on the port-specific
-\emph on
-_MPOOLS
-\emph default
-,
-\emph on
-_MPOOLSTEP, _MPOOLSTART
-\emph default
- and the
-\emph on
-enum _memtypes
-\emph default
-holding
-\emph on
-_MEM_MAX
-\emph default
- which should have been defined in <
-\emph on
-port/support.h
-\emph default
->.
-
-\emph on
-_MPOOLS
-\emph default
- specifies the number of
-\emph on
-pool_t
-\emph default
- objects,
-\emph on
-_MPOOLSTEP
-\emph default
- the
-\emph on
-stepp
-\emph default
- argument to
-\emph on
-pool_init()
-\emph default
-,
-\emph on
-_MPOOLSTART
-\emph default
- the maximum bytesize request for the first
-\emph on
-pool_t
-\emph default
-, and
-\emph on
-_MEM_MAX
-\emph default
- the number of memory types supplied by the system.
- See the port-dependent section for more information on how to set these.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-mpool_destroy(mpool_t\SpecialChar ~
-*mpool)
-\emph default
- Frees all memory currently associated with the specified
-\emph on
-mpool_t
-\emph default
-, which should have previously been initialized by
-\emph on
-mpool_init()
-\emph default
-, and disables the
-\emph on
-mpool_t
-\emph default
-.
- Any pointers to memory which were obtained via
-\emph on
-_malloc()
-\emph default
- using this pool become invalid.
- No more requests will be allowed on this
-\emph on
-mpool_t
-\emph default
- unless re-initialized using
-\emph on
-mpool_init()
-\emph default
-.
- TRUE is returned, or FALSE if the
-\emph on
-mpool_t
-\emph default
- was already destroyed, or that the supplied pointer is NULL.
- It is also possible for this function to return FALSE without error, in
- the case where at least another task shares this
-\emph on
-mpool_t
-\emph default
-.
- (See the
-\emph on
-TS_SHARED
-\emph default
- flag to
-\emph on
-task_alloc()
-\emph default
-).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*_malloc(mpool_t\SpecialChar ~
-*mpool,\SpecialChar ~
-int\SpecialChar ~
-memtype,\SpecialChar ~
-size_t\SpecialChar ~
-size,\SpecialChar ~
-bool\SpecialChar ~
-zero)
-\emph default
- Attempts to allocate the requested
-\emph on
-size
-\emph default
- contiguous bytes from the supplied
-\emph on
-mpool
-\emph default
-, using memory of type
-\emph on
-memtype
-\emph default
-.
- _MEM_ANY is valid, and will cause any available memory type to be returned.
- A pointer is returned to a memory area holding the number of bytes that
- were requested, or NULL if there is not enough memory to satisfy the request,
- or if the parameters are wrong.
- If
-\emph on
-zero
-\emph default
- is TRUE, the memory area is cleared with 0x00 bytes.
- Automatic synchronization is performed if the
-\emph on
-mpool_t
-\emph default
- is shared by more than one task.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*_free(void\SpecialChar ~
-*mem)
-\emph default
- Frees back the memory to which the supplied
-\emph on
-mem
-\emph default
- points, to where it belongs.
- NULL is returned.
- Automatic synchronization is performed if the
-\emph on
-mpool_t
-\emph default
- is shared by more than one task.
-\layout Paragraph
-
-Kernel code
-\layout Standard
-
-Here are variants which can be used by kernel functions.
- Note that the kernel can use all the above previously mentionned functions
- as well, however for consistency it is good to use the following where
- appropriate.
- All of these, contrary to the previously described primitives, internally
- use exclusive locks as necessary to ensure the integrity of the system
- pools, so that disabling the scheduler is unnecessary:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*_kmalloc(int\SpecialChar ~
-memtype,\SpecialChar ~
-size_t\SpecialChar ~
-size,\SpecialChar ~
-bool\SpecialChar ~
-zero)
-\emph default
- Allocates general-purpose memory from the kernel memory pool, which should
- be released using
-\emph on
-kfree()
-\emph default
-.
- When a task uses this function the memory is not restored to the system
- automatically when it exists.
- Special syncronization is used internally so that it is safe to use by
- multiple tasks.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*_kfree(void\SpecialChar ~
-*mem)
-\emph default
- Frees back to the kernel memory pools memory which has previously been
- allocated by
-\emph on
-kmalloc()
-\emph default
-.
- Internal special synchronization is used.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*TMALLOC(int\SpecialChar ~
-memtype,\SpecialChar ~
-size_t\SpecialChar ~
-size)
-\emph default
- Equivalent to calling
-\emph on
-kmalloc(memtype, size, FALSE);
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*TCMALLOC(int\SpecialChar ~
-memtype,\SpecialChar ~
-int\SpecialChar ~
-number,\SpecialChar ~
-size_t\SpecialChar ~
-size)
-\emph default
- Equivalent to calling
-\emph on
-kmalloc(memtype, number * size, TRUE);
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*MALLOC(size_t\SpecialChar ~
-size)
-\emph default
- Identical to calling
-\emph on
-kmalloc(_MEM_ANY, size, FALSE);
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*CMALLOC(int\SpecialChar ~
-number,\SpecialChar ~
-size_t\SpecialChar ~
-size)
-\emph default
- Like calling
-\emph on
-_kmalloc(_MEM_ANY, number * size, TRUE);
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*FREE(void\SpecialChar ~
-*mem)
-\emph default
- Equivalent to
-\emph on
-_kfree(mem);
-\emph default
- but made to match all above macros.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*kmalloc(size_t\SpecialChar ~
-size)
-\emph default
- Like
-\emph on
-MALLOC()
-\emph default
- but implemented as an ANSI-C compliant function.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-kfree(void\SpecialChar ~
-*mem)
-\emph default
- Counterpart to
-\emph on
-kmalloc()
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-pnode_t\SpecialChar ~
-*spool_alloc(u_int32_t\SpecialChar ~
-pool)
-\emph default
- Allows to efficiently allocate a frequently used kernel object for which
- a special system pool exists.
-
-\emph on
-pool
-\emph default
- may consist of one of the
-\emph on
-POOL_
-\emph default
-* names which are defined in the
-\emph on
-enum _syspools
-\emph default
- in <
-\emph on
-src/kernel/memory.h
-\emph default
->.
- Automatic synchronization is performed.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-pnode_t\SpecialChar ~
-*spools_free(u_int32_t\SpecialChar ~
-pool,\SpecialChar ~
-pnode_t\SpecialChar ~
-*node)
-\emph default
- Made to free a system object which was previously allocated using
-\emph on
-spool_alloc()
-\emph default
-.
- Automatic synchronization is performed.
-\layout Paragraph
-
-User space tasks
-\layout Standard
-
-The following can be called by user tasks to allocate memory using their
- own memory pool, which is automatically released back to the system when
- they exit.
- They behave identically to the standard ANSI-C functions bearing the same
- name.
- They are implemented as functions rather than macros, for inclusion into
- the Xisop shared library.
- These are also safe to use in the case where more than one tasks are sharing
- an
-\emph on
-mpool_t
-\emph default
- (see the
-\emph on
-TS_SHARED
-\emph default
- flag to
-\emph on
-task_alloc()
-\emph default
-).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*malloc(size_t\SpecialChar ~
-size)
-\emph default
- Behaves identically to the standard ANSI-C
-\emph on
-malloc()
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*calloc(int\SpecialChar ~
-number,\SpecialChar ~
-size_t\SpecialChar ~
-size)
-\emph default
- Identical to ANSI-C
-\emph on
-calloc()
-\emph default
- semantics.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*realloc(void\SpecialChar ~
-*ptr,\SpecialChar ~
-size_t\SpecialChar ~
-size)
-\emph default
- Like ANSI-C
-\emph on
-realloc()
-\emph default
- semantics.
- Note that like the standard, the returned pointer can point to a new memory
- area, in which case the previous contents will have been copied over.
- The use of this function is generally discouraged against generally more
- efficient dynamic allocation techniques using linked lists.
- It is however provided for compatibility with ANSI-C.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-free(void\SpecialChar ~
-*ptr)
-\emph default
- Again like the ANSI-C
-\emph on
-free()
-\emph default
- function.
- Note that this function can also free memory which has been allocated using
-
-\emph on
-tmalloc()
-\emph default
- and
-\emph on
-tcmalloc()
-\emph default
-.
-\layout Standard
-
-ANSI-C however has no concept of multiple memory types, and as such
-\emph on
-tmalloc()
-\emph default
- had to be included.
-
-\emph on
-free()
-\emph default
- can be used to free back memory which they allocate still:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*tmalloc(int\SpecialChar ~
-memtype,\SpecialChar ~
-size_t\SpecialChar ~
-size)
-\emph default
- Like
-\emph on
-malloc()
-\emph default
- but allows to specify a Xisop port-dependent memory type.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*tcmalloc(int\SpecialChar ~
-memtype,\SpecialChar ~
-int\SpecialChar ~
-number,\SpecialChar ~
-size_t\SpecialChar ~
-size)
-\emph default
- Like
-\emph on
-calloc()
-\emph default
-, but can also be told the type of memory wanted.
-\layout Subsubsection
-
-A note about absolute memory allocation
-\layout Standard
-
-Xisop does not provide an allocation function which can be told the absolute
- memory location area desired.
- There are several reasons for this which this section attempts to explain.
-\layout Standard
-
-Xisop provides the necessary inter-task communications tools to prevent
- the need of using absolute memory addresses for rendez-vous among tasks.
- Moreover, it has the concept of devices, which can be left the task to
- perform synchronization among tasks which need to share a specific resource.
- Additionally, Xisop does not setup the MMU in a way to prevent user tasks
- from accessing any memory.
-\layout Standard
-
-This means that if absolute memory locations need to be reserved by the
- system, they normally should not be included into the general purpose memory
- pools.
- A user device task can then specifically handle those locations as necessary
- and provide multitasking-friendly access to them.
-\layout Standard
-
-For instance, the video memory should not be attached to the system memory
- pools by the port-specific code.
- Instead, a device task should be written and provided to allow safe access
- to the video hardware, and basic console support.
- Optionally, a handler can be provided, which even allows a higher abstraction
- to access the device.
- For instance, the device could supply basic resource allocation and access
- primitives, while the handler could support vt100 emulation over the device.
-\layout Standard
-
-In the case of a single-tasking application being developped around Xisop,
- once the scheduler is disabled there is no need for any special handling
- to access the wanted memory regions.
- The memory allocator is no longer necessary to use, even.
-\layout Standard
-
-Another consideration to realize is that if for instance video memory was
- part of the system memory pools, it could automatically be allocated by
- another task which was only requesting some memory.
- If we had support for reserved pages, then there would be no point in allocatin
-g them, also.
- Only general-purpose memory should be attached into the system.
- There exists the possibility of reserving a specific memory type for some
- memory however, if there is a need for specific regions to only be used
- by certain applications but that general purpose management primitives
- are still desired.
-\layout Standard
-
-Other than the Xisop message port system which allows tasks to share and
- synchronize arbitrary memory regions, it is possible for several tasks
- to inherently share a common memory pool, using the
-\emph on
-TS_SHARED
-\emph default
- flag to
-\emph on
-task_alloc()
-\emph default
-.
- This can in some circumstances be used if the memory resources are quite
- scarce (many tasks which only allocate few but various sized memory blocks
- of different memory types can waste quite a large amount of pages.
- Using the same pool would then permit the same pages to be used among the
- tasks for their similar allocation needs.
- Their allocated blocks generally consist of a page which is split in equally
- sized blocks).
- Moreover, in such a setup, one task which allocates a block of memory does
- not implicitly free it on exit, if other tasks are still running sharing
- the memory.
- Those blocks need to be explicitely freed unless all tasks exit to really
- be released back to the system.
- Moreover, special synchronization must be used by the tasks if they want
- to access the same memory addresses.
- This is usually done using an
-\emph on
-rwlock_t
-\emph default
-, or disabling the scheduler temporarily.
-\layout Subsection
-
-Xisop public interrupt abstraction facilities
-\layout Standard
-
-To allow Xisop architecture-specific devices to attach interrupt handler
- hooks, and for portable code to make use of a few basic interrupt facilities
- like timers, it is a good idea to provide a few access functions to allow
- this.
- The idea is to provide a facility which allows the kernel code, or userspace
- tasks, to run a piece of code once, a certain number of times, or indefinitely,
- as part of the low-level interrupt handling code.
- It thus should be possible to add and delete their code handlers from each
- of the wanted interrupt handlers.
- To provide this multi-purpose facility, the following system was developed,
- and almost entirely consists of portable common code.
-\layout Subsubsection
-
-Internals
-\layout Standard
-
-The following structure is defined in <
-\emph on
-common/kernel/exception.h
-\emph default
->:
-\layout Quote
-
-
-\emph on
-typedef struct _int_hook {
-\newline
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-pnode_t node;
-\newline
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-hookid_t id;
-\newline
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-u_int32_t skipcount, runcount;
-\newline
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-void (*code)(hookid_t, int, void *);
-\newline
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-\SpecialChar ~
-void *data;
-\newline
-} hook_t;
-\layout Standard
-
-Where
-\emph on
-node
-\emph default
- is used for internal linking,
-\emph on
-id
-\emph default
-consists of a unique ID for this facility,
-\emph on
-skipcount
-\emph default
- of the number of times this handler will execute before calling the
-\emph on
-code
-\emph default
- hook,
-\emph on
-runcount
-\emph default
- of the number of times the hook
-\emph on
-code
-\emph default
- will be called before it gets automatically deleted, or 0 if it should
- execute everytime this interrupt occurs.
-
-\emph on
-void\SpecialChar ~
-(*code)(hookid_t, int, void\SpecialChar ~
-*)
-\emph default
- consists of the C function to invoke when the event occurs.
- The abstracted arguments
-\emph on
-void
-\emph default
- pointer may serve any purpose the application wants.
-
-\emph on
-void\SpecialChar ~
-*data
-\emph default
- pointer to abstract user-defined data will be passed as argument to the
- called function.
- The
-\emph on
-hookid_t
-\emph default
- argument will consist of the ID of the
-\emph on
-hook_t
-\emph default
- into the facility which caused the call, and is made to be unique.
- The supplied
-\emph on
-int
-\emph default
- argument may be useless, or can serve to determine the origin of the interrupt,
- somewhat like the
-\emph on
-hookid_t
-\emph default
-, but will use a facility-specific semantics, which could include a key
- being pressed in the case of a keyboard interrupt, etc.
- The kernel uses an efficient memory
-\emph on
-pool_t
-\emph default
- to internally allocate and free these automatically when hooks are attached
- and detached from facilities.
-\layout Standard
-
-The following machine-independent function is also provided so that the
- port-specific code (from assembly or C) may easily order an execution of
- all attached hooks on a facility.
- The hook ids are made to be unique so that it is safe for the one who attached
- a hook to try to delete it, assuming that if it exists in the list, it
- cannot be any other hook supplied by other code (even if it expired and
- another hook replaced it internally).
- This consists of an interface for the port, not of the general user interface
- to the facilities:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-facility_exechooks(u_int32_t\SpecialChar ~
-facility,\SpecialChar ~
-int\SpecialChar ~
-origin)
-\emph default
- executes all attached code hooks of the specified
-\emph on
-facility
-\emph default
-, sequencially (if any).
- This means that for each existing hook, the corresponding function is called
- if it's
-\emph on
-skipcount
-\emph default
- is 0, or
-\emph on
-skipcount
-\emph default
- is simply decreased by 1 otherwise.
- When the hook is to be called, it is passed the corresponding user data
- pointer (which can be NULL) to it, the
-\emph on
-hookid_t
-\emph default
- id for the hook, and
-\emph on
-origin
-\emph default
- into the
-\emph on
-int
-\emph default
- argument.
- The hook is then evaluated for expiration if
-\emph on
-runcount
-\emph default
- was non-zero at insertion with
-\emph on
-hook_attach()
-\emph default
-, and automatically destroyed if it expires.
- The caller should normally disable the interrupt associated with the facility
- temporarily before calling this function, however the associated
-\emph on
-facility_t
-\emph default
- internally uses an
-\emph on
-_rlock_t
-\emph default
- to prevent self-recursion, lock which is also used by
-\emph on
-hook_attach()
-\emph default
- and
-\emph on
-hook_detach()
-\emph default
- for safety.
- This function is made for the port-specific code to call when the interrupt
- this facility is concerned with occurs.
- This function is dependent on
-\emph on
-_FACILITY_MAX
-\emph default
- and the
-\emph on
-enum _facilities
-\emph default
- which are port-specific in <
-\emph on
-port/support.h
-\emph default
->.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-facilities_init(void)
-\emph default
- is provided to ease initialization of the system facility arrays from the
- port-specific code.
- This function depends on the port-specific
-\emph on
-_FACILITY_MAX
-\emph default
- and the
-\emph on
-enum _facilities
-\emph default
- defined in <
-\emph on
-port/support.h
-\emph default
->.
- More informaton on port-specific initialization is provided in a next chapter.
-\layout Standard
-
-Decision was made to provide the above functions even though their functionality
- is simple for a few reasons.
- It prevents code duplication among ports, minimises assembly sections in
- machine-dependent sections, and they are known to work, providing the required
- functionality.
- Being abstracted, their internals may change over time affecting all ports
- simultaneously without requireing changes in the machine-specific sections.
- Moreover, they use an internal memory
-\emph on
-pool_t
-\emph default
- per
-\emph on
-facility_t
-\emph default
- for efficiency.
- Once initialized, those will never need to query the system page primitives,
- and are thus very efficient, as well as safe to use from interrupt context
- without special handling.
-\layout Standard
-
-Because these facilities are transparent to the Xisop microkernel itself,
- and are provided by port-specific code, although driven by common portable
- code, the interrupt sources are not required to internally correspond to
- actual hardware interrupts.
- The port-specific code is free to provide the wanted interrupt sources
- and facilities in the manner it wishes.
- As such they can be used for various hardware and sotfware event types.
-\layout Subsubsection
-
-User interface
-\layout Standard
-
-Here is now described the kernel user interface to manipulate custom interrupt
- hooks on the provided facilities.
- These are also machine-independent.
- They were implemented around
-\emph on
-_rlock_t
-\emph default
- and
-\emph on
-pool_t
-\emph default
- primitives in a way to make it possible for userland to access their functional
-ity without the need for system call traps.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-hookid_t\SpecialChar ~
-hook_attach(u_int32_t\SpecialChar ~
-facility, u_int32_t\SpecialChar ~
-skipcount, u_int32_t\SpecialChar ~
-runcount,
- void\SpecialChar ~
-(*code)(hookid_t,\SpecialChar ~
-int,\SpecialChar ~
-void\SpecialChar ~
-*), void\SpecialChar ~
-*data)
-\emph default
- Allows to append or insert a user supplied code hook to the wanted interrupt
- facility.
- The
-\emph on
-facility
-\emph default
- argument specifies what type of exception, trap or interrupt is wanted,
- and is port-specific.
- An example would be
-\emph on
-_FACILITY_VBLANK
-\emph default
-.
-
-\emph on
-code
-\emph default
- specifies which function to call as the user hook handler, which should
- never be NULL.
-
-\emph on
-data
-\emph default
- points to an optional user data block which will be passed back when calling
- the function handler, and can be NULL.
- The argument of type
-\emph on
-hookid_t
-\emph default
- which will be passed will consist of the unique ID representing this
-\emph on
-hook_t
-\emph default
- into the
-\emph on
-facility
-\emph default
-, while the
-\emph on
-int
-\emph default
- argument, which is facility-specific, could serve to determine the origin
- of the event if the facility serves several.
- The return value is 0 in the case of an error (unknown public facility),
- or a unique ID which can be used to eventually delete that particular hook.
- This is required to not correspond to the hook function address, since
- multiple hooks may call the same function if wanted.
- This ID is always unique to this facility.
- It is safe to attach a code hook function which can expire and then attempt
- to remove it using the supplied
-\emph on
-hookid_t
-\emph default
-.
- If it expired and another hook now uses the same
-\emph on
-hook_t
-\emph default
- address, it's
-\emph on
-hookid_t
-\emph default
- will still be different.
- The semantics of
-\emph on
-skipcount
-\emph default
- and
-\emph on
-runcount
-\emph default
- are explained in the internals above.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-hook_detach(u_int32_t\SpecialChar ~
-facility,\SpecialChar ~
-hookid_t\SpecialChar ~
-id)
-\emph default
- Permits to remove a user supplied hook on the wanted
-\emph on
-facility
-\emph default
-, with the specfied
-\emph on
-id
-\emph default
-.
- Returns TRUE if it could find and delete the hook, or FALSE if the hook
- did not exist, or no longer does (it may have expired).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-facility_disable(u_int32_t\SpecialChar ~
-facility)
-\emph default
- Disables all hooks of the specified facility temporarily.
- They cannot expire during the period they are suspended, and none will
- be executed, even when the interrupt source occurs.
- It does not disable the interrupt source.
- This system is recursive, in that the exact same number of calls to
-\emph on
-facility_enable()
-\emph default
- must be made to re-enable the facility.
- Note that it is safe to attach and detach hooks from a facility at any
- time, and that this function is only provided as a feature.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-facility_enable(u_int32_t\SpecialChar ~
-facility)
-\emph default
- Re-enables the specified facility which was previously suspended using
-
-\emph on
-facility_disable()
-\emph default
-, if the
-\emph on
-facility_t
-\emph default
- internal
-\emph on
-_rlock_t
-\emph default
- reaches 0 (that is all previous calls to
-\emph on
-facility_disable()
-\emph default
- were matched by a
-\emph on
-facility_disable()
-\emph default
-).
-\layout Subsubsection
-
-Common facilities
-\layout Standard
-
-Not all ports have all these facilities, and some may provide more.
- However, this consists of a guide so that facilities which are intended
- to provide the same functionality on the various architectures bear the
- same name.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_SCHEDTIMER
-\emph default
- This is the only facility which must be available on all ports.
- It usually executes at intervals governed by
-\emph on
-_SCHEDTIMER_HZ
-\emph default
-, a frequency defined in instances per second (hertz), which is defined
- in <
-\emph on
-port/support.h
-\emph default
->.
- Systems which only comport one hardware timer source will at least always
- have this timer facility available for multiple uses, although internally
- used by the scheduler.
- Disabling the scheduler does not disable the timer facility, as the scheduler
- lock consists of an
-\emph on
-_rlock_t
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_KEYBOARD
-\emph default
- As the name implies, this facility is concerned with keyboard key presses.
- The key code is usually returned in the
-\emph on
-int
-\emph default
- argument when calling the attached hooks.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_VBLANK
-\emph default
- This facility is very useful for graphic-oriented software which need to
- synchronize operations with the video refresh rate.
- The frequency of a vertical blank interrupt varies with the underlaying
- hardware, but usually consists of 50Hz for PAL (europe) and 60Hz for NTSC
- (american) systems.
- This is very useful to implement double buffering, and can also be used
- to synchronize audio events like music and sound effects with animations.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_TRAP
-\emph default
-* These type of facilities can be directly tied to user traps which may
- remain available, and associated to a related
-\emph on
-_cause()
-\emph default
- function to allow user tasks to both attach handlers to receive those events
- and call
-\emph on
-_cause()
-\emph default
- to trigger such events.
- However, because of the Xisop
-\emph on
-sys_custom()
-\emph default
- system call, the use of such facilities become questionable.
-
-\emph on
-XXX
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_FLOPPYSYNC
-\emph default
- Triggered when a floppy drive notifies that it found the sync code of a
- track.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_FLOPPYBLOCK
-\emph default
- Triggered when the floppy drive finished reading a requested block to memory.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_AUDIO
-\emph default
- Notification that an audio channel finished playing a sample, or starts
- looping back the supplied sample buffer again.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_BLITTERREADY
-\emph default
- Notification by a parallel hardware blitter that it is done with the requested
- operations and may now be ordered new instructions again.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_COPPER
-\emph default
- A hardware raster parrallel blitter originated interrupt
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_SERIAL
-\emph default
- A generic serial interrupt
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_SERIALRBF
-\emph default
- A serial Read Buffer Full interrupt
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_FACILITY_SERIALTBE
-\emph default
- A serial Transmit Buffer Empty interrupt
-\layout Standard
-
-Many other types of facilities may exist, although what should be taken
- as an example consists of the clear names that they are given, which directly
- reference to their origin as much as possible, while attempting to avoid
- cryptic names such as KBD for keyboard, etc.
- Where required, the label can be long enough as long as it remains meaningful.
-\layout Subsection
-
-Kernel statistics
-\layout Standard
-
-The
-\emph on
-src/common/kernel/statistic.
-\emph default
-(
-\emph on
-c
-\emph default
-|
-\emph on
-h
-\emph default
-) module defines primitives for statistic counters.
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsection
-
-Xisop binaries and executables
-\layout Standard
-
-The Xisop kernel currently has no ELF or a.out relocatable executable loader,
- and no custom format was implemented, which would require a GCC ld BFD
- backend to be written.
- This means that at current time, all the shared libraries, devices, handlers
- and user tasks need to be started by the kernel at startup.
- This is easily done however, but prevents the microkernel from using all
- it's features of attaching required components at runtime as wanted, although
- it was designed to eventually be able to do so efficiently.
- It however currently allows Xisop to be useable in embedded systems which
- have defined components, like for monolithic kernels.
-\layout Subsection
-
-Xisop devices
-\layout Standard
-
-Xisop devices generally consist of a medium-level backend to hardware-assisted
- services, such as keyboard input, tty output, RS-232 communication, etc.
- As such, they are generally architecture-dependent.
- Of course, machine-independent devices may be written as wanted, where
- the high-level handler interface is considered unadequate and that shared
- access to some kind of ressource is wanted, however.
-\layout Standard
-
-The Xisop devices are implemented around the message port system.
- There were two main reasons which determined this decision compared to
- using a shared library type system.
- First, the message receiving responsiveness and speed can be determined
- by the task priority, which allows the administrator to decide which devices
- and tasks to prioritize over others.
- Secondly, the message passing system already provides reliable FIFO queuing,
- and each message/request can be replied to when wanted, allowing a device
- to easily serve resources in a multitasking-friendly, asynchroneous manner
- to the simultaneous requesters.
-\layout Standard
-
-A device is thus implemented by a task, which decides to attach a system
- device node and then takes the responsibility to serve the expected requests.
- Each task may only attach one device to the system lists, and has to specify
- the device name and version which are used for other tasks to open the
- device.
- This means that a device name may have several simultaneous versions running
- on the system.
-\layout Standard
-
-Of course, there are cases where only a single device may control a specific
- hardware resource for instance, and in this case the versionning system
- becomes less useful, in which case version 0 is usually used.
- However, the version may still be useful if the various versions of the
- device had changes to the user interface.
- In this case, using the version is still useful, as opening using the wrong
- version (the one a task expects) will at least always fail cleanly with
- an error, rather than leaving the task to open a device which does not
- act as expected when sending requests.
-\layout Subsubsection
-
-User interface
-\layout Standard
-
-Here are described the device interface functions.
- Let's first present the functions which are intended for client tasks to
- access device server ones:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-device_t\SpecialChar ~
-*device_open(const\SpecialChar ~
-char\SpecialChar ~
-*name,\SpecialChar ~
-u_int32_t\SpecialChar ~
-version,\SpecialChar ~
-u_int32_t\SpecialChar ~
-unit)
-\emph default
- Allows the task to open the unit number
-\emph on
-unit
-\emph default
- of device
-\emph on
-name
-\emph default
- of version
-\emph on
-version
-\emph default
-.
- NULL is returned on failure, which can occur because of lack of memory,
- or if the specified device name of the specified version does not exist.
- Otherwise, a
-\emph on
-device_t
-\emph default
- handle pointer is returned, which may then be used at
-\emph on
-iorequest_init()
-\emph default
-.
- Device names are case-sensitive.
- If the task exists and that open devices have not been explicitely closed,
- the kernel automatically closes them.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-device_t\SpecialChar ~
-*device_close(device_t\SpecialChar ~
-*handle)
-\emph default
- Closes a device handle which previously was opened using
-\emph on
-device_open()
-\emph default
-.
- Always returns NULL.
- Any
-\emph on
-iorequest_t
-\emph default
- associated to this
-\emph on
-device_t
-\emph default
- may not be used anymore, as it becomes invalid, unless it be reinitialized
- again using
-\emph on
-iorequest_init()
-\emph default
- using a new
-\emph on
-device_t
-\emph default
- handle.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_init(iorequest_t\SpecialChar ~
-*message,\SpecialChar ~
-device_t\SpecialChar ~
-*handle,\SpecialChar ~
-port_t\SpecialChar ~
-*replyport)
-\emph default
- Initializes an
-\emph on
-iorequest_t
-\emph default
-
-\emph on
-message
-\emph default
-, which is necessary to use other
-\emph on
-iorequest_
-\emph default
-*
-\emph on
-()
-\emph default
- functions to perform device requests.
-
-\emph on
-handle
-\emph default
- specifies the
-\emph on
-device_t
-\emph default
- which will serve requests for this message during future requests, and
-
-\emph on
-replyport
-\emph default
- of a generally
-\emph on
-iorequest_t
-\emph default
--specific private port which was previously created, through which request
- result messages will be sent back to us by the device.
- The
-\emph on
-iorequest_t
-\emph default
- buffer is the responsibility of the task, just like
-\emph on
-port_t
-\emph default
-
-\emph on
-message_t
-\emph default
- are.
- Returns TRUE on success, or FALSE on failure (invalid arguments or out
- of memory).
- The device may optionally internally allocate device-specific additional
- data which will then attach to the
-\emph on
-iorequest_t
-\emph default
-.
- These will however be allocated on the current task's memory pool, and
- are therefore released automatically if the task exists.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_destroy(iorequest_t\SpecialChar ~
-*message)
-\emph default
- Invalidates the
-\emph on
-iorequest_t
-\emph default
-
-\emph on
-message
-\emph default
- which was previously initialized using
-\emph on
-iorequest_init()
-\emph default
-.
- As devices may internally allocate device-specific additional data and
- attach it to an
-\emph on
-iorequest_t
-\emph default
- at initialization, a task should call this function when it no longer needs
- the
-\emph on
-iorequest_t
-\emph default
-.
- Of course, if the task exists, the resources are automatically released
- back to the system, however.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_sync(iorequest_t\SpecialChar ~
-*message)
-\emph default
- Permits to send a synchroneous request to a device, via
-\emph on
-message
-\emph default
-.
- This means that the task is suspended until the request completes.
- The reply result is also automatically extracted from the reply port associated
- to the
-\emph on
-iorequest_t
-\emph default
-.
- Before sending a request, some fields of the
-\emph on
-iorequest_t
-\emph default
- message should be set.
- When it completes, the result fields will have been set.
- Both can have device-dependent semantics, although there is generally a
- standard, which is described in the internals section.
- Returns TRUE if the request could be sent, or FALSE if there was an internal
- problem (invalid
-\emph on
-message
-\emph default
-, or no longer existing device).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_async(iorequest_t\SpecialChar ~
-*message)
-\emph default
- Very similar to
-\emph on
-iorequest_sync()
-\emph default
-, but permits to launch the request without waiting until it completes,
- performing an asynchroneous request.
- Upon completion, the device will internally
-\emph on
-port_reply()
-\emph default
- into the reply port associated with
-\emph on
-message
-\emph default
-, and the task is then responsible for extracting the reply message from
- the reply port.
- This allows to launch several asynchroneous requests and to monitor signals
- or ports to detect when they occur.
- For instance, a task may launch an asynchroneous request to read one character,
- and when the request completes, specifying that data exists to read.
- It can then send synchroneous requests to read larger blocks until no more
- data is available, in which case it may then again send an asynchroneous
- request and resume normal activity.
- TRUE is returned if the request could be launched, or FALSE if it failed
- (invalid
-\emph on
-message
-\emph default
- or no longer existing device).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_abort(iorequest_t\SpecialChar ~
-*message)
-\emph default
- If
-\emph on
-message
-\emph default
- currently consists of an asynchroneous request which was made using
-\emph on
-iorequest_async()
-\emph default
- and is still pending, an abort request is sent to cancel it.
- Like usual, the device will reply still as soon as the request could be
- aborted, and the task becomes responsible to unlink the reply message from
- the reply port associated with
-\emph on
-message
-\emph default
-.
- TRUE is returned on success, or FALSE if
-\emph on
-message
-\emph default
- does not consist of a currently pending asynchroneous request.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_wait(iorequest_t\SpecialChar ~
-*message)
-\emph default
- Waits until a currently pending (or aborted which not yet replied) asynchoneous
- request terminates, and automatically unlinks the reply message received
- through the reply port of
-\emph on
-message
-\emph default
-.
- This can especially be useful after an
-\emph on
-iorequest_abort()
-\emph default
-.
- Returns TRUE on success, or FALSE if
-\emph on
-message
-\emph default
- is not currently a pending (or aborted which did not yet return) asynchroneous
- request.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_pending(iorequest_t\SpecialChar ~
-*message)
-\emph default
- Returns TRUE if
-\emph on
-message
-\emph default
- currently consists of an asynchroneously pending request, or FALSE otherwise.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_aborted(iorequest_t\SpecialChar ~
-*message)
-\emph default
- Returns TRUE if
-\emph on
-message
-\emph default
- consists of a request which just completed, but which was an asynchroneous
- request and was aborted.
-\layout Standard
-
-These utility functions, although performing the most basic Xisop device
- operations, are provided to minimize code duplication, for very simple
- synchroneous I/O.
- Normally, tasks will address requests using
-\emph on
-iorequest_sync()
-\emph default
- and
-\emph on
-iorequest_async()
-\emph default
- as needed, but this can be useful to have:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-ssize_t\SpecialChar ~
-device_read(iorequest_t\SpecialChar ~
-*req,\SpecialChar ~
-void\SpecialChar ~
-*buf,\SpecialChar ~
-size_t\SpecialChar ~
-len)
-\emph default
- Similarily to unix
-\emph on
-read()
-\emph default
-, reads at most
-\emph on
-len
-\emph default
- bytes of data from the opened device and unit associated with
-\emph on
-req
-\emph default
- into
-\emph on
-buf
-\emph default
-, and returns the number of actually read bytes, or -1 on error.
- The current task is suspended until the operation completes, since
-\emph on
-iorequest_sync()
-\emph default
- is internally used.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-ssize_t\SpecialChar ~
-device_write(iorequest_t\SpecialChar ~
-*req,\SpecialChar ~
-void\SpecialChar ~
-*buf,\SpecialChar ~
-size_t\SpecialChar ~
-len)
-\emph default
- Like unix
-\emph on
-write()
-\emph default
-, writes at most
-\emph on
-len
-\emph default
- bytes of data from
-\emph on
-buf
-\emph default
-, to the opened device and unit associated with
-\emph on
-req
-\emph default
-, and returns the number of actually written bytes, or -1 on error.
- The current task is suspended until the operation completes as it internally
- uses
-\emph on
-iorequest_sync()
-\emph default
-.
-\layout Subsubsection
-
-Device server interface
-\layout Standard
-
-Here follows functions which are only useful to device server tasks to call:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-device_attach(const\SpecialChar ~
-char\SpecialChar ~
-*name, u_int32_t\SpecialChar ~
-version, port_t\SpecialChar ~
-*port, void\SpecialChar ~
-(*clean)(vo
-id), bool\SpecialChar ~
-(*open)(void\SpecialChar ~
-**,\SpecialChar ~
-u_int32_t), void\SpecialChar ~
-(*close)(void\SpecialChar ~
-*,\SpecialChar ~
-u_int32_t), void\SpecialChar ~
-*(*iorini
-t)(void), void\SpecialChar ~
-(*iordestroy)(void\SpecialChar ~
-*), u_int8_t\SpecialChar ~
-flags)
-\emph default
-
-\newline
-Allows the current task to become a system device.
-
-\emph on
-name
-\emph default
- consists of the unique case-sensitive device name to assign to the system
- device node, which will be required to use at
-\emph on
-device_open()
-\emph default
-, and will be truncated to 32 characters if longer.
-
-\emph on
-version
-\emph default
- specifies the version number to use, which will also need to match at
-\emph on
-device_open()
-\emph default
-, for this device name.
-
-\emph on
-port
-\emph default
- consists of the device server's private port, through which requests should
- be sent.
-
-\emph on
-flags
-\emph default
- consists of one or combination of
-\emph on
-DNF_
-\emph default
-* flags described in the header file, like
-\emph on
-DNF_RESIDENT
-\emph default
- which tells Xisop to never cause the device task to exit if there exist
- no more client open instances.
- The various function pointers which must be supplied are explained below:
-\begin_deeper
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-(*clean)(void)
-\emph default
- is a function which the device task can provide for Xisop to call when
- no more client open instances exist for this device, unless the device
- is resident (and
-\emph on
-flags
-\emph default
- comported
-\emph on
-DNF_RESIDENT
-\emph default
- at device creation).
- This function is responsible to restore the hardware which this device
- may have been serving to a consistant and known state.
- It will also be called automatically by Xisop when the task exits normally.
- Note that
-\emph on
-clean()
-\emph default
- should not comport any special function to cause the task to end.
- Xisop will send a
-\emph on
-SIGTERM
-\emph default
- signal when the task should do so.
- If the task exits by itself,
-\emph on
-clean()
-\emph default
- will be called automatically nevertheless.
- Because this function may be called under the context of any other task,
- it should not expect to execute under the device's task memory pool (and
- therefore should normally not call allocation functions).
- It is possible to specify NULL for this function if the device has no need
- for any special cleanup function.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-(*open)(void\SpecialChar ~
-**udata,\SpecialChar ~
-u_int32_t\SpecialChar ~
-unit)
-\emph default
- All devices are required to provide this function.
- It's purpose is to validate if
-\emph on
-unit
-\emph default
- can be opened (some devices limit the number of open instances of a unit,
- to protect their resources), and to optionally allocate any needed device-speci
-fic data which it may need to attach to
-\emph on
-device_t
-\emph default
- nodes.
-
-\emph on
-open()
-\emph default
- is called at each
-\emph on
-device_open()
-\emph default
- function instance called on this device.
- It is expected to return FALSE if it refuses to open the specified
-\emph on
-unit
-\emph default
- or if it cannot allocate any required resources, or TRUE on success.
- If the task allocates data which is needed to be attached to the
-\emph on
-device_t
-\emph default
- handle, it should supply the address of the allocated data block into the
- supplied
-\emph on
-udata
-\emph default
-.
- It should set NULL there otherwise.
- Although the device may at it's discretion maintain counters on the number
- of currently opened units, etc, Xisop will automatically send a SIGTERM
- to the device task if it is non-resident and that there exist no more openers.
- Note that this function is called under the context of the task which calls
-
-\emph on
-device_open()
-\emph default
-.
- As such, the memory allocations, such as the optional
-\emph on
-udata
-\emph default
- block will be automatically freed when the other task ends, not ours.
- For this reason, the function can use
-\emph on
-malloc()
-\emph default
- and companions safely, but should not use lower-level Xisop kernel allocation
- primitives.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-(*close)(void\SpecialChar ~
-*udata,\SpecialChar ~
-u_int32_t\SpecialChar ~
-unit)
-\emph default
- This function is also required for all device tasks to provide, and is
- called by
-\emph on
-device_close()
-\emph default
- on a
-\emph on
-device_t
-\emph default
- handle which was previously associated to this task by
-\emph on
-device_open()
-\emph default
-.
- The function is responsible for calling
-\emph on
-free()
-\emph default
- on the supplied
-\emph on
-udata
-\emph default
- pointer if needed, and to perform the necessary device-specific cleanup
- required when a device handle closes.
-
-\emph on
-unit
-\emph default
- specifies the unit which was opened by this handle.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*(*iorinit)(void)
-\emph default
- Devices may optionally provide this function to allocate
-\emph on
-iorequest_t
-\emph default
- message specific data which it might need, very similarily to
-\emph on
-open()
-\emph default
- which can attach data to a
-\emph on
-device_t
-\emph default
- handle.
- NULL can be supplied if there is no need for
-\emph on
-iorequest_t
-\emph default
- specific extention data.
- This function is called under the context of the task calling
-\emph on
-iorequest_init()
-\emph default
- and as such no Xisop low-level allocation functions should be called.
- The function may allocate the data block with
-\emph on
-malloc()
-\emph default
-, initialize it and return a pointer to it, or NULL on failure (out of memory).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-(iordestroy)(void\SpecialChar ~
-*udata)
-\emph default
- May also be supplied NULL if NULL was supplied for
-\emph on
-iorinit()
-\emph default
-.
- Otherwise, this function is responsible to
-\emph on
-free()
-\emph default
- the supplied
-\emph on
-udata
-\emph default
-, which corresponds to a block of memory which was returned by a previous
-
-\emph on
-iorinit()
-\emph default
- call.
-\end_deeper
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-iorequest_satisfy(iorequest_t\SpecialChar ~
-*message,\SpecialChar ~
-bool\SpecialChar ~
-result)
-\emph default
- Is a useful utility function to set the main request success result code
- and reply to the task that it has completed.
-\layout Standard
-
-Various macros of interest may be used by device tasks:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*DEVICEHANDLE_UDATA(device_t\SpecialChar ~
-*handle)
-\emph default
- Returns the
-\emph on
-udata
-\emph default
- pointer associated with the supplied
-\emph on
-device_t
-\emph default
-
-\emph on
-handle
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-*IOREQUEST_UDATA(iorequest_t\SpecialChar ~
-*message)
-\emph default
- Returns the
-\emph on
-udata
-\emph default
- pointer associated with the supplied
-\emph on
-iorequest_t message
-\emph default
-.
-\layout Subsubsection
-
-Internals
-\layout Standard
-
-This system based upon the Xisop message ports system and the previously
- described functions permit to open or provide devices, and to communicate
- requests from the client-side and completion from the server-side, through
- a special message, the
-\emph on
-iorequest_t
-\emph default
-.
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsection
-
-Xisop handlers
-\layout Standard
-
-Handlers provide a higher-level abstraction to devices or resources.
- It should be noted that unlike devices, these are implemented in the form
- of a standard shared library format.
- This means that devices are usually a nice abstraction to mount handlers
- on, as devices naturally perform the queuing, etc.
-
-\emph on
-XXX
-\layout Subsection
-
-Xisop shared libraries
-\layout Standard
-
-The concept of Xisop shared libraries is both very simple, and unusual.
- It is important that all functions a library provides publically be reentrant.
- Opening a library basically obtains the pointer to a structure which is
- necessary to access it's function pointers.
- As such, only one resident copy is required for all applications, and new
- applications can
-\begin_inset Quotes eld
-\end_inset
-
-attach
-\begin_inset Quotes erd
-\end_inset
-
- the libraries they need as required.
- The system keeps track of how many times it is currently being open by
- various tasks, and can therefore know when the library should be expunged
- from memory.
- Obviously, when a library is requested which is not currently in RAM, it
- should be loaded in the system from disk.
-\layout Standard
-
-Each library may have concurrent versions on the same system, in memory
- and on disk.
- When an application requests access to a library, it optionally specifies
- the expected version, without which the latest is assumed.
- This way, it is possible for the administrator to get rid of the obsolete
- libraries but only after making sure that no applications require them
- anymore.
- While software is being developped, it becomes possible to have concurrent
- versions of applications each using their respectively related material.
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsubsection
-
-The Xisop library
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsection
-
-Xisop system calls
-\layout Standard
-
-System calls are different than normal functions in that they allow userspace
- tasks to execute instructions which are normally only allowed to call in
- supervisor mode, or in kernel space.
- Moreover, system calls are currently uninterruptible in Xisop, which means
- that the task is guaranteed to not be preempted while executing a system
- call function, until it returned.
- These are internally implemented using processor traps, by the port-specific
- code.
- However, the system call functions themselves are portable and part of
- the Xisop common code.
- Because Xisop does not use MMU facilities, system calls are very fast to
- execute compared to on unix systems.
- Xisop design attempts to require the less of these possible however, because
- they also disable the scheduler when executing.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-struct\SpecialChar ~
-xisop_root\SpecialChar ~
-*sys_getroot(void)
-\emph default
- Permits a task to obtain the address of the main Xisop control structure,
- where system lists are stored.
- Obviously, if a task uses this information in any way, it has to be careful
- not to disrupt Xisop activities.
- It is recommended to disable the scheduler and/or interrupts where required
- to access the information which that structure provides.
- It is made for people who know Xisop inside out only.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-sys_int_disable(void)
-\emph default
- This internally calls
-\emph on
-_splhigh()
-\emph default
- which ensures to mask all interrupts, including that of the preemptive
- scheduler.
- Precautions should be made about the calls used similarly to when disabling
- the scheduler.
- However, this call permits a task to completely take control over Xisop.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-sys_int_enable(void)
-\emph default
- Internally calling
-\emph on
-_spl0()
-\emph default
-, this re-enables all interrupts.
-
-\emph on
-XXX
-\emph default
- heh actually, can the
-\emph on
-_syscall()
-\emph default
- trap actually occur after a
-\emph on
-sys_int_disable()
-\emph default
-? Will need to check this out.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-sys_idle(void)
-\emph default
- Permits to suspend the processor until the next trap, interrupt or exception
- occurs.
- This internally calls the
-\emph on
-_idle()
-\emph default
- processor-specific function.
- This is mostly used by Xisop
-\emph on
-main()
-\emph default
- which is returned control to when no tasks are currently on the ready queue.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-sys_custom(void\SpecialChar ~
-*res,\SpecialChar ~
-void\SpecialChar ~
-(*func)(void\SpecialChar ~
-*,\SpecialChar ~
-void\SpecialChar ~
-*),\SpecialChar ~
-void\SpecialChar ~
-*args)
-\emph default
- A very special system call, allows user tasks to execute arbitrary code
- in supervisor mode, uninterruptibly (scheduler will not preempt, but interrupts
- can still take place).
-
-\emph on
-func
-\emph default
- specifies the function to call, which will be passed
-\emph on
-res
-\emph default
- as the first argument and
-\emph on
-args
-\emph default
- as the second argument, which can be used by the function to acquire parameters
- and return results.
-
-\emph on
-res
-\emph default
- and
-\emph on
-args
-\emph default
- can be NULL when they are not needed.
- Because Xisop attempts to be more useful to the programmer than to secure
- the kernel against userland, this was beleived to be a very useful function,
- where user tasks can create their custom system calls as required.
-\layout Subsection
-
-Xisop general programming interfaces
-\layout Standard
-
-In an attempt to keep the code unified and clean, multipurpose interfaces
- were provided.
-\layout Subsubsection
-
-Byte alignment macros
-\layout Standard
-
-In
-\emph on
-<common/types.h>
-\emph default
- the following macros are provided for byte alignment.
-\layout Standard
-
-These macros permit object size related byte alignment:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-size_t\SpecialChar ~
-OALIGN_CEIL(size_t\SpecialChar ~
-v,\SpecialChar ~
-o)
-\emph default
-
-\emph on
-o
-\emph default
--aligns
-\emph on
-v
-\emph default
-.
- This macro rounds
-\emph on
-v
-\emph default
- to the nearest larger unit as required.
-
-\emph on
-o
-\emph default
- should be any native C or custom structure type, to which
-\emph on
-v
-\emph default
- should be aligned relative to.
-
-\emph on
-v
-\emph default
- is not modified, the new value is returned.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-size_t\SpecialChar ~
-OALIGN_FLOOR(size_t\SpecialChar ~
-v,\SpecialChar ~
-o)
-\emph default
-
-\emph on
-o
-\emph default
--aligns
-\emph on
-v
-\emph default
-.
- Unlike
-\emph on
-OALIGN_CEIL()
-\emph default
- this macro rounds to the nearest smaller unit as required.
-\layout Standard
-
-And these macros permit byte size related alignment:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-size_t\SpecialChar ~
-BALIGN_CEIL(size_t\SpecialChar ~
-v,\SpecialChar ~
-size_t\SpecialChar ~
-s)
-\emph default
- This macro aligns
-\emph on
-v
-\emph default
- to the nearest larger unit relative to
-\emph on
-s
-\emph default
- size as required.
-
-\emph on
-v
-\emph default
- is not modified, the new value is returned.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-size_t\SpecialChar ~
-BALIGN_FLOOR(size_t\SpecialChar ~
-v,\SpecialChar ~
-size_t\SpecialChar ~
-s)
-\emph default
- Very similar to
-\emph on
-BALIGN_CEIL()
-\emph default
-, but rounds
-\emph on
-v
-\emph default
- to the nearest smaller unit relative to
-\emph on
-s
-\emph default
- size as required.
-\layout Subsubsection
-
-Byte order manipulation macros
-\layout Standard
-
-In
-\emph on
-<common/types.h>
-\emph default
- the following macros are provided for byte order/endian conversions.
- These are most useful for network Remote Procedure Call implementations,
- as well as for binary file formats which can be in network order so that
- multiple architectures may easily use the same file format.
- Depending on the native host byte order (the
-\emph on
-_ARCH_BIG_ENDIAN
-\emph default
- and
-\emph on
-_ARCH_LITTLE_ENDIAN
-\emph default
- definitions defined by processor-specific code), these will perform no
- action where no conversion is necessary.
- The network order should be used for transferring over networks or writing
- to binary files, and actually corresponds to the native host byte order
- on big endian architectures.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int16_t\SpecialChar ~
-BYTEORDER_NETWORK16(u_int16_t)
-\emph default
- Converts the specified 16-bit word to network order for sending through
- the network or writing to a binary file.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int16_t\SpecialChar ~
-BYTEORDER_HOST16(u_int16_t)
-\emph default
- Converts the specified network order 16-bit word to native host order after
- reading from a binary file or receiving through the network.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int32_t\SpecialChar ~
-BYTEORDER_NETWORK32(u_int32_t)
-\emph default
- Converts the specified 32-bit word to network order for sending through
- the network or writing to a binary file.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int32_t\SpecialChar ~
-BYTEORDER_HOST32(u_int32_t)
-\emph default
- Converts the specified network order 32-bit word to native host order after
- reading from a binary file or receiving through the network.
-\layout Subsubsection
-
-Doubly linked lists
-\layout Standard
-
-These macros, as well as the
-\emph on
-list_t
-\emph default
- and
-\emph on
-node_t
-\emph default
- types are defined in
-\emph on
-<common/types.h>
-\emph default
- and
-\emph on
-<common/kernlib/list.h>
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-DLIST_INIT(list_t\SpecialChar ~
-*list)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-DLIST_APPEND(list_t\SpecialChar ~
-*list,\SpecialChar ~
-node_t\SpecialChar ~
-*node)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-DLIST_INSERT(list_t\SpecialChar ~
-*list,\SpecialChar ~
-node_t\SpecialChar ~
-*node)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-DLIST_INSERTAT(list_t\SpecialChar ~
-*list,\SpecialChar ~
-node_t\SpecialChar ~
-*atnode,\SpecialChar ~
-node_t\SpecialChar ~
-*node)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-DLIST_SWAP(list\SpecialChar ~
-t\SpecialChar ~
-*dstlist,\SpecialChar ~
-list_t\SpecialChar ~
-*srclist,\SpecialChar ~
-node_t\SpecialChar ~
-*srcnode,\SpecialChar ~
-bool\SpecialChar ~
-insert)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-DLIST_UNLINK(list_t\SpecialChar ~
-*list,\SpecialChar ~
-node_t\SpecialChar ~
-*node)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int32_t\SpecialChar ~
-DLIST_NODES(list_t\SpecialChar ~
-*list)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-node_t\SpecialChar ~
-*DLIST_TOP(list_t\SpecialChar ~
-*list)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-node_t\SpecialChar ~
-*DLIST_BOTTOM(list_t\SpecialChar ~
-*list)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-node_t\SpecialChar ~
-*DLIST_NEXT(node_t\SpecialChar ~
-*node)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-node_t\SpecialChar ~
-*DLIST_PREV(node_t\SpecialChar ~
-*node)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-DLIST_FOREACH(list_t\SpecialChar ~
-*list,\SpecialChar ~
-node_t\SpecialChar ~
-*iterator)
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsubsection
-
-Hash based fast lookup tables
-\layout Standard
-
-The prototypes and types for these are defined in
-\emph on
-<common/types.h>
-\emph default
- and
-\emph on
-<common/kernlib/hash.h>
-\emph default
-.
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsubsection
-
-FIFO (First In, First Out) buffers
-\layout Standard
-
-These macros as well as the default FIFO types are defined in
-\emph on
-<common/types.h>
-\emph default
- and
-\emph on
-<common/kernlib/fifo.h>
-\emph default
-.
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsubsection
-
-LIFO (Last In, First Out / Stack) buffers
-\layout Standard
-
-These macros as well as the default LIFO types are defined in
-\emph on
-<common/types.h>
-\emph default
- and
-\emph on
-<common/kernlib/lifo.h>
-\emph default
-.
-\layout Standard
-
-
-\emph on
-XXX
-\layout Subsection
-
-Xisop source tree organization
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-doc/
-\emph default
- This directory holds this file, as well as various notes of interest
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/
-\emph default
- Where all source resides
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/common/
-\emph default
- All machine-independent Xisop source
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/common/kernel/
-\emph default
- Xisop kernel main code
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/common/kernlib/
-\emph default
- Xisop kernel main machine-independent libraries
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/common/library/
-\emph default
- Xisop machine-independent shared libraries
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/common/device/
-\emph default
- Xisop machine-independent devices
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/common/handler/
-\emph default
- Xisop machine-independent handlers
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/common/task/
-\emph default
- Xisop machine-independent resident tasks
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/processors/
-\emph default
- Holds all processor-specific code
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/processors/m68k/
-\emph default
- The Motorola m68k support (MC68000L8/L10)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/processors/m68k/kernlib/
-\emph default
- m68k specific replacement functions for wanted standard machine-independent
- kernlib ones (optional)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/ports/
-\emph default
- Holds all port-specific code, including boot loaders
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/ports/amiga/
-\emph default
- The Amiga port of Xisop code resides here
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/ports/amiga/kernlib/
-\emph default
- Amiga-specific replacement functions for wanted standard machine-independent
- kernlib ones (optional)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/ports/amiga/boot/
-\emph default
- The Amiga-specific code to generate a bootable kernel
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/ports/amiga/library/
-\emph default
- Amiga-specific shared libraries (optional)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/ports/amiga/device/
-\emph default
- Amiga-specific devices (optional)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/ports/amiga/handler/
-\emph default
- Amiga-specific handlers (optional)
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/ports/amiga/task/
-\emph default
- Amiga-specific resident tasks (optional)
-\layout Subsection
-
-The build process
-\layout Standard
-
-Here is described the way the Xisop source is built to create a binary kernel
- image.
-
-\emph on
-/bin/sh
-\emph default
- is also assumed to exist for the building process.
- The convention for the script names are as follows:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/generic_makedefs.sh
-\emph default
- contains various sh functions and variables assigned to local utilities
- which are required by all
-\emph on
-clean.sh
-\emph default
- scripts, and the main
-\emph on
-src/make.sh
-\emph default
- script.
- Those scripts
-\emph on
-source
-\emph default
- this file to obtain the common information.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/makedefs.sh
-\emph default
- consists of a symbolic link to
-\emph on
-port/makedefs.sh
-\emph default
-, which contains the configuration information required to build the for
- the target port system.
- The paths to the various useful utilities are assigned to shell variables.
- If functions need to be supplied for other build scripts, they also should
- be defined here.
- This file is sourced (included) by all other build scripts.
- They all should use the variables supplied by this file when accessing
- the cross-compile or local utilities.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/make.sh
-\emph default
- allows to fully compile the kernel to result in a kernel image.
- Requires the target port name to be specified, as it also sets up required
- symbolic links.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-src/clean.sh
-\emph default
- cleans the source tree, that is, deletes all files which may have been
- created by the build process.
-\layout List
-\labelwidthstring 00.00.0000
-
-*
-\emph on
-/make.sh
-\emph default
- The various sections described below will need such a script which should
- perform the necessary steps to compile the section.
- These
-\emph on
-source
-\emph default
- src/
-\emph on
-makedefs.sh
-\emph default
- to know which commands to invoke and access useful
-\emph on
-/bin/sh
-\emph default
- macros..
-\layout List
-\labelwidthstring 00.00.0000
-
-*
-\emph on
-/clean.sh
-\emph default
- The sections also need such a script which is expected to delete all files
- which the
-\emph on
-make.sh
-\emph default
- script counterpart creates.
- These
-\emph on
-source
-\emph default
-
-\emph on
-src/generic_makedefs.sh
-\emph default
-.
-\layout Paragraph
-
-Cleaning the whole source tree
-\layout Standard
-
-The
-\emph on
-src/clean.sh
-\emph default
- script ensures to clean the whole source tree by calling the
-\emph on
-clean.sh
-\emph default
- script for each section, including all ports and processors.
- The invoker is expected to be in the
-\emph on
-src/
-\emph default
- directory.
-\layout Paragraph
-
-Building the system
-\layout Standard
-
-The
-\emph on
-src/make.sh
-\emph default
- script builds the entire system.
- The main steps performed to compile the system are described as follows,
- in order.
-\layout Subsubsection
-
-Preliminary building process setup
-\layout Standard
-
-The
-\emph on
-src/processor,
-\emph default
-
-\emph on
-src/port
-\emph default
- and
-\emph on
-src/makedefs.sh
-\emph default
- symbolic links should point to their corresponding
-\emph on
-src/processors/<directory>
-\emph default
- and
-\emph on
-src/ports/<directory>
-\emph default
- and
-\emph on
-src/ports/<directory>/makedefs.sh
-\emph default
-.
- These are setup depending on the target system for which Xisop is being
- built.
- Each of these (port and processor sections) supplies a
-\emph on
-support.h
-\emph default
- headerfile which should be sufficient for the rest of the kernel code to
- access the functionality of the hardware specific code they provide.
- Moreover, each of them will after building provide
-\emph on
-ar
-\emph default
- archives (
-\emph on
-.a
-\emph default
- files) into their respective
-\emph on
-ar/
-\emph default
- directory, resulting from their various object files, which will be sufficient
- to link with the rest of the kernel code to achieve the end result.
- The
-\emph on
-src/make.sh
-\emph default
- script creates these symbolic links when supplied with a valid
-\emph on
-target
-\emph default
- (using the
-\emph on
--t
-\emph default
- parameter).
-\layout Standard
-
-
-\emph on
-src/make.sh
-\emph default
- also ensures to set the
-\emph on
-$SRCDIR
-\emph default
- environment variable to the absolute path to the current directory (
-\emph on
-src/
-\emph default
-), which should be used by other build scripts when compiling modules so
- that
-\emph on
-#include
-\emph default
- directives in the source can locate the file using
-\emph on
--I
-\emph default
- parameter, etc.
-\layout Subsubsection
-
-Building the processor-specific support code
-\layout Standard
-
-Control is delegated to
-\emph on
-src/processor
-\emph default
-/
-\emph on
-make.sh
-\emph default
-.
- This is expected to assemble and compile the various sections it comports
- to binary objects independently (
-\emph on
-.o
-\emph default
-) using
-\emph on
--c
-\emph default
- parameter to
-\emph on
-gcc
-\emph default
- command, and to then archive them as
-\emph on
-ar
-\emph default
- archives into the
-\emph on
-src/processor/ar
-\emph default
- directory using the
-\emph on
-ar
-\emph default
- and
-\emph on
-ranlib
-\emph default
- commands.
- This final object will be linked with the kernel code by another section
- of the build process.
- It is to be noted that it will be linked before the general common machine-inde
-pendent kernel library, so that it is possible to provide processor-specific
- replacements to standard low-level functions, such as
-\emph on
-memcpy()
-\emph default
-,
-\emph on
-memset()
-\emph default
-, etc.
- These could however be overriden by the port-specific support code.
- When control is given to
-\emph on
-make.sh
-\emph default
- of this section, the current directory will have been changed as well so
- that it is safe to access the section files relatively to the current (section)
- directory.
-\layout Subsubsection
-
-Building the port-specific support code
-\layout Standard
-
-Control is given to
-\emph on
-src/port/make.sh
-\emph default
- to compile this section, and very similarly to the processor-specific section,
- the goal is to generate one or more
-\emph on
-ar
-\emph default
- archive in the
-\emph on
-src/port/ar
-\emph default
- directory.
-
-\emph on
-src/port/support.h
-\emph default
- will also contain all necessary information for the rest of the kernel
- code to use the port-specific support.
- This code will be linked with the global kernel before s
-\emph on
-rc/processor/ar/*.a
-\emph default
-, which means that it is possible to provide port-specific standard functions
- overriding the processor-specific ones, as well as kernel machine-independent
- common ones.
- This could allow for instance to provide
-\emph on
-memcpy()
-\emph default
- and
-\emph on
-memset()
-\emph default
- functions using specialized data moving hardware such as blitters, etc.
- It is however recommended that libraries be built in a way to not force
- the whole library to be included into the final kernel if only a few of
- the functions were necessary.
- To do this, a library could consists of a directory, with all functions
- isolated in their own
-\emph on
-.c
-\emph default
- file.
- The
-\emph on
-ar
-\emph default
- archive then results in a bunch of very small
-\emph on
-.o
-\emph default
- files which will be ignored by the linker when unrequired.
- It is important to also run
-\emph on
-ranlib
-\emph default
- on the
-\emph on
-ar
-\emph default
- archive.
-\layout Standard
-
-
-\emph on
-XXX
-\emph default
-
-\emph on
-Hmm I should find a nice way to define the resident libraries, devices and
- handlers, both common and port-specific ones.
- Also, which tasks should be initially started, etc.
- Actually, resident devices and handlers, being tasks, would just need to
- be included in the tasks to start, I guess...
-\layout Standard
-
-
-\emph on
-XXX This next paragraph is currently invalid.
-\layout Standard
-
-The
-\emph on
-ar/*.a
-\emph default
- result should also include
-\emph on
-_init_libraries()
-\emph default
-,
-\emph on
-_init_devices()
-\emph default
- and
-\emph on
-_init_handlers()
-\emph default
- functions, which should as required attach the wanted shared libraries,
- devices and handlers in the kernel by calling their init function.
- These also should initialize machine-independent ones.
- If there are port-dependent ones, their code should be located into the
-
-\emph on
-library/
-\emph default
-,
-\emph on
-device/
-\emph default
- and
-\emph on
-handler/
-\emph default
- directories in
-\emph on
-src/port/
-\emph default
-.
-\layout Subsubsection
-
-Building the machine-independent main Xisop code
-\layout Standard
-
-The
-\emph on
-src/common/kernlib/
-\emph default
- directory comports a directory for each internal kernel library, which
- each contain functions isolated into a single file each.
- These are built separately as object modules, and are archived using
-\emph on
-ar
-\emph default
- and
-\emph on
-ranlib
-\emph default
- into
-\emph on
-src/common/kernlib/ar
-\emph default
-directory.
- The reason for this is that it allows the resulting kernel image to be
- smaller when not all of the functions of a particular library are used.
- If all string functions were located into the same
-\emph on
-string.c
-\emph default
- file for instance, all of the string functions would automatically be linked
- within the result even if only two string functions were actually used,
- for instance.
-\layout Standard
-
-The
-\emph on
-src/common/kernel/
-\emph default
- and
-\emph on
-src/common/kernlib/
-\emph default
- sections containing only portable C code are compiled, and their modules
- archived with
-\emph on
-ar
-\emph default
- and
-\emph on
-ranlib
-\emph default
-, to
-\emph on
-src/common/kernel/ar/*.a
-\emph default
- and
-\emph on
-src/common/kernlib/ar/*.a
-\emph default
- files.
- At current time,
-\emph on
-src/common/library/
-\emph default
-,
-\emph on
-src/common/device/
-\emph default
- and
-\emph on
-src/common/handler/
-\emph default
- sections are all compiled and archived together as
-\emph on
-src/common/ar/*.a
-\emph default
-.
-
-\emph on
-XXX This last statement is false as nothing is done for libraries, devices
- and handlers at current time..
-\layout Subsubsection
-
-Linking the final kernel
-\layout Standard
-
-
-\emph on
-src/common/kernel/kernel
-\emph default
-.
-\emph on
-a
-\emph default
-,
-\emph on
-src/port/support.
-\emph default
-a,
-\emph on
-src/processor/support.a
-\emph default
-,
-\emph on
-src/common/kernlib/kernlib.a
-\emph default
- and
-\emph on
-src/common/shared.a
-\emph default
- are linked together, in that order, into the ELF relocatable
-\emph on
-src/xisop.o
-\emph default
- file (using
-\emph on
--r
-\emph default
- option to ld).
- This allows processor-specific libraries to replace
-\emph on
-common/kernlib
-\emph default
- functions, and port-specific ones to override both processor-specific and
- common ones.
-\layout Subsubsection
-
-Linking the final kernel and building the bootable Xisop result
-\layout Standard
-
-After building both the machine dependent and independent sections described
- above, the control is then left to
-\emph on
-src/port/boot/make.sh
-\emph default
-, after changing to the
-\emph on
-src/port/boot directory
-\emph default
-.
- The role of this final script is to complete the linking and building process.
- Here is what currently happens:
-\layout Standard
-
-As a general rule, image_script.ld file in that directory comports the necessary
- information to statically link monolithically the whole kernel as a binary
- image.
- Here is an excerpt of what
-\emph on
-src/ports/amiga/boot/make.sh
-\emph default
- does:
-\layout Itemize
-
-show $C_COMPC -I$SRCDIR init.c
-\layout Itemize
-
-A=`$L_LS ../../../common/kernel/ar/*.a ../ar/*.a ../../../processor/ar/*.a ../..
- /../common/kernlib/ar/*.a`
-\layout Itemize
-
-show $C_LD -nostdlib -e _start -Ttext 0x005f8000 -o image.o init.o $A
-\layout Itemize
-
-show $C_LD -T image_script.ld -nostdlib -o image.bin init.o $A
-\layout Itemize
-
-...
-\layout Standard
-
-The first operation compiles it's initialization code, which provides the
-
-\emph on
-_start
-\emph default
- entrypoint, which eventually calls Xisop
-\emph on
-main()
-\emph default
-.
- Then is compiled a list of the various
-\emph on
-ar
-\emph default
- archives which should be linked.
- The order of these archives is important, as it permits the processor-specific
- code to override the common code, and the port-specific code to override
- the processor specific and common code.
-\layout Standard
-
-Then follows the linking process, which is done two time.
- The first instance creates
-\emph on
-image.o
-\emph default
- which can then be viewed and disassembled using the
-\emph on
-objdump
-\emph default
- utility.
- This is mostly used for debugging so that at runtime in the emulator it
- is possible to stop the emulation process and fall into the debugger, which
- then discloses the current executing address.
- That same address in image.o disassembly should match, and this is where
- it can be handy.
-\layout Standard
-
-The second linking command creates the binary Xisop kernel image using the
-
-\emph on
-image_script.ld
-\emph default
- linker script, to result in
-\emph on
-image.bin
-\emph default
-.
- This is the actual image, which expects to be loaded into memory at the
- address 0x005f8000, and jumped to.
- (See the
-\emph on
-image_script.ld
-\emph default
- and
-\emph on
-make.sh
-\emph default
- scripts for more information).
-\layout Standard
-
-The script then proceeds to compile the disk boot loader (floppy in this
- case), which is located into the
-\emph on
-bootf/
-\emph default
- directory.
- It then compiles the tools which are needed to assemble the result and
- fix the bootblock checksum using the the local compiler (not the cross
- compiler, although the same compiler could be used for both local and cross,
- if the target and the build system are the same).
- It finally uses those tools to create the final xisop.adf floppy image,
- and advertizes the location of this file to the user.
-\layout Standard
-
-Those last steps are very port-specific and are best done by someone with
- a good amount of experience for the particular port to ensure that it works
- right.
- It is important to advertize the location of the final result to the user
- at the very end.
-\layout Section
-\pagebreak_top
-Hardware specific development notes
-\layout Standard
-
-This chapter describes which hardware specific sections are required to
- support Xisop.
- They in fact provide the low-level glue which all the machine-independent
- common code replies on.
- As such, they should be small, effective, and as efficient and stable as
- possible.
- They should be well tested before releasing an official new Xisop port.
-\layout Standard
-
-To aid in having a well organized source tree, and to prevent code duplication,
- hence enhancing stability with time, the processor-specific and port-specific
- sections have been separated into two.
- Several ports may then take advantage of the same processor-specific code,
- such as atomic locking primitives, which are known to work well, which
- helps alot to speed up development.
-\layout Subsection
-
-Microprocessor specific notes
-\layout Subsubsection
-
-Required backend functions and support
-\layout Standard
-
-Each processor is different but it is great to abstract most CPU-specific
- functions into a standard set.
- The headerfile which will be invoked by the common machine-independent
- parts of the kernel to acquire support for the hardware specific functions
- is
-\emph on
-processor/support.h
-\emph default
-, where
-\emph on
-processor
-\emph default
- will consist of a symbolic link to the actual
-\emph on
-processor
-\emph default
- directory in use in the
-\emph on
-processors
-\emph default
- directory.
- Although the general organization of the processor specific code is implementat
-ion dependent, it is important that
-\emph on
-support.h
-\emph default
- loads support for all necessary functions and data types which are processor-sp
-ecific, and that
-\emph on
-ar/*.a
-\emph default
- be the only necessary modules to link with the rest of the kernel.
-\layout Standard
-
-It is allowed if desired to also supply processor specific functions to
- replace some of the common machine-independent ones, which may be desired
- for speed at occasions.
- When this is done, the functions should behave identically as expected,
- should bear the same names, and no prototypes should be provided for them
- in
-\emph on
-support.h
-\emph default
-.
- The processor-specific code will be linked to the final kernel before the
- common libraries and those functions will replace the machine-independent
- ones then.
- These functions could be for instance:
-\emph on
-memcmp()
-\emph default
-,
-\emph on
- memcpy()
-\emph default
-,
-\emph on
-memset()
-\emph default
-, etc.
-\layout Standard
-
-Here are the various recommended functions which each CPU should support,
- and make public to the rest of the kernel, at a minimum:
-\layout Paragraph
-
-Context manipulation
-\layout Standard
-
-The
-\emph on
-_ctx_t
-\emph default
- structure should be defined by the CPU-specific support headerfile and
- should be used as an abstract type representing all required information
- to save or restore a context, thus all registers, status register (SR),
- stack pointer (SP), and program counter (PC).
- The load and save context code sections are generally port-specific are
- called from an interrupt, and as needed some care will be taken to save
- the user stack pointer (USP) rather than the supervisor stack pointer (SSP),
- as their purpose consist of task switching.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_ctx_init(_ctx_t\SpecialChar ~
-*context,\SpecialChar ~
-u_int32_t\SpecialChar ~
-*stack,\SpecialChar ~
-size_t\SpecialChar ~
-stacksize,\SpecialChar ~
-void\SpecialChar ~
-*start)
-\emph default
- Allows to create a new CPU context.
- On some systems the stack grows downwards while upwards on others.
- For this reason, the
-\emph on
-stacksize
-\emph default
- argument is used which permits to set the stack pointer as required, because
-
-\emph on
-stack
-\emph default
- should be a pointer to the top of the stack.
-
-\emph on
-start
-\emph default
- is a function pointer to the code to execute (stack startup address).
- Usually, SP and PC are set accordingly in
-\emph on
-context
-\emph default
-, SR set to the current one, and other registers zeroed.
- In some cases it may be good to also ensure to turn off the supervisor
- bit from SR in the context, because user tasks are expected to run in userstate
- processor mode.
-\layout Paragraph
-
-Simple lock support
-\layout Standard
-
-
-\emph on
-_lock_t
-\emph default
- should also be defined for abstraction, and help to perform various synchroniza
-tion tasks.
- These need not be symetric multiprocessor (SMP) safe, but they should at
- least be atomic for the current processor.
- Atomic in the sense that test-and-set must be performed at once to acquire
- a lock.
- If a processor does not allow to make this atomic, it is possible to provide
- these by the port-specific code, in which case it could disable interrupts
- (at least the scheduler interrupt) before performing it's tasks, so that
- operations seem atomic in a multitasking environment.
- These lock primitives should not be nestled or recursive, they are intended
- for exclusive access.
-\layout Standard
-
-In any case, all following lock primitives should be callable by normal
- user tasks, which means that when required a trap can be used internally
- to swich to supervisor mode if the processor requires to perform privileged
- instructions to achieve the expected atomic behavior, both for
-\emph on
-_lock_t
-\emph default
- and
-\emph on
-_rlock_t
-\emph default
- primitives.
- Fortunately, they can usually be implemented properly using normal instructions.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_lock_init(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Permits to initialize a
-\emph on
-_lock_t
-\emph default
- entity as required for future operations on this
-\emph on
-lock
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_lock_acquire(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Allows to obtain exclusive access on the supplied
-\emph on
-lock
-\emph default
-, or wait looping indefinitely until the lock could be obtained, in order
- to obtain it as fast as possible.
- It is important that this operation be atomic so that in the event of scheduled
- context switching race conditions do not occur.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_lock_release(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Should free the specified
-\emph on
-lock
-\emph default
-, which will enable any other requester to acquire the lock to obtain it.
- This operation also should be atomic.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-_lock_try(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Attempts to obtain exclusive access to
-\emph on
-lock
-\emph default
-, returning TRUE/1 if it could obtain it immediately, or FALSE/0 if the
- lock is already being held, in which case it also returns immediately.
- It is important that this be implemented atomically, in a single test-and-set
- instruction.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-bool\SpecialChar ~
-_lock_available(_lock_t\SpecialChar ~
-*lock)
-\emph default
- Verifies if
-\emph on
-lock
-\emph default
- is available.
- This is not to be used to implement
-\emph on
-_lock_try()
-\emph default
-, it is used in situations where we only want to make sure that no lockers
- currently own the lock, but that we still do not want to obtain it ourselves.
- An example of this is where a lock is used as an ON/OFF switch, which can
- be implemented using this mechanism, without disabling the event which
- needs to access a resource, which may serve other functions but will skip
- executing the critical code if the scheduler lock is held.
- This is usually best implemented using recursive
-\emph on
-_rlock_t
-\emph default
- however, which will be described below.
-\layout Standard
-
-The following locking primitives are different in that a lock may be locked
- by any number of lockers, but has to be unlocked the same number of times
- for the lock to become free again.
- These are called recursive locks.
- A useful example consists of the scheduler which wants to ensure that no
- task disabled the scheduler temporarily before performing a context switch.
- It then evaluates the lock using
-\emph on
-_rlock_available()
-\emph default
-, or
-\emph on
-_rlock_try()
-\emph default
- if it needs to prevent recursion, and only performs the switch if it is
- (and hence the lock counter equals to 0, or 1).
- It is recommended that the
-\emph on
-_rlock_t
-\emph default
- type consist of an
-\emph on
-int32_t
-\emph default
- (signed), but the processor-specific code is left to define it differently
- if need be.
-\layout Standard
-
-Although for several architectures C code can also be used to implement
- these, it is a good idea to implement them in assembly because there is
- no guarantee that the compiler will always use atomic increase and decrease
- operations (GCC 2.95.3 at least seems to not always do so for m68k with C
- macros, it often loads the value from the lock counter, increase it during
- other processing and posts back the new value over the counter, instead
- of always generating an atomic increment instruction which m68k is well
- capable of).
- Moreover, for some other architectures the use of an internal
-\emph on
-_lock_t
-\emph default
- or
-\emph on
-_splhigh()
-\emph default
- may be required to implement these properly, and if there are privileged
- instructions required to implement these, a trap may be needed.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_rlock_init(_rlock_t\SpecialChar ~
-*rlock)
-\emph default
- Initializes
-\emph on
-rlock
-\emph default
- to 0.
- This normally is rarely done except at system initialization, or lock creation.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_rlock_acquire(_rlock_t\SpecialChar ~
-*rlock)
-\emph default
- Atomically increases the
-\emph on
-rlock
-\emph default
- counter by one.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_rlock_release(_rlock_t\SpecialChar ~
-*rlock)
-\emph default
- Atomically decreases the
-\emph on
-rlock
-\emph default
- counter by one.
- There is no need to perform any check against 0 in this function.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_rlock_try(_rlock_t\SpecialChar ~
-*rlock)
-\emph default
- Permits to atomically increase the
-\emph on
-rlock
-\emph default
- counter by one, and returns TRUE if the caller consists of the only locker
- (in which case the lock counter should now be 1).
- If the counter is higher, it means that more than one locker exists and
- the function is then expected to decrease the counter atomically again,
- and return FALSE.
- This allows exclusive access to a recursive lock.
- This function is both used by the scheduler and public interrupt facility
- systems.
- Because they want to make sure that noone holds the lock when they execute
- their critical tasks, and that they also need to lock it to prevent potential
- self-recursion, this call is a great facility to use.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_rlock_available(_rlock_t\SpecialChar ~
-*rlock)
-\emph default
- Returns TRUE if the
-\emph on
-rlock
-\emph default
- counter equals to 0, or FALSE otherwise (in which case there at least remains
- one current locker).
- This function is provided for callers which do not need to prevent possible
- recursion, but only need to make sure that the lock is currently free.
- Following
-\emph on
-_rlock_available()
-\emph default
- with
-\emph on
-_rlock_acquire()
-\emph default
- is not safe, and
-\emph on
-_rlock_try()
-\emph default
- should be used instead when this is desired.
-\layout Paragraph
-
-Byte order conversion support
-\layout Standard
-
-The processor-specific code needs to #define
-\emph on
-_ARCH_BIG_ENDIAN
-\emph default
- or
-\emph on
-_ARCH_LITTLE_ENDIAN
-\emph default
- in their
-\emph on
-support.h
-\emph default
- depending on their native byte order.
- These will be used at a higher level in the libraries for the endian-conversion
- functions between network (big endian) and host order (big or little endian).
- These are most useful when saving a binary file format which needs to be
- loadable by another processor of another endian order as well as in the
- implementation of networking based Remote Procedure Calls, etc.
- Moreover, the two following functions should be provided:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int16_t\SpecialChar ~
-_bswap16(u_int16_t)
-\emph default
- Swaps the order of the two bytes held in the supplied 16-bit word and returns
- the result.
- For instance, 0x1234 becomes 0x3412.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-u_int32_t\SpecialChar ~
-_bswap32(u_int32_t)
-\emph default
- Reverses the order of the four bytes held in the supplied 32-bit word and
- returns the result.
- For instance, 0x12345678 becomes 0x78563412.
-\layout Paragraph
-
-String library optimizations support
-\layout Standard
-
-Note that the following are not necessary to consider if a specially optimized
- library functions for string and memory are implemented in assembly for
- the architecture.
-\layout Standard
-
-Every other architecture should
-\emph on
-#define
-\emph default
- the architecture-specific
-\emph on
-__ARCH_INT_BITS
-\emph default
- macro, which should be set to the native word size used by the particular
- processor for int, using the compiler.
- This should be expressed in bits, not in bytes.
- The most common value is 32, but it can vary.
-\layout Standard
-
-It is also important to
-\emph on
-#define
-\emph default
- the
-\emph on
-_ARCH_LOWCACHE
-\emph default
- macro (with no value), if it is beleived that loop unrolling is of no benefit.
- This can be the case on architectures with very low instruction caches,
- or ones which are using none.
- If loop unrolling is wanted, this should not be defined.
-\layout Standard
-
-The
-\emph on
-_ARCH_USEINDEXING
-\emph default
- macro also should be
-\emph on
-#defined
-\emph default
- with no value if it is beleived that the compiler generates better code
- for this particular processor when using indexed instructions rather than
- many post-increment/pre-decrement pointer based instructions.
- For instance, the i386 processor has no such special support, and using
- indexing can generate better code using GCC2.
- For m68k, it is usually better not to use indexing.
- Note that this is only taken in consideration if
-\emph on
-_ARCH_LOWCACHE
-\emph default
- is not defined, as it only affects loop unrolling of the C string and memory
- library.
-\layout Standard
-
-These definitions are expected to be found in the
-\emph on
-support.h
-\emph default
- file for every particular processor.
-\layout Paragraph
-
-CPU-saving
-\layout Standard
-
-On CPUs which support this, it is very useful to not hug the processor constantl
-y in a loop where the only event that is awaited for consists of an interrupt.
- On microprocessors which do not provide such a feature, it is safe to just
- make this function do nothing.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_idle(void)
-\emph default
- Suspends execution of instructions by the current processor until the next
- interrupt, trap or exception occurs.
-\layout Paragraph
-
-Interrupt level control
-\layout Standard
-
-Most microprocessors support several interrupt levels, where the higher
- the level the better precedence of execution over others.
- Manipulating the interrupt priority level (IPL) using Set Priority Level
- functions becomes useful for critical code sections which need to disable
- all interrupts at the specified level and under.
- Although port-specific code attempts to provide it's own finer grained
- interrupt control code when considered required, these should be available.
- The
-\emph on
-_ipl_t
-\emph default
- type itself is left to be defined with
-\emph on
-typedef
-\emph default
- by the processor-specific code to the best variable type to hold the processor
- IPL state.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_ipl_t\SpecialChar ~
-_spl
-\emph default
-n
-\emph on
-(void)
-\emph default
- Immediately sets the current priority level to the one specified by
-\emph on
-n
-\emph default
-.
- Thus, functions such as
-\emph on
-_spl0()
-\emph default
-,
-\emph on
-_spl1()
-\emph default
-,
-\emph on
-_spl2()
-\emph default
-, etc should be provided, for each interrupt level.
-
-\emph on
-_spl0()
-\emph default
- should enable all interrupt levels to occur.
- The returned value serves to eventually restore the previous interrupt
- level using
-\emph on
-_splx()
-\emph default
-, and is an abstract type defined by the processor-specific code.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_ipl_t\SpecialChar ~
-_splhigh(void)
-\emph default
- Usually a macro to the highest
-\emph on
-_spl
-\emph default
-n
-\emph on
-()
-\emph default
- function, it disables all interrupts by setting the highest priority level,
- thus masking all interrupts.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_splx(_ipl_t\SpecialChar ~
-state)
-\emph default
- Permits to restore the previous interrupt priority level which was active
- before a call to an
-\emph on
-_spl
-\emph default
-*
-\emph on
-()
-\emph default
- function was made, using the state value which was supplied by them.
-\layout Subsubsection
-
-m68k
-\layout Standard
-
-The Motorola MC68000L8 and MC68000L10 processors were the first to run Xisop.
- No support for MMU is present, such memory management units usually come
- on other external circuits for this processor, and there are various types.
- As Xisop does not need MMU, those processors were an ideal target.
- Although internally built as 16-bit processors, these processors behave
- as 32-bit ones from the programming point of view.
- It is also expected to run the same (or with minor modifications to disable
- their MMU) on the other processors of the 680x0 family, which are fully
- 32-bit.
- The assembly code observes the following rules to properly work with GCC
- compiled C modules:
-\layout Itemize
-
-Stack pointer consists of A7/SP and frame pointer of A6/FP.
- In supervisor mode, the user stack pointer consists of USP and specialized
- instructions need to be used to access and manipulate it.
-\layout Itemize
-
-Registers A0 and D0 serve the special purpose of return code for functions.
- Where the C code expects A0 or D0 to be used depends on the function prototype
- as seen from C.
- Functions expected to return a pointer should do so in A0, and D0 is used
- for other integer values.
- Assembly functions expected to be called from C must have a C function
- prototype defined in a headerfile which the C code must include.
-\layout Itemize
-
-Assembly functions save all the registers they modify, and restore them
- before returning.
- As such, functions returning nothing (void) are expected to never return
- with a different register state.
- However, this is not always true of GCC generated code.
- Of course, the D0 register in the case of integer returning functions,
- and A0 for pointer returning ones should as expected modify the corresponding
- register as well.
- Registers to be saved are pushed on the stack, which grows upwards.
-\layout Itemize
-
-Because there are various registers which GCC generated code modifies without
- always saving, exception handlers save all general purpose registers A0-A6
- and D0-D7 registers, and restore them before returning with RTE.
- It was a false assumption to previously only backup A0 and D0 which are
- used as function return codes, and many problems occurred back then with
- this attempt.
-\layout Itemize
-
-When an assembly function calls a C function, it needs to push the arguments
- in the stack, growing upwards, in reverse order, before calling it.
- After the C function returned, the stack pointer should be updated by additioni
-ng the number of bytes that were pushed.
- In the case where 16-bit or 8-bit values are passed as arguments, GCC still
- expects a stack entry of 32-bit size.
-\layout Itemize
-
-An assembly function expected to be called from C must obtain the arguments
- (if any) from the stack.
- These are ordered growing downwards, as they were inserted in reverse order
- in the stack, growing upwards.
- Obviously, when registers are temporarily saved on the stack to preserve
- their state and restore them before returning, the offset at which these
- parameters are found on the stack changes, and the code has to account
- for this.
-\layout Itemize
-
-The
-\emph on
-_ctx_t
-\emph default
- manipulation functions had to be implemented as follows:
-\emph on
-_ctx_init()
-\emph default
- can be called from usermode and only creates a context with zero registers,
- etc.
- However,
-\emph on
-_yield()
-\emph default
- had to be implemented using a trap to execute in supervisor mode, and the
- scheduler preemptive interrupt also executes in supervisor mode.
- Both save the current context to
-\emph on
-root->curctx
-\emph default
-, call
-\emph on
-schedule()
-\emph default
- and load back the context from
-\emph on
- root->curctx
-\emph default
-, as expected.
- They need to use privileged instructions to change SR (status register)
- and USP (user stack pointer), because SR/A7 becomes the SSP (supervisor
- stack pointer) when in supervisor mode.
- To make sure to respect the PC (program counter) address of the contexts,
- they are manipulated on the supervisor stack (SSP), where the m68k saves
- them when jumping to the exception handler.
- As such, RTE (return from exception) automatically jumps where it should
- for the current context.
- The offset to the SR 16-bit register is usually %sp@ and the one for PC
- 32-bit one in %sp@(2) when initially intering the trap or interrupt exception.
- This offset has to be recalculated as registers are being saved on the
- stack, of course.
-\layout Standard
-
-Other m68k specific notes about aspects which had to be taken in consideration:
-\layout Itemize
-
-At kernel initialization, room for the supervisor stack pointer needs to
- be setup, and the Supervisor Stack Pointer (SSP/A7) should be set properly.
- To do this it is necessary to go in supervisor mode, and then set the A7
- register to the right address.
- The way this must be done depends on the architecture.
- Because at bootup ROM code may have taken control already and one must
- use it's own facilities to obtain supervisor privileges.
-\layout Itemize
-
-Dropping to user state from supervisor state to call Xisop
-\emph on
-main()
-\emph default
- is rather simple.
- 1024 bytes are taken from the current SSP (A7), and assigned to the USP.
- The supervisor bit in SR is then unset, and a jump to
-\emph on
-main()
-\emph default
- is made.
- The function is very tiny and only ensures to launch the various initial
- tasks, then waits forever in a loop using
-\emph on
-_idle()
-\emph default
- calls via
-\emph on
-sys_idle()
-\emph default
-.
- It corresponds to the
-\emph on
-_scontext
-\emph default
-
-\emph on
-_ctx_t
-\emph default
- in
-\emph on
-root->curctx
-\emph default
- when no tasks are on the ready queue.
- Such a small stack buffer is then safe.
-\layout Itemize
-
-Using GCC 2.95.3, -O2 and -fomit-frame-pointer compilation directives seem
- to generate both well optimized and small m68k code.
- I however noted that using -fno-function-cse was also required with -O2,
- without which the resulting code crashed.
- -m68000 was used to generate true MC68000 code (no support for 020+ specific
- instructions which wouldn't run on a plain 68000).
-\layout Itemize
-
-Although m68k is very good to produce position-independent code, the default
- output GCC produces still comports instructions using direct addressing,
- except when -fpic is used, in which case additional symbols, with a .got
- table need to also be in the code, even if m68k generally doesn't require
- these for position-independent code (it has all the relative addressing
- instructions required for large memory model).
- As the only solution I found to properly relocate the code upon loading
- would be to write an ELF or a.out loader, which isn't done yet, the kernel
- code is loaded at a specific location, defined before compilation.
- A GCC ld BFD backend will need to be written, or an ELF loader, to allow
- to relocate the kernel, as well as file executable binaries.
-\layout Itemize
-
-The
-\emph on
-_spl
-\emph default
-n
-\emph on
-()
-\emph default
- and
-\emph on
-_splhigh()
-\emph default
- functions were implemented as macros, calling the assembly
-\emph on
-_spl()
-\emph default
- function which takes a 16-bit argument (the new SR to apply).
-
-\emph on
-_splx()
-\emph default
- assembly functions restores the previous SR.
- As SR is 16-bit, the
-\emph on
-_ipl_t
-\emph default
- type was defined as an
-\emph on
-u_int16_t
-\emph default
- internally.
- This allowed to generate very compact code for the eight interrupt priority
- level control functions which m68k required.
-\layout Itemize
-
-The
-\emph on
-_lock_
-\emph default
-*
-\emph on
-()
-\emph default
- functions were implemented using the TAS instruction for atomicity, and
- the
-\emph on
-_lock_t
-\emph default
- data type was internally defined as an
-\emph on
-u_int8_t
-\emph default
-.
-\layout Itemize
-
-The
-\emph on
-_rlock
-\emph default
-*
-\emph on
-()
-\emph default
- functions could be implemented without the use of an internal
-\emph on
-_lock_t
-\emph default
- to guarrantee atomicity, because the m68k processor is capable of addition
- and substraction on a 32-bit value in a single instruction.
- An
-\emph on
-_rlock_t
-\emph default
- consists of a
-\emph on
-int32_t
-\emph default
- for this architecture.
-\layout Itemize
-
-Before the port-specific code calls Xisop
-\emph on
-main()
-\emph default
-, it is necessary to switch back to user processor mode.
- The
-\emph on
-_usermode()
-\emph default
- function was implemented for this and added to the m68k set of processor-specif
-ic functions, which allows to create a 1024 bytes user stack from the current
- supervisor stack, switches to usermode, and jumps to the specified function.
-\layout Subsection
-
-Port specific notes
-\layout Subsubsection
-
-Required backend and support functions
-\layout Standard
-
-Each architecture needs a specific initialization section, such as setting
- up exceptions, interrupts and memory.
- Although this can also be CPU-specific, the various architectures using
- the same processor would most likely still need these to be different.
- They are thus considered as port-specific low-level backend support.
- Similarly to the processor-specific support code,
-\emph on
-support.h
-\emph default
- and
-\emph on
-ar/*.a
-\emph default
- are the main targets that should be provided to allow the rest of the kernel
- to use it, as it will include
-\emph on
-port/support.h
-\emph default
- and will link in
-\emph on
-port/ar/
-\emph default
-*.
-\emph on
-a
-\emph default
-, where
-\emph on
-port
-\emph default
- consists of a symbolic link to the actual
-\emph on
-ports/<name>
-\emph default
- directory.
-\layout Standard
-
-It is allowed if desired to also supply port specific functions to replace
- some of the common machine-independent ones, which may be desired for speed
- at occasions.
- When this is done, the functions should behave identically as expected,
- should bear the same names, and no prototypes should be provided for them
- in
-\emph on
-support.h
-\emph default
-.
- The port-specific
-\emph on
-ar/*.a
-\emph default
-modules will be linked before the processor-specific and Xisop common libraries
- and those functions will replace them if they are supplied.
- These functions could be for instance:
-\emph on
-memcmp()
-\emph default
-,
-\emph on
- memcpy()
-\emph default
-,
-\emph on
-memset()
-\emph default
-, etc.
-\layout Standard
-
-After the kernel was built, using both common, processor and port code,
- control is left again to the port specific code which should handle boot
- support; This can be creating a floppy image, etc.
- See the section on the building process for more information.
-\layout Standard
-
-It is important that the port-specific code provides the
-\emph on
-_start
-\emph default
- entry point.
- This code should then set the processor in supervisor mode where required,
- setup the supervisor mode stack pointer, disable interrupts and perform
- initialization of the various systems described below.
- Afterwards, it should switch back to userstate processor mode, and leave
- control to the Xisop
-\emph on
-main()
-\emph default
- function, which will not return.
- That
-\emph on
-_start
-\emph default
- entry point is where the boot kernel loader needs to jump to.
-\layout Paragraph
-
-Memory initialization and requirements
-\layout Standard
-
-The port-specific
-\emph on
-support.h
-\emph default
- should define C enumerators (enum) and definitions (#define) for the machine-in
-dependent Xisop memory code.
- It is recommended to also read the section on Xisop memory management for
- more information.
- The port-specific requirements are explained as follows:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-#define\SpecialChar ~
-_PAGE_SIZE\SpecialChar ~
-<value>
-\emph default
- The memory management system needs to know the amount of bytes in which
- to split pages.
- On operating systems which support Memory Management Units (MMU), this
- is required to match the page size which the hardware requires.
- In the case of Xisop, it is safe to use any reasonable multiple of 16 bytes
- here.
- A common
-\emph on
-_PAGE_SIZE
-\emph default
- is 4096 bytes.
- On systems with very low memory it may be useful to use 1024, or even 256.
- This value is used by the page memory allocation primitives.
-\layout Standard
-
-The
-\emph on
-mpool_t
-\emph default
-, a multi-purpose memory pool which allows management primitives such as
-
-\emph on
-_malloc()
-\emph default
- and
-\emph on
-_free()
-\emph default
-, requires specific definitions:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-#define\SpecialChar ~
-_MPOOLS\SpecialChar ~
-<value>
-\emph default
- This is the number of internal
-\emph on
-pool_t
-\emph default
- which are necessary to initialize for an
-\emph on
-mpool_t
-\emph default
-, to be able to use these efficient pools when dealing with byte requirements
- which are too small to be rounded at page boundaries.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-#define\SpecialChar ~
-_MPOOLSTART\SpecialChar ~
-<value>
-\emph default
- The smallest amount of bytes which an
-\emph on
-mpool_t
-\emph default
- can allocate, which is multiplied by two for each consecutive
-\emph on
-pool_t
-\emph default
- initialized for the
-\emph on
-mpool_t
-\emph default
- at
-\emph on
-mpool_init()
-\emph default
-.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-#define\SpecialChar ~
-_MPOOLSTEP\SpecialChar ~
-<value>
-\emph default
- The number of physical
-\emph on
-_PAGE_SIZE
-\emph default
- bytes pages into a
-\emph on
-page_t
-\emph default
- for every
-\emph on
-pool_t
-\emph default
- of an
-\emph on
-mpool_t
-\emph default
-.
-\layout Standard
-
-To explain better the previous definitions, what the
-\emph on
-mpool_init()
-\emph default
- function actually does is iterate
-\emph on
-_MPOOLS
-\emph default
- times to initialize the
-\emph on
-pool_t
-\emph default
- objects, setting the first
-\emph on
-pool_t
-\emph default
- to allocate units of
-\emph on
-_MPOOLSTART
-\emph default
- bytes, the second
-\emph on
-_MPOOLSTART
-\emph default
- * 2, and continueing to iterate multiplying the unit size by two until
-
-\emph on
-_MPOOLS
-\emph default
- number of
-\emph on
-pool_t
-\emph default
- were initialized.
- Larger unit sizes which cannot be handled by the
-\emph on
-pool_t
-\emph default
- will be rounded at page boundaries.
-
-\emph on
-_MPOOLSTEP
-\emph default
- simply consists of the
-\emph on
-stepp
-\emph default
- argument to
-\emph on
-pool_init()
-\emph default
-.
- As such, all the definitions above intimately correlate to eachother, and
- are quite versatile to match various requirements an architecture may need.
-\layout Standard
-
-For a system with a
-\emph on
-_PAGE_SIZE
-\emph default
- of 4096 bytes, an
-\emph on
-_MPOOLSTART
-\emph default
- of 16 and
-\emph on
-_MPOOLSTEP
-\emph default
- of 1, 7 consists of an ideal value for
-\emph on
-_MPOOLS
-\emph default
-.
- On a system with a fair amount of memory, if it is wanted to minimize calls
- to the page management primitives even more, it is possible to set a larger
-
-\emph on
-_MPOOLSTEP
-\emph default
- and raise
-\emph on
-_MPOOLS
-\emph default
- accordingly, while keeping the same underlaying
-\emph on
-_PAGE_SIZE
-\emph default
-.
- Basically
-\emph on
-_MPOOLS
-\emph default
- should be set just below the
-\emph on
-pool_init()
-\emph default
- breaking point, where it returns FALSE because
-\emph on
-sizeof(mnode_t)
-\emph default
- plus the current
-\emph on
-_MPOOLSTART
-\emph default
- multiple consist of too large objects to fit into a
-\emph on
-pool_t
-\emph default
- page (which in turn depends on
-\emph on
-_MPOOLSTEP
-\emph default
- which changes the
-\emph on
-page_t
-\emph default
- size for a
-\emph on
-pool_t
-\emph default
-).
-\layout Standard
-
-It is recommended to read the source for
-\emph on
-mpool_init()
-\emph default
- which is located in
-\emph on
-src/common/kernel/memory.c
-\emph default
- for a better understanding, as well as the documentation on Xisop memory
- management primitives.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-enum\SpecialChar ~
-_memtypes
-\emph default
- This enumeration should define the various memory types the architecture
- provides, in preference order when
-\emph on
-_MEM_ANY
-\emph default
- is used (-1) when calling the allocation primitives.
- The enumeration should set
-\emph on
-_MEM_
-\emph default
-* names, the first one corresponding to 0, and the last element should consist
- of
-\emph on
-_MEM_MAX
-\emph default
-, corresponding to the number of memory types the system provides.
- Not all architectures provide more than a single memory type, under which
- case
-\emph on
-_MEM_ALL
-\emph default
- will correspond to 0 and
-\emph on
-_MEM_MAX
-\emph default
- to 1, respectively.
-\layout Standard
-
-Other than defining these requirements in it's
-\emph on
-support.h
-\emph default
- headerfile, the port-specific initialization code is responsible for attaching
- the available physical memory pages to the system pools, before initializing
- the public interrupt facilities.
- This is done by first calling the
-\emph on
-memory_init()
-\emph default
- machine-independent function.
- Then, the
-\emph on
- mchunk_init()
-\emph default
- and
-\emph on
-mchunk_attach()
-\emph default
- functions which are documented in the Xisop memory management section are
- normally called once for each contiguous memory area which is to be used
- as general purpose memory.
- The video memory, or other special memory sections should not be included
- in the system memory pools.
- It is important to perform this step before continuing on with the next
- initialization sections.
- The
-\emph on
-mchunk_init()
-\emph default
- and
-\emph on
-mchunk_attach()
-\emph default
- functions are described in detail in the Xisop memory management section.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-memory_init(void
-\emph default
-) This function is only called once by the port-specific initialization
- code before starting to attach memory chunks to the system pools.
- After this function executes, it becomes possible to use the
-\emph on
-mchunk_init()
-\emph default
- and
-\emph on
-mchunk_attach()
-\emph default
- functions to attach contiguous memory regions to the system page pools.
-\layout Paragraph
-
-Exceptions initialization and public interrupt facilities
-\layout Standard
-
-It is recommended to maintain an
-\emph on
-_interrupt_depth
-\emph default
- variable or recursive lock, which each trap or interrupt handler should
- increase at startup and decrease before returning, which can be used for
- the scheduler interrupt to determine if it should call
-\emph on
-schedule()
-\emph default
- or not.
- As the scheduler timer interrupt would also raise it at startup, it can
- then mask interrupts and evaluate if it equals 1 afterwards, in which case
- it is sure to have the right to perform a context switch.
- This permits to make system calls uninterruptible for the time of their
- execution and to protect the scheduler from performing context switches
- while an interrupt handler is executing, which at occasions could result
- in recursivity and context corruption.
- As an effort is made to minimize the number of system call traps required
- during normal Xisop function, the preemptive nature of the scheduler is
- not bothered by having uninterruptible system calls, unless a task voluntarily
- abuses
-\emph on
-sys_custom()
-\emph default
-, which is by all meals legal if one wants to.
-\layout Standard
-
-Obviously, most exception handlers are responsible to return with the registers
- unchanged, and as such should normally save all general-purpose registers
- on the stack at startup, and return them before returning.
- This is especially true if C functions are to be called from the interrupt
- handler, where unexpected registers may be tempered with.
-\layout Standard
-
-After setting up the memory, the public interrupt facilities can be defined
- to the system and their internal handler vectors setup.
- Usually,
-\emph on
-facilities_init()
-\emph default
- will be called before vectors are initialized, or if not possible, dummy
- do-nothing vectors can be installed, and can then be setup definitely after
- calling f
-\emph on
-acilities_init()
-\emph default
-, when it becomes safe.
-\layout Standard
-
-This function call depends on the
-\emph on
-enum _facilities
-\emph default
- C enumerator which should be defined in the port specific
-\emph on
-support.h
-\emph default
- headerfile.
- This enumerator defines each facility in the form of
-\emph on
-_FACILITY_
-\emph default
-*, where * consists of the name of the facility.
- The first entry should evaluate to 0, and the last one to the total number
- of facilities (
-\emph on
-_FACILITY_MAX
-\emph default
-).
- The various interrupt handlers need to internally call
-\emph on
-facility_exechooks()
-\emph default
- on the facility they are serving for the public facilities to become alive.
- At it's discretion, the handler may temporarily disable the interrupt source
- when calling the function, but
-\emph on
-facility_exechooks()
-\emph default
- internally performs recursion prevention and makes sure to not execute
- the hooks if a hook is currently being inserted or removed, using an
-\emph on
-_rlock_t
-\emph default
- for each facility internally.
-\layout Standard
-
-There only is at least one facility which is required for all ports to provide.
- This facility should be named
-\emph on
-_FACILITY_SCHEDTIMER
-\emph default
-, and should call the hooks at
-\emph on
-_SCHEDTIMER_HZ
-\emph default
- frequency, which should also be defined by the port-specific
-\emph on
-support.h
-\emph default
-.
- This way, simple time-based Xisop applications can work portably on all
- ports.
- This facility should correspond to the timer interrupt which the port chose
- to use for the preemptive scheduler timer.
- This facility does not interfere with the scheduler activities; it is called
- when the interrupt occurs even if the scheduler
-\emph on
-rlock_t
-\emph default
- is set (
-\emph on
-schedule()
-\emph default
- handles the scheduler locked/disabled case already).
- Generally, the scheduler interrupt handler works as follows:
-\layout Itemize
-
-Increase the global
-\emph on
-_interrupt_depth
-\emph default
- variable like for all handlers
-\layout Itemize
-
-Temporarily disable the interrupt source (by raising the IPL using
-\emph on
-_spl
-\emph default
-*
-\emph on
-()
-\emph default
- or otherwise) to prevent any possible recursion or other interruption.
-\layout Itemize
-
-Save the current user CPU context to the
-\emph on
-root->curctx _ctx_t
-\layout Itemize
-
-Execute the facility hooks using
-\emph on
-facility_exechooks(_FACILITY_SCHEDTIMER)
-\layout Itemize
-
-Verify if the
-\emph on
-_interrupt_depth
-\emph default
- variable equals to 1.
- If so, call
-\emph on
-schedule(NULL)
-\emph default
-, which may or may not change the
-\emph on
-root->curctx
-\emph default
- backed up context pointer and
-\emph on
-root->curtask
-\layout Itemize
-
-Load back the CPU context from the new
-\emph on
-root->curctx
-\emph default
- (which possibly can be the same, but this must not be assumed)
-\layout Itemize
-
-Re-enable the scheduler interrupt source
-\layout Itemize
-
-Decrease the global
-\emph on
-_interrupt_depth
-\emph default
- variable like for other handlers
-\layout Itemize
-
-Return from interrupt handler while ensuring to jump to the PC of the new
- context.
- Generally, the address to return to is backed up into the supervisor stack,
- which needs to be modified for this.
- That address within the supervisor stack pointer is where context save
- and load operations obtain and set the Program Counter address.
-\layout Standard
-
-Because of the context load/save operations, and return address hack, the
- scheduler interrupt handler is usually implemented entirely in assembly
- (although it calls the
-\emph on
-schedule()
-\emph default
- and
-\emph on
-facility_exechooks()
-\emph default
- C functions).
-\layout Standard
-
-The other facilities, which are optional and can be provided by the port-specifi
-c code will often be implemented as a mix of assembly and C code and will
- similarily at least:
-\layout Itemize
-
-increase global
-\emph on
-_interrupt_depth
-\emph default
- and optionally disable interrupt source
-\layout Itemize
-
-save registers
-\layout Itemize
-
-perform any additional wanted operation
-\layout Itemize
-
-call
-\emph on
-facility_exechooks()
-\emph default
- on their facility
-\layout Itemize
-
-restore registers
-\layout Itemize
-
-re-enable the interrupt source if it was temporarily disabled, and decrease
- global
-\emph on
-_interrupt_depth
-\layout Itemize
-
-return
-\layout Standard
-
-The facility public interface and
-\emph on
-facility_exechooks()
-\emph default
- are described in more details in the Xisop public facilities section.
-\layout Paragraph
-
-System trap triggers and handlers initialization
-\layout Standard
-
-It is important for the port-specific code to define the
-\emph on
-_syscall()
-\emph default
- and
-\emph on
-_yield()
-\emph default
- functions.
- The role of the system call trap handler is to serve system call functions
- uninterruptibly, internally calling
-\emph on
-_scatch()
-\emph default
- Xisop common function with the requested arguments.
- Here is described the trigger, which function should be supplied by the
- port-dependent code:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_syscall(u_int32_t\SpecialChar ~
-function,\SpecialChar ~
-void\SpecialChar ~
-*res,\SpecialChar ~
-void\SpecialChar ~
-*args)
-\emph default
- Internally places the supplied parameters into a static buffer or in registers,
- and generate a processor trap interrupt.
- It is safe to save the registers we modify on the stack, and restore them
- from the stack after the trap returns, and then return ourselves, because
- nor
-\emph on
-_yield()
-\emph default
- nor context switching are implemented via syscalls.
- Although the understanding of the arguments is not necessary at this point,
-
-\emph on
-function
-\emph default
- specifies the syscall number which is to be performed,
-\emph on
-res
-\emph default
- a pointer to eventual results expected from that system call (or NULL),
- and
-\emph on
-args
-\emph default
- optional arguments which need to be passed to the system call (or NULL).
- Although this function is also highly processor-specific, the choice of
- the trap vector to implement system calls is left to the port writer, and
- as such this function as well.
-\layout Standard
-
-The other end, consisting of the system call trap handler, is responsible
- for the following:
-\layout Itemize
-
-Increment
-\emph on
-_interrupt_depth
-\emph default
- global variable
-\layout Itemize
-
-Save all general purpose registers
-\layout Itemize
-
-Read arguments supplied by
-\emph on
-_syscall()
-\emph default
- from the static buffer or registers, and insert them on the stack as C
- arguments, then call
-\emph on
-_scatch()
-\emph default
- C function.
- Fix the stack pointer to forget the pushed stack arguments.
-\layout Itemize
-
-Restore general purpose registers we saved
-\layout Itemize
-
-Decrement
-\emph on
-_interrupt_depth
-\emph default
- global variable
-\layout Itemize
-
-Return
-\layout Standard
-
-The
-\emph on
-_scatch()
-\emph default
- function (which is defined in
-\emph on
-src/common/kernel/syscall.c
-\emph default
-) is responsible for performing the necessary sanity checking on the arguments,
- and does not need to be provided by the machine-specific code:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_scatch(u_int32_t\SpecialChar ~
-function,\SpecialChar ~
-void\SpecialChar ~
-*results,\SpecialChar ~
-void\SpecialChar ~
-*arguments)
-\emph default
- Consists of the heart of the syscall trap.
-
-\emph on
-function
-\emph default
- specifies the requested syscall function number which was called.
- These are standard and are described in the
-\begin_inset Quotes eld
-\end_inset
-
-System Calls
-\begin_inset Quotes erd
-\end_inset
-
- section.
-
-\emph on
-results
-\emph default
- consists of a pointer to the block of memory which will be modified to
- store the syscall results by this
-\emph on
-function
-\emph default
-.
- It can be NULL.
-
-\emph on
-arguments
-\emph default
- similarly specifies the location of the arguments expected for this
-\emph on
-function
-\emph default
-, or NULL.
- This function refuses to perform any call if the supplied
-\emph on
-function
-\emph default
- is invalid (out of bounds).
-\layout Standard
-
-After setting up the
-\emph on
-_syscall()
-\emph default
- trap vector, the interrupts can remain disabled/masked still, like since
- the beginning.
- More information on the generic user system calls interface is provided
- in the Xisop system calls section.
-\layout Standard
-
-Another requirement that the port-specific code must satisfy consists of
- the
-\emph on
-_yield()
-\emph default
- internal function:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-_yield(task_t\SpecialChar ~
-*task)
-\emph default
- permits the current task to immediately perform a context switch to another
- task, in fact preempting itself.
-
-\emph on
-task
-\emph default
- is an optional preference of which task to switch to, or NULL, which parameter
- should be passed when the trap handler internally calls
-\emph on
-schedule()
-\emph default
-.
- This is usually implemented in the form of a trap like for
-\emph on
-_syscall()
-\emph default
-, which sets the supplied argument in a static buffer to prevent modifying
- a register (it is unsafe to save registers on the stack as we most likely
- won't be the next task to return from the trap).
- The backend trap handler acts as follows:
-\layout Itemize
-
-Increase the global
-\emph on
-_interrupt_depth
-\emph default
- variable like for all handlers
-\layout Itemize
-
-Temporarily disable all interrupt sources (by raising the IPL using
-\emph on
-_splhigh()
-\emph default
- or equivalent to prevent any possible interruption, but without modifying
- registers, which can be saved and restored safely before performing the
- next steps
-\layout Itemize
-
-Save the current user CPU context to the
-\emph on
-root->curctx _ctx_t
-\layout Itemize
-
-Insert the supplied argument from the static buffer in the stack as a C
- argument
-\layout Itemize
-
-Call
-\emph on
-schedule()
-\emph default
-, which may or may not change the
-\emph on
-root->curctx
-\emph default
- backed up context pointer and
-\emph on
-root->curtask
-\layout Itemize
-
-Adjust stack pointer to forget the passed argument
-\layout Itemize
-
-Load back the CPU context from the new
-\emph on
-root->curctx
-\emph default
- (which possibly can be the same, but this must not be assumed)
-\layout Itemize
-
-Re-enable interrupts calling
-\emph on
-_spl0()
-\emph default
- or performing equivalent taking care not to modify registers.
- Using the stack is now safe.
- (remember that the old level obtained from
-\emph on
-_splhigh()
-\emph default
- cannot be obtained back unless saved to a static buffer, in which case
- it can be restored properly.
- Saving it on the stack is also safe if the context switching function only
- modified the user stack pointer and the supervisor stack pointer consists
- of the active stack during the trap).
-\layout Itemize
-
-Decrease the global
-\emph on
-_interrupt_depth
-\emph default
- variable like for other handlers
-\layout Itemize
-
-Return from interrupt handler while ensuring to jump to the PC of the new
- context.
- Generally, the address to return to is backed up into the supervisor stack,
- which needs to be modified for this.
- That address within the supervisor stack pointer is where context save
- and load operations obtain and set the Program Counter address.
-\layout Paragraph
-
-Suggestions
-\layout Standard
-
-The rest of the port-specific code internals which it needs to perform are
- left to the implementor, as long as they suit well the purpose.
- However, a few suggestions are made which can help to keep some consistency
- among the various ports, in their choice of function names for instance.
- This example attempts to restrict the assembly code to the minimum, while
- calling C functions as much as possible to handle most exception code.
-\layout Standard
-
-It is generally a good idea for hardware interrupts to provide one separate
- assembly handler per interrupt level, to prevent the C code from having
- to perform unnecessary additional conditional instructions to evaluate
- the level, as it already usually needs to detect the source.
- For other general-purpose trap vectors, it is allowed to provide support
- to execute a single C function for all of them, passing in an argument
- the required information on the trap vector number.
- This however would be less desireable than having a different facility
- for each, if their frequency was high and a large number of hooks were
- attached, because they then would obviously all run often, evaluating one
- by one if they are interested in the trap.
- Suggestion names for various common facility types are shown in the Xisop
- public interrupt facilities section.
-\layout Standard
-
-It is to be noted that the timer interrupt chosen to be used as the preemptive
- scheduler one is special as it needs to perform context switching, often
- also implying stack pointer access and modifications.
- This handler is therefore usually fully written in assembly, as previously
- demonstrated.
- As a general rule, the role of the exception vectors is to call a C function
- which can then handle the event.
-\layout Standard
-
-Here are various C functions which can be called by the machine language
- backend to exceptions, traps and interrupts:
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_icatch
-\emph default
-n
-\emph on
-(void)
-\emph default
- For every hardware interrupt level, une such C function can be called.
- For interrupt level 3,
-\emph on
-icatch3()
-\emph default
- would be called, for instance.
- It is possible to pass parameters if required to detect the interrupt source
- in the C functions.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_tcatch(int\SpecialChar ~
-vector)
-\emph default
- This C function can be called for all software traps which occur that do
- not correspond to the syscall or yield trap vectors.
-
-\emph on
-vector
-\emph default
- argument specifies the number of the trap vector.
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_ecatch(int\SpecialChar ~
-vector)
-\emph default
- A C function called when an hardware exception is generated, such as bus
- error, division by zero, etc.
- It is recommended that the code processing these do not hang or crash the
- system permanently if high-reliability is required.
-
-\emph on
-vector
-\emph default
- consists of the exception vector, or reason which caused it, and
-\emph on
-stack
-\emph default
- points as usual to the retrurn from exception address on the stack.
- Although this is CPU-specific, port-specific exception handling code may
- be provided.
-\layout Paragraph
-
-Port-specific system shared libraries to attach and system tasks to launch
-\layout Standard
-
-Although Xisop has a few common portable system libraries and tasks which
- it initializes at startup, it is ideal for the port-specific section to
- be able to describe which other tasks should be launched, and their parameters
- such as priority level, stack size, etc.
- To do this the port-specific code needs to provide the
-\emph on
-_port_init()
-\emph default
- function which the Xisop init task will invoke and which then can use the
- required flexibility.
- It should be noted that as the init task only has a 4096 bytes stack, this
- function is expected to at least create a task with a larger stack if it
- needs to before performing it's own initialization if it overuses the stack.
- The kernel expects the current state to remain the same when the function
- returns (apart of course from the new libraries and tasks which may now
- exist and be resident).
-\layout List
-\labelwidthstring 00.00.0000
-
-
-\emph on
-void\SpecialChar ~
-_port_init(void)
-\emph default
- Port-specific function who's purpose is to attach the wanted port-specific
- libraries, and to launch the wanted port-specific tasks.
- This function is called by the Xisop
-\emph on
-init
-\emph default
- task which runs with a stack of 4096 bytes, and runs in userstate mode
- like normal tasks.
-\layout Paragraph
-
-Last steps of the port-specific initialization code
-\layout Standard
-
-After switching to supervisor mode, setting up memory, system call and yield
- traps, as well as scheduler timer interrupt, and initializing the public
- interrupt facilities, port-specific initialization code is then complete,
- and it now should execute the following steps:
-\layout Itemize
-
-Call the machine-independent
-\emph on
-xisop_init()
-\emph default
- function, which sets up various internal structures and disables the scheduler,
- and then internally calls
-\emph on
- _spl0()
-\emph default
- as interrupts finally become safe to enable.
-\layout Itemize
-
-Call the famous Xisop machine-independent
-\emph on
-main()
-\emph default
- function which is expected to never return.
- If the processor currently runs into supervisor mode, it is necessary to
- drop to usermode before calling
-\emph on
-main()
-\emph default
-.
- The role of this function is to enable the scheduler, launch the Xisop
-
-\emph on
-init
-\emph default
- task, which in turn will make sure to launch the
-\emph on
-task reaper
-\emph default
- task, and call the port-dependent
-\emph on
-_port_init()
-\emph default
- function which then can also attach and launch the wanted resources.
-\newline
-Note that the
-\emph on
-main()
-\emph default
- function with it's current stack becomes the initial context that the scheduler
- always switches to when there remains no tasks in the ready queue to run.
- As such, after launching the init task, it loops forever calling
-\emph on
-sys_idle()
-\emph default
- system call which internall calls processor-specific
-\emph on
-_idle()
-\emph default
-, which permits the processor to stop spinning until the next interrupt
- or trap event occurs.
-\layout Subsubsection
-
-Amiga
-\layout Itemize
-
-The Amiga port uses the
-\emph on
-processors/m68k
-\emph default
- processor-specific code.
-\layout Itemize
-
-In addition to the m68k
-\emph on
-_spl
-\emph default
-*
-\emph on
-()
-\emph default
- functions, the amiga low level library also supplies
-\emph on
-aspl
-\emph default
-*
-\emph on
-()
-\emph default
- functions using INTENA control register for finer grained control to disable
- certain interrupts for a period of time.
- For instance,
-\emph on
-asplvblank()
-\emph default
- disables the vertical blank interrupt,
-\emph on
-asplsched()
-\emph default
- disables the scheduler, etc.
-
-\emph on
-asplx()
-\emph default
- is used to restore the previous state, as usual.
- For
-\emph on
-asplsched()
-\emph default
-, a
-\emph on
-_lock_t
-\emph default
- is used to turn the scheduler ON/OFF, so that the timer interrupt it ties
- to still can execute other code if required.
-
-\emph on
-XXX
-\layout Itemize
-
-The chosen
-\emph on
-_syscall()
-\emph default
- trap vector was 0.
-\layout Itemize
-
-The chosen
-\emph on
-_yield()
-\emph default
- trap vector was 1.
-\layout Itemize
-
-The Amiga has four multi-purpose timers in it's two CIA chips.
- The use Xisop currently makes of them is as follows: CIA-A TimerA is reserved
- for keyboard timing, which is a hardware requirement.
- CIA-B Timer A is used by the Xisop scheduler, which generates high-level
- hardware interrupts of high priority (IPL 6).
- The B timers and TOD counters are unused and remain available for devices
- and user code for each CIA.
-\layout Itemize
-
-The two memory page pools (
-\emph on
-ppool_t
-\emph default
-) initialized at startup by
-\emph on
-_init_memory()
-\emph default
- consist of one for CHIP RAM, and another one for FAST RAM.
- The
-\emph on
-enum _memtypes
-\emph default
- as such set
-\emph on
-_MEM_FAST
-\emph default
- to 0 and
-\emph on
-_MEM_CHIP
-\emph default
- to 1, FAST memory being the prefered if
-\emph on
-_MEM_ANY
-\emph default
- is used.
- Currently, the addresses mapped in the standard distribution are 0x - 0x
- for CHIP (enhanced 2 megabytes agnus chip (fatter)), and 0x00200000-0x00600000
- for FAST (usual 4 first megabytes of ZorroII memory found on A2000).
- This currently needs to be modified in the code itself as the booting process
- currently does not detect the available RAM amounts.
- The provided UAE Amiga Emulator configuration file which is configured
- as such can be located in the
-\emph on
-src/ports/amiga/boot
-\emph default
- directory.
-
-\emph on
-XXX
-\layout Itemize
-
-To setup the initial supervisor stack, the AmigaOS SuperState() exec.library
- call must be used to gain supervisor privileges.
- The SSP/A7 register can then be set to the proper location.
- To do this a special function is provided in assembly by the Amiga support
- library,
-\emph on
-void\SpecialChar ~
-_supervisor(u_int32_t\SpecialChar ~
-*sp,\SpecialChar ~
-size_t\SpecialChar ~
-ssize\SpecialChar ~
-void (*func)(void))
-\emph default
- which allows to set the new entry point function and stack.
- To the provided
-\emph on
-stack
-\emph default
- pointer will be additionned the supplied stack size
-\emph on
-ssize
-\emph default
- automatically because of the stack which grows upwards.
- The supplied function
-\emph on
-func
-\emph default
- is then given control to.
- This function is expected to never return.
-\the_end
+++ /dev/null
-#!/bin/sh
-
-# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ./generic_makedefs.sh
-
-show cd processors/m68k
-./clean.sh
-show cd ../../ports/amiga
-./clean.sh
-show cd boot
-./clean.sh
-show cd ../../../common
-./clean.sh
-show cd ..
-show $L_RM processor port makedefs.sh
+++ /dev/null
-#!/bin/sh
-
-# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../generic_makedefs.sh
-
-show cd kernlib
-./clean.sh
-show cd ../kernel
-./clean.sh
-show cd ..
+++ /dev/null
-#!/bin/sh
-
-# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../generic_makedefs.sh
-
-cleanlib .
-show $L_RM ar/*.a
+++ /dev/null
-/* $Id: debug.c,v 1.3 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/debug.h>
-#include <common/kernel/main.h>
-#include <common/kernlib/fifo.h>
-#include <common/kernlib/string.h>
-#include <config.h>
-
-
-
-#ifdef DEBUG
-
-
-
-static fifo8_t fifo;
-static u_int8_t *fifobuf;
-
-
-
-static size_t btoa(char *, u_int32_t);
-static size_t dtoa(char *, int32_t);
-static size_t utoa(char *, u_int32_t);
-static size_t xtoa(char *, u_int32_t);
-static void fifowrite(const char *, size_t);
-
-
-
-static const char ddigits[] = "0123456789";
-static const char xdigits[] = "0123456789ABCDEF";
-
-
-
-void debug_init(void)
-{
- fifobuf = MALLOC(DEBUG);
- FIFO_INIT(&fifo, fifobuf, DEBUG);
- root->debugfifo = &fifo;
- _lock_init(&root->debuglock);
- /* Not ideal but considerably reduces kernel size compared to a static
- * array buffer. Has the disadventage that debugging is only available
- * after memory initialization.
- */
- debug_printf("Xisop debugging enabled\n");
-}
-
-
-void debug_printf(const char *fmt, ...)
-{
- debug_va_list ap;
- register char c;
- register const char *ptr = fmt, *optr;
-
- if (ptr == NULL)
- return;
-
- lock_acquire(&root->debuglock);
-
- debug_va_start(ap, fmt);
- optr = ptr;
- while ((c = *ptr++) != '\0') {
- if (c == '%') {
- ptr--;
- /* Output pending chars */
- if (optr < ptr)
- fifowrite(optr, ptr - optr);
- ptr++;
- /* "%\0" ! */
- if ((c = *ptr++) == '\0') {
- ptr--;
- break;
- }
-
- /* Process debug_va_arg */
- switch (c) {
- case '%':
- ptr--;
- break;
- case 'B': /* Boolean */
- {
- register bool t = debug_va_arg(ap, bool);
-
- if (t)
- fifowrite("TRUE", 4);
- else
- fifowrite("FALSE", 5);
- }
- break;
- case 'b': /* Binary 32-bit */
- {
- char buf[34];
-
- btoa(buf, debug_va_arg(ap, u_int32_t));
- fifowrite(buf, 33);
- }
- break;
- case 'c': /* Character */
- {
- char ch = debug_va_arg(ap, char);
-
- fifowrite(&ch, 1);
- }
- break;
- case 'd': /* 32-bit decimal */
- {
- char buf[12];
- size_t len;
-
- len = dtoa(buf, debug_va_arg(ap, int32_t));
- fifowrite(buf, len);
- }
- break;
- case 'p': /* 32-bit pointer */
- {
- char buf[11];
- register void *p = debug_va_arg(ap, void *);
-
- if (p != NULL) {
- xtoa(buf, (u_int32_t)p);
- fifowrite(buf, 10);
- } else
- fifowrite("NULL", 4);
- }
- break;
- case 's': /* String */
- {
- register const char *s = debug_va_arg(ap, char *);
-
- if (s != NULL)
- fifowrite(s, strlen(s));
- else
- fifowrite("<NULL>", 6);
- }
- break;
- case 'T': /* Current task and PC address, no va_arg() */
- {
- register task_t *t = CURTASK();
- register void *pc = root->curctx->pc;
- char str[24];
- register char *ptr = str;
-
- *ptr++ = '[';
- xtoa(ptr, (u_int32_t)t);
- ptr += 10;
- *ptr++ = '.';
- xtoa(ptr, (u_int32_t)pc);
- ptr += 10;
- *ptr++ = ']';
- *ptr = '\0';
- fifowrite(str, 23);
- }
- case 'u': /* 32-bit unsigned decimal */
- {
- char buf[11];
- size_t len;
-
- len = utoa(buf, debug_va_arg(ap, u_int32_t));
- fifowrite(buf, len);
- }
- break;
- case 'x': /* 32-bit hexadecimal */
- {
- char buf[11];
-
- xtoa(buf, debug_va_arg(ap, u_int32_t));
- fifowrite(buf, 10);
- }
- break;
- default: /* Display %<c>, it's a bug, and we debug! */
- {
- char s[2];
-
- s[0] = '%';
- s[1] = c;
- fifowrite(s, 2);
- }
- break;
- }
- /* Adjust optr for our pending chars record */
- optr = ptr;
- }
- }
- debug_va_end(ap);
-
- /* Any pending chars remaining? */
- if (optr < ptr)
- fifowrite(optr, ptr - optr);
-
- _lock_release(&root->debuglock);
-}
-
-
-/* XXX Need to work on stdarg-like system and vsnprintf() instead of everything
- * in debug_printf().
-void dprintf2(const char *file, const char *function, u_int32_t line,
- const char *fmt, ...)
-{
- debug_printf("%s:%s():%d - %s", file, func, line);
-}
-*/
-
-
-
-/* This function writes into the debug FIFO buffer in a way to cause automatic
- * recycling so that the last DEBUG bytes will always be available in the
- * history until read. We do not use the lock here, because we only want
- * full lines to be recorded, we let debug_printf() do it.
- */
-static void fifowrite(const char *buf, size_t len)
-{
- register fifo8_t *f = &fifo;
-
- while (len > 0) {
- FIFO_PUT(f, buf);
- buf++;
- len--;
- }
-
- /* XXX This would be more efficient if it worked :) I need to debug this.
- if (len == 1)
- FIFO_PUT(f, buf);
- else {
- u_int8_t *ptr;
- size_t size;
- register size_t l = len;
-
- if ((size = FIFO_AVAIL(f)) < l) {
- l -= size;
-
- while (size ) XXX;
- size = l;
- FIFO_FREE(f, &ptr, &size, l);
- }
-
- while (l > 0) {
- FIFO_ALLOC(f, &ptr, &size, l);
- memcpy(ptr, buf, size);
- buf += size;
- l -= size;
- }
- }
- */
-}
-
-
-/* Allows to read data from the FIFO buffer. The bytes are unlinked from the
- * buffer dynamically when they are read.
- */
-size_t dread(char *buf, size_t len)
-{
- register fifo8_t *f = &fifo;
- register size_t l = len;
-
- if (buf == NULL || len == 0)
- return 0;
-
- lock_acquire(&root->debuglock);
-
- while (l > 0) {
- if (FIFO_EMPTY(f))
- break;
- FIFO_GET(f, buf);
- buf++;
- l--;
- }
- len = len - l;
-
- /* XXX Debug this more efficient alternative
- if (len == 1) {
- if (!FIFO_EMPTY(f))
- FIFO_GET(f, buf);
- else
- len = 0;
- } else {
- u_int8_t *ptr;
- int size;
- register int l = len, i;
-
- len = 0;
- for (i = 0; i < 2 && l > 0; i++) {
- FIFO_FREE(f, &ptr, &size, l);
- if (size == 0)
- break;
- memcpy(buf, ptr, size);
- buf += size;
- len += size;
- l -= size;
- }
- }
- */
-
- _lock_release(&root->debuglock);
-
- return len;
-}
-
-
-/* Buffer should at least be 34 bytes. Performs 32-bit value to ASCII binary
- * convertion. Returns length of string.
- */
-static size_t btoa(char *buf, u_int32_t val)
-{
- register int i;
- register char *ptr = buf;
-
- *ptr++ = '%';
- for (i = 31; i > -1; i--)
- *ptr++ = (val & (1L << i)) ? '1' : '0';
- *ptr = '\0';
-
- return (ptr - buf);
-}
-
-
-/* Buffer should be at least 12 bytes. Converts signed 32-bit value to decimal
- * ASCII representation. Returns length of string.
- */
-static size_t dtoa(char *buf, int32_t val)
-{
- register int32_t v = val;
- register char *ptr = buf;
- register const char *d = ddigits;
-
- if (v < 0) {
- *ptr++ = '-';
- v = -v;
- }
- for (; v > 9; v /= 10)
- *ptr++ = d[v % 10];
- *ptr++ = d[v];
- *ptr = '\0';
-
- return (ptr - buf);
-}
-
-
-/* Buffer should be at least 11 bytes. Unsigned 32-bit value to decimal ASCII
- * convertion. Returns length of string.
- */
-static size_t utoa(char *buf, u_int32_t val)
-{
- register u_int32_t v = val;
- register char *ptr = buf;
- register const char *d = ddigits;
-
- for (; v > 9; v /= 10)
- *ptr++ = d[v % 10];
- *ptr++ = d[v];
- *ptr = '\0';
-
- return (ptr - buf);
-}
-
-
-/* Buffer should be at least 11 bytes. Converts 32-bit value to hexadecimal
- * ASCII representation. Returns length of string.
- */
-static size_t xtoa(char *buf, u_int32_t val)
-{
- register u_int32_t v = val;
- register char *ptr = buf;
- register const char *d = xdigits;
- register int i;
-
- *ptr++ = '0';
- *ptr++ = 'x';
- for (i = 32 - 4; i > -1; i -= 4)
- *ptr++ = d[v >> i & 15];
- *ptr = '\0';
-
- return (ptr - buf);
-}
-#endif
+++ /dev/null
-/* $Id: debug.h,v 1.3 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_DEBUG_H
-#define KERNEL_DEBUG_H
-
-
-
-#include <common/types.h>
-#include <config.h>
-
-
-
-/* This implements various debugging primitives. Macros are defined
- * so that no debugging code be compiled in the kernel if DEBUG
- * is not defined.
- */
-
-
-
-#ifndef DEBUG
-
-/* Debugging disabled, ensure that unnecessary code doesn't get compiled in.
- * if (DEBUG_TRUE(condition)) will always cause the condition to be TRUE, while
- * if (DEBUG_FALSE(condition)) will always cause the condition to be FALSE.
- * DEBUG_PRINTF() will do nothing.
- */
-
-#define DEBUG_PRINTF(s, ...)
-#define DEBUG_READ(b, s) 0
-#define DEBUG_TRUE(c) /* CONSTCOND */1
-#define DEBUG_FALSE(c) /* CONSTCOND */0
-
-#else
-
-/* Debugging enabled, macros must now do something */
-
-#define DEBUG_PRINTF debug_printf
-#define DEBUG_READ(b, s) debug_read(b, s)
-#define DEBUG_TRUE(c) (c)
-#define DEBUG_FALSE(c) (c)
-
-
-
-/* These should eventually be available even if no debugging is wanted.
- * However, these are really simple and are mostly made to serve 32-bit
- * values (although characters also work).
- */
-
-typedef u_int32_t * debug_va_list;
-
-#define debug_va_start(a, l) (a) = (u_int32_t *)(&(l) + sizeof(*(l)))
-#define debug_va_arg(a, t) (t)(*a++)
-#define debug_va_copy(d, s) (d) = (s)
-#define debug_va_end(a)
-
-
-
-void debug_init(void);
-void debug_printf(const char *, ...);
-/*void dprintf2(const char *, const char *, int, const char *, ...);*/
-size_t debug_read(char *, size_t);
-
-
-
-#endif
-
-
-
-#endif
+++ /dev/null
-/* $Id: device.c,v 1.8 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/task.h>
-#include <common/kernel/device.h>
-#include <common/kernel/main.h>
-#include <common/kernel/object.h>
-#include <common/kernel/port.h>
-#include <common/kernel/signal.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/debug.h>
-#include <common/kernlib/string.h>
-#include <common/kernlib/hash.h>
-
-
-
-/* Allows current task to open a devicenode_t, obtaining a device_t handle */
-device_t *device_open(const char *name, u_int32_t ver, u_int32_t unit)
-{
- device_t *dh = NULL;
-
- if (name != NULL) {
- register devicenode_t *dn;
-
- SYSTABLE_RLOCK(SYSTABLE_DEVICES);
- dn = (devicenode_t *)hashtable_lookup(SYSTABLE(SYSTABLE_DEVICES),
- name, strnlen(name, 32));
- SYSTABLE_UNLOCK(SYSTABLE_DEVICES);
- if (dn != NULL && (dn->version == ver || ver == 0)) {
- if ((dh = (device_t *)spool_alloc(POOL_DEVICEHANDLE)) != NULL) {
- if (dn->open(&dh->udata, unit)) {
- /* Validate object and set dependancy. We also register
- * it since iorequest_t depend on device_t.
- */
- OBJECT_VALIDATE(dh, OBJECT_DEVICEHANDLE);
- OBJECT_REGISTER(dh);
- OBJECT_SETDEP(dh, dn);
- dh->devnode = dn;
- dn->usecount++;
- /* Initialize other device_t fields */
- dh->devport = dn->port;
- dh->owner = CURTASK();
- dh->unit = unit;
- /* Attach to task resources for automatic freeing */
- SCHED_DISABLE();
- DLIST_APPEND(&CURTASK()->resources.devices, &dh->tasknode);
- SCHED_ENABLE();
- } else {
- dh = (device_t *)spool_free(POOL_DEVICEHANDLE,
- (pnode_t *)dh);
- DEBUG_PRINTF("- %T device_open(%s, %u, %u) - Refused\n",
- name, ver, unit);
- }
- } else
- DEBUG_PRINTF("* %T device_open(%s, %u, %u) - Out of memory\n",
- name, ver, unit);
- } else
- DEBUG_PRINTF("- %T device_open(%s, %u, %u) - Unknown device\n",
- name, ver, unit);
- } else
- DEBUG_PRINTF("* %T device_open(%s, %u, %u) - Illegal parameters\n",
- name, ver, unit);
-
- return dh;
-}
-
-
-/* Closes and frees a device_t from the task it belongs to */
-device_t *device_close(device_t *dh)
-{
- if (OBJECT_VALID(dh, OBJECT_DEVICEHANDLE)) {
- if (OBJECT_DEPENDS(dh, dh->devnode)) {
- register devicenode_t *dn = dh->devnode;
-
- SYSTABLE_RLOCK(SYSTABLE_DEVICES);
- /* devicenode_t for this device_t still exists */
- dn->close(dh->udata, dh->unit);
- if ((--(dn->usecount)) == 0 && (dn->flags & DNF_RESIDENT) == 0) {
- if (OBJECT_VALID(dn->task, OBJECT_TASK))
- signal_send(dn->task, SIGMASK(SIGTERM));
- }
- SYSTABLE_UNLOCK(SYSTABLE_DEVICES);
- } else
- DEBUG_PRINTF("* %T device_close(%p) - Device has died\n", dh);
- /* Unlink task resource and free handle */
- SCHED_DISABLE();
- DLIST_UNLINK(&(dh->owner->resources.devices), &dh->tasknode);
- SCHED_ENABLE();
- OBJECT_INVALIDATE(dh);
- spool_free(POOL_DEVICEHANDLE, (pnode_t *)dh);
- } else
- DEBUG_PRINTF("* %T device_close(%p) - Invalid device_t pointer\n",
- dh);
-
- return NULL;
-}
-
-
-/* Initializes an iorequest_t for use with specified device_t, necessary
- * before using it to send device requests.
- */
-bool iorequest_init(iorequest_t *req, device_t *dh, port_t *rport)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(dh, OBJECT_DEVICEHANDLE) &&
- OBJECT_DEPENDS(dh, dh->devnode) &&
- OBJECT_VALID(rport, OBJECT_PORT)) {
- req->udata = NULL;
- if (dh->devnode->iorinit == NULL ||
- (req->udata = dh->devnode->iorinit()) != NULL) {
- /* Validate object and register dependancy on device_t */
- OBJECT_VALIDATE(req, OBJECT_IOREQUEST);
- OBJECT_SETDEP(req, dh);
- req->devhandle = dh;
- /* Initialize other iorequest_t fields */
- req->devport = dh->devport;
- req->rport = rport;
- req->flags = 0;
- req->success = FALSE;
- req->result = 0;
- req->actual = 0;
- ok = TRUE;
- } else
- DEBUG_PRINTF("* %T iorequest_init(%p, %p, %p) - Out of memory\n",
- req, dh, rport);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_init(%p, %p, %p) - Invalid device_t pointer\n",
- req, dh, rport);
-
- return ok;
-}
-
-
-bool iorequest_destroy(iorequest_t *req)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- if (OBJECT_DEPENDS(req, req->devhandle) &&
- OBJECT_DEPENDS(req->devhandle, req->devhandle->devnode)) {
- register devicenode_t *dn = req->devhandle->devnode;
-
- if (dn->iordestroy != NULL && req->udata != NULL)
- dn->iordestroy(req->udata);
- }
- OBJECT_INVALIDATE(req);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_destroy(%p) - Invalid iorequest_t poinder\n",
- req);
-
- return ok;
-}
-
-
-/* Sends the iorequest_t message to it's corresponding device, and waits for
- * results to be obtained, then returns. This consists of a synchroneous
- * device request.
- */
-bool iorequest_sync(iorequest_t *req)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- if ((req->flags & IOF_PENDING) == 0) {
- if (port_send(req->devport, req->rport, (message_t *)req)) {
- register sigmask_t sigmport = PORT_SIGMASK(req->devport);
-
- req->flags = IOF_SYNC | IOF_PENDING;
- while (((signal_wait(sigmport, NULL)) & sigmport) == 0) ;
- port_get(req->devport);
- /* Request satisfied */
- req->flags &= ~(IOF_SYNC & IOF_PENDING);
- ok = TRUE;
- } else
- DEBUG_PRINTF("* %T iorequest_sync(%p) - port_send()\n", req);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_sync(%p) - iorequest_t already pending\n",
- req);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_sync(%p) - Invalid iorequest_t pointer\n",
- req);
-
- return ok;
-}
-
-
-/* Sends the iorequest_t message to it's corresponding device, but returns
- * immediately, without waiting for results. This consists of an asynchroneous
- * request. The application is responsible to monitor the reply port status
- * for the request completion, and to unqueue the reply from the reply port
- * before performing another request using this iorequest_t.
- */
-bool iorequest_async(iorequest_t *req)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- if ((req->flags & IOF_PENDING) == 0) {
- if (port_send(req->devport, req->rport, (message_t *)req)) {
- req->flags = IOF_ASYNC | IOF_PENDING;
- ok = TRUE;
- } else
- DEBUG_PRINTF("* %T iorequest_async(%p) - port_send()\n",
- req);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_async(%p) - iorequest_t already pending\n",
- req);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_async(%p) - Invalid iorequest_t pointer\n",
- req);
-
- return ok;
-}
-
-
-/* Aborts a pending asynchroneous request which has not yet completed.
- * This in fact sends back a new request using the same iorequest_t, but
- * does not expect a reply back from the device for the abort request. However,
- * the reply port will be sent the reply as usual when the aborted iorequest_t
- * ends (which always happens even when a request is aborted).
- */
-bool iorequest_abort(iorequest_t *req)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- if ((req->flags & IOF_PENDING) != 0 && (req->flags & IOF_ASYNC) != 0) {
- req->function = IO_ABORT;
- if (port_send(req->devport, req->rport, (message_t *)req)) {
- req->flags |= IOF_ABORTING;
- ok = TRUE;
- } else
- DEBUG_PRINTF("* %T iorequest_abort(%p) - port_send()\n",
- req);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_abort(%p) - iorequest_t not pending\n",
- req);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_abort(%p) - Invalid iorequest_t pointer\n",
- req);
-
- if (!ok)
- DEBUG_PRINTF("* %T iorequest_abort(%p)\n", req);
-
- return ok;
-}
-
-
-/* Waits until the currently pending asynchroneous request completes. The reply
- * message is automatically unqueued from the reply port in this case.
- */
-bool iorequest_wait(iorequest_t *req)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- if ((req->flags & IOF_PENDING) != 0 && (req->flags & IOF_ASYNC) != 0) {
- register sigmask_t sigmport = PORT_SIGMASK(req->devport);
-
- while (((signal_wait(sigmport, NULL)) & sigmport) == 0) ;
- port_get(req->devport);
- /* Request satisfied */
- req->flags &= ~(IOF_ASYNC & IOF_PENDING);
- ok = TRUE;
- } else
- DEBUG_PRINTF(
- "* %T iorequest_wait(%p) - iorequest_t not pending\n",
- req);
- } else
- DEBUG_PRINTF(
- "* %T iorequest_wait(%p) - Invalid iorequest_t pointer\n",
- req);
-
- return ok;
-}
-
-
-/* Returns TRUE if the request is asynchroneous and still pending. */
-bool iorequest_pending(iorequest_t *req)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- if ((req->flags & IOF_PENDING) != 0 && (req->flags & IOF_ASYNC) != 0)
- ok = TRUE;
- } else
- DEBUG_PRINTF(
- "* %T iorequest_pending(%p) - Invalid iorequest_t pointer\n",
- req);
-
- return ok;
-}
-
-
-/* Returns TRUE if the request last terminated by iorequest_abort(). */
-bool iorequest_aborted(iorequest_t *req)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- if ((req->flags & IOF_ABORTED) != 0)
- ok = TRUE;
- } else
- DEBUG_PRINTF(
- "* %T iorequest_aborted(%p) - Invalid iorequest_t pointer\n",
- req);
-
- return ok;
-}
-
-
-/* Made for devices to satisfy a user iorequest_t, at the same time setting
- * the boolean result code for it.
- */
-bool iorequest_satisfy(iorequest_t *req, bool result)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- if ((req->flags & IOF_PENDING) != 0) {
- req->flags &= ~(IOF_PENDING | IOF_SYNC | IOF_ASYNC);
- if ((req->flags & IOF_ABORTING) != 0) {
- req->flags &= ~IOF_ABORTING;
- req->flags |= IOF_ABORTED;
- req->success = result;
- }
- if (!(ok = port_reply((message_t *)req)))
- DEBUG_PRINTF(
- "* %T iorequest_satisfy(%p, %B) - port_reply(%p)\n",
- req, result, req);
- }
- } else
- DEBUG_PRINTF("* %T iorequest_satisfy(%p, %B) - Invalid iorequest_t\n",
- req, result);
-
- return ok;
-}
-
-
-/* Allows a task to attach a new device to the system lists. It then of course
- * should serve requests through it's port. The task may only become one
- * device, that is, it may not attach more than a single device.
- * It however can serve multiple units on that device, of course.
- */
-bool device_attach(const char *name, u_int32_t version, port_t *port,
- void (*clean)(void), bool (*open)(void **, u_int32_t),
- void (*close)(void *, u_int32_t), void *(*iorinit)(void),
- void (*iordestroy)(void *), u_int8_t flags)
-{
- if (CURTASK()->resources.device == NULL && name != NULL &&
- OBJECT_VALID(port, OBJECT_PORT) && open != NULL && close != NULL) {
- register bstr_t *bstr;
-
- if ((bstr = bstr_new(name, 32, FALSE)) != NULL) {
- register devicenode_t *dn;
-
- SYSTABLE_RLOCK(SYSTABLE_DEVICES);
- if ((dn = (devicenode_t *)hashtable_lookup(
- SYSTABLE(SYSTABLE_DEVICES),
- bstr->data, bstr->len)) == NULL ||
- version != dn->version) {
- if ((dn = (devicenode_t *)spool_alloc(POOL_DEVICENODE))
- != NULL) {
- /* Validate and register for dependancies */
- OBJECT_VALIDATE(dn, OBJECT_DEVICENODE);
- OBJECT_REGISTER(dn);
- /* Initialize other devicenode_t fields */
- dn->version = version;
- dn->name = bstr;
- dn->usecount = 0;
- dn->flags = flags;
- dn->port = port;
- dn->task = CURTASK();
- dn->clean = clean;
- dn->open = open;
- dn->close = close;
- dn->iorinit = iorinit;
- dn->iordestroy = iordestroy;
- /* Attach */
- SYSTABLE_UPGRADE(SYSTABLE_DEVICES);
- (void) hashtable_link(SYSTABLE(SYSTABLE_DEVICES),
- (hashnode_t *)dn, bstr->data,
- bstr->len, FALSE);
- SYSTABLE_UNLOCK(SYSTABLE_DEVICES);
-
- return TRUE;
- } else
- DEBUG_PRINTF("* %T device_attach() - Out of memory\n");
- } else
- DEBUG_PRINTF(
- "* %T device_attach() - Device exists already\n");
-
- SYSTABLE_UNLOCK(SYSTABLE_DEVICES);
- bstr_free(bstr);
- } else
- DEBUG_PRINTF("* %T device_attach() - Out of memory\n");
- } else
- DEBUG_PRINTF("* %T device_attach() - Invalid parameters\n");
-
- DEBUG_PRINTF("* %T device_attach(%s, %u, %p, %p, %p, %p, %p, %p, %x)\n",
- name, version, port, clean, open, close, iorinit, iordestroy,
- (u_int32_t)flags);
-
- return FALSE;
-}
-
-
-bool device_detach(devicenode_t *dn)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(dn, OBJECT_DEVICENODE)) {
- SYSTABLE_WLOCK(SYSTABLE_DEVICES);
- hashtable_unlink(SYSTABLE(SYSTABLE_DEVICES), (hashnode_t *)dn);
- SYSTABLE_UNLOCK(SYSTABLE_DEVICES);
- if (dn->clean != NULL)
- dn->clean();
- OBJECT_INVALIDATE(dn);
- if (dn->name != NULL)
- bstr_free(dn->name);
- spool_free(POOL_DEVICENODE, (pnode_t *)dn);
- } else
- DEBUG_PRINTF(
- "* %T device_detach(%p) - Invalid devicenode_t pointer\n",
- dn);
-
- return ok;
-}
-
-
-
-/* Utility functions for very simple synchroneous I/O */
-
-/* Like read(), but on a Xisop device */
-ssize_t device_read(iorequest_t *req, void *buf, size_t size)
-{
- ssize_t len = -1;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- req->function = IO_READ;
- req->len = size;
- req->data = buf;
- if (iorequest_sync(req)) {
- if (req->success)
- len = req->actual;
- } else
- DEBUG_PRINTF(
- "* %T device_read(%p, %p, %u) - iorequest_sync(%p)\n",
- req, buf, size, req);
- } else
- DEBUG_PRINTF(
- "* %T device_read(%p, %p, %u) - Invalid iorequest_t pointer\n",
- req, buf, size);
-
- return len;
-}
-
-
-/* Like write(), but on a Xisop device */
-ssize_t device_write(iorequest_t *req, void *buf, size_t size)
-{
- ssize_t len = -1;
-
- if (OBJECT_VALID(req, OBJECT_IOREQUEST)) {
- req->function = IO_WRITE;
- req->len = size;
- req->data = buf;
- if (iorequest_sync(req)) {
- if (req->success)
- len = req->actual;
- } else
- DEBUG_PRINTF(
- "* %T device_read(%p, %p, %u) - iorequest_sync(%p)\n",
- req, buf, size, req);
- } else
- DEBUG_PRINTF(
- "* %T device_write(%p, %p, %u) - Invalid iorequest_t ptr\n",
- req, buf, size);
-
- return len;
-}
+++ /dev/null
-/* $Id: device.h,v 1.2 2004/01/18 17:42:59 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_DEVICE_H
-#define KERNEL_DEVICE_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/port.h>
-#include <common/kernel/task.h>
-#include <common/kernel/memory.h>
-#include <common/kernlib/list.h>
-#include <common/kernlib/hash.h>
-#include <common/kernlib/string.h>
-
-
-
-/* devicenode/device_t flags */
-#define DNF_RESIDENT (1 << 0)
-
-/* iorequest_t flags */
-#define IOF_SYNC (1 << 0)
-#define IOF_ASYNC (1 << 1)
-#define IOF_PENDING (1 << 2)
-#define IOF_ABORTING (1 << 3)
-#define IOF_ABORTED (1 << 4)
-
-/* Standard device commands */
-enum _devicecommands {
- IO_ABORT = 0,
- IO_READ,
- IO_WRITE,
- IO_CONTROL /* General purpose like unix ioctl() */
-};
-
-
-
-/* For device task functions to access user data they associated with
- * device_t and iorequest_t handles (if any, or NULL).
- */
-#define DEVICEHANDLE_UDATA(d) (d)->udata
-#define IOREQUEST_UDATA(r) (r)->udata
-
-
-
-/* This structure holds the only necessary information which Xisop needs to
- * know. A device has to internally handle other information but which is of
- * no use to Xisop itself.
- */
-struct devicenode {
- /* System link and information, for devices system list */
- hashnode_t node;
- u_int32_t version;
- bstr_t *name;
- u_int32_t usecount;
- u_int8_t flags;
- /* Validity sceal. device_t objects depend on us */
- u_int32_t object_magic, object_id;
-
- /* Port used to send device requests to */
- port_t *port;
- /* Task associated with device */
- task_t *task;
-
- /* Functions provided by device, described in Xisop documentation. */
- void (*clean)(void);
- bool (*open)(void **, u_int32_t);
- void (*close)(void *, u_int32_t);
- void *(*iorinit)(void);
- void (*iordestroy)(void *);
-};
-
-/* Consists of a device handle, returned to tasks when opening a device */
-struct devicehandle {
- pnode_t usernode; /* Used by device for optional queuing */
- node_t tasknode; /* Used to remember task resource */
- /* Validity and dependancy sceal, we depend on devicenode_t */
- u_int32_t object_magic, object_id, objdep_magic, objdep_id;
- devicenode_t *devnode;
- /* Other fields */
- task_t *owner; /* Owner task of this handle */
- port_t *devport; /* Device port to use */
- u_int32_t devnodeid; /* Id of devnode */
- u_int32_t unit; /* Unit opened on device */
- void *udata; /* Device may link custom data here */
-};
-
-/* An iorequest_t consists of a message. This also means that the device task
- * may queue the message into custom list_t as required after they obtain it,
- * as long as they unlink it before they return it of course.
- */
-struct iorequest {
- message_t msg; /* An iorequest_t is a message */
- /* Validity sceal and dependancy link */
- u_int32_t object_magic, objdep_magic, objdep_id;
- device_t *devhandle;
- /* Other */
- port_t *devport, *rport;
- void *udata;
- u_int8_t flags;
-
- /* The following are the operation request control fields */
- u_int32_t function, subfunction;
- size_t len;
- size_t offset; /* Useful for block devices */
- void *data;
-
- /* And operation results fields */
- bool success;
- int result;
- size_t actual;
-};
-
-
-
-/* User API functions */
-device_t *device_open(const char *, u_int32_t, u_int32_t);
-device_t *device_close(device_t *);
-bool iorequest_init(iorequest_t *, device_t *, port_t *);
-bool iorequest_destroy(iorequest_t *);
-bool iorequest_sync(iorequest_t *);
-bool iorequest_async(iorequest_t *);
-bool iorequest_abort(iorequest_t *);
-bool iorequest_wait(iorequest_t *);
-bool iorequest_pending(iorequest_t *);
-bool iorequest_aborted(iorequest_t *);
-
-/* Device task functions */
-bool iorequest_satisfy(iorequest_t *, bool);
-bool device_attach(const char *, u_int32_t, port_t *, void (*)(void),
- bool (*)(void **, u_int32_t), void (*)(void *, u_int32_t),
- void *(*)(void), void (*)(void *), u_int8_t);
-bool device_detach(devicenode_t *);
-
-/* Useful but very simple synchroneous functions */
-ssize_t device_read(iorequest_t *, void *, size_t);
-ssize_t device_write(iorequest_t *, void *, size_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: exception.c,v 1.4 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/main.h>
-#include <common/kernel/exception.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/debug.h>
-#include <common/kernlib/list.h>
-#include <processor/support.h>
-#include <port/support.h>
-
-
-
-/* These are the machine-independant kernel frontend to interrupt hooks
- * facilities. They internally use a recursive _rlock_t which allows to
- * ensure reliability when adding and removing hooks to a facility,
- * while removing the need for system call traps to access the functionality.
- */
-
-hookid_t hook_attach(u_int32_t facility, u_int32_t skipcount,
- u_int32_t runcount, void (*code)(hookid_t, int, void *), void *data)
-{
- register hookid_t id = 0;
-
- if (facility < (enum _facilities)_FACILITY_MAX && code != NULL) {
- register facility_t *f;
- register hook_t *hook;
-
- f = (facility_t *)&root->int_facilities[facility];
- _rlock_acquire(&f->rlock);
- if ((hook = (hook_t *)pool_alloc(&f->pool, FALSE)) != NULL) {
- if (++f->idcnt == 0)
- f->idcnt++;
- id = hook->id = f->idcnt;
- hook->skipcount = skipcount;
- hook->runcount = runcount;
- hook->code = code;
- hook->data = data;
- DLIST_APPEND(&f->hooks, (node_t *)hook);
- STAT(STAT_HOOKS_ATTACHED, 1);
- } else {
- STAT(STAT_HOOKS_ATTACHED_NOMEM, 1);
- DEBUG_PRINTF("* %T hook_attach() - Out of memory\n");
- }
- _rlock_release(&f->rlock);
- } else {
- STAT(STAT_HOOKS_ATTACHED_FAILED, 1);
- DEBUG_PRINTF("* %T hook_attach(%u, %u, %u, %p, %p)\n",
- facility, skipcount, runcount, code, data);
- }
-
- return id;
-}
-
-
-bool hook_detach(u_int32_t facility, hookid_t id)
-{
- register bool ok = FALSE;
-
- if (facility < (enum _facilities)_FACILITY_MAX && id != 0) {
- register facility_t *f;
- register hook_t *node, *next;
-
- f = (facility_t *)&root->int_facilities[facility];
- _rlock_acquire(&f->rlock);
-
- for (node = DLIST_TOP(&f->hooks); node != NULL; node = next) {
- next = DLIST_NEXT(node);
- if (node->id == id) {
- DLIST_UNLINK(&f->hooks, (node_t *)node);
- pool_free((pnode_t *)node);
- ok = TRUE;
- STAT(STAT_HOOKS_DETACHED, 1);
- break;
- }
- }
- if (node == NULL)
- STAT(STAT_HOOKS_DETACHED_NOEXIST, 1);
-
- _rlock_release(&f->rlock);
- } else {
- STAT(STAT_HOOKS_DETACHED_FAILED, 1);
- DEBUG_PRINTF("* %T hook_detach(%u, %u)\n", facility, id);
- }
-
- return ok;
-}
-
-
-void facility_disable(u_int32_t facility)
-{
- if (facility < (enum _facilities)_FACILITY_MAX) {
- _rlock_acquire(&(root->int_facilities[facility].rlock));
- STAT(STAT_FACILITY_DISABLED, 1);
- } else {
- STAT(STAT_FACILITY_DISABLED_FAILED, 1);
- DEBUG_PRINTF("* %T facility_disable(%u)\n", facility);
- }
-}
-
-
-void facility_enable(u_int32_t facility)
-{
- if (facility < (enum _facilities)_FACILITY_MAX) {
- _rlock_release(&(root->int_facilities[facility].rlock));
- STAT(STAT_FACILITY_ENABLED, 1);
- } else {
- STAT(STAT_FACILITY_ENABLED_FAILED, 1);
- DEBUG_PRINTF("* %T facility_enable(%u)\n", facility);
- }
-}
-
-
-/* This is called by the port-specific code to execute the hooks associated
- * with a facility. Note that a recursive lock is internally maintained which
- * ensures to prevent recursion, or to execute the hooks while new ones are
- * being added, or when a hook is being deleted.
- */
-
-
-/* Execute the hooks and transparently delete expired ones */
-void facility_exechooks(u_int32_t facility, int origin)
-{
- if (facility < (enum _facilities)_FACILITY_MAX) {
- register facility_t *f = &root->int_facilities[facility];
-
- if (_rlock_try(&f->rlock)) {
- register list_t *hooks = &f->hooks;
- register hook_t *node, *tmp;
-
- STAT(STAT_FACILITY_EXECUTED, 1);
- node = DLIST_TOP(hooks);
- while (node != NULL) {
- if (node->skipcount > 0) {
- node->skipcount--;
- STAT(STAT_HOOKS_SKIPPED, 1);
- node = DLIST_NEXT(node);
- } else {
- node->code(node->id, origin, node->data);
- STAT(STAT_HOOKS_EXECUTED, 1);
- if (node->runcount != 0) {
- tmp = DLIST_NEXT(node);
- if ((--node->runcount) == 0) {
- /* This hook is temporary and expired,
- * extract it.
- */
- DLIST_UNLINK(hooks, (node_t *)node);
- pool_free((pnode_t *)node);
- STAT(STAT_HOOKS_EXPIRED, 1);
- }
- node = tmp;
- } else
- node = DLIST_NEXT(node);
- }
- }
- _rlock_release(&f->rlock);
- } else
- STAT(STAT_FACILITY_EXECUTED_LOCKED, 1);
- } else {
- STAT(STAT_FACILITY_EXECUTED_FAILED, 1);
- DEBUG_PRINTF("* %T facility_exechooks(%u, %d)\n", facility, origin);
- }
-}
-
-
-/* Initialize all facility_t */
-void facilities_init(void)
-{
- register u_int32_t i;
-
- for (i = 0; i < (enum _facilities)_FACILITY_MAX; i++) {
- register facility_t *f = &root->int_facilities[i];
-
- f->idcnt = 0;
- _spl0();
- pool_init(&f->pool, 1, 1, 1, sizeof(hook_t), 0);
- _splhigh();
- DLIST_INIT(&f->hooks);
- _rlock_init(&f->rlock);
- _rlock_acquire(&f->rlock);
- }
- for (i = 0; i < (enum _facilities)_FACILITY_MAX; i++) {
- register facility_t *f = &root->int_facilities[i];
-
- _rlock_release(&f->rlock);
- }
-}
+++ /dev/null
-/* $Id: exception.h,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_EXCEPTION_H
-#define KERNEL_EXCEPTION_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/memory.h>
-#include <common/kernlib/list.h>
-#include <processor/support.h>
-#include <port/support.h>
-
-
-
-/* Used to hold user code hooks which should be executed at exceptions
- * depending on the facility they are attached to.
- */
-struct _int_hook {
- pnode_t node;
- hookid_t id;
- u_int32_t skipcount, runcount;
- void (*code)(hookid_t, int, void *);
- void *data;
-};
-
-/* There are _FACILITIES_MAX of these in the root->facilities array.
- * The facilities are defined by the port-specific code.
- */
-struct _int_facility {
- hookid_t idcnt;
- list_t hooks;
- pool_t pool;
- _rlock_t rlock;
-};
-
-
-hookid_t hook_attach(u_int32_t, u_int32_t, u_int32_t,
- void (*)(hookid_t, int, void *), void *);
-bool hook_detach(u_int32_t, hookid_t);
-void facility_disable(u_int32_t);
-void facility_enable(u_int32_t);
-
-void facilities_init(void);
-void facility_exechooks(u_int32_t, int);
-
-
-
-#endif
+++ /dev/null
-/* $Id: main.c,v 1.8 2004/06/04 02:25:15 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/main.h>
-#include <common/kernel/syscall.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/task.h>
-#include <common/kernel/port.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/debug.h>
-#include <common/kernlib/list.h>
-#include <common/kernlib/hash.h>
-#include <common/kernlib/string.h>
-#include <port/support.h>
-#include <processor/support.h>
-#include <config.h>
-
-
-
-COPYRIGHT("\0\nXisop Copyright 2001-2003, Matthew Mondor, \
-All rights reserved.\n");
-
-
-
-/* The famous global structure where all Xisop control information is stored */
-struct xisop_root root[1];
-
-static const char *systables_names[SYSTABLE_MAX] = {
- "systable_publicports",
- "systable_libraries",
- "systable_devices",
- "systable_handlers",
- "systable_volumes"
-};
-
-
-
-/* Xisop main code. The port-specific code should switch to supervisor mode,
- * setup kernel stack, disable interrupts and setup it's interrupt handlers,
- * setup the memory ppools, call xisop_init(), switch back to usermode, and
- * jump definitively to this function.
- */
-int main(void)
-{
- /* And we're Xisop-hosted! */
-
- /* Setup and launch our main Xisop init task. It's source is in
- * src/common/kernel/task.c.
- */
- {
- register task_t *task;
-
- if ((task = task_alloc(task_init, NULL, NULL, 0, 4096, TF_KERNEL))
- != NULL)
- task_start(task);
- }
-
- /* Here we enable the scheduler, which means that this current context
- * will soon be the first to be saved in root->curctx, which currently
- * consists of the _scontext _ctx_t buffer. When no more tasks are
- * in the ready queue, _scontext will also be the restored context,
- * in which case we want to avoid wasting power and overheating the
- * CPU unnecessarily, and are using _idle() in an endless loop to do that.
- * Because we are in usermode, we use sys_idle().
- */
- SCHED_ENABLE();
-
- for (;;) {
- /* Just idle processor */
- /* XXX Amiga-specific, can be taken out, purple color on blitter to
- * show that the system is all idle
- */
- CUSTOM->COLOR[0] = 0x0F0F;
- sys_idle();
- }
-
- /* NOTREACHED */
- return 0;
-}
-
-
-void xisop_init(void)
-{
- /* Enable interrupts and therefore syscalls, facilities and scheduler
- * timer as well. Note that until we SCHED_ENABLE(), the scheduler will
- * not attempt to perform contex switches, even though the scheduler
- * timer interrupt is enabled.
- */
-
-#ifdef STATISTICS
- statistic_init();
-#endif
-
- /* Unique number generator */
- _lock_init(&root->unique_lock);
- root->unique = 0;
-
- /* Kernel memory allocators */
- _lock_init(&root->kernpool_lock);
- spools_init(); /* memory.h */
- if ((root->kernpool = (mpool_t *)spool_alloc(POOL_MPOOL)) == NULL) {
- /* XXX Panic */
- CUSTOM->COLOR[0] = 0x0F00;
- }
- if (!mpool_init(root->kernpool)) {
- /* XXX Panic */
- CUSTOM->COLOR[0] = 0x0F00;
- }
-
- /* And task multitasking scheduler */
- scheduler_init(); /* scheduler.c */
-
- /* Syscalls service */
- syscall_init();
-
-#ifdef DEBUG
- /* Debugging messages FIFO */
- debug_init();
-#endif
-
- /* System hash tables */
- /* XXX Hmm the following calls kmalloc() which calls _kmalloc() which
- * in turn calls lock_acquire(&root->kernpool_lock) which finally calls
- * _yield(NULL) if it cannot immediately obtain the lock. However, _yield()
- * function requires the scheduler interrupt to be running, and the
- * scheduler lock to be released, of course. This appears to be the reason
- * why the system now locks in trap_catch1() which corresponds to the
- * _yield() handler... But, why can't the lock be obtained immediately?
- * Since it is properly initialized first...
- * I tried this in xisop_init(), in start of main() and in main() after
- * SCHED_ENABLE(), always with the same results. Would it be possible that
- * something we lock a lock, and are calling something which also attempts
- * to lock it (and we already hold it)? If so, it would be either
- * _kmalloc() or pages_alloc(). But they are both using a different lock..
- * Or, would it be possible that I messed up port.c locking?
- * XXX Oh! It works fine when I strip out DEBUG and STATISTICS. This
- * probably means that the problem is the the xisop kernel size and boot
- * loader which need adjusting.
- */
- {
- register int i;
-
- for (i = 0; i < (enum systables)SYSTABLE_MAX; i++) {
- rwlock_init(&root->systables[i].lock);
- if (!hashtable_init(SYSTABLE(i), systables_names[i],
- HT_DEFAULT_CAPACITY, kmalloc, kfree,
- memcmp, memhash32, TRUE)) {
- /* XXX PANIC! */
- CUSTOM->COLOR[0] = 0x0F00;
- }
- }
- }
-
- /* Enable interrupts */
- _spl0();
-}
-
-
-u_int32_t unique_id(void)
-{
- register u_int32_t id;
-
- lock_acquire(&root->unique_lock);
- id = (++root->unique);
- _lock_release(&root->unique_lock);
-
- return id;
-}
-
-
-hashtable_t *systable_lock(u_int32_t systable, bool exclusive)
-{
- hashtable_t *list = NULL;
-
- if (systable < (enum systables)SYSTABLE_MAX) {
- if (exclusive)
- SYSTABLE_WLOCK(systable);
- else
- SYSTABLE_RLOCK(systable);
- list = SYSTABLE(systable);
- }
-
- return list;
-}
-
-
-void systable_unlock(u_int32_t systable)
-{
- if (systable < (enum systables)SYSTABLE_MAX)
- SYSTABLE_UNLOCK(systable);
-}
-
-
-void systable_upgrade(u_int32_t systable)
-{
- if (systable < (enum systables)SYSTABLE_MAX)
- SYSTABLE_UPGRADE(systable);
-}
+++ /dev/null
-/* $Id: main.h,v 1.5 2004/01/19 18:07:11 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_MAIN_H
-#define KERNEL_MAIN_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/task.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/exception.h>
-#include <common/kernlib/list.h>
-#include <common/kernlib/string.h>
-#include <common/kernlib/fifo.h>
-#include <common/kernlib/hash.h>
-#include <processor/support.h>
-#include <port/support.h>
-#include <config.h>
-
-
-
-/* To access and manipulate system lists */
-#define SYSTABLE(l) (&(root->systables[(enum systables)(l)].table))
-#define SYSLOCK(l) (&(root->systables[(enum systables)(l)].lock))
-#define SYSTABLE_RLOCK(l) rwlock_acquire(SYSLOCK(l), FALSE)
-#define SYSTABLE_WLOCK(l) rwlock_acquire(SYSLOCK(l), TRUE)
-#define SYSTABLE_UNLOCK(l) rwlock_release(SYSLOCK(l))
-#define SYSTABLE_UPGRADE(l) rwlock_upgrade(SYSLOCK(l))
-
-
-/* A system list. The lock allows simultaneous read-only access, or exclusive
- * access when write operations are required.
- */
-struct systable {
- rwlock_t lock;
- hashtable_t table;
-};
-
-enum systables {
- SYSTABLE_PUBLICPORTS = 0,
- SYSTABLE_LIBRARIES,
- SYSTABLE_DEVICES,
- SYSTABLE_HANDLERS,
- SYSTABLE_VOLUMES,
- SYSTABLE_MAX
-};
-
-
-/* The Xisop main root structure */
-struct xisop_root {
-
- /* Do not change the order of the following block fields, as port-specific
- * assembly code may assume their offsets.
- */
-
- /* Scheduling */
- _rlock_t sched_lock;
- list_t tasks_ready, tasks_wait, tasks_dead;
- task_t *curtask;
- _ctx_t *curctx;
-
- /* The remaining fields are only accessed by common C code. */
- task_t *task_init, *task_reaper;
-
- /* Memory management. First are the system page pools */
- ppool_t ppools[(enum _memtypes)_MEM_MAX];
- /* Then the system object pools */
- _lock_t spools_locks[(enum _syspools)POOL_MAX];
- pool_t spools[(enum _syspools)POOL_MAX];
- /* And the kernel general purpose pool */
- _lock_t kernpool_lock;
- mpool_t *kernpool;
-
- /* Interrupts abstraction system */
- facility_t int_facilities[(enum _facilities)_FACILITY_MAX];
-
- /* Various important system lists */
- struct systable systables[(enum systables)SYSTABLE_MAX];
-
- /* System calls */
- void (**syscalls)(void *, void *);
-
- /* Useful counter to create unique IDs for arbitrary objects, like for
- * ports, using the UNIQUE() macro. Should normally be used when the
- * scheduler is disabled.
- */
- _lock_t unique_lock;
- u_int32_t unique;
-
-#ifdef STATISTICS
- /* Statistics support */
- u_int32_t stats[(enum stat_keys)STAT_MAX];
-#endif
-#ifdef DEBUG
- /* dprintf() support */
- _lock_t debuglock;
- fifo8_t *debugfifo;
-#endif
-};
-
-
-
-/* xisop global data */
-extern struct xisop_root root[1];
-
-
-
-int main(void);
-void xisop_init(void);
-u_int32_t unique_id(void);
-
-hashtable_t *systable_lock(u_int32_t, bool);
-void systable_upgrade(u_int32_t);
-void systable_unlock(u_int32_t);
-
-
-
-#endif
+++ /dev/null
-#!/bin/sh
-
-# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../makedefs.sh
-
-buildlib .
-show $C_AR ar/kernel.a *.o
-show $C_RANLIB ar/kernel.a
+++ /dev/null
-/* $Id: memory.c,v 1.9 2004/06/04 03:09:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Support for multiple memory types and dynamically attaching pages at
- * runtime was implemented the 25 Febuary 2003, when this code was rewritten
- * from scratch.
- *
- * This memory management system works with physical pages. This means that
- * we must provide facilities to work with actual physical contiguous pages
- * when large memory areas are required. Pages only consist of useful units
- * for management; They thus can be of any size (although multiples of 16
- * bytes), and do not need to correspond to the page size required by the
- * MMU system (if any).
- *
- * It may not be the best or most efficient way to deal with this, as I
- * wrote this code from scratch using my own ideas, without reference.
- * But it works well and seems quite fast. Moreover, _PAGE_SIZE, _MEM_MAX
- * and _MPOOLS are provided by the port-specific code, and allows to adapt
- * the system to a variety of situations. It thus well serves it's intended
- * purpose. We provide operations on pages, on basic fixed-sized pools and
- * multiple block size pools for general purpose memory management functions.
- *
- * A previous pool_t implementation attempted to not have to delete all the
- * nodes from a page when moving an unused page to the page cache, and
- * statistics would be kept to know when to free them, at which time their
- * pnode_t nodes were unlinked as well. Some care was taken to append freed
- * nodes of less used pages at the end of the list and insert freed nodes of
- * very used ones at the top of the list, so that over time hopefully
- * the system would stabilize well. It used to result in more fragmentation
- * than the current method which still caches unused pages and uses statistics
- * to free them less often, but immediately removes all nodes of a page from
- * the free nodes list when a page is unused, and has to re-initialize all
- * those nodes for a page when retreiving a page back from the cache.
- * Because Xisop works with actual physical pages and that it requires actual
- * contiguous pages for large data blocks, it is very important to do what is
- * necessary to avoid fragmentation as much as possible, in favor of stability
- * and performance over long uptimes periods, and so I reverted to this method.
- * We favor reuse of recently used nodes and pages as much as possible.
- *
- * It would be nice if the free list consisted of nodes linking to the next
- * contiguous pages block rather than only individual pages. This would speed
- * up multiple page allocations, which currently requires running among free
- * pages to locate contiguous ones. Possibly that freeing could also adapt
- * the pages index dynamically so that nodes could be restored without running
- * among individual pages to know where to insert the node.
- *
- * The system was first tested on NetBSD in userspace, to ensure that
- * everything works as expected before the code was imported in Xisop.
- *
- * Matt
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/main.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/syscall.h>
-#include <common/kernel/task.h>
-#include <common/kernel/object.h>
-#include <common/kernel/port.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/device.h>
-#include <common/kernel/debug.h>
-#include <common/kernlib/list.h>
-#include <common/kernlib/string.h>
-#include <port/support.h>
-#include <processor/support.h>
-#include <config.h>
-
-
-
-/* Used by spools_init() */
-static const struct syspools_params syspools_params
- [(enum _syspools)POOL_MAX] = {
- {"tasks_pool", 1, 0, 0, sizeof(task_t)},
- {"ports_pool", 1, 0, 0, sizeof(port_t)},
- {"devicenodes_pool", 1, 0, 0, sizeof(devicenode_t)},
- {"devices_pool", 1, 0, 0, sizeof(device_t)},
- {"mpools_pool", 1, 0, 0, sizeof(mpool_t)}
-};
-
-
-
-/* This function is required to call once before initializing the memory
- * pages, by the port-specific code.
- */
-void memory_init(void)
-{
- register u_int32_t i;
-
- for (i = 0; i < (enum _memtypes)_MEM_MAX; i++) {
- _lock_init(&root->ppools[i].lock);
- DLIST_INIT(&root->ppools[i].mchunks);
- }
-}
-
-
-/* This function is extremely useful to prepare a given contiguous memory area
- * for it to be attached to the system pages using mchunk_attach().
- * Sanity checking is performed to fix page alignment if needed, and the
- * internal control structures are automatically prepared.
- * Returns a pointer to an mchunk_t, or NULL if the supplied memory area is
- * too small (under two valid pages).
- */
-mchunk_t *mchunk_init(void *mem, size_t size)
-{
- mchunk_t *mchunk = NULL;
- void *begin, *end;
- u_int32_t pages;
-
- /* Page-align pointers, and calculate number of supplied memory pages */
- end = mem + size;
- begin = (void *)BALIGN_CEIL(mem, _PAGE_SIZE);
- end = (void *)BALIGN_FLOOR(end, _PAGE_SIZE);
- pages = (end - begin) / _PAGE_SIZE;
-
- if (pages > 1) {
- void *reserved;
- page_t **index, *page;
- u_int32_t extrapages;
- size_t extrabytes;
-
- /* Evaluate how many pages are required to be reserved to setup the
- * mchunk_t and it's components. As we evaluate this on the number of
- * available pages, which will shrink a bit after we reserve the area,
- * we will re-evaluate it later, but will still be using the same
- * reserved area, thus loosing a few bytes. The loss is however quite
- * negligable.
- */
- extrabytes = (size_t)OALIGN_CEIL(sizeof(mchunk_t), u_int32_t);
- extrabytes += (sizeof(page_t *) + sizeof(page_t)) * pages;
- extrapages = extrabytes / _PAGE_SIZE;
- if (extrabytes % _PAGE_SIZE)
- extrapages++;
- reserved = begin;
- begin += extrapages * _PAGE_SIZE;
- pages -= extrapages;
-
- /* Now our necessary reserved area is at <reserved>, and has little
- * more room than required to store all the control data. We have to
- * setup <pages> pages, starting at <begin> and ending at <end>.
- * First setup our control structures pointers.
- */
- mchunk = (mchunk_t *)reserved;
- reserved += sizeof(mchunk_t);
- reserved = (void *)OALIGN_CEIL(reserved, u_int32_t);
- page = reserved;
- reserved += sizeof(page_t) * pages;
- reserved = (void *)OALIGN_CEIL(reserved, u_int32_t);
- index = reserved;
-
- /* Run through pages filling the control index and headers, while
- * linking the page_t nodes into the mchunk_t.
- */
- DLIST_INIT(&mchunk->free);
- mchunk->index = index;
- mchunk->pages = pages;
- {
- register u_int8_t *run = begin;
- register list_t *l = &mchunk->free;
- register u_int32_t i;
-
- for (i = 0; i < pages; i++, run += _PAGE_SIZE) {
- register page_t *p = &page[i];
-
- p->mchunk = mchunk;
- p->address = run;
- p->last = NULL;
- p->id = i;
- p->state = PS_FREE;
- p->pool = NULL;
- index[i] = p;
- DLIST_APPEND(l, (node_t *)p);
- }
- }
- }
-
- if (mchunk != NULL)
- OBJECT_VALIDATE(mchunk, OBJECT_MCHUNK);
-
- return mchunk;
-}
-
-
-/* Allows to dynamically attach new memory at runtime. This can be useful
- * for instance to assign memory for a hotplug device such as PCMCIA RAM.
- * The mchunk_t should be setup using mchunk_init(). It is recommended to
- * read the Xisop documentation for more information. One of the existing
- * memory types supplied by the port-specific code should be chosen to which
- * attach the chunk. mchunk_attach() is also used by the port-specific code
- * to attach the initial memory pages.
- */
-bool mchunk_attach(int memtype, mchunk_t *mchunk)
-{
- bool ok = FALSE;
-
- if (memtype < (enum _memtypes)_MEM_MAX && memtype != _MEM_ANY &&
- OBJECT_VALID(mchunk, OBJECT_MCHUNK)) {
- register ppool_t *p;
- register mchunk_t *m;
-
- p = &root->ppools[memtype];
- mchunk->ppool = p;
-
- /* Obtain the system pages protection lock */
- lock_acquire(&p->lock);
-
- /* Make sure that this chunk is not already in the pool. We can
- * afford this as this is a rare call, although dangerous.
- */
- DLIST_FOREACH(&p->mchunks, m) {
- if (m == mchunk)
- break;
- }
- if (m == NULL) {
- DLIST_APPEND(&p->mchunks, (node_t *)mchunk);
- ok = TRUE;
- STAT(STAT_MCHUNKS_ATTACH, 1);
- }
-
- _lock_release(&p->lock);
- }
-
- if (!ok) {
- STAT(STAT_MCHUNKS_ATTACH_FAILED, 1);
- DEBUG_PRINTF("* %T mchunk_attach(%d, %p)\n", memtype, mchunk);
- }
-
- return ok;
-}
-
-
-/* Permits to dynamically detach memory at runtime, which was previously
- * attached using mchunk_attach(). Note that this function fails with FALSE
- * if any page of memory currently remains allocated, or if the mchunk_t is
- * not currently attached.
- */
-bool mchunk_detach(int memtype, mchunk_t *mchunk)
-{
- bool ok = FALSE;
-
- if (memtype < (enum _memtypes)_MEM_MAX && memtype != _MEM_ANY &&
- OBJECT_VALID(mchunk, OBJECT_MCHUNK)) {
- register ppool_t *p;
- register mchunk_t *m;
-
- p = &root->ppools[memtype];
-
- /* Lock system pages safely lock */
- lock_acquire(&p->lock);
-
- /* Make sure that this chunk is attached in the expected memory type,
- * and also make sure that all pages are free (unallocated).
- */
- DLIST_FOREACH(&p->mchunks, m) {
- if (m == mchunk)
- break;
- }
- if (m == mchunk) {
- if (m->pages == DLIST_NODES(&m->free)) {
- DLIST_UNLINK(&p->mchunks, (node_t *)m);
- ok = TRUE;
- STAT(STAT_MCHUNKS_DETACH, 1);
- }
- }
-
- _lock_release(&p->lock);
- }
-
- if (!ok) {
- STAT(STAT_MCHUNKS_DETACH_FAILED, 1);
- DEBUG_PRINTF("* %T mchunk_detach(%d, %p)\n", memtype, mchunk);
- }
-
- return ok;
-}
-
-
-/* Requests obtention of one or more contiguous physical pages from the system.
- * Returns a pointer to the first page_t on success, or NULL on failure
- * (out of memory). If <zero> is true, the returned memory area will be
- * cleared to 0x00 bytes. To properly be freed, pages_free() is expected to
- * be called on the same supplied pointer, which will free back all pages
- * which were allocated at once with this function. On success,
- * pages_t->address can be used to access our requested memory.
- */
-page_t *pages_alloc(int memtype, u_int32_t many, bool zero)
-{
- register ppool_t *fp = NULL, *tp = NULL;
- page_t *pages = NULL;
- bool ok;
-
- /* If a memory type was specified, only run through that ppool_t, but
- * run through each ppool_t in order otherwise until we satisfy the
- * request. It is safe to do this interruptible as the memory types
- * pools always remain static.
- */
- ok = TRUE;
- if (many < 1)
- ok = FALSE;
- else {
- if (memtype > -1) {
- if (memtype < (enum _memtypes)_MEM_MAX) {
- /* Will only try requested memory type */
- fp = tp = &root->ppools[memtype];
- tp++;
- } else
- ok = FALSE;
- } else if (memtype == _MEM_ANY) {
- /* Will try all memory types sequencially in order */
- fp = &root->ppools[0];
- tp = &root->ppools[(enum _memtypes)_MEM_MAX];
- tp++;
- } else
- ok = FALSE;
- }
-
- if (ok) {
- /* Loop through ppool_t types */
- for (; fp < tp; fp++) {
- register mchunk_t *m;
-
- lock_acquire(&fp->lock);
-
- /* Loop through mchunk_t nodes of the ppool_t */
- DLIST_FOREACH(&fp->mchunks, m) {
- /* Skip any mchunk_t which doesn't have enough pages */
- if (DLIST_NODES(&m->free) >= many) {
- if (many == 1) {
- register page_t *p;
-
- /* No need to look for contiguous pages as only a
- * single one was requested. Detach the first page.
- */
- p = DLIST_TOP(&m->free);
- DLIST_UNLINK(&m->free, &p->node);
- p->node.next = p->node.prev = NULL;
- p->last = p;
- p->state = PS_ALLOCATED;
- _lock_release(&fp->lock);
- if (zero)
- pageclr(p->address, 1);
- pages = p;
- STAT(STAT_PAGES_ALLOC, 1);
- goto end;
- } else {
- register page_t *n, *o;
- register u_int32_t c, oid;
-
- /* Scan for <many> contiguous pages in this mchunk_t */
- c = 1;
- o = DLIST_TOP(&m->free);
- oid = o->id;
- for (n = DLIST_NEXT(o); n != NULL; n = DLIST_NEXT(n)) {
- if (++oid == n->id) {
- /* Contiguous, count and continue */
- c++;
- if (c == many)
- break;
- } else {
- /* Not contiguous, reset and continue */
- c = 1;
- o = n;
- }
- }
- if (c == many) {
- register node_t *next, *prev;
-
- /* <many> contiguous pages were found, starting at
- * o and ending at n, unlink them all at once
- * efficiently and set last pointer.
- */
- prev = o->node.prev;
- next = n->node.next;
- if (prev)
- prev->next = next;
- else
- m->free.top = next;
- if (next)
- next->prev = prev;
- else
- m->free.bottom = prev;
- o->node.prev = n->node.next = NULL;
- o->last = n;
- m->free.nodes -= c;
-
- /* Then initialize the pages. Unfortunately
- * we need to perform this with lock held
- * because freeing pages runs among pages
- * looking for PS_FREE nodes.
- */
- for (n = o; c > 0;
- n = (page_t *)n->node.next, c--)
- n->state = PS_ALLOCATED;
-
- /* Finished with system pools, restore level */
- _lock_release(&fp->lock);
-
- /* It's safe to do the following interruptible */
- if (zero)
- pageclr(o->address, many);
- pages = o;
- STAT(STAT_PAGES_ALLOC, many);
- goto end;
- }
- }
- }
- }
-
- _lock_release(&fp->lock);
- }
- /* If we reach this point the allocation process desperatly failed */
- }
-
-end:
- if (pages == NULL) {
- STAT(STAT_PAGES_ALLOC_NOMEM, 1);
- DEBUG_PRINTF("- %T pages_alloc(%d, %u, %B) - Out of memory\n",
- memtype, many, zero);
- }
-
- return pages;
-}
-
-
-/* Frees one or more contiguous pages of physical memory which were obtained
- * using pages_alloc(). It is important to call this function on the same
- * page_t pointer which was obtained from pages_alloc().
- */
-bool pages_free(page_t *pages)
-{
- bool ok = FALSE;
-
- if (pages != NULL && pages->last != NULL) {
- register mchunk_t *m;
- register page_t **idx, *fp, *lp, *lfp, *llp;
- register u_int32_t id;
-
- /* Find mchunk_t we belong to, setup variables */
- fp = pages;
- lp = pages->last;
- m = pages->mchunk;
- idx = m->index;
-
- lock_acquire(&m->ppool->lock);
-
- /* Reset pages fields. Because the freeing process requires running
- * among pages looking for PS_FREE ones, we need to perform this
- * while we own the lock.
- */
- for (lfp = fp; lfp != NULL; lfp = (page_t *)lfp->node.next)
- lfp->state = PS_FREE;
-
- /* Determine where in the free list_t of our mchunk_t should our page_t
- * nodes be inserted. It is important that the free list always remain
- * sorted. Using the page index ID of our first and last allocated
- * pages, we can run up and down among pages to obtain this
- * information. Another possible method would be to use a minheap
- * implementation.
- */
- for (id = fp->id - 1; id > -1 && idx[id]->state != PS_FREE; id--) ;
- if (id == -1 || idx[id]->state == PS_ALLOCATED)
- lfp = NULL;
- else
- lfp = idx[id];
- for (id = lp->id + 1; id < m->pages && idx[id]->state != PS_FREE;
- id++) ;
- if (id == m->pages || idx[id]->state == PS_ALLOCATED)
- llp = NULL;
- else
- llp = idx[id];
-
- /* Now lfp == free page_t to attach first page_t to (or NULL)
- * and llp == free page_t to attach last page_t to (or NULL).
- * We also know that within this mchunk_t no pages should exist
- * between our first and last allocated pages because they are
- * contiguous; We thus can safely insert our allocated pages in one
- * efficient step between lfp and llp. We could have done the following
- * immediately after checking for ID boundaries in the previous loops,
- * but decide to perform NULL checking instead for code clarity.
- * Link lfp (or top of list) to fp in both directions, and make sure
- * to set the list top and bottom pointers when necessary as well.
- */
- if (lfp == NULL) {
- m->free.top = (node_t *)fp;
- fp->node.prev = NULL;
- } else {
- lfp->node.next = (node_t *)fp;
- fp->node.prev = (node_t *)lfp;
- }
- /* Link llp (or bottom of list) to lp in both directions */
- if (llp == NULL) {
- m->free.bottom = (node_t *)lp;
- lp->node.next = NULL;
- } else {
- llp->node.prev = (node_t *)lp;
- lp->node.next = (node_t *)llp;
- }
- /* Fix number of free nodes counter */
- m->free.nodes += (lp->id - fp->id) + 1;
-
- _lock_release(&m->ppool->lock);
-
- STAT(STAT_PAGES_FREE, (lp->id - fp->id) + 1);
-
- /* Make sure to not agree to free these anymore until this page_t *
- * is obtained again from pages_alloc(). As the page may have been
- * part of a pool_t, we also must zero the pool pointer.
- */
- pages->last = NULL;
- pages->pool = NULL;
-
- ok = TRUE;
- }
-
- if (!ok) {
- STAT(STAT_PAGES_FREE_FAILED, 1);
- DEBUG_PRINTF("* %T pages_free(%p)\n", pages);
- }
-
- return ok;
-}
-
-
-/* Initializes a pool_t and allocates the required minimum pages if required.
- * The pages of a pool_t can be virtually sized as large as required by
- * specifying a steppages larger than 1. steppages * _PAGE_SIZE is the
- * actual size of a pool_t page. A pool can then be used to allocate objects
- * exceeding _PAGE_SIZE. No locking or synchronization is used by
- * pool_t functions, those must be provided externally by the caller if
- * needed. Of course internal page allocation/free will be done protected by
- * the system pages pool lock. A pool_t links the pages to it's lists via
- * the pages_t->poolnode node_t. If minpages == 0, no pages are allocated
- * until the first pool_alloc() is called on the pool_t. If maxpages == 0
- * the pool will always attempt to grow dynamically if required (and pnode_t
- * allocations will fail if out of memory). If minpages and maxpages are
- * non-zero and the same, pages are pre-allocated immediately and the pool
- * will never shrink or grow. This can be very useful in the case of critical
- * code sections which need to allocate and free objects in an interrupt
- * context. A pool_t, unlike an mpool_t, will not allow to specify which
- * memory type to allocate at each pnode_t allocation. It is however safe to
- * use _MEM_ANY for memtype here in which case any free memory will be used,
- * requested and freed as necessary.
- */
-bool pool_init(pool_t *pool, u_int32_t steppages, u_int32_t minpages,
- u_int32_t maxpages, size_t nodesize, int memtype)
-{
- bool ok = FALSE;
-
- if (pool != NULL && steppages != 0 && nodesize >= sizeof(pnode_t) &&
- memtype < (enum _memtypes)_MEM_MAX && memtype > -2) {
- register size_t psize;
- register u_int32_t nodesperpage, step = steppages;
-
- /* Evaluate how many nodes can fit a page, and that it's realistic.
- * There is no point in using a pool_t if we cannot fit at least two
- * pnode_t objects per page_t. So we'll automatically increase
- * steppages if needed here, upto 8 maximum, and return FALSE if
- * we couln't reach a decent size relatively to the object size.
- * The reason we're doing this is that Xisop system objects pools
- * should grow steppages automatically if needed, and are always sure
- * to be small enough to at least fit into 8 pages (usually 1-2).
- * We want main.c's spools_init() to always succeed even if
- * an object was slightly too large for a single page.
- */
- OBJECT_INVALIDATE(pool);
- nodesize = (size_t)OALIGN_CEIL(nodesize, u_int32_t);
- /* All your optimization are belong to us. */
- for (psize = _PAGE_SIZE * step;
- (nodesperpage = psize / nodesize) < 2 && step < 9;
- psize = _PAGE_SIZE * (++step))
- STAT(STAT_POOLS_ENLARGED, 1);
- if (nodesperpage > 1) {
- pool->nodesperpage = nodesperpage;
- pool->steppages = step;
- pool->minpages = minpages;
- pool->maxpages = maxpages;
- pool->avgtotal = pool->avgcnt = minpages;
- pool->nodesize = nodesize;
- pool->memtype = memtype;
- DLIST_INIT(&pool->pages);
- DLIST_INIT(&pool->fpages);
- DLIST_INIT(&pool->nodes);
- pool->mpool = NULL;
- /* Allocate and initialize pages and nodes if needed */
- for (; minpages > 0; minpages--) {
- register page_t *p;
-
- if ((p = pages_alloc(memtype, step, FALSE)) != NULL) {
- register u_int8_t *ptr, *toptr;
-
- /* pool_t pages are linked via page_t->poolnode */
- p->pnodes = nodesperpage;
- p->pool = pool;
- DLIST_APPEND(&pool->pages, &p->poolnode);
- for (ptr = (u_int8_t *)p->address, toptr = ptr + psize;
- ptr + nodesize < toptr;
- ptr += nodesize) {
- ((pnode_t *)ptr)->page = p;
- DLIST_APPEND(&pool->nodes, (node_t *)ptr);
- }
- } else
- break;
- }
- OBJECT_VALIDATE(pool, OBJECT_POOL);
- if (minpages == 0)
- ok = TRUE;
- else if (minpages < pool->minpages) {
- /* Some of minpages were allocated, we need to free them */
- pool_destroy(pool);
- DEBUG_PRINTF(
- "* %T pool_init(%p, %u, %u, %u, %u, %d) - Out of memory\n",
- pool, steppages, minpages, maxpages, nodesize, memtype);
- }
- }
- }
-
- if (ok)
- STAT(STAT_POOLS_CREATED, 1);
- else {
- STAT(STAT_POOLS_CREATED_FAILED, 1);
- DEBUG_PRINTF("* %T pool_init(%p, %u, %u, %u, %u, %d)\n",
- pool, steppages, minpages, maxpages, nodesize, memtype);
- }
-
- return ok;
-}
-
-
-/* Frees all memory allocated by a pool_t, thus rendering any allocated
- * pnode_t objects invalid as well. The pool_t is then marked as invalid,
- * it can only be valid again if initialized using pool_init().
- * FALSE is returned if the pool_t is invalidated already.
- */
-bool pool_destroy(pool_t *pool)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(pool, OBJECT_POOL)) {
- register node_t *p, *t;
-
- /* Just free all pages allocated by the pool, and mark the pool
- * as freed/nonfunctional. All nodes on the list will be reset if
- * a new pool is initialized using this pool_t *. They become
- * invalid as soon as pages are freed, since they only consist of
- * pointers into those pages. Remember that pool pages are tied
- * up in the list_t via the page_t->poolnode node_t.
- */
- for (p = DLIST_TOP(&pool->pages); p != NULL; p = t) {
- t = DLIST_NEXT(p);
- pages_free((page_t *)(--p));
- }
- for (p = DLIST_TOP(&pool->fpages); p != NULL; p = t) {
- t = DLIST_NEXT(p);
- pages_free((page_t *)(--p));
- }
- OBJECT_INVALIDATE(pool);
- ok = TRUE;
- }
-
- if (ok)
- STAT(STAT_POOLS_DESTROYED, 1);
- else {
- STAT(STAT_POOLS_DESTROYED_FAILED, 1);
- DEBUG_PRINTF("* %T pool_destroy(%p)\n", pool);
- }
-
- return ok;
-}
-
-
-/* Attempts to allocate a pnode_t from the specified pool_t, and optionally
- * clear the object to 0x00 bytes if zero is TRUE. NULL is returned if
- * no memory was available or if the pool was setup to not be able to hold
- * more objects. The size of the pnode_t prefixed object depends on the
- * attributes which were set forthe pool_t at pool_init(). As each node
- * object should start with a pnode_t structure, we return a pointer to
- * the object structure itself at the same time. The type of memory used
- * can only be the one the pool_t was initialized for. The pnode_t allocation
- * process is efficient, compared to managing page_t. Special care is taken
- * to avoid calling page primitives as much as possible using buffering,
- * while still allowing a pool_t to be dynamically resizing if wanted.
- */
-pnode_t *pool_alloc(pool_t *pool, bool zero)
-{
- pnode_t *pnode = NULL;
-
- if (OBJECT_VALID(pool, OBJECT_POOL)) {
- register pnode_t *pn;
-
- /* If there are pre-buffered nodes, simply return the first one. */
- if ((pn = DLIST_TOP(&pool->nodes)) != NULL) {
- DLIST_UNLINK(&pool->nodes, (node_t *)pn);
- pn->page->pnodes--;
- if (zero) {
- register page_t *p;
-
- p = pn->page;
- memclr(pn, pool->nodesize);
- pn->page = p;
- }
- pnode = pn;
- } else {
- register page_t *p = NULL;
- register node_t *n;
-
- /* No pnode_t left, we need to allocate a new page_t to grow and
- * initialize the new bnode_t objects. First verify if there is
- * any available page already in our cache, which pool_free()
- * maintains using statistics, to minimize calls to page
- * primitives functions. If there are none, allocate a new page_t.
- * Remember that we link the pages via the page_t->poolnode node_t.
- */
- if (pool->maxpages == 0 ||
- DLIST_NODES(&pool->pages) < pool->maxpages) {
- if ((n = DLIST_TOP(&pool->fpages)) != NULL) {
- DLIST_UNLINK(&pool->fpages, n);
- p = (page_t *)(--n);
- STAT(STAT_PAGES_REUSED, pool->steppages);
- } else
- p = pages_alloc(pool->memtype, pool->steppages, FALSE);
- if (p != NULL) {
- register u_int8_t *ptr, *toptr;
- register size_t nodesize = pool->nodesize;
-
- p->pnodes = pool->nodesperpage;
- p->pool = pool;
- DLIST_APPEND(&pool->pages, &p->poolnode);
- for (ptr = (u_int8_t *)p->address,
- toptr = ptr + _PAGE_SIZE * pool->steppages;
- ptr + nodesize < toptr;
- ptr += nodesize) {
- ((pnode_t *)ptr)->page = p;
- DLIST_APPEND(&pool->nodes, (node_t *)ptr);
- }
- /* Now grab first pnode_t */
- pn = DLIST_TOP(&pool->nodes);
- DLIST_UNLINK(&pool->nodes, (node_t *)pn);
- p->pnodes--;
- if (zero)
- memclr(pn, nodesize);
- pn->page = p;
- pnode = pn;
- }
- } else {
- STAT(STAT_POOLS_ALLOC_NOMEM, 1);
- DEBUG_PRINTF("- %T pool_alloc(%p, %B) - Out of memory\n",
- pool, zero);
- }
- }
- } else {
- STAT(STAT_POOLS_ALLOC_FAILED, 1);
- DEBUG_PRINTF("* %T pool_alloc(%p, %B)\n", pool, zero);
- }
-
- if (pnode != NULL)
- STAT(STAT_POOLS_ALLOC, 1);
-
- return pnode;
-}
-
-
-/* Used to free a node previously allocated using pool_alloc().
- * Keeps statistics and a page cache to reduce the frequency at which
- * pages_*() functions need to be called.
- */
-pnode_t *pool_free(pnode_t *pnode)
-{
- if (pnode != NULL && OBJECT_VALID(pnode->page->pool, OBJECT_POOL)) {
- register page_t *p = pnode->page;
- register pool_t *pool = p->pool;
- register u_int32_t exceeding;
-
- /* Efficiently return this node in the free list */
- DLIST_INSERT(&pool->nodes, (node_t *)pnode);
- p->pnodes++;
- STAT(STAT_POOLS_FREE, 1);
- if ((pool->minpages < pool->maxpages) ||
- (pool->minpages == 0 && pool->maxpages == 0)) {
- register u_int32_t pages = DLIST_NODES(&pool->pages);
-
- /* This is a pool_t which can shrink, book-keep statistics on
- * average pages usage.
- */
- pool->avgtotal += pages;
- pool->avgcnt++;
- if (pool->avgcnt >
- ((pool->steppages * _PAGE_SIZE / pool->nodesize) * 3)) {
- pool->avgcnt = 1;
- pool->avgtotal = pages;
- }
-
- if (p->pnodes == pool->nodesperpage && pool->minpages < pages) {
- register u_int8_t *ptr, *toptr;
- register size_t nodesize;
-
- /* All pnode_t objects belonging to this page_t were freed.
- * Swap the page to the cache to be freed. We also need
- * to sequencially unlink all the pnode_t objects this page
- * supplied in the free nodes list_t. Remember that pages
- * are linked via the page_t->poolnode node_t.
- */
- for (ptr = (u_int8_t *)p->address,
- toptr = ptr + _PAGE_SIZE * pool->steppages,
- nodesize = pool->nodesize;
- ptr + nodesize < toptr;
- ptr += nodesize)
- DLIST_UNLINK(&pool->nodes, (node_t *)ptr);
- /* Insert to preferably reuse recently used pages */
- DLIST_SWAP(&pool->fpages, &pool->pages, &(p->poolnode), TRUE);
- STAT(STAT_PAGES_BUFFERED, pool->steppages);
- }
-
- /* Do statistics suggest that we should shrink the pool? If so,
- * free pages from our cache back to the system.
- */
- if ((exceeding = (DLIST_NODES(&pool->pages) +
- DLIST_NODES(&pool->fpages)) -
- (pool->avgtotal / pool->avgcnt)) > 0) {
- register list_t *fpages = &pool->fpages;
- register node_t *n;
-
- /* Preferably free pages which haven't been used recently */
- for (; exceeding > 0 && (n = fpages->bottom) != NULL;
- exceeding--) {
- DLIST_UNLINK(fpages, n);
- p = (page_t *)(--n);
- pages_free(p);
- STAT(STAT_PAGES_UNBUFFERED, pool->steppages);
- }
- }
- }
- } else {
- STAT(STAT_POOLS_FREE_FAILED, 1);
- DEBUG_PRINTF("* %T pool_free(%p)\n", pnode);
- }
-
- return NULL;
-}
-
-
-/* Finally, the higher-level general purpose allocation system consists of
- * a bunch of pool_t, as well as a page_t list_t for requests which exceed
- * in size and need to be rounded at page boundaries. Memory of all available
- * types may be allocated and freed at will, and of arbitrary sized blocks.
- * The current implementation requires more memory for mpool setup than
- * previously, there however are advantages in versatility, performance
- * and code quality over the latter.
- * We depend on _PAGE_SIZE and _MPOOLS which should be defined by the
- * port-specific code (port/support.h).
- * No special synchronization is performed in these functions, the caller
- * is required to provide it where necessary (i.e. shared mpool_t). However,
- * the underlaying page_t primitives use the system pages pool lock.
- */
-bool mpool_init(mpool_t *mpool)
-{
- bool ok = FALSE;
-
- if (mpool != NULL) {
- register int t, p;
- register size_t c = _MPOOLSTART;
-
- DLIST_INIT(&mpool->pages);
- ok = TRUE;
- OBJECT_VALIDATE(mpool, OBJECT_MPOOL);
- for (p = 0; p < _MPOOLS; p++) {
- for (t = 0; t < (enum _memtypes)_MEM_MAX; t++) {
- if (!pool_init(&mpool->pools[p][t], _MPOOLSTEP, 0, 0,
- sizeof(mnode_t) + c, t))
- ok = FALSE;
- else
- mpool->pools[p][t].mpool = mpool;
- }
- c *= 2;
- }
- if (!ok) {
- /* Make sure to free everything if part of the system only could be
- * setup
- */
- mpool_destroy(mpool);
- } else {
- mpool->shared = FALSE;
- _lock_init(&mpool->lock);
- mpool->usecount = 0;
- }
- }
-
- if (ok)
- STAT(STAT_MPOOLS_CREATED, 1);
- else {
- STAT(STAT_MPOOLS_CREATED_FAILED, 1);
- DEBUG_PRINTF("* %T mpool_init(%p)\n", mpool);
- }
-
- return ok;
-}
-
-
-bool mpool_destroy(mpool_t *mpool)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(mpool, OBJECT_MPOOL)) {
- register int t, p;
- register node_t *pg, *tmp;
- register u_int32_t usecount = 0;
-
- if (mpool->shared) {
- lock_acquire(&mpool->lock);
- usecount = --mpool->usecount;
- _lock_release(&mpool->lock);
- }
- if (usecount < 1) {
- /* All your pool_t are belong to us. */
- for (p = 0; p < _MPOOLS; p++)
- for (t = 0; t < (enum _memtypes)_MEM_MAX; t++)
- pool_destroy(&mpool->pools[p][t]);
- /* And rounded page requests too. Remember that they are tied via
- * the page_t->poolnode node_t in our list_t.
- */
- for (pg = DLIST_TOP(&mpool->pages); pg != NULL; pg = tmp) {
- tmp = DLIST_NEXT(pg);
- pages_free((page_t *)(--pg));
- }
- OBJECT_INVALIDATE(mpool);
- ok = TRUE;
- STAT(STAT_MPOOLS_DESTROYED, 1);
- }
- } else {
- STAT(STAT_MPOOLS_DESTROYED_FAILED, 1);
- DEBUG_PRINTF("* %T mpool_destroy(%p)\n", mpool);
- }
-
- return ok;
-}
-
-
-/* malloc() clone which can be specified mpool_t to use, memory type
- * and optional memory clear flag.
- */
-void *_malloc(mpool_t *mpool, int memtype, size_t size, bool zero)
-{
- void *mem = NULL;
-
- if (OBJECT_VALID(mpool, OBJECT_MPOOL) &&
- (memtype == _MEM_ANY || memtype < (enum _memtypes)_MEM_MAX)) {
- register int p;
- register size_t rsize;
-
- /* Synchronization for cases where multiple tasks share an mpool_t */
- if (mpool->shared)
- lock_acquire(&mpool->lock);
-
- /* Verify if any of our pool_t can serve the requested size */
- rsize = (size_t)OALIGN_CEIL(sizeof(mnode_t), u_int32_t);
- rsize += size;
- for (p = 0; p < _MPOOLS; p++) {
- if (mpool->pools[p][0].nodesize >= rsize) {
- register mnode_t *n = NULL;
-
- /* This pool can serve this request. */
- if (memtype == _MEM_ANY) {
- register int t;
-
- /* Try available memory types in order */
- for (t = 0; t < (enum _memtypes)_MEM_MAX; t++) {
- n = (mnode_t *)pool_alloc(&mpool->pools[p][t], zero);
- if (n != NULL)
- break;
- }
- } else
- n = (mnode_t *)pool_alloc(&mpool->pools[p][memtype], zero);
- if (n != NULL) {
- /* Best case, everything went fine */
- n->type = MNT_PNODE;
- OBJECT_VALIDATE(n, OBJECT_MNODE);
- mem = (++n);
- break;
- } else {
- STAT(STAT_MPOOLS_ALLOC_NOMEM, 1);
- DEBUG_PRINTF(
- "- %T _malloc(%p, %d, %u, %B) - Out of memory\n",
- mpool, memtype, size, zero);
- }
- }
- }
- if (p == _MPOOLS) {
- register u_int32_t many;
- register page_t *p;
-
- /* We need to round size on page boundaries and allocate pages.
- * If we were requested zeroed memory, the pageclr() will be used
- * internally which is more efficient than memclr().
- */
- many = rsize / _PAGE_SIZE;
- if (rsize % _PAGE_SIZE)
- many++;
- if ((p = pages_alloc(memtype, many, zero)) != NULL) {
- register mnode_t *n = p->address;
-
- /* Link in our pages list via the page_t->poolnode node_t */
- DLIST_APPEND(&mpool->pages, &p->poolnode);
- n->u.pgnode.pages = p;
- n->u.pgnode.mpool = mpool;
- n->type = MNT_PAGES;
- OBJECT_VALIDATE(n, OBJECT_MNODE);
- mem = (++n);
- } else {
- STAT(STAT_MPOOLS_ALLOC_NOMEM, 1);
- DEBUG_PRINTF("- %T _malloc(%p, %d, %u, %B) - Out of memory\n",
- mpool, memtype, size, zero);
- }
- }
-
- if (mpool->shared)
- _lock_release(&mpool->lock);
- } else {
- STAT(STAT_MPOOLS_ALLOC_FAILED, 1);
- DEBUG_PRINTF("* %T _malloc(%p, %d, %u, %B)\n",
- mpool, memtype, size, zero);
- }
-
- if (mem != NULL)
- STAT(STAT_MPOOLS_ALLOC, 1);
-
- return mem;
-}
-
-
-/* free() clone for _malloc() */
-void *_free(void *block)
-{
- register mpool_t *mpool = NULL;
-
- if (block != NULL) {
- register mnode_t *mn = block;
-
- mn--;
- if (OBJECT_VALID(mn, OBJECT_MNODE)) {
-
- OBJECT_INVALIDATE(mn);
- switch (mn->type) {
- case MNT_PNODE:
- /* A pool_t pnode_t which needs to be freed back. Track our
- * mpool_t for synchronization if required.
- */
- if ((mpool = mn->u.pnode.page->pool->mpool) != NULL) {
- if (mpool->shared)
- lock_acquire(&mpool->lock);
- else
- mpool = NULL;
- }
- pool_free((pnode_t *)mn);
- break;
- case MNT_PAGES:
- {
- register page_t *p = mn->u.pgnode.pages;
-
- /* A page_t which needs to be unlinked and freed.
- * Remember that pages are linked via page_t->poolnode
- * node_t. Track our mpool_t for synchronization if needed.
- */
- mpool = mn->u.pgnode.mpool;
- if (mpool->shared)
- lock_acquire(&mpool->lock);
- else
- mpool = NULL;
- DLIST_UNLINK(&mn->u.pgnode.mpool->pages, &p->poolnode);
- pages_free(p);
- break;
- }
- }
- STAT(STAT_MPOOLS_FREE, 1);
- } else {
- STAT(STAT_MPOOLS_FREE_FAILED, 1);
- DEBUG_PRINTF("* %T _free(%p)\n", block);
- }
- } else {
- STAT(STAT_MPOOLS_FREE_FAILED, 1);
- DEBUG_PRINTF("* %T _free(%p)\n", block);
- }
- if (mpool != NULL)
- _lock_release(&mpool->lock);
-
- return NULL;
-}
-
-
-
-/* This function initializes the various pool_t and _lock_t which are reserved
- * to allocate specific types of Xisop system objects for efficiency.
- */
-void spools_init(void)
-{
- register int i;
-
- /* XXX Should panic on failure */
- for (i = 0; i < (enum _syspools)POOL_MAX; i++) {
- _lock_init(&root->spools_locks[i]);
- (void) pool_init(&root->spools[i], syspools_params[i].steppages,
- syspools_params[i].minpages,
- syspools_params[i].maxpages,
- syspools_params[i].nodesize, 0);
- }
-}
-
-
-pnode_t *spool_alloc(u_int32_t pool)
-{
- pnode_t *node = NULL;
-
- if (pool < (enum _syspools)POOL_MAX) {
- lock_acquire(&root->spools_locks[pool]);
- node = pool_alloc(&root->spools[pool], FALSE);
- _lock_release(&root->spools_locks[pool]);
- }
-
- if (node == NULL)
- DEBUG_PRINTF("* %T spool_alloc(%u)\n", pool);
-
- return node;
-}
-
-
-pnode_t *spool_free(u_int32_t pool, pnode_t *node)
-{
- if (pool < (enum _syspools)POOL_MAX) {
- lock_acquire(&root->spools_locks[pool]);
- if (!pool_free(node))
- DEBUG_PRINTF("* %T spool_free(%u, %p)\n", pool, node);
- _lock_release(&root->spools_locks[pool]);
- }
-
- return NULL;
-}
-
-
-/* Specifically made to allocate general purpose memory for the kernel.
- * Although mpool_t now can support synchronization itself when shared,
- * it is faster to perform it unconditionally.
- */
-void *_kmalloc(int memtype, size_t size, bool zero)
-{
- void *mem = NULL;
-
- lock_acquire(&root->kernpool_lock);
- mem = _malloc(root->kernpool, memtype, size, zero);
- _lock_release(&root->kernpool_lock);
-
- return mem;
-}
-
-void *_kfree(void *mem)
-{
- lock_acquire(&root->kernpool_lock);
- _free(mem);
- _lock_release(&root->kernpool_lock);
-
- return NULL;
-}
-
-
-/* Closer to ANSI-C but still for general purpose kernel memory */
-void *kmalloc(size_t size)
-{
- return MALLOC(size);
-}
-
-void kfree(void *mem)
-{
- FREE(mem);
-}
-
-
-/* These functions are ANSI-C standard, and only use the current task pool. */
-
-void *malloc(size_t size)
-{
- return _malloc(CURTASK()->mpool, _MEM_ANY, size, FALSE);
-}
-
-void *calloc(int number, size_t size)
-{
- return _malloc(CURTASK()->mpool, _MEM_ANY, number * size, TRUE);
-}
-
-void *realloc(void *ptr, size_t size)
-{
- register void *nptr = ptr;
-
- /* Not very efficient, but mostly provided for compatibility. */
- if (nptr != NULL) {
- if (size == 0)
- /* Basically a request to free the buffer */
- nptr = _free(nptr);
- else {
- register mnode_t *mnode;
-
- /* Climb up to obtain actual buffer size */
- mnode = (mnode_t *)nptr;
- mnode--;
- if (OBJECT_VALID(mnode, OBJECT_MNODE)) {
- register size_t osize;
-
- switch (mnode->type) {
- case MNT_PNODE:
- {
- osize = mnode->u.pnode.page->pool->nodesize -
- sizeof(mnode_t);
- break;
- }
- case MNT_PAGES:
- {
- register page_t *pages = mnode->u.pgnode.pages;
-
- osize = ((pages->last->id - pages->id) +1) *
- _PAGE_SIZE;
- break;
- }
- default:
- nptr = NULL;
- osize = 0;
- break;
- }
-
- /* Is the requested size larger than the currently available
- * number of bytes? If not, don't do anything, return the
- * current buffer pointer.
- */
- if (nptr != NULL && osize < size) {
- /* Allocate a buffer which can at least hold the requested
- * number of bytes. Well at least, attempt to. If we can't
- * don't do anything and return NULL.
- */
- if ((nptr = malloc(size)) != NULL) {
- /* Allocation successful, copy all previous buffer
- * contents to the new one, then free the old buffer,
- * and return the new buffer pointer.
- */
- memcpy(nptr, ptr, osize);
- free(ptr);
- }
- }
- } else
- nptr = NULL;
- }
- } else
- /* A request to simply allocate */
- nptr = malloc(size);
-
- return nptr;
-}
-
-void free(void *ptr)
-{
- _free(ptr);
-}
-
-
-/* Xisop extentions as we support multiple memory types */
-void *tmalloc(int memtype, size_t size)
-{
- return _malloc(CURTASK()->mpool, memtype, size, FALSE);
-}
-
-void *tcmalloc(int memtype, int number, size_t size)
-{
- return _malloc(CURTASK()->mpool, memtype, number * size, TRUE);
-}
-
-
-
-/* The following code is only compiled in if DEBUG was #defined in config.h */
-#ifdef DEBUG
-
-
-/* Dump the current state of all memory pages in the system. */
-void pages_dump(void)
-{
- register u_int32_t ppool;
- _ipl_t x;
-
- /* Become absolutely uninterruptible */
- x = _splhigh();
-
- debug_printf("\nCURRENT STATE OF SYSTEM MEMORY PAGES\n");
- for (ppool = 0; ppool < (enum _memtypes)_MEM_MAX; ppool++) {
- register mchunk_t *mchunk;
-
- debug_printf("\nDumping mchunks for ppool_t of _MEM_TYPE %u\n",
- ppool);
- DLIST_FOREACH(&(root->ppools[ppool].mchunks), mchunk) {
- register page_t **index = mchunk->index;
- register u_int32_t id, pages = mchunk->pages;
-
- debug_printf(
- " Dumping pages for mchunk_t *%p (%u pages, page_t **%p)\n",
- mchunk, pages, index);
- debug_printf(" mchunk->free.top = ");
- if (mchunk->free.top != NULL)
- debug_printf(
- "%u (page_t *%p)\n", ((page_t *)mchunk->free.top)->id,
- mchunk->free.top);
- else
- debug_printf("NULL\n");
- debug_printf(" mchunk->free.bottom = ");
- if (mchunk->free.bottom != NULL)
- debug_printf("%u (page_t *%p)\n",
- ((page_t *)mchunk->free.bottom)->id,
- mchunk->free.bottom);
- else
- debug_printf("NULL\n");
- for (id = 0; id < pages; id++) {
- register page_t *page = index[id];
-
- /* Print summary information */
- debug_printf(" - %u (page_t *%p, void *%p)\n next = ",
- id, page, page->address);
- if (page->node.next == NULL)
- debug_printf("NULL, prev = ");
- else
- debug_printf("%u (page_t *%p), prev = ",
- ((page_t *)page->node.next)->id,
- page->node.next);
- if (page->node.prev == NULL)
- debug_printf("NULL\n");
- else
- debug_printf("%u (page_t *%p)\n",
- ((page_t *)page->node.prev)->id,
- page->node.prev);
-
- /* Rest will vary depending if page is allocated */
- if(page->state == PS_ALLOCATED) {
- debug_printf(" Allocated, last = ");
- if (page->last != NULL)
- debug_printf("%u (page_t *%p)\n", page->last->id,
- page->last);
- else
- debug_printf("NULL\n");
- if (page->pool != NULL)
- debug_printf(" pool_t = *%p, mem %d, nodesize %u\n",
- page->pool, page->pool->memtype,
- page->pool->nodesize);
- } else
- debug_printf(" Free\n");
-
- /* Perform some basic sanity checking and report problems
- * which are known to disrupt the system. If those occur,
- * the pools were corrupted and the system may need a reset
- * for proper operation.
- */
- if (page->node.next != NULL) {
- register node_t *n = page->node.next;
-
- if (n->prev == NULL)
- debug_printf(
- " * page->node.next->node.prev = NULL\n");
- else if (n->prev != &page->node)
- debug_printf(
- " * page->node.next->node.prev != page\n");
- }
- if (page->node.prev != NULL) {
- register node_t *n = page->node.prev;
-
- if (n->next == NULL)
- debug_printf(
- " * page->node.prev->node.next = NULL\n");
- else if (n->next != &page->node)
- debug_printf(
- " * page->node.prev->node.next != page\n");
- }
- if (page->id != id)
- debug_printf(" * page->id != index[id]\n");
- if (page->state != PS_FREE && page->state != PS_ALLOCATED)
- debug_printf(" * Unknown page->state\n");
- if (page->state == PS_FREE && page->last != NULL)
- debug_printf(" * PS_FREE && last != NULL\n");
- if (page->state == PS_FREE && page->pool != NULL)
- debug_printf(" * PS_FREE && page->pool != NULL");
- if (page->mchunk != mchunk)
- debug_printf(" * page->mchunk != mchunk\n");
- }
- }
- }
-
- _splx(x);
-}
-
-
-#endif
+++ /dev/null
-/* $Id: memory.h,v 1.4 2004/06/04 03:09:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_MEMORY_H
-#define KERNEL_MEMORY_H
-
-
-
-#include <common/types.h>
-#include <common/kernlib/list.h>
-#include <processor/support.h>
-#include <port/support.h>
-
-
-
-/* Only common memory type which is always available */
-#define _MEM_ANY -1
-
-/* Page state */
-#define PS_FREE 0
-#define PS_ALLOCATED 1
-
-/* mnode_t types */
-#define MNT_PNODE 0
-#define MNT_PAGES 1
-
-
-
-/* This is a memory page node. One is required for each physical page which
- * is to be included in the memory management system. The pool_t related
- * fields seem to pollute the page space, but there is no better place to put
- * them; Using an additional page_t based structure for pool_t page_t nodes
- * would require allocating more memory. The pool_t primitives are such a
- * vital element to Xisop that we can add support for it inherently.
- */
-struct page {
- node_t node; /* Link to pages_free of mchunk or pages_t */
- node_t poolnode; /* Used for pool_t page linking */
- mchunk_t *mchunk; /* mchunk_t we belong to for freeing */
- void *address; /* Page address in memory (should be 32-bit aligned) */
- page_t *last; /* Allocated? Link to last contiguous node, or NULL */
- pool_t *pool; /* If part of a pool_t, pointer to the pool_t */
- u_int32_t id; /* Page index offset in mchunk_t */
- u_int32_t pnodes; /* Number of free blocks in this page_t for pool_t */
- u_int32_t state; /* PS_FREE | PS_ALLOCATED */
-};
-
-/* A chunk of memory consists of an arbitrary amount of contiguous pages
- * of physical memory. This construct allows to dynamically append memory
- * which can be mapped anytime, i.e. PCMCIA memory which just was inserted.
- * Obviously, to add new memory some memory is required to be setup for the
- * lists, index and page nodes. This memory should never be freed back unless
- * it is certain that the device was detached, and all allocated memory on it
- * is to be discarded. Each such chunk will be scanned in order for available
- * memory when memory is to be allocated from a ppool_t.
- */
-struct mchunk {
- node_t node; /* Link to mpool_t */
- list_t free; /* List of free pages */
- ppool_t *ppool; /* Link to ppool_t we belong to */
- page_t **index; /* Index of all pages in this mchunk_t */
- u_int32_t pages; /* Number of pages in this mchunk_t */
- u_int32_t object_magic; /* Validity sceal */
-};
-
-/* A pool of pages, there is one such pool per memory type. These are
- * initialized by the port-specific _init_memory() function.
- */
-struct ppool {
- _lock_t lock; /* Secure exclusive access lock */
- list_t mchunks; /* List of mchunk_t objects */
-};
-
-
-
-/* A pool used to allocate objects of a fixed size, which are smaller than
- * half a page. maxpages permits to restrict the pool from growing more than
- * wanted, minpages specifies the minimum number of pages which should always
- * remain allocated (and setup as objects), avgtotal and avgcnt are used for
- * statistics when freeing pages (and destroying their objects). An unused
- * page is moved to fpages list, and moved back in pages list when an object
- * on the page is used. If statistics show that the pool should shrink, pages
- * from fpages are freed back to the system (unless minpages is reached).
- * steppages specify how many pages to allocate and free at once. A page size
- * for the pool consists of steppages * _PAGE_SIZE. All pool objects always
- * start by a node_t, used for internal linking when freed, and can be used
- * for custom linking after allocation until freed back.
- */
-struct pool {
- u_int32_t steppages, minpages, /* Size requirements */
- maxpages;
- u_int32_t avgtotal, avgcnt; /* Statistics for page_t cache */
- u_int32_t nodesperpage; /* Number of pnode_t per page_t */
- size_t nodesize; /* 32-bit aligned size of pnode_t */
- list_t pages, fpages, /* Allocated and cached page_t's */
- nodes; /* Ready free pnode_t's */
- int memtype; /* Memory type to allocate */
- u_int32_t object_magic; /* Validity sceal */
- mpool_t *mpool; /* mpool_t we belong to (or NULL) */
-};
-
-/* A pool_t node */
-struct pnode {
- node_t node; /* pool_t->nodes or user list_t linking */
- page_t *page; /* page_t this pnode_t belongs to */
-};
-
-
-
-/* And finally, the general purpose memory management functions, allowing
- * blocks of memory of arbitrary size and type to be allocated and freed back,
- * internally based upon the pool_t system.
- */
-struct mpool {
- /* All required pool_t for the various memory types and request sizes */
- pool_t pools[_MPOOLS][(enum _memtypes)_MEM_MAX];
- list_t pages; /* Large requests rounded on page boundaries */
- u_int32_t object_magic; /* Validity sceal */
- /* These are used in the case where several tasks share a common mpool_t,
- * In which case the mpool_t is synchronized and only destroyed when no
- * more tasks are using it. Because Xisop does not support MMU the
- * requirement for this is questionable. Especially that through the
- * message passing system tasks can easily synchronize on wanted shared
- * memory regions. It can however save some memory on very small systems.
- * All tasks could potentially share the same mpool_t if the init task
- * was setup to do so, for instance.
- */
- bool shared;
- _lock_t lock;
- u_int32_t usecount;
-};
-
-/* An mpool_t node, which prefixes all allocated blocks, the ones from pools
- * as well as the ones made of rounded pages (which were too large to satisfy
- * using the pool_t objects). Contrary to pool_*() and pages_*() functions, the
- * mnode_t is abstracted and hidden in the supplied data block. The pointer
- * supplied to the caller points immediately after this structure
- * (32-bit aligned). We use a union to reduce as much as possible the size
- * of the structure, and be able to reference back to both pnode_t or
- * page_t + mpool_t objects.
- */
-struct mnode {
- union {
- pnode_t pnode; /* We're a pnode_t */
- struct {
- page_t *pages; /* We're a page_t from mpool_t */
- mpool_t *mpool;
- } pgnode;
- } u;
- int type; /* MNT_PAGES || MNT_PNODE */
- u_int32_t object_magic; /* Validity sceal */
-};
-
-
-
-/* The pool_t types Xisop initializes for efficient management of common
- * system objects. These should correspond to the osize structure defined in
- * memory.c's spools_init() function.
- */
-enum _syspools {
- POOL_TASK = 0,
- POOL_PORT,
- POOL_DEVICENODE,
- POOL_DEVICEHANDLE,
- POOL_MPOOL,
- POOL_MAX
-};
-
-/* An array of these is filled in memory.c for pools initialization */
-struct syspools_params {
- const char *label;
- u_int32_t steppages, minpages, maxpages;
- size_t nodesize;
-};
-
-
-
-/* Useful macros intended to be used by kernel code */
-#define TMALLOC(t, s) _kmalloc((t), (s), FALSE)
-#define TCMALLOC(t, n, s) _kmalloc((t), (n) * (s), TRUE)
-#define MALLOC(s) _kmalloc(_MEM_ANY, (s), FALSE)
-#define CMALLOC(n, s) _kmalloc(_MEM_ANY, (n) * (s), TRUE)
-#define FREE(p) _kfree((p))
-
-
-
-void memory_init(void);
-
-void spools_init(void);
-pnode_t *spool_alloc(u_int32_t);
-pnode_t *spool_free(u_int32_t, pnode_t *);
-
-mchunk_t *mchunk_init(void *, size_t);
-bool mchunk_attach(int, mchunk_t *);
-bool mchunk_detach(int, mchunk_t *);
-
-page_t *pages_alloc(int, u_int32_t, bool);
-bool pages_free(page_t *);
-
-bool pool_init(pool_t *, u_int32_t, u_int32_t, u_int32_t, size_t, int);
-bool pool_destroy(pool_t *);
-pnode_t *pool_alloc(pool_t *, bool);
-pnode_t *pool_free(pnode_t *);
-
-bool mpool_init(mpool_t *);
-bool mpool_destroy(mpool_t *);
-void *_malloc(mpool_t *, int, size_t, bool);
-void *_free(void *);
-#define mpool_alloc _malloc
-#define mpool_free _free
-
-void *_kmalloc(int, size_t, bool);
-void *_kfree(void *);
-void *kmalloc(size_t);
-void kfree(void *);
-
-void *malloc(size_t);
-void *calloc(int, size_t);
-void *realloc(void *, size_t);
-void free(void *);
-void *tmalloc(int, size_t);
-void *tcmalloc(int, int, size_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: object.h,v 1.4 2004/06/04 19:03:36 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* For a good example of code which uses these, look at port.c's port_create(),
- * port_reply() and port_destroy() functions.
- */
-
-
-
-#ifndef COMMON_KERNEL_OBJECT_H
-#define COMMON_KERNEL_OBJECT_H
-
-
-
-#include <common/kernel/main.h>
-
-
-
-/* These should be unique and quite uncommon to happen randomly */
-#define OBJECT_PORT 0x504f5254 /* PORT */
-#define OBJECT_TASK 0x5441534b /* TASK */
-#define OBJECT_POOL 0x504f4f4c /* POOL */
-#define OBJECT_MPOOL 0x4d504f4c /* MPOL */
-#define OBJECT_MNODE 0x4d4e4f44 /* MNOD */
-#define OBJECT_MCHUNK 0x4d43484b /* MCHK */
-#define OBJECT_DEVICEHANDLE 0x44455648 /* DEVH */
-#define OBJECT_DEVICENODE 0x4445564e /* DEVN */
-#define OBJECT_IOREQUEST 0x494f5251 /* IORQ */
-#define OBJECT_HASHTABLE 0x4854424c /* HTBL */
-#define OBJECT_HASHNODE 0x484e4f44 /* HNOD */
-
-
-
-/* Validate an object */
-#define OBJECT_VALIDATE(o, m) (o)->object_magic = (m)
-/* Invalidate an object */
-#define OBJECT_INVALIDATE(o) (o)->object_magic = 0
-/* Register an object which others could depend on */
-#define OBJECT_REGISTER(o) (o)->object_id = unique_id()
-/* Register a dependancy for the object */
-#define OBJECT_SETDEP(o, r) do { \
- (o)->objdep_id = (r)->object_id; \
- (o)->objdep_magic = (r)->object_magic; \
-} while (/* CONSTCOND */0)
-
-/* Returns TRUE if the object is valid and of expected type, FALSE if not */
-#define OBJECT_VALID(o, m) ((o) != NULL && (o)->object_magic == (m))
-/* Returns TRUE if the object's dependancy matches with (d) */
-#define OBJECT_DEPENDS(o, d) ((d) != NULL && \
- (d)->object_magic == (o)->objdep_magic && \
- (d)->object_id == (o)->objdep_id)
-
-
-
-#endif
+++ /dev/null
-/* $Id: port.c,v 1.7 2004/10/14 15:05:23 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/task.h>
-#include <common/kernel/port.h>
-#include <common/kernel/main.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/object.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/debug.h>
-#include <common/kernlib/hash.h>
-#include <common/kernlib/string.h>
-
-
-
-port_t *port_create(const char *name)
-{
- bstr_t *bstr = NULL;
- signum_t signum;
-
- if (name != NULL) {
- if ((bstr = bstr_new(name, 32, FALSE)) == NULL) {
- STAT(STAT_PORTS_CREATED_NOMEM, 1);
- DEBUG_PRINTF("* %T port_create(%p) - Out of memory\n", name);
- } else {
- SYSTABLE_RLOCK(SYSTABLE_PUBLICPORTS);
- if ((hashtable_lookup(SYSTABLE(SYSTABLE_PUBLICPORTS), bstr->data,
- bstr->len)) != NULL) {
- SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS);
- STAT(STAT_PORTS_CREATED_EXISTS, 1);
- DEBUG_PRINTF("* %T port_create(%p) - Port exists (%s)\n",
- bstr->data);
- bstr = bstr_free(bstr);
- }
- }
- }
- if (name == NULL || bstr != NULL) {
- if ((signum = signal_alloc()) != -1) {
- register task_t *task = CURTASK();
- register port_t *port = NULL;
-
- if ((port = (port_t *)spool_alloc(POOL_PORT)) != NULL) {
- /* Validate object, and register it since message_t depends on
- * us for reply-port validation
- */
- OBJECT_VALIDATE(port, OBJECT_PORT);
- OBJECT_REGISTER(port);
- /* Initialize other port_t fields */
- port->sigtask = task;
- port->signum = signum;
- DLIST_INIT(&port->messages);
- port->name = bstr;
- if (bstr != NULL) {
- /* Public port, link via sysnode */
- SYSTABLE_UPGRADE(SYSTABLE_PUBLICPORTS);
- (void) hashtable_link(SYSTABLE(SYSTABLE_PUBLICPORTS),
- (hashnode_t *)port, bstr->data,
- bstr->len, FALSE);
- SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS);
- }
- /* Register task resource */
- SCHED_DISABLE();
- DLIST_APPEND(&task->resources.ports, &port->tasknode);
- SCHED_ENABLE();
- STAT(STAT_PORTS_CREATED, 1);
-
- return port;
- } else {
- if (bstr != NULL)
- SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS);
- STAT(STAT_PORTS_CREATED_NOMEM, 1);
- DEBUG_PRINTF("* %T port_create(%p) - Out of memory\n", name);
- }
- signal_free(SIGMASK(signum));
- } else {
- if (bstr != NULL)
- SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS);
- STAT(STAT_PORTS_CREATED_NOSIG, 1);
- DEBUG_PRINTF("* %T port_create(%p) - Out of signals\n", name);
- }
- }
-
- if (bstr != NULL)
- bstr_free(bstr);
-
- return NULL;
-}
-
-
-/* Can destroy a port of any task. */
-port_t *port_destroy(port_t *port)
-{
- if (OBJECT_VALID(port, OBJECT_PORT)) {
- register task_t *task = port->sigtask;
-
- SCHED_DISABLE();
- OBJECT_INVALIDATE(port);
- _signal_free(task, PORT_SIGMASK(port));
- /* Unlink task resource, linked via tasknode */
- DLIST_UNLINK(&task->resources.ports, &port->tasknode);
- SCHED_ENABLE();
- if (port->name != NULL) {
- bstr_free(port->name);
- /* Unlink system resource, linked via sysnode */
- SYSTABLE_WLOCK(SYSTABLE_PUBLICPORTS);
- hashtable_unlink(SYSTABLE(SYSTABLE_PUBLICPORTS),
- (hashnode_t *)port);
- SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS);
- }
- spool_free(POOL_PORT, (pnode_t *)port);
- STAT(STAT_PORTS_DESTROYED, 1);
- } else {
- STAT(STAT_PORTS_DESTROYED_FAILED, 1);
- DEBUG_PRINTF("* %T port_destroy(%p)\n", port);
- }
-
- return NULL;
-}
-
-
-port_t *port_find(const char *name)
-{
- register port_t *port = NULL;
-
- if (name != NULL) {
- SYSTABLE_RLOCK(SYSTABLE_PUBLICPORTS);
- port = (port_t *)hashtable_lookup(SYSTABLE(SYSTABLE_PUBLICPORTS),
- name, strnlen(name, 32));
- SYSTABLE_UNLOCK(SYSTABLE_PUBLICPORTS);
- }
-
- if (port != NULL)
- STAT(STAT_PORTS_FIND_FOUND, 1);
- else
- STAT(STAT_PORTS_FIND_NOTFOUND, 1);
-
- return port;
-}
-
-
-bool port_send(port_t *port, port_t *replyport, message_t *msg)
-{
- bool ok = FALSE;
- message_t *tmsg;
-
- if (OBJECT_VALID(port, OBJECT_PORT) && msg != NULL) {
- ok = TRUE;
- if (replyport != NULL) {
- if (OBJECT_VALID(replyport, OBJECT_PORT)) {
- msg->replyport = replyport;
- OBJECT_SETDEP(msg, replyport);
- } else
- ok = FALSE;
- } else
- msg->replyport = NULL;
- /* Queue message */
- SCHED_DISABLE();
- tmsg = DLIST_TOP(&port->messages);
- DLIST_APPEND(&port->messages, (node_t *)msg);
- SCHED_ENABLE();
- /* Notify that a message arrived, but only if the port was previously
- * empty.
- */
- if (tmsg == NULL)
- signal_send(port->sigtask, PORT_SIGMASK(port));
- }
-
- if (ok)
- STAT(STAT_PORTS_SEND, 1);
- else {
- STAT(STAT_PORTS_SEND_FAILED, 1);
- DEBUG_PRINTF("* %T port_send(%p, %p, %p)\n", port, replyport, msg);
- }
-
- return ok;
-}
-
-
-message_t *port_get(port_t *port)
-{
- register message_t *msg = NULL;
-
- if (OBJECT_VALID(port, OBJECT_PORT)) {
- SCHED_DISABLE();
- if ((msg = DLIST_TOP(&port->messages)) != NULL)
- DLIST_UNLINK(&port->messages, (node_t *)msg);
- SCHED_ENABLE();
- } else {
- STAT(STAT_PORTS_GET_FAILED, 1);
- DEBUG_PRINTF("* %T port_get(%p)\n", port);
- }
-
- if (msg != NULL)
- STAT(STAT_PORTS_GET, 1);
-
- return msg;
-}
-
-
-bool port_reply(message_t *msg)
-{
- bool ok = FALSE;
-
- if (msg != NULL && OBJECT_DEPENDS(msg, msg->replyport))
- ok = port_send(msg->replyport, NULL, msg);
-
- if (ok)
- STAT(STAT_PORTS_REPLY, 1);
- else {
- STAT(STAT_PORTS_REPLY_FAILED, 1);
- DEBUG_PRINTF("* %T port_reply(%p)\n", msg);
- }
-
- return ok;
-}
-
-
-/* Somewhat like poll(), but returns the port which caused the awakening.
- * The task argument is optional, and specifies a prefered task to run next,
- * or NULL. Note that a task can only wait for messages on it's own ports.
- */
-port_t *port_wait(port_t **ports, u_int32_t num, task_t *yield)
-{
- register port_t *port = NULL;
-
- if (ports != NULL && num > 0) {
- register sigmask_t sigmask = 0;
- register u_int32_t i;
-
- SCHED_DISABLE();
- /* By definition, a port has a tied signal. Form the sigmask. */
- for (i = 0; i < num; i++) {
- register port_t *p = ports[i];
-
- if (OBJECT_VALID(p, OBJECT_PORT)) {
- sigmask |= PORT_SIGMASK(p);
- if (DLIST_TOP(&p->messages) != NULL) {
- port = p;
- break;
- }
- }
- }
- SCHED_ENABLE();
- /* Only wait if all ports were empty */
- if (port == NULL) {
- STAT(STAT_PORTS_WAIT, 1);
- sigmask = signal_wait(sigmask, yield);
- STAT(STAT_PORTS_WAIT_RETURNED_SLEEP, 1);
- SCHED_DISABLE();
- for (i = 0; i < num; i++) {
- register port_t *p = ports[i];
-
- if (OBJECT_VALID(p, OBJECT_PORT) &&
- (sigmask & PORT_SIGMASK(p))) {
- port = p;
- break;
- }
- }
- /* A big problem! We awaken, but not as the result of one of the
- * port signals. When such a thing can occur, the task should
- * use signal_wait() and manually include in the mask the wanted
- * port signals, not port_wait(). We'll return NULL of course.
- */
- if (i == num) {
- STAT(STAT_PORTS_WAIT_RETURNED_NOSIG, 1);
- DEBUG_PRINTF(
- "- %T port_wait(%p, %u, %p) - Unexpected signal\n",
- ports, num, yield);
- }
- SCHED_ENABLE();
- } else
- STAT(STAT_PORTS_WAIT_RETURNED_IMMEDIATE, 1);
- } else {
- STAT(STAT_PORTS_WAIT_FAILED, 1);
- DEBUG_PRINTF("* %T port_wait(%p, %u, %p)\n", ports, num, yield);
- }
-
- return port;
-}
-
-
-/* Unlinks all currently queued messages (if any) from the supplied port_t.
- * Should normally be performed on the tasks of the caller process only.
- */
-bool port_flush(port_t *port)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(port, OBJECT_PORT)) {
- ok = TRUE;
- SCHED_DISABLE();
- if (DLIST_TOP(&port->messages) != NULL)
- DLIST_INIT(&port->messages);
- SCHED_ENABLE();
- STAT(STAT_PORTS_FLUSHED, 1);
- } else {
- STAT(STAT_PORTS_FLUSHED_FAILED, 1);
- DEBUG_PRINTF("* %T port_flush(%p)\n", port);
- }
-
- return ok;
-}
+++ /dev/null
-/* $Id: port.h,v 1.2 2004/01/18 17:43:00 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_PORT_H
-#define KERNEL_PORT_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/task.h>
-#include <common/kernel/signal.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/main.h>
-#include <common/kernel/memory.h>
-#include <common/kernlib/hash.h>
-#include <common/kernlib/string.h>
-
-
-
-struct port {
- hashnode_t sysnode;
- node_t tasknode; /* Link to task_t ressources list_t */
- /* Validity sceal, message_t can depend on us */
- u_int32_t object_magic, object_id;
- /* Other */
- task_t *sigtask;
- signum_t signum;
- list_t messages;
- /* The following are used for public ports */
- bstr_t *name;
-};
-
-struct message {
- pnode_t node; /* Allows for linking and using pool_t */
- /* The following two are used to allow replying back, but only to the
- * actually expected reply port.
- */
- port_t *replyport;
- /* Object dependancy */
- u_int32_t objdep_magic, objdep_id;
-};
-
-/* This message type is made to pass through the special multiplexed port */
-/* XXX */
-struct mmessage {
- message_t node;
- int type;
- union {
- } u;
-};
-
-
-
-#define PORT_SIGMASK(p) SIGMASK((p)->signum)
-
-
-
-port_t *port_create(const char *);
-port_t *port_destroy(port_t *);
-port_t *port_find(const char *);
-bool port_send(port_t *, port_t *, message_t *);
-message_t *port_get(port_t *);
-bool port_reply(message_t *);
-port_t *port_wait(port_t **, u_int32_t, task_t *);
-bool port_flush(port_t *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: scheduler.c,v 1.5 2004/01/29 21:52:12 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/main.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/task.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/object.h>
-#include <processor/support.h>
-
-
-
-/* The famous initial context (the first scheduler interrupt context save
- * operation saves main()'s context there. This context is resumed when there
- * are no more tasks in the ready queue to run, in which case main()'s CPU
- * ideling loop resumes.
- */
-static _ctx_t _scontext;
-
-
-
-/* Used to initialize the xisop_root necessary fields */
-void scheduler_init(void)
-{
- /* Initialize scheduler recursive lock, and obtain it. The system will
- * need to SCHED_ENABLE() to start the scheduler.
- */
- _rlock_init(&root->sched_lock);
- _rlock_acquire(&root->sched_lock);
- DLIST_INIT(&root->tasks_ready);
- DLIST_INIT(&root->tasks_wait);
- DLIST_INIT(&root->tasks_dead);
- root->curtask = NULL;
- root->curctx = &_scontext;
-}
-
-
-/* This function only has effect if the scheduler recursive lock is free,
- * in which case scheduling is enabled. When so, it evaluates which task
- * should run next and internally performs task and context switching if
- * needed, via root->curtask and _scontext, the static context buffer used
- * by the scheduler interrupt handler to store and load CPU context. We are
- * only dealing with the tasks which are currently on the root->tasks_ready
- * queue, and are working appropriately around the events of no tasks in the
- * queue and the first context switch, and against recursion.
- *
- * What the scheduler timer interrupt does is disable the interrupt source,
- * save the current user CPU context in root->curctx, which may be _scontext
- * or old task context, execute the _FACILITY_SCHEDTIMER hooks calling
- * facility_exechooks(), call us, load back the saved user CPU context
- * from root->curctx, re-enable the interrupt source and return from exception
- * to the new context's PC address. So basically we are between the context
- * save and load operations, and can access and alter root->curtask and
- * root->curctx, thus the buffer it uses to load the context back.
- *
- * Our task priority scheduling algorithm uses a credits attribution method
- * where when starting a round, each task in the queue receives credits
- * according to it's specific priority. The round expires when none of the
- * tasks have any credits left, in which case a new round restarts over.
- * During a round, an effort is made to both allow tasks with the most credits
- * to run more often and faster than others, while still distributing as much
- * as possible their turns evenly to make the signal and message port systems
- * work as efficiently as possible in all cases.
- *
- * This does not account for how long each task has run, a task may
- * take any time from 0 to the scheduler rate before it is interrupted and
- * a credit is substracted from it. Another type of scheduler could be
- * more suitable for a realtime system.
- */
-void old_schedule(void)
-{
- /* Do nothing unless the scheduler is enabled */
- if (_rlock_try(&root->sched_lock)) {
- register task_t *ntsk, *old;
-
- if ((old = root->curtask) == NULL)
- ntsk = DLIST_TOP(&root->tasks_ready);
- else {
- if (old->state == STATE_RUN)
- old->state = STATE_READY;
- for (;;) {
- register task_t *tsk;
- register priority_t credit;
-
- /* Find which task has the most credits and deserves next run,
- * but continue the loop at previous task, otherwise the
- * highest priority task will get all turns at once until it
- * reaches equality with other tasks.
- */
- credit = -128;
- tsk = old;
- ntsk = NULL;
- for (;;) {
- if ((tsk = (task_t *)tsk->node.node.next) == NULL)
- tsk = DLIST_TOP(&root->tasks_ready);
- if (tsk != old) {
- if (credit < tsk->credits) {
- credit = tsk->credits;
- ntsk = tsk;
- }
- } else
- break;
- }
- if (ntsk == NULL) {
- /* If this happens there is only one task in the queue,
- * or none. Revert back to previous task, if any.
- */
- if (DLIST_NODES(&root->tasks_ready) > 0)
- ntsk = old;
- else break;
- }
-
- if (credit == -128 /* XXX && ntsk != old */) {
- /* All out of credits, redistribute them. This is the
- * end and beginning of a new round.
- */
- DLIST_FOREACH(&root->tasks_ready, tsk) {
- if ((tsk->credits = tsk->priority) == -128)
- tsk->credits++;
- }
- continue;
- }
-
- break;
- }
- }
-
- if (ntsk != NULL) {
- /* This is the next task to run */
- ntsk->credits--;
- if (ntsk != old) {
- /* Perform actual context switch */
- STAT(STAT_SCHED_PREEMPTED, 1);
- ntsk->state = STATE_RUN;
- root->curtask = ntsk;
- root->curctx = &ntsk->ctx;
- }
- } else {
- root->curtask = NULL;
- root->curctx = &_scontext;
- }
-
- _rlock_release(&root->sched_lock);
- }
-}
-
-/* Because there seems to be a bug with the previous function and that
- * all we need for testing and building the rest of Xisop is at least
- * round robin, here is a function which at least works for now.
- */
-void schedule(task_t *pref)
-{
- if (_rlock_try(&root->sched_lock)) {
- register task_t *tsk;
-
- /* First evaluate if requested task is ok to switch to */
- if (OBJECT_VALID(pref, OBJECT_TASK) && pref != root->curtask &&
- pref->state == STATE_READY)
- tsk = pref;
- else {
- if ((tsk = root->curtask) == NULL)
- tsk = DLIST_TOP(&root->tasks_ready);
- else {
- /* The current task may not be in the ready list anymore */
- if (tsk->state == STATE_READY || tsk->state == STATE_RUN) {
- tsk->state = STATE_READY;
- if ((tsk = DLIST_NEXT(tsk)) == NULL)
- tsk = DLIST_TOP(&root->tasks_ready);
- } else
- tsk = DLIST_TOP(&root->tasks_ready);
- }
- }
- if (tsk != NULL) {
- tsk->state = STATE_RUN;
- root->curtask = tsk;
- root->curctx = &tsk->ctx;
- } else {
- /* No tasks in the ready queue to run, return to the original
- * main() context, which idles the CPU as much as possible
- * using sys_idle().
- */
- root->curtask = NULL;
- root->curctx = &_scontext;
- }
- _rlock_release(&root->sched_lock);
- }
-}
-
-
-/* Allows to make a task sleep forcibly. The only way it can then wake up
- * is by task_wakeup() using at least one of the bits in the flags.
- */
-void task_sleep(task_t *task, u_int32_t flags, task_t *yield)
-{
- bool current = FALSE;
-
- SCHED_DISABLE();
- if (OBJECT_VALID(task, OBJECT_TASK) &&
- (task->state == STATE_READY || task->state == STATE_RUN)) {
- task->sleepflags = flags;
- task->state = STATE_WAIT;
- DLIST_SWAP(&root->tasks_wait, &root->tasks_ready, (node_t *)task,
- FALSE);
- if (task == root->curtask)
- current = TRUE;
- }
- SCHED_ENABLE();
- if (current)
- _yield(yield);
-}
-
-/* Awakes a task which should have been put asleep using task_sleep().
- * The task is only awaken if at least one of the reasons in flags matches.
- */
-bool task_wakeup(task_t *task, u_int32_t flags)
-{
- bool ok = FALSE;
-
- SCHED_DISABLE();
- if (OBJECT_VALID(task, OBJECT_TASK) &&
- task->state == STATE_WAIT && (task->sleepflags & flags)) {
- task->state = STATE_READY;
- task->sleepflags = 0;
- task->credits = task->priority;
- if (task->credits == -128)
- task->credits++;
- DLIST_SWAP(&root->tasks_ready, &root->tasks_wait, (node_t *)task,
- FALSE);
- ok = TRUE;
- }
- SCHED_ENABLE();
-
- return ok;
-}
-
-
-void sched_disable(void)
-{
- SCHED_DISABLE();
-}
-
-void sched_enable(void)
-{
- SCHED_ENABLE();
-}
-
-
-/* Nestled locking is not permitted with these locks */
-void lock_acquire(_lock_t *lock)
-{
- /* Always immediately _yield() unless we can obtain the lock.
- * This way we use the less CPU time possible while at the same time
- * allowing the locker to eventually release the lock.
- */
- for (;;) {
- if (_lock_try(lock))
- break;
- _yield(NULL);
- }
-}
-
-void lock_release(_lock_t *lock)
-{
- _lock_release(lock);
-}
-
-
-/* A lock type permitting multiple readers but exclusive access for write */
-void rwlock_init(rwlock_t *lock)
-{
- _lock_init(&lock->exclusive_lock);
- _rlock_init(&lock->recursive_lock);
- lock->state = RWLOCK_SLOCKED;
-}
-
-void rwlock_acquire(rwlock_t *lock, bool exclusive)
-{
- lock_acquire(&lock->exclusive_lock);
- if (exclusive) {
- while (!_rlock_try(&lock->recursive_lock))
- _yield(NULL);
- lock->state = RWLOCK_XLOCKED;
- _rlock_release(&lock->recursive_lock);
- } else {
- _rlock_acquire(&lock->recursive_lock);
- lock->state = RWLOCK_SLOCKED;
- _lock_release(&lock->exclusive_lock);
- }
-}
-
-void rwlock_release(rwlock_t *lock)
-{
- switch (lock->state) {
- case RWLOCK_SLOCKED:
- _rlock_release(&lock->recursive_lock);
- break;
- case RWLOCK_XLOCKED:
- lock->state = RWLOCK_SLOCKED;
- _lock_release(&lock->exclusive_lock);
- break;
- }
-}
-
-bool rwlock_try(rwlock_t *lock, bool exclusive)
-{
- bool ok = FALSE;
-
- if (_lock_try(&lock->exclusive_lock)) {
- if (exclusive) {
- if (_rlock_try(&lock->recursive_lock)) {
- _rlock_release(&lock->recursive_lock);
- _lock_release(&lock->exclusive_lock);
- } else {
- lock->state = RWLOCK_XLOCKED;
- ok = TRUE;
- }
- } else {
- _rlock_acquire(&lock->recursive_lock);
- lock->state = RWLOCK_SLOCKED;
- _lock_release(&lock->exclusive_lock);
- ok = TRUE;
- }
- }
-
- return ok;
-}
-
-void rwlock_upgrade(rwlock_t *lock)
-{
- if (lock->state == RWLOCK_SLOCKED) {
- lock_acquire(&lock->exclusive_lock);
- _rlock_release(&lock->recursive_lock);
- while (!_rlock_try(&lock->recursive_lock))
- _yield(NULL);
- lock->state = RWLOCK_XLOCKED;
- _rlock_release(&lock->recursive_lock);
- }
-}
-
-void rwlock_downgrade(rwlock_t *lock)
-{
- if (lock->state == RWLOCK_XLOCKED) {
- _rlock_acquire(&lock->recursive_lock);
- lock->state = RWLOCK_SLOCKED;
- _lock_release(&lock->exclusive_lock);
- }
-}
+++ /dev/null
-/* $Id: scheduler.h,v 1.2 2004/01/19 00:03:23 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_SCHEDULER_H
-#define KERNEL_SCHEDULER_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/task.h>
-#include <processor/support.h>
-
-
-
-/* Scheduler control without affecting the scheduler interrupt */
-#define SCHED_DISABLE() _rlock_acquire(&root->sched_lock)
-#define SCHED_ENABLE() _rlock_release(&root->sched_lock)
-
-/* Sleep reason flags for task_sleep() and task_wakeup() */
-#define TSF_SIGNAL (1L << 0)
-#define TSF_KERNEL (1L << 1)
-#define TSF_CUSTOM (1L << 2)
-
-/* State of an rwlock_t */
-enum _rwlock_states {
- RWLOCK_SLOCKED,
- RWLOCK_XLOCKED
-};
-
-struct rwlock {
- _lock_t exclusive_lock;
- _rlock_t recursive_lock;
- enum _rwlock_states state;
-};
-
-
-
-void scheduler_init(void);
-void schedule(task_t *);
-void task_sleep(task_t *, u_int32_t, task_t *);
-bool task_wakeup(task_t *, u_int32_t);
-void sched_disable(void);
-void sched_enable(void);
-
-void lock_acquire(_lock_t *);
-void lock_release(_lock_t *);
-void rwlock_init(rwlock_t *);
-void rwlock_acquire(rwlock_t *, bool);
-void rwlock_release(rwlock_t *);
-bool rwlock_try(rwlock_t *, bool);
-void rwlock_upgrade(rwlock_t *);
-void rwlock_downgrade(rwlock_t *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: signal.c,v 1.3 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/signal.h>
-#include <common/kernel/task.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/main.h>
-#include <common/kernel/syscall.h>
-#include <common/kernel/object.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/debug.h>
-
-
-
-/* Attempts to allocate a general purpose user signal from the current task */
-signum_t signal_alloc(void)
-{
- register signum_t signum = -1;
- register task_t *task = CURTASK();
-
- if (task->sigalloc != 0xFFFFFFFF) {
- register sigmask_t sigmask = task->sigalloc;
-
- for (signum = 0; signum < 32; signum++) {
- if ((sigmask & SIGMASK(signum)) == 0) {
- sigmask |= SIGMASK(signum);
- task->sigalloc = sigmask;
- STAT(STAT_SIGNALS_ALLOC, 1);
- break;
- }
- }
- if (signum == 32)
- signum = -1;
- } else {
- STAT(STAT_SIGNALS_ALLOC_NOSIG, 1);
- DEBUG_PRINTF("- %T signal_alloc() - Out of signals\n");
- }
-
- return signum;
-}
-
-
-void _signal_free(task_t *task, sigmask_t sigmask)
-{
- if (OBJECT_VALID(task, OBJECT_TASK)) {
- /* Refuse to free reserved signals */
- sigmask &= ~SIGRESMASK;
- /* But free all other requested ones */
- task->sigalloc &= ~sigmask;
- STAT(STAT_SIGNALS_FREE, 1);
- }
-}
-
-
-/* Frees back specified allocated signals of the current process, for future
- * obtention with signal_alloc() again.
- */
-void signal_free(sigmask_t sigmask)
-{
- _signal_free(CURTASK(), sigmask);
-}
-
-
-/* Suspends the current task until at least one signal in sigmask is received
- * by it. The task is moved to the wait queue and sigwait is set, as
- * opposition to yield() which does not change the task state and just causes
- * a reschedule.
- * Obviously, using a sigmask of 0 here woild suspend the task indefinitely,
- * and is invalid. If the task needs to be awakened on a timeout event, it
- * should allocate a signal for that event and ensure to receive that signal
- * when the delay expires. This can be done using a timer.device or another
- * task.
- * The optional task argument, which may be NULL, specifies a preference to
- * which task to run next (as we yield()).
- */
-sigmask_t signal_wait(sigmask_t sigmask, task_t *yield)
-{
- register sigmask_t recvmask = 0;
-
- if (sigmask != 0) {
- register task_t *t = CURTASK();
-
- sigmask |= SIGRESMASK; /* Always awake on those */
-
- SCHED_DISABLE();
- /* Set new sigwait mask */
- t->sigwait = sigmask;
- SCHED_ENABLE();
-
- /* Swap task to waiting queue (we know that we are currently in the
- * ready one, in STATE_RUN, otherwise we would not be running), and
- * relay control back to scheduler immediately.
- */
- task_sleep(t, TSF_SIGNAL, yield);
-
- /* If we get here, it's because we were swapped back to the ready queue
- * as the result of receiving a signal we were waiting for, and
- * were awaken by the scheduler, and we are back in STATE_RUN. Clear
- * the sigwait mask, but also return it so that the caller knows which
- * signal(s) caused our task to wake up again.
- */
- SCHED_DISABLE();
- recvmask = t->sigrecv;
- t->sigrecv = 0;
- SCHED_ENABLE();
-
- STAT(STAT_SIGNALS_WAIT_RETURNED_SLEEP, 1);
- } else {
- STAT(STAT_SIGNALS_WAIT_FAILED, 1);
- DEBUG_PRINTF("* %T signal_wait(%b, %p)\n", sigmask, yield);
- }
-
- return recvmask;
-}
-
-
-/* Sends a signal to a task. The task is immediately moved to the ready
- * queue if it currently was waiting for it. The current task is however
- * left executing, but may _yield() if it wants the other end to be able to
- * react to the signal as soon as possible. Otherwise the scheduler frequency
- * and tasks priorities will decide when the other end can run.
- */
-void signal_send(task_t *task, sigmask_t sigmask)
-{
- if (OBJECT_VALID(task, OBJECT_TASK) && sigmask != 0) {
- bool awake = FALSE;
-
- SCHED_DISABLE();
- /* Apply signals */
- task->sigrecv |= sigmask;
- SCHED_ENABLE();
-
- STAT(STAT_SIGNALS_SEND, 1);
-
- /* If needed, swap task to ready queue */
- if ((task->sigwait & task->sigrecv) != 0)
- awake = task_wakeup(task, TSF_SIGNAL);
-
- /* XXX UGH! Unless we _yield(), synchronization problems quickly occur
- * among communicating tasks, they eventually all reside in the wait
- * queue, in which case they obviously deadlock. And strangely enough,
- * we have to _yield() twice to solve this issue! Why is currently
- * still a mystery, but possibly that it has to do with the fact that
- * when communicating through message ports, we expect a reply?
- * Maybe that the scheduler should be left to evaluate when a task
- * should be moved to the waiting queue, and when it needs to be
- * brought back on the ready queue because it received an expected
- * signal... rather than having tasks do so themselves, which is what
- * we currently are doing.
- */
- if (awake) {
- STAT(STAT_SIGNALS_SEND_WOKE, 1);
- _yield(task);
- _yield(NULL);
- }
- } else {
- STAT(STAT_SIGNALS_SEND_FAILED, 1);
- DEBUG_PRINTF("* %T signal_send(%p, %b)\n", task, sigmask);
- }
-}
+++ /dev/null
-/* $Id: signal.h,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_SIGNAL_H
-#define KERNEL_SIGNAL_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/task.h>
-
-
-
-/* Reserved signals */
-#define SIGRESERVED 2
-#define SIGRESMASK 0x00000003L
-#define SIGTERM 0
-#define SIGPOLL 1
-
-
-/* Useful to form a sigmask_t from signum_t */
-#define SIGMASK(n) (sigmask_t )(1L << (n))
-
-
-signum_t signal_alloc(void);
-void signal_free(sigmask_t);
-void _signal_free(task_t *, sigmask_t);
-sigmask_t signal_wait(sigmask_t, task_t *);
-void signal_send(task_t *, sigmask_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: statistic.c,v 1.3 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/debug.h>
-#include <common/kernel/main.h>
-#include <common/kernlib/string.h>
-#include <config.h>
-
-
-
-#ifdef STATISTICS
-
-
-
-/* Should match with stat_keys enum of statistic.h */
-const static char *stat_strings[] = {
- "mem.mchunks.attach",
- "mem.mchunks.attach.failed",
- "mem.mchunks.detach",
- "mem.mchunks.detach.failed",
- "mem.pages.alloc",
- "mem.pages.alloc.nomem",
- "mem.pages.free",
- "mem.pages.free.failed",
- "mem.pages.buffered",
- "mem.pages.unbuffered",
- "mem.pages.reused",
- "mem.pools.created",
- "mem.pools.created.failed",
- "mem.pools.enlarged",
- "mem.pools.destroyed",
- "mem.pools.destroyed.failed",
- "mem.pools.alloc",
- "mem.pools,alloc.failed",
- "mem.pools.alloc.nomem",
- "mem.pools.free",
- "mem.pools.free.failed",
- "mem.mpools.created",
- "mem.mpools.created.failed",
- "mem.mpools.destroyed",
- "mem.mpools.destroyed.failed",
- "mem.mpools.alloc",
- "mem.mpools.alloc.failed",
- "mem.mpools.alloc.nomem",
- "mem.mpools.free",
- "mem.mpools.free.failed",
- "syscalls.invalid",
- "syscalls.invoked",
- "hooks.attached",
- "hooks.attached.failed",
- "hooks.attached.nomem",
- "hooks.detached",
- "hooks.detached,failed",
- "hooks.detached.noexist",
- "hooks.expired",
- "hooks.executed",
- "hooks.skipped",
- "facility.disabled",
- "facility.disabled.failed",
- "facility.enabled",
- "facility.enabled.failed",
- "facility.executed",
- "facility.executed.failed",
- "facility.executed.locked",
- "sched.preempted",
- "tasks.alloc",
- "tasks.alloc.failed",
- "tasks.alloc.nomem",
- "tasks.free",
- "tasks.free.failed",
- "tasks.started",
- "tasks.started.failed",
- "tasks.ended",
- "tasks.ended.failed",
- "ports.created",
- "ports.created.exists",
- "ports.created.nomem",
- "ports.created.nosig",
- "ports.destroyed",
- "ports.destroyed.failed",
- "ports.find.found",
- "ports.find.notfound",
- "ports.send",
- "ports.send.failed",
- "ports.send",
- "ports.send.failed",
- "ports.get",
- "ports.get.failed",
- "ports.reply",
- "ports.reply.failed",
- "ports.wait",
- "ports.wait.failed",
- "ports.wait.sleep",
- "ports.wait.returned.immediate",
- "ports.wait.returned.sleep",
- "ports.wait.returned.nosig",
- "ports.flushed",
- "ports.flushed.failed",
- "signals.alloc",
- "signals.alloc.nosig",
- "signals.free",
- "signals.wait",
- "signals.wait.failed",
- "signals.wait.returned.sleep",
- "signals.send",
- "signals.send.failed",
- "signals.send.woke",
- NULL
-};
-
-
-
-void statistic_init(void)
-{
- register u_int32_t i;
-
- for (i = 0; i < (enum stat_keys)STAT_MAX; i++)
- root->stats[i] = 0;
-}
-
-
-void stat_dump(void)
-{
- register u_int32_t i;
-
- for (i = 0; i < (enum stat_keys)STAT_MAX; i++)
- DEBUG_PRINTF("%u\t%s\n", root->stats[i], stat_strings[i]);
-}
-
-
-
-#endif
+++ /dev/null
-/* $Id: statistic.h,v 1.2 2004/01/18 17:43:00 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_STATISTIC_H
-#define KERNEL_STATISTIC_H
-
-
-
-#include <config.h>
-
-
-
-#ifndef STATISTICS
-
-#define STAT(k, m)
-
-#else
-
-
-
-enum stat_keys {
- STAT_MCHUNKS_ATTACH = 0,
- STAT_MCHUNKS_ATTACH_FAILED,
- STAT_MCHUNKS_DETACH,
- STAT_MCHUNKS_DETACH_FAILED,
- STAT_PAGES_ALLOC,
- STAT_PAGES_ALLOC_NOMEM,
- STAT_PAGES_FREE,
- STAT_PAGES_FREE_FAILED,
- STAT_PAGES_BUFFERED,
- STAT_PAGES_UNBUFFERED,
- STAT_PAGES_REUSED,
- STAT_POOLS_CREATED,
- STAT_POOLS_CREATED_FAILED,
- STAT_POOLS_ENLARGED,
- STAT_POOLS_DESTROYED,
- STAT_POOLS_DESTROYED_FAILED,
- STAT_POOLS_ALLOC,
- STAT_POOLS_ALLOC_FAILED,
- STAT_POOLS_ALLOC_NOMEM,
- STAT_POOLS_FREE,
- STAT_POOLS_FREE_FAILED,
- STAT_MPOOLS_CREATED,
- STAT_MPOOLS_CREATED_FAILED,
- STAT_MPOOLS_DESTROYED,
- STAT_MPOOLS_DESTROYED_FAILED,
- STAT_MPOOLS_ALLOC,
- STAT_MPOOLS_ALLOC_FAILED,
- STAT_MPOOLS_ALLOC_NOMEM,
- STAT_MPOOLS_FREE,
- STAT_MPOOLS_FREE_FAILED,
- STAT_SYSCALLS_INVALID,
- STAT_SYSCALLS_INVOKED,
- STAT_HOOKS_ATTACHED,
- STAT_HOOKS_ATTACHED_FAILED,
- STAT_HOOKS_ATTACHED_NOMEM,
- STAT_HOOKS_DETACHED,
- STAT_HOOKS_DETACHED_FAILED,
- STAT_HOOKS_DETACHED_NOEXIST,
- STAT_HOOKS_EXPIRED,
- STAT_HOOKS_EXECUTED,
- STAT_HOOKS_SKIPPED,
- STAT_FACILITY_DISABLED,
- STAT_FACILITY_DISABLED_FAILED,
- STAT_FACILITY_ENABLED,
- STAT_FACILITY_ENABLED_FAILED,
- STAT_FACILITY_EXECUTED,
- STAT_FACILITY_EXECUTED_FAILED,
- STAT_FACILITY_EXECUTED_LOCKED,
- STAT_SCHED_PREEMPTED,
- STAT_TASKS_ALLOC,
- STAT_TASKS_ALLOC_FAILED,
- STAT_TASKS_ALLOC_NOMEM,
- STAT_TASKS_FREE,
- STAT_TASKS_FREE_FAILED,
- STAT_TASKS_STARTED,
- STAT_TASKS_STARTED_FAILED,
- STAT_TASKS_ENDED,
- STAT_TASKS_ENDED_FAILED,
- STAT_PORTS_CREATED,
- STAT_PORTS_CREATED_EXISTS,
- STAT_PORTS_CREATED_NOMEM,
- STAT_PORTS_CREATED_NOSIG,
- STAT_PORTS_DESTROYED,
- STAT_PORTS_DESTROYED_FAILED,
- STAT_PORTS_FIND_FOUND,
- STAT_PORTS_FIND_NOTFOUND,
- STAT_PORTS_SEND,
- STAT_PORTS_SEND_FAILED,
- STAT_PORTS_GET,
- STAT_PORTS_GET_FAILED,
- STAT_PORTS_REPLY,
- STAT_PORTS_REPLY_FAILED,
- STAT_PORTS_WAIT,
- STAT_PORTS_WAIT_FAILED,
- STAT_PORTS_WAIT_SLEEP,
- STAT_PORTS_WAIT_RETURNED_IMMEDIATE,
- STAT_PORTS_WAIT_RETURNED_SLEEP,
- STAT_PORTS_WAIT_RETURNED_NOSIG,
- STAT_PORTS_FLUSHED,
- STAT_PORTS_FLUSHED_FAILED,
- STAT_SIGNALS_ALLOC,
- STAT_SIGNALS_ALLOC_NOSIG,
- STAT_SIGNALS_FREE,
- STAT_SIGNALS_WAIT,
- STAT_SIGNALS_WAIT_FAILED,
- STAT_SIGNALS_WAIT_RETURNED_SLEEP,
- STAT_SIGNALS_SEND,
- STAT_SIGNALS_SEND_FAILED,
- STAT_SIGNALS_SEND_WOKE,
- STAT_MAX
-};
-
-
-
-#define STAT(k, n) root->stats[(enum stat_keys)(k)] += (n)
-
-
-
-void statistic_init(void);
-void statistic_dump(void);
-
-
-
-#endif
-
-
-
-#endif
+++ /dev/null
-/* $Id: syscall.c,v 1.2 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* It also would be possible to simply use a structure with all the
- * function pointers of the various types... And have the user functions
- * refer those with the structure as well, just like for Xisop libraries...
- * However, this system is a little more secure for the kernel stack,
- * which state is always controled. It also allows to export an ID which
- * we can validate.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/main.h>
-#include <common/kernel/syscall.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/debug.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/exception.h>
-#include <common/kernel/memory.h>
-#include <port/support.h> /* XXX */
-
-
-
-static void _sys_getroot(void *, void *);
-static void _sys_int_disable(void *, void *);
-static void _sys_int_enable(void *, void *);
-static void _sys_idle(void *, void *);
-static void _sys_custom(void *, void *);
-
-
-
-/* Should match with enum in syscall.h */
-static void (*_syscalls[])(void *, void *) = {
- _sys_getroot,
- _sys_int_disable,
- _sys_int_enable,
- _sys_idle,
- _sys_custom
-};
-
-
-
-/* Called by port-specific code before initializing interrupt facilities */
-void syscall_init(void)
-{
- root->syscalls = _syscalls;
-}
-
-
-/* The following consist of the kernel-side functions which execute in
- * supervisor mode.
- */
-
-/* Syscall trap handler */
-void _scatch(u_int32_t func, void *res, void *args)
-{
- if (func < (enum syscalls)SYS_MAX && root->syscalls[func])
- root->syscalls[func](res, args);
- else {
- STAT(STAT_SYSCALLS_INVALID, 1);
- DEBUG_PRINTF("* %T _scatch(%u, %p, %p) - Invalid syscall number\n",
- func, res, args);
- }
- STAT(STAT_SYSCALLS_INVOKED, 1);
-}
-
-
-static void _sys_getroot(void *res, void *args)
-{
- struct _sres {
- struct xisop_root *root;
- } *sres = res;
-
- sres->root = root;
-}
-
-
-/* ARGSUSED */
-static void _sys_int_disable(void *res, void *args)
-{
- _splhigh();
-}
-
-/* ARGSUSED */
-static void _sys_int_enable(void *res, void *args)
-{
- _spl0();
-}
-
-/* ARGSUSED */
-static void _sys_idle(void *res, void *args)
-{
- _idle();
-}
-
-
-static void _sys_custom(void *res, void *args)
-{
- struct _sargs {
- void (*func)(void *, void *);
- void *args;
- } *sargs = args;
-
- sargs->func(res, sargs->args);
-}
-
-
-
-/* And here are the functions which are called from userspace to trigger
- * system calls.
- */
-
-/* Returns the Xisop system pointer. This obviously should be used with care */
-struct xisop_root *sys_getroot(void)
-{
- struct _sres {
- struct xisop_root *root;
- } sres;
-
- _syscall(SYS_GETROOT, &sres, NULL);
-
- return sres.root;
-}
-
-
-/* And these allow to disable all interrupts, which of course also causes
- * the scheduler to be disabled. Should however not generally be used as a
- * substitute to sched_disable().
- */
-void sys_int_disable(void)
-{
- _syscall(SYS_INT_DISABLE, NULL, NULL);
-}
-
-void sys_int_enable(void)
-{
- _syscall(SYS_INT_ENABLE, NULL, NULL);
-}
-
-/* Causes the CPU to sleep until the next interrupt occurs */
-void sys_idle(void)
-{
- _syscall(SYS_IDLE, NULL, NULL);
-}
-
-
-/* Since Xisop does not use MMU, and in now way claims to be secure against
- * it's own tasks, other than providing clean facilities, a very useful
- * syscall to execute arbitrary code in supervisor mode. If one needs
- * unix security, they should be running NetBSD :)
- * This allows userspace tasks to extend system calls. The user provided
- * function uses the same semantics as Xisop syscall ones. An int value
- * can be returned, which will then be returned by sys_custom(), and
- * arbitrary data may be written into the supplied results pointer if wanted
- * (the first void * argument, which is set to the supplied res pointer).
- * The user function can obtain it's arguments from the second void *,
- * which is supplied using args. This way possibilities are endless.
- */
-void sys_custom(void *res, void (*func)(void *, void *), void *args)
-{
- struct _sargs {
- void (*func)(void *, void *);
- void *args;
- } sargs = {
- func,
- args
- };
-
- _syscall(SYS_CUSTOM, res, &sargs);
-}
+++ /dev/null
-/* $Id: syscall.h,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERN_SYSCALL_H
-#define KERN_SYSCALL_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/exception.h>
-#include <common/kernel/memory.h>
-
-
-
-/* Currently available syscalls */
-enum syscalls {
- SYS_GETROOT = 0,
- SYS_INT_DISABLE,
- SYS_INT_ENABLE,
- SYS_IDLE,
- SYS_CUSTOM,
- SYS_MAX
-};
-
-
-
-void syscall_init(void);
-void _scatch(u_int32_t, void *, void *);
-
-struct xisop_root *sys_getroot(void);
-void sys_int_disable(void);
-void sys_int_enable(void);
-void sys_idle(void);
-void sys_custom(void *, void (*)(void *, void *), void *);
-
-
-
-extern void (*_syscalls[])(void *, void *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: task.c,v 1.8 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/task.h>
-#include <common/kernel/port.h>
-#include <common/kernel/main.h>
-#include <common/kernel/scheduler.h>
-#include <common/kernel/object.h>
-#include <common/kernel/statistic.h>
-#include <common/kernel/debug.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/syscall.h>
-#include <port/support.h>
-#include <processor/support.h>
-
-
-
-static void task_startend_code(void);
-
-static int task_reaper(void *, void *);
-
-
-
-task_t *task_alloc(int (*start)(void *, void *), void *res, void *args,
- priority_t priority, size_t stacksize, u_int8_t flags)
-{
- if (start != NULL && stacksize != 0) {
- register page_t *stack = NULL;
- register u_int32_t pages;
-
- pages = stacksize / _PAGE_SIZE;
- if (stacksize % _PAGE_SIZE)
- pages++;
- if ((stack = pages_alloc(0, pages, FALSE)) != NULL) {
- register task_t *task = NULL;
-
- if ((task = (task_t *)spool_alloc(POOL_TASK)) != NULL) {
- if ((flags & TF_SHARED) != 0) {
- register mpool_t *mpool = CURTASK()->mpool;
-
- if (OBJECT_VALID(mpool, OBJECT_MPOOL)) {
- /* This task will share mpool_t with parent */
- lock_acquire(&mpool->lock);
- mpool->shared = TRUE;
- mpool->usecount++;
- task->mpool = mpool;
- _lock_release(&mpool->lock);
- }
- } else {
- /* This task needs it's own unique mpool_t */
- if ((task->mpool = (mpool_t *)spool_alloc(POOL_MPOOL))
- != NULL) {
- if (!mpool_init(task->mpool))
- task->mpool = (mpool_t *)spool_free(POOL_MPOOL,
- (pnode_t *)task->mpool);
- }
- }
- if (task->mpool != NULL) {
- /* Validate task_t object */
- OBJECT_VALIDATE(task, OBJECT_TASK);
- /* Initialize other task_t fields */
- task->sleepflags = 0;
- task->flags = flags;
- task->state = STATE_START;
- task->priority = priority;
- task->sigalloc = task->sigwait = task->sigrecv = 0;
- task->start = start;
- task->res = res;
- task->args = args;
- task->rescode = 0;
- task->stack = stack;
- task->stacksize = pages * _PAGE_SIZE;
- _ctx_init(&task->ctx, (u_int32_t *)stack->address,
- task->stacksize, (void *)task_startend_code);
- /* Resources */
- DLIST_INIT(&task->resources.ports);
- DLIST_INIT(&task->resources.devices);
- task->resources.device = NULL;
- STAT(STAT_TASKS_ALLOC, 1);
-
- return task;
- } else {
- STAT(STAT_TASKS_ALLOC_NOMEM, 1);
- DEBUG_PRINTF(
- "* %T task_alloc(%p. %p, %p, %d, %u, %x) - mpool_init()\n",
- start, res, args, (int32_t)priority, stacksize,
- (u_int32_t)flags);
- }
- spool_free(POOL_TASK, (pnode_t *)task);
- } else {
- STAT(STAT_TASKS_ALLOC_NOMEM, 1);
- DEBUG_PRINTF(
- "* %T task_alloc(%p, %p, %p, %d, %u, %x) - Out of memory\n",
- start, res, args, (int32_t)priority, stacksize,
- (u_int32_t)flags);
- }
- pages_free(stack);
- } else {
- STAT(STAT_TASKS_ALLOC_NOMEM, 1);
- DEBUG_PRINTF(
- "* %T task_alloc(%p, %p, %p, %d, %u, %x) - Out of memory\n",
- start, res, args, (int32_t)priority, stacksize,
- (u_int32_t)flags);
- }
- } else {
- STAT(STAT_TASKS_ALLOC_FAILED, 1);
- DEBUG_PRINTF("* %T task_alloc(%p, %p, %p, %d, %u, %x)\n",
- start, res, args, (int32_t)priority, stacksize,
- (u_int32_t)flags);
- }
-
- return NULL;
-}
-
-
-/* Free a task_t. Also unlinks the task from the system lists. The task
- * must first have been processed by task_end() and thus moved into the
- * dead queue.
- */
-task_t *task_free(task_t *task)
-{
- if (OBJECT_VALID(task, OBJECT_TASK) &&
- (task->state == STATE_DEAD || task->state == STATE_START)) {
- SCHED_DISABLE();
- if (task->state == STATE_DEAD)
- DLIST_UNLINK(&root->tasks_dead, (node_t *)task);
- SCHED_ENABLE();
- if (task->stack != NULL)
- pages_free(task->stack);
- /* Free task resources */
- /* All ports the task created. Linked via port_t->tasknode. */
- {
- register port_t *port, *next;
-
- for (port = DLIST_TOP(&(task->resources.ports)); port != NULL;
- port = next) {
- next = DLIST_NEXT(&port->tasknode);
- port_destroy((port_t *)&(((hashnode_t *)port)[-1]));
- }
- }
- /* Devices we opened. Linked via device_t->tasknode. */
- {
- register node_t *node, *next;
-
- for (node = DLIST_TOP(&(task->resources.devices)); node != NULL;
- node = next) {
- next = DLIST_NEXT(node);
- device_close((device_t *)&(((pnode_t *)node)[-1]));
- }
- }
- /* If we are a device */
- if (task->resources.device)
- device_detach(task->resources.device);
- /* XXX other future resources handling, like opened libraries, etc */
- /* Invalidate task */
- OBJECT_INVALIDATE(task);
- /* All memory the task allocated using malloc(). If mpool_destroy()
- * fails with FALSE, either a problem occured or the mpool_t is still
- * in use by another task sharing it, in which case we do not free it.
- */
- if (mpool_destroy(task->mpool))
- spool_free(POOL_MPOOL, (pnode_t *)task->mpool);
- /* And finally the task node itself. */
- spool_free(POOL_TASK, (pnode_t *)task);
- STAT(STAT_TASKS_FREE, 1);
- } else {
- STAT(STAT_TASKS_FREE_FAILED, 1);
- DEBUG_PRINTF("* %T task_free(%p)\n", task);
- }
-
- return NULL;
-}
-
-
-/* Allows to start a new task */
-bool task_start(task_t *task)
-{
- bool ok = FALSE;
-
- if (OBJECT_VALID(task, OBJECT_TASK) && task->state == STATE_START) {
- /* This task was just allocated, start it */
- task->state = STATE_READY;
- if ((task->credits = task->priority) == -128)
- task->credits++;
- SCHED_DISABLE();
- DLIST_APPEND(&root->tasks_ready, (node_t *)task);
- SCHED_ENABLE();
- ok = TRUE;
- STAT(STAT_TASKS_STARTED, 1);
- } else {
- STAT(STAT_TASKS_STARTED_FAILED, 1);
- DEBUG_PRINTF("* %T task_start(%p)\n", task);
- }
-
- return ok;
-}
-
-
-bool task_end(task_t *task)
-{
- bool ok = FALSE, current = FALSE;
-
- if (OBJECT_VALID(task, OBJECT_TASK) && task->state != STATE_DEAD) {
- ok = TRUE;
- SCHED_DISABLE();
- switch (task->state) {
- case STATE_RUN:
- /* FALLTHROUGH */
- case STATE_READY:
- DLIST_SWAP(&root->tasks_dead, &root->tasks_ready, (node_t *)task,
- FALSE);
- break;
- case STATE_WAIT:
- DLIST_SWAP(&root->tasks_dead, &root->tasks_wait, (node_t *)task,
- FALSE);
- break;
- default:
- ok = FALSE;
- break;
- }
- if (ok) {
- STAT(STAT_TASKS_ENDED, 1);
- task->state = STATE_DEAD;
- /* Wakeup reaper as there's at least one task on the dead queue */
- task_wakeup(root->task_reaper, TSF_KERNEL);
- /* If it's the current task, ensure to yield now. */
- if (task == root->curtask)
- current = TRUE;
- }
- SCHED_ENABLE();
- }
-
- if (current) {
- /* We also probably could go to sleep indefinitely instead,
- * but as we are in the dead queue, we are certain that we will not
- * be given another chance to run until we get freed.
- */
- for (;;)
- _yield(NULL);
- }
- if (!ok) {
- STAT(STAT_TASKS_ENDED_FAILED, 1);
- DEBUG_PRINTF("* %T task_end(%p)\n", task);
- }
-
- return ok;
-}
-
-
-priority_t task_getpriority(task_t *task)
-{
- register priority_t p = 0;
-
- if (OBJECT_VALID(task, OBJECT_TASK))
- p = task->priority;
-
- return p;
-}
-
-
-priority_t task_setpriority(task_t *task, priority_t new)
-{
- register priority_t p = 0;
-
- if (OBJECT_VALID(task, OBJECT_TASK)) {
- SCHED_DISABLE();
- p = task->priority;
- task->priority = new;
- if ((task->credits = new) == -128)
- task->credits++;
- SCHED_ENABLE();
- }
-
- return p;
-}
-
-
-/* This is the code which each new task automatically starts executing, which
- * performs some initializations and then executes the task-specific code.
- * It also takes control again when the task ends and returns.
- */
-static void task_startend_code(void)
-{
- register task_t *task = CURTASK();
-
- /* XXX Setup our reserved signals and port */
-
- /* Call the supplied entry point function */
- task->rescode = task->start(task->res, task->args);
-
- /* Adios amigo! Translation: KTHXBYE!111 */
- task_end(task);
- /* NOTREACHED */
-}
-
-
-
-/* System tasks
- * ============
- */
-
-/* This consists of Xisop init task. It's purpose is to launch the system
- * tasks as well as all tasks which the port-specific code defined which should
- * be launched. We also link in system and port-defined shared libraries.
- * Resident devices and handlers actually consist of tasks and are given
- * no special treatment.
- */
-/* ARGSUSED */
-int task_init(void *res, void *args)
-{
- /* Register ourself */
- root->task_init = CURTASK();
-
- /* Attach system shared libraries */
- {
- /* XXX */
- }
-
- /* Launch system tasks, which auto-register themselves by storeing their
- * address in the Xisop root structure.
- */
- {
- register task_t *task;
-
- /* Task reaper */
- if ((task = task_alloc(task_reaper, NULL, NULL, 0, 4096,
- TF_KERNEL | TF_SHARED)) != NULL)
- task_start(task);
- }
-
- /* Attach port-specified shared libraries and launch port-specific tasks */
- _port_init();
-
- /* And perform our init task shores, which are neverending. */
- {
- port_t *pubport;
-
- if ((pubport = port_create("INIT")) != NULL) {
- port_t *ports[] = {
- pubport
- };
- register message_t *msg;
-
- /* Currently just reply to any message we get and do nothing */
- for (;;) {
- if ((port_wait(ports, 1, NULL)) == pubport) {
- while ((msg = port_get(pubport)) != NULL)
- port_reply(msg);
- }
- }
- }
- }
-
- /* XXX We actually could safely die here for now, but don't want to.
- * Eventually we will make sure that the tasks remain running, and
- * restart tasks which are marked to be persistant if they ever die.
- */
- /* NOTREACHED */
- return 0;
-}
-
-
-/* This consists of the reaper task, which is started by Xisop init task.
- * Our duty consists of sleeping, but to free the tasks which are on the
- * dead queue, if any, when wakeing up. The only event which awakes us up
- * consists of the task_end() function. As tasks may have alot of resources
- * to free back to the system, it is a good idea to have this task dedicating
- * it's own CPU time to do it, it releives all other tasks, as well as the
- * kernel from having to.
- */
-/* ARGSUSED */
-static int task_reaper(void *res, void *args)
-{
- /* Register ourselves for task_end() */
- root->task_reaper = CURTASK();
-
- for (;;) {
- task_sleep(CURTASK(), TSF_KERNEL, NULL);
- /* We were awaken by task_end() */
- for (;;) {
- register task_t *task;
-
- SCHED_DISABLE();
- task = DLIST_TOP(&root->tasks_dead);
- SCHED_ENABLE();
- if (task != NULL) {
- /* Free this task and let some CPU time to others */
- task_free(task);
- _yield(NULL);
- } else
- break;
- }
- }
-
- /* NOTREACHED */
- return 0;
-}
+++ /dev/null
-/* $Id: task.h,v 1.2 2004/01/19 18:07:11 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_TASK_H
-#define KERNEL_TASK_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/signal.h>
-#include <common/kernel/port.h>
-#include <common/kernel/device.h>
-#include <common/kernlib/string.h>
-#include <common/kernlib/list.h>
-#include <processor/support.h>
-/*#include <kern/handler.h> XXX I must be able to include this!*/
-
-
-
-/* Useful macro to be used in kernel code */
-#define CURTASK() (root->curtask)
-
-
-
-/* task.state */
-#define STATE_START 0
-#define STATE_READY 1
-#define STATE_WAIT 2
-#define STATE_DEAD 3 /* To be removed */
-#define STATE_RUN 4
-
-/* task.flags and tn_message.event */
-#define TF_KERNEL (1 << 0) /* Kernelspace task */
-#define TF_SYSTEM (1 << 1) /* A system task */
-#define TF_DEVICE (1 << 2) /* Task is a device */
-#define TF_HANDLER (1 << 3) /* Task is a handler */
-#define TF_OS (1 << 4) /* OS resident task */
-#define TF_SHARED (1 << 5) /* Shares mpool_t w/ parent */
-
-/* some defined values for task.priority */
-#define PRI_MAX 127 /* highest priority */
-#define PRI_DEFAULT 0 /* default priority for normal tasks */
-#define PRI_MIN -127 /* lowest priority */
-
-
-
-/* Task's allocated resources */
-struct resources {
- list_t ports; /* Linked via port_t->tasknode */
- list_t devices; /* Linked via device_t->tasknode */
- devicenode_t *device; /* If we are a device */
- /*handlernode_t *handler;*/ /* If we are a handler */
-};
-
-/* XXX Don't know if this will be useful/required yet */
-/* These, similarly to the task's memory pool, will only be freed once the
- * parent task is freed, in case it has threads.
- * XXX hmm I think that the message ports cannot be shared among threads, since
- * they require a signal bit, allocated on the specific task!
- * now would signal shareing be wanted? several tasks would then be awaken
- * by the same signal. I doubt we want this on xisop.
- */
-/*
-struct procstate {
- lock *currentdir;
- lock *in;
- lock *out;
- lock *err;
- struct handlerpacket pkt;
- struct msgport *pktrp;
-};
-*/
-
-/* This is a task node, as the kernel sees it. */
-struct task {
- /* Tasks use the primary node for swapping */
- pnode_t node;
- /* User multipurpose node */
- node_t usernode;
-
- /* Validity sceal */
- u_int32_t object_magic;
-
- /* Info */
- u_int32_t sleepflags;
- u_int8_t flags;
- u_int8_t state;
-
- /* Credits are given according to priority by the scheduler */
- priority_t priority, credits;
-
- /* Signal */
- sigmask_t sigalloc, sigwait, sigrecv;
- /* XXX sigfunc sighandlers[32]; */
-
- /* Entry point and parameters/results */
- int (*start)(void *, void *);
- void *res, *args;
- int rescode;
-
- /* Context */
- page_t *stack;
- size_t stacksize;
- _ctx_t ctx;
-
- /* Memory pool, which can be shared or unique */
- mpool_t *mpool;
-
- /* Resources we have opened which need special handling other than
- * freeing the memory they allocated.
- */
- struct resources resources;
-};
-
-
-
-task_t *task_alloc(int (*)(void *, void *), void *, void *, priority_t,
- size_t, u_int8_t);
-task_t *task_free(task_t *);
-bool task_start(task_t *);
-bool task_end(task_t *);
-priority_t task_getpriority(task_t *);
-priority_t task_setpriority(task_t *, priority_t);
-
-int task_init(void *, void *);
-
-
-
-#endif
+++ /dev/null
-#!/bin/sh
-
-# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../generic_makedefs.sh
-
-cleanlib .
-cleanlib string
-show $L_RM ar/*.a
+++ /dev/null
-/* $Id: fifo.h,v 1.2 2004/06/04 19:03:36 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNLIB_FIFO_H
-#define KERNLIB_FIFO_H
-
-
-
-#include <common/types.h>
-
-
-
-/* Allows to create a new fifo_t type structure, to fit any data type */
-#define FIFO_DEFINE(n, o) typedef struct n { \
- o *top, *bottom, *head, *tail; \
- u_int32_t size; \
-} n
-
-FIFO_DEFINE(fifo8_t, u_int8_t);
-FIFO_DEFINE(fifo16_t, u_int16_t);
-FIFO_DEFINE(fifo32_t, u_int32_t);
-FIFO_DEFINE(fifo64_t, u_int64_t);
-
-
-
-/* Initializes a FIFO */
-#define FIFO_INIT(f, b, s) do { \
- (f)->top = (f)->head = (f)->tail = (b); \
- (f)->bottom = &((b)[(s)]); \
- (f)->size = (s) - 1; \
-} while (/* CONSTCOND */0)
-
-/* Used to compute the next location of a tail or head pointer, accounting
- * for the necessary occasional rotation.
- */
-#define FIFO_NEXT(f, p) (&((p)[1]) == (f)->bottom ? (f)->top : &((p)[1]))
-
-/* Returns TRUE if the FIFO is full, that is, cannot hold more elements */
-#define FIFO_FULL(f) (FIFO_NEXT((f), (f)->head) == (f)->tail)
-
-/* Returns TRUE if the FIFO is empty */
-#define FIFO_EMPTY(f) ((f)->head == (f)->tail)
-
-/* Returns the number of currently held elements into a FIFO */
-#define FIFO_STAT(f) (FIFO_EMPTY(f) ? 0 : \
- (f)->head - (f)->tail > 0 ? \
- ((f)->head - (f)->tail) / sizeof(*(f)->head): \
- ((f)->size - ((f)->tail - (f)->head)) / sizeof(*(f)->head))
-
-#define FIFO_AVAIL(f) ((f)->size - FIFO_STAT(f))
-
-#define FIFO_FLUSH(f) ((f)->tail = (f)->head)
-
-/* If no available room the oldest element is lost. The caller may verify
- * with FIFO_FULL() first if needed.
- */
-#define FIFO_PUT(f, e) do { \
- *(f)->head = *(e); \
- (f)->head = FIFO_NEXT((f), (f)->head); \
- if (FIFO_EMPTY(f)) \
- (f)->tail = FIFO_NEXT((f), (f)->tail); \
-} while (/* CONSTCOND */0)
-
-/* Has no action if the buffer has no more elements, but does not return any
- * result to say so. The caller may use FIFO_EMPTY() to check if needed.
- */
-#define FIFO_GET(f, e) do { \
- if (!FIFO_EMPTY(f)) { \
- *(e) = *(f)->tail; \
- (f)->tail = FIFO_NEXT((f), (f)->tail); \
- } \
-} while (/* CONSTCOND */0)
-
-#define FIFO_FIND(f, p, e) do { \
- register typeof(*(e)) *r; \
- \
- *(p) = NULL; \
- for (r = (f)->tail; r != (f)->head; r = FIFO_NEXT(f, r)) \
- if (*r == *(e)) { \
- *(p) = r; \
- break; \
- } \
-} while (/* CONSTCOND */0)
-
-/* XXX Those are bugged for now */
-#define FIFO_ALLOC(f, p, a, s) do { \
- register int r; \
- \
- if ((r = (f)->tail - (f)->head) != 0) { \
- if (r < 1) \
- r = (f)->bottom - (f)->head; \
- if ((r /= sizeof(*(f)->head)) > (int)(s)) \
- r = (int)(s); \
- *(p) = (f)->head; \
- (f)->head = (&((f)->head[r]) == (f)->bottom ? (f)->top : \
- &((f)->head[r])); \
- } \
- *(a) = (size_t)r; \
-} while (/* CONSTCOND */0)
-
-#define FIFO_FREE(f, p, a, s) do { \
- register int r; \
- \
- if ((r = (f)->head - (f)->tail) != 0) { \
- if (r < 1) \
- r = (f)->bottom - (f)->tail; \
- if ((r /= sizeof(*(f)->tail)) > (int)(s)) \
- r = (int)(s); \
- *(p) = (f)->tail; \
- (f)->tail = (&((f)->tail[r]) == (f)->bottom ? (f)->top : \
- &((f)->tail[r])); \
- } \
- *(a) = (size_t)r; \
-} while (/* CONSTCOND */0)
-
-#define FIFO_WRITE(f, p, a, s) do { \
- /* XXX */ \
-} while (/* CONSTCOND */0)
-
-#define FIFO_READ(f, p, a, s) do { \
- /* XXX */ \
-} while (/* CONSTCOND */0)
-
-
-
-size_t getnfifo8(u_int8_t *, fifo8_t *, size_t);
-size_t putnfifo8(fifo8_t *, u_int8_t *, size_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: hash.c,v 1.6 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software written by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/debug.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/object.h>
-#include <common/kernlib/list.h>
-#include <common/kernlib/hash.h>
-
-
-
-/* This system is safe to use 32-bit hashes internally, despite the possibility
- * for collisions. We maintain an array or buckets, within which the entries
- * are distributed. A 32-bit hash collision entry will end up on the same
- * bucket. We however also make sure to not allow to store exact duplicate
- * keys. The number if buckets will increase and decrease whenever the system
- * detects that it becomes necessary for efficiency. The larger the number of
- * buckets, the less nodes are likely to coexist in each bucket. The bucket
- * index to use is evaluated using a modulo to convert the 32-bit hash to
- * fit into the current number of buckets in the table. Of course, when
- * the number of buckets is to be updated (which ideally happens rarely),
- * the entries are rehashed to be properly indexed within the new capacity.
- *
- * The number of buckets is automatically doubled when the table fills up
- * at a factor of 1. This way, we avoid having to calculate a fill factor
- * using floating point arithmetic. Commonly used value for the initial hash
- * table bucket capacity is 16, which are set HT_DEFAULT_CAPACITY represents.
- *
- * Searching for a key by pattern, which requires iterating through the
- * nodes, rather than through it's absolute key can actually be a little
- * slower than running through a simple linked list of absolute hash values,
- * since all buckets must be scanned. However, we ensure to stop running
- * through buckets when the total number of mappings have been scanned already.
- * Lookups using the absolute key of the node will be much faster in the case
- * where the hash table grows considerably, however.
- */
-
-
-
-#define HASH_INDEX(h, s) ((h) % (s))
-
-
-
-static void hashtable_rehash(hashtable_t *, unsigned int);
-
-
-
-bool hashtable_init(hashtable_t *t, const char *label,
- unsigned int initialcapacity, void *(*allocfunc)(size_t),
- void (*freefunc)(void *),
- int (*keycomp)(const void *, const void *, size_t),
- u_int32_t (*keyhash)(const void *, size_t), bool dynamic)
-{
- if (!OBJECT_VALID(t, OBJECT_HASHTABLE)) {
- if ((t->array = allocfunc(sizeof(list_t) * initialcapacity)) != NULL) {
- unsigned int i;
-
- OBJECT_VALIDATE(t, OBJECT_HASHTABLE);
- t->label = label;
- t->malloc = allocfunc;
- t->free = freefunc;
- t->keycomp = keycomp;
- t->keyhash = keyhash;
- t->nodes = 0;
- t->initial = t->capacity = initialcapacity;
- t->avgtotal = t->avgcnt = initialcapacity;
- t->dynamic = dynamic;
- t->iterating = FALSE;
- for (i = 0; i < initialcapacity; i++)
- DLIST_INIT(&(t->array[i]));
-
- return TRUE;
- } else
- DEBUG_PRINTF("* %T hashtable_init(%p = %s) - malloc(%d)\n",
- label, t, (int)sizeof(list_t) * initialcapacity);
- } else
- DEBUG_PRINTF(
- "* %T hashtable_init(%p = %s) - Table already initialized",
- label, t);
-
- return FALSE;
-}
-
-
-void hashtable_destroy(hashtable_t *t, bool freeall)
-{
- if (OBJECT_VALID(t, OBJECT_HASHTABLE)) {
- if (t->array != NULL) {
- if (freeall) {
- register unsigned int i, done;
- register list_t *l;
- register hashnode_t *k, *kt;
-
- for (i = done = 0; done < t->nodes && i < t->capacity; i++) {
- l = &(t->array[i]);
- if (DLIST_NODES(l) > 0) {
- for (k = DLIST_TOP(l); k != NULL; k = kt) {
- kt = DLIST_NEXT(k);
- pool_free((pnode_t *)k);
- done++;
- }
- }
- }
- }
- t->free(t->array);
- }
- OBJECT_INVALIDATE(t);
- } else
- DEBUG_PRINTF(
- "* %T hashtable_destroy(%p) - Invalid hashtable_t pointer",
- t);
-}
-
-
-hashnode_t *hashtable_lookup(hashtable_t *t, const void *key, size_t keysize)
-{
- register u_int32_t hash;
- register unsigned int i;
- register list_t *l;
- register hashnode_t *k = NULL;
-
- if (OBJECT_VALID(t, OBJECT_HASHTABLE)) {
- hash = t->keyhash(key, keysize);
- i = HASH_INDEX(hash, t->capacity);
- l = &(t->array[i]);
- if (DLIST_NODES(l) > 0) {
- DLIST_FOREACH(l, k) {
- if (k->hash == hash && k->keysize == keysize &&
- t->keycomp(k->key, key, keysize) == 0)
- break;
- }
- }
- } else
- DEBUG_PRINTF(
- "* %T hashtable_lookup(%p) - Invalid hashtable_t pointer",
- t);
-
- return k;
-}
-
-
-bool hashtable_link(hashtable_t *t, hashnode_t *k, const void *key,
- size_t keysize, bool check)
-{
- register u_int32_t hash;
- register unsigned int i;
- register list_t *l;
- bool ok = TRUE;
-
- if (!OBJECT_VALID(t, OBJECT_HASHTABLE)) {
- DEBUG_PRINTF("* %T hashtable_link(%p) - Invalid hashtable_t pointer",
- t);
- return FALSE;
- }
- if (k == NULL) {
- DEBUG_PRINTF(
- "* %T hashtable_link(NULL) - Table (%p = %s) Invalid "
- "hashnode_t pointer",
- t, t->label);
- return FALSE;
- }
-
- hash = t->keyhash(key, keysize);
- i = HASH_INDEX(hash, t->capacity);
- l = &(t->array[i]);
- if (check) {
- /* We do not allow exact duplicates, so verify first. Duplicate
- * hashes are fine, however, as long as the key data is not identical.
- */
- if (DLIST_NODES(l) > 0) {
- register hashnode_t *tk;
-
- DLIST_FOREACH(l, tk) {
- if (tk == k || (tk->hash == hash && tk->keysize == keysize &&
- t->keycomp(tk->key, key, keysize) == 0)) {
- DEBUG_PRINTF(
- "* %T hashtable_link(%p = %s, %p) - Duplicate key "
- "insert attempt", t, t->label, k);
- ok = FALSE;
- break;
- }
- }
- }
- }
- if (ok) {
- OBJECT_VALIDATE(k, OBJECT_HASHNODE);
- k->hash = hash;
- k->list = l;
- k->key = key;
- k->keysize = keysize;
- DLIST_INSERT(l, (node_t *)k);
- /* Grow capacity if necessary */
- t->nodes++;
- if (t->dynamic && !t->iterating) {
- if (t->dynamic && !t->iterating)
- hashtable_rehash(t, t->capacity * 2);
- }
- }
-
- return ok;
-}
-
-
-void hashtable_unlink(hashtable_t *t, hashnode_t *k)
-{
- if (OBJECT_VALID(t, OBJECT_HASHTABLE)) {
- if (OBJECT_VALID(k, OBJECT_HASHNODE)) {
- unsigned int exceeding;
-
- OBJECT_INVALIDATE(k);
- DLIST_UNLINK(k->list, (node_t *)k);
- k->list = NULL;
- t->nodes--;
-
- /* Verify if the capacity should be reduced, using statistics */
- t->avgtotal += t->capacity;
- t->avgcnt++;
- if (t->avgcnt > t->capacity / (t->initial * 3)) {
- t->avgcnt = 1;
- t->avgtotal = t->capacity;
- }
- /* Rehash with a smaller capacity if necessary */
- if (t->dynamic && !t->iterating) {
- if ((exceeding = t->capacity - (t->avgtotal / t->avgcnt)) > 0)
- hashtable_rehash(t, t->capacity - exceeding);
- }
- } else
- DEBUG_PRINTF(
- "* %T hashtable_unlink(%p) - Table (%p = %s) Invalid "
- "hashnode_t pointer",
- k, t, t->label);
- } else
- DEBUG_PRINTF(
- "* %T hashtable_unlink(%p) - Invalid hashtable_t pointer",
- t);
-}
-
-
-/* Note that as the user generally has a pool_t dedicated to the hashnode_t
- * elements for a particular table, it may be more efficient to not use
- * the <freeall> option and to pool_free() which does not need to iterate
- * through nodes. This function has to however, because it obviously cannot
- * assume that the caller wishes to free all nodes of the origin pool_t, or
- * that all entries origin from the same pool_t.
- */
-void hashtable_empty(hashtable_t *t, bool freeall)
-{
- register unsigned int i;
- register list_t *l;
- register hashnode_t *k, *kt;
-
- if (OBJECT_VALID(t, OBJECT_HASHTABLE)) {
- if (freeall) {
- for (i = 0; i < t->capacity; i++) {
- l = &(t->array[i]);
- if (DLIST_NODES(l) > 0) {
- for (k = DLIST_TOP(l); k != NULL; k = kt) {
- kt = DLIST_NEXT(k);
- pool_free((pnode_t *)k);
- }
- }
- }
- } else {
- for (i = 0; i < t->capacity; i++)
- DLIST_INIT(&(t->array[i]));
- }
- if (t->dynamic && !t->iterating)
- hashtable_rehash(t, t->initial);
- } else
- DEBUG_PRINTF(
- "* %T hashtable_empty(%p) - Invalid hashtable_t pointer",
- t);
-}
-
-
-void hashtable_iterate(hashtable_t *t,
- bool (*func)(hashnode_t *, void *), void *udata)
-{
- register unsigned int i;
- register list_t *l;
- register hashnode_t *k, *kt;
-
- if (OBJECT_VALID(t, OBJECT_HASHTABLE)) {
- if (t->nodes > 0) {
- t->iterating = TRUE;
- for (i = 0; i < t->capacity; i++) {
- l = &(t->array[i]);
- if (DLIST_NODES(l) > 0) {
- /* Note that we use a temporary variable to hold the next
- * key, in case the user function alters the key node
- * (i.e. unlinks it)
- */
- for (k = DLIST_TOP(l); k != NULL; k = kt) {
- kt = DLIST_NEXT(k);
- if (!func(k, udata)) {
- t->iterating = FALSE;
- return;
- }
- }
- }
- }
- t->iterating = FALSE;
- }
- } else
- DEBUG_PRINTF(
- "* %T hashtable_iterate(%p) - Invalid hashtable_t pointer",
- t);
-}
-
-
-/* Rehashes the whole hashtable so that the capacity may be changed to the
- * specified one. The memory area is also automatically changed. Ideally,
- * this only occurs rarely. If it fails because of a lack of memory, the
- * hash table will simply not be affected, but lookups will become slower.
- */
-static void hashtable_rehash(hashtable_t *t, unsigned int newcapacity)
-{
- list_t *newarray;
-
- if ((newarray = t->malloc(sizeof(list_t) * newcapacity)) != NULL) {
- register unsigned int i, done;
-
- for (i = 0; i < newcapacity; i++)
- DLIST_INIT(&newarray[i]);
-
- for (i = done = 0; done < t->nodes && i < t->capacity; i++) {
- register hashnode_t *k, *kt;
- register list_t *l, *newl;
-
- l = &(t->array[i]);
- if (DLIST_NODES(l) > 0) {
- for (k = DLIST_TOP(l); k != NULL; k = kt) {
- kt = DLIST_NEXT(k);
- newl = &newarray[HASH_INDEX(k->hash, newcapacity)];
- DLIST_SWAP(newl, l, (node_t *)k, TRUE);
- k->list = newl;
- done++;
- }
- }
- }
-
- t->capacity = newcapacity;
- t->free(t->array);
- t->array = newarray;
- }
-}
+++ /dev/null
-/* $Id: hash.h,v 1.5 2004/06/04 02:15:47 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNLIB_HASH_H
-#define KERNLIB_HASH_H
-
-
-
-#include <common/types.h>
-#include <common/kernel/memory.h>
-#include <common/kernlib/list.h>
-
-
-
-struct hashnode {
- pnode_t node;
- u_int32_t object_magic, hash;
- list_t *list;
- const void *key;
- size_t keysize;
- /* Custom user data will follow, uncluding the key element to which the
- * previous key pointer is expected to point.
- */
-};
-
-struct hashtable {
- pnode_t node; /* In case we want a pool_t of hashtable_t */
- u_int32_t object_magic;
- unsigned int initial, capacity, nodes;
- const char *label;
- list_t *array;
- void *(*malloc)(size_t);
- void (*free)(void *);
- int (*keycomp)(const void *, const void *, size_t);
- u_int32_t (*keyhash)(const void *, size_t);
- unsigned int avgtotal, avgcnt;
- bool dynamic, iterating;
-};
-
-
-
-#define HT_DEFAULT_CAPACITY 16
-
-#define HASHTABLE_NODES(t) ((t)->nodes)
-
-
-
-bool hashtable_init(hashtable_t *, const char *, unsigned int,
- void *(*)(size_t), void (*)(void *),
- int (*)(const void *, const void *, size_t),
- u_int32_t (*)(const void *, size_t), bool);
-void hashtable_destroy(hashtable_t *, bool);
-hashnode_t *hashtable_lookup(hashtable_t *, const void *, size_t);
-bool hashtable_link(hashtable_t *, hashnode_t *, const void *, size_t, bool);
-void hashtable_unlink(hashtable_t *, hashnode_t *);
-void hashtable_empty(hashtable_t *, bool);
-void hashtable_iterate(hashtable_t *, bool (*)(hashnode_t *, void *),
- void *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: lifo.h,v 1.3 2004/06/04 19:14:07 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNLIB_LIFO_H
-#define KERNLIB_LIFO_H
-
-
-
-#include <common/types.h>
-
-
-
-/* LIFO_DEFINE(lifotypename, objecttype); */
-#define LIFO_DEFINE(n, o) typedef struct n { \
- u_int32_t size, elements; \
- o *buffer, *endbuffer, *head; \
-} n
-
-/* Because of the way this is implemented using macros, it would also be
- * possible to provide lifo types to hold structures.
- */
-LIFO_DEFINE(lifo8_t, u_int8_t);
-LIFO_DEFINE(lifo16_t, u_int16_t);
-LIFO_DEFINE(lifo32_t, u_int32_t);
-LIFO_DEFINE(lifo64_t, u_int64_t);
-
-
-
-/* XXX Although it's great to use macros for these operations, it also
- * prevents assembly functions to be provided to replace them.
- */
-/* void LIFO_INIT(lifo*_t *, u_int*_t *, u_int32_t); */
-#define LIFO_INIT(f, b, s) do { \
- (f)->size = (s); \
- (f)->elements = 0; \
- (f)->buffer = (f)->endbuffer = (f)->head = (b); \
-} while (/* CONSTCOND */0)
-
-/* bool LIFO_FULL(lifo*_t *); */
-#define LIFO_FULL(f) ((f)->elements == (f)->size)
-
-/* u_int32_t LIFO_STAT(lifo*_t *); */
-#define LIFO_STAT(f) ((f)->elements)
-
-/* void LIFO_FLUSH(lifo*_t *); */
-#define LIFO_FLUSH(f) do { \
- (f)->head = (f)->buffer; \
- (f)->elements = 0; \
-} while (/* CONSTCOND */0)
-
-/* void LIFO_PUT(lifo*_t *, u_int*_t *); */
-#define LIFO_PUT(s, e) do { \
- if ((s)->elements < (s)->size) { \
- *((s)->head++) = *(e); \
- (s)->elements++; \
- } \
-} while (/* CONSTCOND */0)
-
-/* void LIFO_GET(lifo*_t *, u_int*_t *); */
-#define LIFO_GET(s, e) do { \
- if ((s)->elements > 0) { \
- *(e) = *(--(s)->head); \
- (s)->elements--; \
- } \
-} while (/* CONSTCOND */0)
-
-/* LIFO_ALLOC(lifo*_t *, u_int*_t **, size_t *); */
-
-/* LIFO_FREE(lifo*_t, u_int*_t **, size_t *); */
-
-
-
-#endif
+++ /dev/null
-/* $Id: list.h,v 1.5 2004/06/04 19:14:07 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNEL_LIST_H
-#define KERNEL_LIST_H
-
-
-
-#include <common/types.h>
-
-
-
-typedef struct list list_t;
-typedef struct node node_t;
-
-
-
-struct node {
- node_t *prev, *next;
-};
-
-struct list {
- node_t *top, *bottom;
- u_int32_t nodes;
-};
-
-
-
-/* Some macros to optimize operations on doubly linked lists */
-#define DLIST_INITIALIZER {NULL, NULL, 0}
-
-#define DLIST_INIT(lst) do { \
- (lst)->top = (lst)->bottom = NULL; \
- (lst)->nodes = 0; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_UNLINK(lst, nod) do { \
- register node_t *prev = (nod)->prev, *next = (nod)->next; \
- \
- if (prev != NULL) \
- prev->next = next; \
- else \
- (lst)->top = next; \
- if (next != NULL) \
- next->prev = prev; \
- else \
- (lst)->bottom = prev; \
- (lst)->nodes--; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_APPEND(lst, nod) do { \
- register node_t *tmp = (lst)->bottom; \
- \
- if (tmp != NULL) { \
- tmp->next = (nod); \
- (nod)->prev = tmp; \
- (nod)->next = NULL; \
- (lst)->bottom = (nod); \
- } else { \
- (lst)->bottom = (lst)->top = (nod); \
- (nod)->next = (nod)->prev = NULL; \
- } \
- (lst)->nodes++; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_INSERT(lst, nod) do { \
- register node_t *tmp = (lst)->top; \
- \
- if (tmp != NULL) { \
- tmp->prev = (nod); \
- (nod)->prev = NULL; \
- (nod)->next = tmp; \
- (lst)->top = (nod); \
- } else { \
- (lst)->top = (lst)->bottom = (nod); \
- (nod)->next = (nod)->prev = NULL; \
- } \
- (lst)->nodes++; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_INSERTAT(lst, atnode, nod) do { \
- register node_t *prev = (atnode)->prev, *next = (atnode); \
- \
- (nod)->next = next; \
- next->prev = (nod); \
- if (prev != NULL) { \
- prev->next = (nod); \
- (nod)->prev = prev; \
- } else { \
- (lst)->top = (nod); \
- (nod)->prev = NULL; \
- } \
- (lst)->nodes++; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_SWAP(dst, src, nod, ins) do { \
- register node_t *prev = (nod)->prev, *next = (nod)->next; \
- \
- if (prev != NULL) \
- prev->next = next; \
- else \
- (src)->top = next; \
- if (next != NULL) \
- next->prev = prev; \
- else \
- (src)->bottom = prev; \
- (src)->nodes--; \
- if ((ins)) { \
- if ((prev = (dst)->top) != NULL) { \
- prev->prev = (nod); \
- (nod)->prev = NULL; \
- (nod)->next = prev; \
- (dst)->top = (nod); \
- } else { \
- (dst)->top = (dst)->bottom = (nod); \
- (nod)->next = (nod)->prev = NULL; \
- } \
- } else { \
- if ((prev = (dst)->bottom) != NULL) { \
- prev->next = (nod); \
- (nod)->prev = prev; \
- (nod)->next = NULL; \
- (dst)->bottom = (nod); \
- } else { \
- (dst)->bottom = (dst)->top = (nod); \
- (nod)->next = (nod)->prev = NULL; \
- } \
- } \
- (dst)->nodes++; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_TOP(lst) ((void *)((list_t *)(lst))->top)
-#define DLIST_BOTTOM(lst) ((void *)((list_t *)(lst))->bottom)
-#define DLIST_NEXT(var) ((void *)((node_t *)(var))->next)
-#define DLIST_PREV(var) ((void *)((node_t *)(var))->prev)
-
-#define DLIST_FOREACH(lst, var) \
- for ((var) = DLIST_TOP((lst)); (var) != NULL; (var) = DLIST_NEXT((var)))
-
-#define DLIST_NODES(lst) (((list_t *)(lst))->nodes)
-
-
-
-#endif
+++ /dev/null
-#!/bin/sh
-
-# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../makedefs.sh
-
-buildlib string
-show $C_AR ar/string.a string/*.o
-show $C_RANLIB ar/string.a
-
-buildlib .
-show $C_AR ar/kernlib.a *.o
-show $C_RANLIB ar/kernlib.a
+++ /dev/null
-/* $Id: rand.c,v 1.2 2004/01/29 04:56:50 mmondor Exp $ */
-
-/*
- * Copyright (C) 2001-2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software written by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* Algorithm was borrowed from:
- * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
- * "Random number generators: good ones are hard to find",
- * Park and Miller, Communications of the ACM, vol. 31, no. 10,
- * October 1988, p. 1195.
- *
- * The 10,000nth invokation with default initial seed of 1 should result
- * in 1043618065. Of course, this is a highly predictable algorithm, but
- * it is rather well distributed, while also being quite fast, and is thus
- * suitable in the implementation of ANSI/C89 rand(3)/srand(3) functions.
- * Do NOT use for cryptography related work.
- * Matt
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/rand.h>
-
-
-
-static unsigned int global_seed = 1;
-
-
-
-int rand(void)
-{
- /*
- register int a, b;
-
- a = b = (signed int)global_seed;
- a /= 127773;
- b %= 127773;
-
- b *= 16807;
- a *= 2836;
- b -= a;
- if (b <= 0)
- b += 0x7fffffff;
-
- global_seed = b;
-
- return b;
- */
-
- int v;
-
- if ((v = (global_seed % 127773 * 16807) -
- (global_seed / 127773 * 2836)) < 1)
- v += 0x7fffffff;
- global_seed = v;
-
- return v;
-}
-
-
-void srand(unsigned int seed)
-{
- global_seed = seed;
-}
-
-
-/* This is the POSIX reentrant variant where caller supplies seed */
-int rand_r(unsigned int *seed)
-{
- int v;
-
- if ((v = (*seed % 127773 * 16807) - (*seed / 127773 * 2836)) < 1)
- v += 0x7fffffff;
- *seed = v;
-
- return v;
-}
+++ /dev/null
-/* $Id: rand.h,v 1.1 2004/01/29 04:55:06 mmondor Exp $ */
-
-/*
- * Copyright (C) 2001-2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software written by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNLIB_RAND_H
-#define KERNLIB_RAND_H
-
-
-
-#include <common/types.h>
-
-
-
-int rand(void);
-void srand(unsigned int);
-int rand_r(unsigned int *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: setjmp.h,v 1.1 2004/01/30 07:01:24 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNLIB_SETJMP_H
-#define KERNLIB_SETJMP_H
-
-
-
-/* These functions, as well as _ctx_t are defined by the processor-specific
- * support headerfile, <processor/support.h>. These are the C89/ANSI
- * setjmp()/longjmp().
- */
-
-
-
-#include <common/types.h>
-#include <processor/support.h>
-
-
-
-typedef _ctx_t jmp_buf[1];
-
-
-
-int setjmp(jmp_buf);
-void longjmp(jmp_buf, int);
-
-
-
-#endif
+++ /dev/null
-/* $Id: string.h,v 1.3 2004/06/03 05:40:46 mmondor Exp $ */
-
-/*
- * Copyright (C) 1989-2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef KERNLIB_STRING_H
-#define KERNLIB_STRING_H
-
-
-
-#include <common/types.h>
-#include <processor/support.h>
-
-
-
-/* This is used for some kernel strings, such as public message port names */
-typedef struct bstr {
- size_t size, len; /* Maximum and current lengths */
- u_int8_t data[1]; /* Actual buffer follows */
-} bstr_t;
-
-
-bstr_t *bstr_alloc(size_t);
-bstr_t *bstr_new(const char *, size_t, bool);
-bstr_t *bstr_free(bstr_t *);
-
-
-
-/* More conventional string functions */
-size_t strlen(const char *);
-size_t strnlen(const char *, size_t);
-
-char *_strcpy(char *, const char *);
-size_t _strncpy(char *, const char *, size_t);
-
-char *_strcat(char *, const char *);
-char *_strncat(char *, const char *, size_t);
-
-int strcmp(const char *, const char *);
-int strncmp(const char *, const char *, size_t);
-
-char *strchr(const char *, int);
-char *strnchr(const char *, int, size_t);
-char *strrchr(const char *, int);
-char *strnrchr(const char *, int, size_t);
-
-char *_strdup(const char *);
-char *_strndup(const char *, size_t);
-
-int straspl(char **, char *, int);
-int strspl(char **, char *, int, char);
-
-int strcasecmp(const char *, const char *);
-int strncasecmp(const char *, const char *, size_t);
-void _strlower(char *);
-void _strupper(char *);
-u_int32_t _strpack32(const char *, size_t);
-u_int32_t _memcasehash32(const void *, size_t);
-int _memcasecmp(const void *, const void *, size_t);
-
-u_int32_t htol(const char *);
-void _strrev(char *);
-u_int32_t memhash32(const void *, size_t);
-
-#define memclr(a, l) memset((a), 0, (l))
-int memcmp(const void *, const void *, size_t);
-void *memcpy(void *, const void *, size_t);
-void *memmove(void *, const void *, size_t);
-void *memset(void *, int, size_t);
-void pageclr(void *, u_int32_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: _strcat.c,v 1.1 2004/06/03 05:40:02 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* XXX Unlike ANSI, returns pointer at end of destination rather to beginning
- * to allow special optimizations in loops.
- */
-char *_strcat(char *dest, const char *src)
-{
- for (; *dest != '\0'; dest++) ;
- for (; (*dest = *src++) != '\0'; dest++) ;
-
- return (dest);
-}
+++ /dev/null
-/* $Id: _strcpy.c,v 1.1 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* XXX Unlike standard strcpy(), returns pointer to end of copied string in
- * destination, rather than to beginning, more useful to optimize some loops.
- * This variant should never be called strcpy() (i.e., could be called
- * _strcpy() however).
- */
-char *_strcpy(char *dest, const char *src)
-{
- for (; (*dest = *src++) != '\0'; dest++) ;
-
- return (dest);
-}
+++ /dev/null
-/* $Id: _strdup.c,v 1.3 2004/06/03 05:54:44 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/main.h>
-#include <common/kernlib/string.h>
-
-
-
-/* Uses kernel memory pool, should only be used by kernel code */
-char *_strdup(const char *str)
-{
- char *new;
- register const char *ptr;
- register size_t len;
-
- for (new = NULL, ptr = str; *ptr != '\0'; ptr++) ;
-
- len = (size_t)(ptr - str) + 1;
- if ((new = MALLOC(len)) != NULL)
- (void) memcpy(new, str, len);
-
- return new;
-}
+++ /dev/null
-/* $Id: _strncat.c,v 1.1 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* XXX Unlike ANSI, returns pointer at end of destination rather to beginning
- * to allow special optimizations in loops.
- */
-char *_strncat(char *dest, const char *src, size_t max)
-{
- if (max != 0) {
- register const char *toptr;
-
- for (toptr = dest, toptr += max; dest < toptr && *dest != '\0';
- dest++) ;
- for (; dest < toptr && (*dest = *src++) != '\0'; dest++) ;
- if (dest < toptr)
- *dest = '\0';
- }
-
- return dest;
-}
+++ /dev/null
-/* $Id: _strncpy.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* Unlike the useless return code of the standard ANSI one,
- * this function returns the number of bytes successfully copied.
- */
-size_t _strncpy(char *dest, const char *src, size_t max)
-{
- if (max > 0) {
- register const char *sptr;
- register char *toptr;
-
- for (sptr = src, toptr = dest, toptr += max;
- dest < toptr && (*dest = *sptr) != '\0'; sptr++, dest++) ;
- if (dest == toptr)
- *dest = '\0';
-
- return ((size_t)(sptr - src));
- }
-
- *dest = '\0';
- return 0;
-}
+++ /dev/null
-/* $Id: _strndup.c,v 1.3 2004/06/03 05:54:44 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/main.h>
-#include <common/kernlib/string.h>
-
-
-
-/* Uses kernel memory. */
-char *_strndup(const char *str, size_t max)
-{
- char *new;
- register const char *ptr, *toptr;
- size_t len;
-
- for (toptr = ptr = str, toptr += max, new = NULL;
- ptr < toptr && *ptr != '\0'; ptr++) ;
- len = (size_t)(ptr - str);
- if ((new = MALLOC(len + 1)) != NULL) {
- (void) memcpy(new, str, len);
- new[len] = '\0';
- }
-
- return new;
-}
+++ /dev/null
-/* $Id: _strrev.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* Iteratively reverses the supplied string */
-void _strrev(char *str)
-{
- register char *p1, *p2, t;
-
- for (p1 = p2 = str; *p2; p2++) ;
- if (p2 > p1)
- p2--;
-
- for (;p1 < p2; p1++, p2--) {
- t = *p1;
- *p1 = *p2;
- *p2 = t;
- }
-}
+++ /dev/null
-/* $Id: bstr_alloc.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/main.h>
-
-
-
-/* This is always allocated using kernel memory, and is for use by kernel
- * functions.
- */
-bstr_t *bstr_alloc(size_t size)
-{
- bstr_t *bstr = NULL;
-
- if ((bstr = MALLOC(sizeof(bstr_t) + size + 1)) != NULL) {
- bstr->size = size;
- bstr->len = 0;
- *bstr->data = 0;
- }
-
- return bstr;
-}
+++ /dev/null
-/* $Id: bstr_free.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-#include <common/kernel/memory.h>
-
-
-
-bstr_t *bstr_free(bstr_t *bstr)
-{
- if (bstr != NULL)
- FREE(bstr);
-
- return NULL;
-}
+++ /dev/null
-/* $Id: bstr_new.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/main.h>
-
-
-
-/* This is always allocated using kernel memory, and is for use by kernel
- * functions.
- */
-bstr_t *bstr_new(const char *string, size_t max, bool fixed)
-{
- register bstr_t *bstr = NULL;
- register size_t len = strnlen(string, max);
- register size_t size;
-
- if (fixed)
- size = max;
- else
- size = len;
-
- if ((bstr = MALLOC(sizeof(bstr_t) + size + 1)) != NULL) {
- bstr->size = size;
- bstr->len = len;
- memcpy(bstr->data, string, len);
- bstr->data[len] = 0;
- }
-
- return bstr;
-}
+++ /dev/null
-/* $Id: case.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-static const unsigned char toupper_table[] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
- 0x1E, 0x1F, ' ', '!', '"', '#', '$', '%', '&', 0x27,
- '(', ')', '*', '+', ',', '-', '.', '/', '0', '1',
- '2', '3', '4', '5', '6', '7', '8', '9', ':', ';',
- '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E',
- 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
- 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
- 'Z', '[', 0x5C, ']', '^', '_', '`', 'A', 'B', 'C',
- 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
- 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
- 'X', 'Y', 'Z', '{', '|', '}', '~', 0x7F, 0x80, 0x81,
- 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B,
- 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
- 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
- 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
- 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
- 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
- 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
- 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1,
- 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB,
- 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
- 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
- 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9,
- 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
-};
-
-static const unsigned char tolower_table[] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
- 0x1E, 0x1F, ' ', '!', '"', '#', '$', '%', '&', 0x27,
- '(', ')', '*', '+', ',', '-', '.', '/', '0', '1',
- '2', '3', '4', '5', '6', '7', '8', '9', ':', ';',
- '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e',
- 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
- 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
- 'z', '[', 0x5C, ']', '^', '_', '`', 'a', 'b', 'c',
- 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
- 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
- 'x', 'y', 'z', '{', '|', '}', '~', 0x7F, 0x80, 0x81,
- 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B,
- 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
- 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
- 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
- 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
- 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
- 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
- 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1,
- 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB,
- 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
- 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
- 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9,
- 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
-};
-
-
-
-int strcasecmp(const char *s1, const char *s2)
-{
- register const unsigned char *us1, *us2;
- register unsigned char cs1, cs2;
-
- for (us1 = (const unsigned char *)s1, us2 = (const unsigned char *)s2,
- cs1 = tolower_table[(int)*us1], cs2 = tolower_table[(int)*us2];
- cs1 != '\0' && cs2 != '\0' &&
- (cs1 = tolower_table[(int)*us1]) ==
- (cs2 = tolower_table[(int)*us2]);
- us1++, us2++) ;
-
- return ((int)(cs1 - cs2));
-}
-
-
-int strncasecmp(const char *s1, const char *s2, size_t max)
-{
- register const unsigned char *us1, *us2, *toptr;
- register unsigned char cs1, cs2;
-
- if (max == 0)
- return 0;
-
- for (us1 = (const unsigned char *)s1, us2 = (const unsigned char *)s2,
- cs1 = tolower_table[(int)*us1], cs2 = tolower_table[(int)*us2],
- toptr = us1, toptr += max;
- us1 < toptr && cs1 != '\0' && cs2 != '\0' &&
- (cs1 = tolower_table[(int)*us1]) ==
- (cs2 = tolower_table[(int)*us2]);
- us1++, us2++) ;
-
- return (us1 < toptr ? ((int)(cs1 - cs2)) : 0);
-}
-
-
-void _strlower(char *str)
-{
- register unsigned char *ustr;
-
- for (ustr = (unsigned char *)str; *ustr != '\0'; ustr++)
- *ustr = tolower_table[(int)*ustr];
-}
-
-
-void _strupper(char *str)
-{
- register unsigned char *ustr;
-
- for (ustr = (unsigned char *)str; *ustr != '\0'; ustr++)
- *ustr = toupper_table[(int)*ustr];
-}
-
-
-/* This function generates a 32-bit hash using the supplied string which
- * is suitable for fast lookup for command comparision. It simply converts
- * characters to uppercase and stores them in the value. It of course can
- * only perform this for 4 bytes. It will stop at either end of string '\0'
- * or space ' '. If the string has more than 4 characters -1 is returned.
- */
-u_int32_t _strpack32(const char *str, size_t min)
-{
- register unsigned const char *ustr;
- register u_int32_t hash = 0;
- size_t i;
-
- for (ustr = (unsigned const char *)str, i = 0; *ustr > 32 && i < 5; i++) {
- hash <<= 8;
- hash |= toupper_table[(int)*ustr++];
- }
- if (i < min || i > 4)
- hash = 0;
-
- return hash;
-}
-
-
-/* These functions are useful to use in conjunction with hash tables if
- * case-insensitive processing of data is required while case-sensitivity of
- * records storage must be preserved.
- */
-
-u_int32_t _memcasehash32(const void *mem, size_t size)
-{
- register u_int32_t hash;
- register const unsigned char *curmem, *tomem;
-
- hash = 0;
- curmem = tomem = mem;
- tomem += size;
-
-#if !defined(_ARCH_LOWCACHE)
- while (curmem < tomem - 4) {
-#if !defined(_ARCH_USEINDEXING)
- hash = toupper_table[(int)*curmem++] + (31 * hash);
- hash = toupper_table[(int)*curmem++] + (31 * hash);
- hash = toupper_table[(int)*curmem++] + (31 * hash);
- hash = toupper_table[(int)*curmem++] + (31 * hash);
-#else /* !defined(_ARCH_USEINDEXING) */
- hash = toupper_table[(int)curmem[0]] + (31 * hash);
- hash = toupper_table[(int)curmem[1]] + (31 * hash);
- hash = toupper_table[(int)curmem[2]] + (31 * hash);
- hash = toupper_table[(int)curmem[3]] + (31 * hash);
- curmem += 4;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (curmem < tomem)
- hash = toupper_table[(int)*curmem++] + (31 * hash);
-
- return hash;
-}
-
-int _memcasecmp(const void *s1, const void *s2, size_t size)
-{
- register const unsigned char *ptr1, *ptr2, *toptr;
-
-#define CMP() toupper_table[(int)*ptr1++] != toupper_table[(int)*ptr2++]
-#define RET() return (int)(toupper_table[(int)*(--ptr1)] - \
- toupper_table[(int)*(--ptr2)])
-
- ptr1 = toptr = s1;
- toptr += size;
- ptr2 = s2;
-
-#if !defined(_ARCH_LOWCACHE)
- while (ptr1 < toptr - 4)
- if (CMP() || CMP() || CMP() || CMP())
- RET();
-#endif
- while (ptr1 < toptr)
- if (CMP())
- RET();
-
-#undef CMP
-#undef RET
-
- return 0;
-}
+++ /dev/null
-/* $Id: htol.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* Converts an hexadecimal string to u_int32_t */
-u_int32_t htol(const char *s)
-{
- register u_int32_t v = 0;
-
- while (*s) {
- if (*s >= '0' && *s <= '9') {
- if (v <= (u_int16_t)-1)
- v = (u_int16_t)v * 16;
- else
- v = v * 16;
- v += *s++ - '0';
- } else if (*s >= 'a' && *s <= 'f') {
- if (v <= (u_int16_t)-1)
- v = (u_int16_t)v * 16;
- else
- v = v * 16;
- v += *s++ - 87;
- } else if (*s >= 'A' && *s <= 'F') {
- if (v <= (u_int16_t)-1)
- v = (u_int16_t)v * 16;
- else
- v = v * 16;
- v += *s++ - 55;
- } else break;
- }
-
- return v;
-}
+++ /dev/null
-/* $Id: memcmp.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-int memcmp(const void *dest, const void *src, size_t len)
-{
- register const unsigned char *ptr, *toptr, *dptr;
-
- ptr = toptr = src;
- toptr += len;
- dptr = dest;
-
-#define RET(a, b) return (int)(*(--(a)) - *(--(b)))
-
-#if _ARCH_INT_BITS == 8
-
-#if !defined(_ARCH_LOWCACHE)
- while (ptr < toptr - 8)
- if (*ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ ||
- *ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ ||
- *ptr++ != *dptr++ || *ptr++ != *dptr++)
- RET(dptr, ptr);
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr < toptr)
- if (*ptr++ != *dptr++)
- RET(dptr, ptr);
-
-#else /* _ARCH_INT_BITS != 8 */
-
- if (len < 32 || ((int)ptr % sizeof(int)) != ((int)dptr % sizeof(int))) {
-#if !defined(_ARCH_LOWCACHE)
- while (ptr < toptr - 8)
- if (*ptr++ != *dptr++ || *ptr++ != *dptr++ || *ptr++ != *dptr++ ||
- *ptr++ != *dptr++ || *ptr++ != *dptr++ ||
- *ptr++ != *dptr++ || *ptr++ != *dptr++ ||
- *ptr++ != *dptr++)
- RET(dptr, ptr);
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr < toptr)
- if (*ptr++ != *dptr++)
- RET(dptr, ptr);
- } else {
- register const unsigned int *lptr, *ltoptr, *ldptr, *ldtoptr;
- register const unsigned char *dtoptr;
-
- dtoptr = dptr;
- dtoptr += len;
- lptr = (const unsigned int *)OALIGN_CEIL(ptr, int);
- ltoptr = (const unsigned int *)OALIGN_FLOOR(toptr, int);
- ldptr = (const unsigned int *)OALIGN_CEIL(dptr, int);
- ldtoptr = (const unsigned int *)OALIGN_FLOOR(dtoptr, int);
- if (ldtoptr - ldptr < ltoptr - lptr)
- ltoptr--;
-
- while (ptr < (const unsigned char *)lptr)
- if (*ptr++ != *dptr++)
- RET(dptr, ptr);
-#if !defined(_ARCH_LOWCACHE)
- while (lptr < ltoptr - (sizeof(int) * 8)) {
- if (*lptr++ != *ldptr++ || *lptr++ != *ldptr++ ||
- *lptr++ != *ldptr++ || *lptr++ != *ldptr++ ||
- *lptr++ != *ldptr++ || *lptr++ != *ldptr++ ||
- *lptr++ != *ldptr++ || *lptr++ != *ldptr++)
- RET(ldptr, lptr);
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (lptr < ltoptr)
- if (*lptr++ != *ldptr++)
- RET(ldptr, lptr);
- ptr = (const unsigned char *)lptr;
- dptr = (const unsigned char *)ldptr;
- while (ptr < toptr)
- if (*ptr++ != *dptr++)
- RET(dptr, ptr);
- }
-
-#endif /* _ARCH_INT_BITS == 8 */
-
-#undef RET
-
- return 0;
-}
+++ /dev/null
-/* $Id: memcpy.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-void *memcpy(void *dest, const void *src, size_t len)
-{
- register const unsigned char *ptr, *toptr;
- register unsigned char *dptr;
-
- ptr = toptr = src;
- toptr += len;
- dptr = dest;
-
-#if _ARCH_INT_BITS == 8
-
-#if !defined(_ARCH_LOWCACHE)
- while (ptr < toptr - 8) {
-#if !defined(_ARCH_USEINDEXING)
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
-#else /* !defined(_ARCH_USEINDEXING) */
- dptr[0] = ptr[0];
- dptr[1] = ptr[1];
- dptr[2] = ptr[2];
- dptr[3] = ptr[3];
- dptr[4] = ptr[4];
- dptr[5] = ptr[5];
- dptr[6] = ptr[6];
- dptr[7] = ptr[7];
- dptr += 8;
- ptr += 8;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr < toptr)
- *dptr++ = *ptr++;
-
-#else /* _ARCH_INT_BITS != 8 */
-
- if (len < 32 || ((int)ptr % sizeof(int)) != ((int)dptr % sizeof(int))) {
-#if !defined(_ARCH_LOWCACHE)
- while (ptr < toptr - 8) {
-#if !defined(_ARCH_USEINDEXING)
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
-#else /* !defined(_ARCH_USEINDEXING) */
- dptr[0] = ptr[0];
- dptr[1] = ptr[1];
- dptr[2] = ptr[2];
- dptr[3] = ptr[3];
- dptr[4] = ptr[4];
- dptr[5] = ptr[5];
- dptr[6] = ptr[6];
- dptr[7] = ptr[7];
- dptr += 8;
- ptr += 8;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr < toptr)
- *dptr++ = *ptr++;
- } else {
- register const unsigned int *lptr, *ltoptr;
- register unsigned int *ldptr, *ldtoptr;
- register unsigned char *dtoptr;
-
- dtoptr = dptr;
- dtoptr += len;
- lptr = (const unsigned int *)OALIGN_CEIL(ptr, int);
- ltoptr = (const unsigned int *)OALIGN_FLOOR(toptr, int);
- ldptr = (unsigned int *)OALIGN_CEIL(dptr, int);
- ldtoptr = (unsigned int *)OALIGN_FLOOR(dtoptr, int);
- if (ldtoptr - ldptr < ltoptr - lptr)
- ltoptr--;
-
- while (ptr < (const unsigned char *)lptr)
- *dptr++ = *ptr++;
-#if !defined(_ARCH_LOWCACHE)
- while (lptr < ltoptr - (sizeof(int) * 8)) {
-#if !defined(_ARCH_USEINDEXING)
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
-#else /* !defined(_ARCH_USEINDEXING) */
- ldptr[0] = lptr[0];
- ldptr[1] = lptr[1];
- ldptr[2] = lptr[2];
- ldptr[3] = lptr[3];
- ldptr[4] = lptr[4];
- ldptr[5] = lptr[5];
- ldptr[6] = lptr[6];
- ldptr[7] = lptr[7];
- ldptr = &ldptr[8];
- lptr = &lptr[8];
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (lptr < ltoptr)
- *ldptr++ = *lptr++;
- ptr = (unsigned const char *)lptr;
- dptr = (unsigned char *)ldptr;
- while (ptr < toptr)
- *dptr++ = *ptr++;
- }
-
-#endif /* _ARCH_INT_BITS == 8 */
-
- return dest;
-}
+++ /dev/null
-/* $Id: memhash32.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-u_int32_t memhash32(const void *mem, size_t size)
-{
- register u_int32_t hash;
- register const unsigned char *curmem, *tomem;
-
- hash = 0;
- curmem = tomem = mem;
- tomem += size;
-
-#if !defined(_ARCH_LOWCACHE)
- while (curmem < tomem - 4) {
-#if !defined(_ARCH_USEINDEXING)
- hash = *curmem++ + (31 * hash);
- hash = *curmem++ + (31 * hash);
- hash = *curmem++ + (31 * hash);
- hash = *curmem++ + (31 * hash);
-#else /* !defined(_ARCH_USEINDEXING) */
- hash = curmem[0] + (31 * hash);
- hash = curmem[1] + (31 * hash);
- hash = curmem[2] + (31 * hash);
- hash = curmem[3] + (31 * hash);
- curmem += 4;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (curmem < tomem)
- hash = *curmem++ + (31 * hash);
-
- return hash;
-}
+++ /dev/null
-/* $Id: memmove.c,v 1.1 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* Can work with overlapping areas */
-void *memmove(void *dest, const void *src, size_t len)
-{
- register const unsigned char *ptr = NULL, *toptr = NULL;
- register unsigned char *dptr = NULL;
- size_t d;
-
- if (dest < src)
- d = (const unsigned char *)src - (unsigned char *)dest;
- else
- d = (unsigned char *)dest - (const unsigned char *)src;
-
-#if _ARCH_INT_BITS == 8
-
- if (dest < src) {
- /* Copy in increasing order */
- ptr = toptr = (const unsigned char *)src;
- toptr += len;
- dptr = dest;
-#if !defined(_ARCH_LOWCACHE)
- while (ptr < toptr - 8) {
-#if !defined(_ARCH_USEINDEXING)
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
-#else /* !defined(_ARCH_USEINDEXING) */
- dptr[0] = ptr[0];
- dptr[1] = ptr[1];
- dptr[2] = ptr[2];
- dptr[3] = ptr[3];
- dptr[4] = ptr[4];
- dptr[5] = ptr[5];
- dptr[6] = ptr[6];
- dptr[7] = ptr[7];
- dptr += 8;
- ptr += 8;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr < toptr)
- *dptr++ = *ptr++;
- } else if (dest > src) {
- /* Copy in reverse order */
- ptr = toptr = (const unsigned char *)src;
- ptr += len;
- dptr = dest;
- dptr += len;
-#if !defined(_ARCH_LOWCACHE)
- while (ptr >= toptr + 8) {
-#if !defined(_ARCH_USEINDEXING)
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
-#else /* !defined(_ARCH_USEINDEXING) */
- dptr[0] = ptr[0];
- dptr[-1] = ptr[-1];
- dptr[-2] = ptr[-2];
- dptr[-3] = ptr[-3];
- dptr[-4] = ptr[-4];
- dptr[-5] = ptr[-5];
- dptr[-6] = ptr[-6];
- dptr[-7] = ptr[-7];
- dptr -= 8;
- ptr -= 8;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr >= toptr)
- *dptr-- = *ptr--;
- }
-
-#else /* _ARCH_INT_BITS != 8 */
-
- if (len < 32 || d < sizeof(int) ||
- ((int)ptr % sizeof(int)) != ((int)dptr % sizeof(int))) {
- if (dest < src) {
- /* Copy in increasing order */
- ptr = toptr = (const unsigned char *)src;
- toptr += len;
- dptr = dest;
-#if !defined(_ARCH_LOWCACHE)
- while (ptr < toptr - 8) {
-#if !defined(_ARCH_USEINDEXING)
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
- *dptr++ = *ptr++;
-#else /* !defined(_ARCH_USEINDEXING) */
- dptr[0] = ptr[0];
- dptr[1] = ptr[1];
- dptr[2] = ptr[2];
- dptr[3] = ptr[3];
- dptr[4] = ptr[4];
- dptr[5] = ptr[5];
- dptr[6] = ptr[6];
- dptr[7] = ptr[7];
- dptr += 8;
- ptr += 8;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr < toptr)
- *dptr++ = *ptr++;
- } else if (dest > src) {
- /* Copy in reverse order */
- ptr = toptr = (const unsigned char *)src;
- ptr += len;
- dptr = dest;
- dptr += len;
-#if !defined(_ARCH_LOWCACHE)
- while (ptr >= toptr + 8) {
-#if !defined(_ARCH_USEINDEXING)
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
- *dptr-- = *ptr--;
-#else /* !defined(_ARCH_USEINDEXING) */
- dptr[0] = ptr[0];
- dptr[-1] = ptr[-1];
- dptr[-2] = ptr[-2];
- dptr[-3] = ptr[-3];
- dptr[-4] = ptr[-4];
- dptr[-5] = ptr[-5];
- dptr[-6] = ptr[-6];
- dptr[-7] = ptr[-7];
- dptr -= 8;
- ptr -= 8;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr >= toptr)
- *dptr-- = *ptr--;
- }
- } else {
- if (dest < src) {
- /* Increasing order */
- register const unsigned int *lptr, *ltoptr;
- register unsigned int *ldptr, *ldtoptr;
- register unsigned char *dtoptr;
-
- ptr = toptr = (const unsigned char *)src;
- toptr += len;
- dptr = dest;
-
- dtoptr = dptr;
- dtoptr += len;
- lptr = (const unsigned int *)OALIGN_CEIL(ptr, int);
- ltoptr = (const unsigned int *)OALIGN_FLOOR(toptr, int);
- ldptr = (unsigned int *)OALIGN_CEIL(dptr, int);
- ldtoptr = (unsigned int *)OALIGN_FLOOR(dtoptr, int);
- if (ldtoptr - ldptr < ltoptr - lptr)
- ltoptr--;
-
- while (ptr < (const unsigned char *)lptr)
- *dptr++ = *ptr++;
-#if !defined(_ARCH_LOWCACHE)
- while (lptr < ltoptr - (sizeof(int) * 8)) {
-#if !defined(_ARCH_USEINDEXING)
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
- *ldptr++ = *lptr++;
-#else /* !defined(_ARCH_USEINDEXING) */
- ldptr[0] = lptr[0];
- ldptr[1] = lptr[1];
- ldptr[2] = lptr[2];
- ldptr[3] = lptr[3];
- ldptr[4] = lptr[4];
- ldptr[5] = lptr[5];
- ldptr[6] = lptr[6];
- ldptr[7] = lptr[7];
- ldptr = &ldptr[8];
- lptr = &lptr[8];
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (lptr < ltoptr)
- *ldptr++ = *lptr++;
- ptr = (const unsigned char *)lptr;
- dptr = (unsigned char *)ldptr;
- while (ptr < toptr)
- *dptr++ = *ptr++;
- } else if (dest > src) {
- /* Reverse order */
- register const int *lptr, *ltoptr;
- register int *ldptr, *ldtoptr;
- register char *dtoptr;
-
- ptr = toptr = (const unsigned char *)src;
- ptr += len;
- dptr = dest;
- dptr += len;
-
- dtoptr = dest;
- lptr = (const unsigned int *)OALIGN_FLOOR(ptr, int);
- ldptr = (unsigned int *)OALIGN_FLOOR(dptr, int);
- ltoptr = (const unsigned int *)OALIGN_CEIL(toptr, int);
- ldtoptr = (unsigned int *)OALIGN_CEIL(dtoptr, int);
- if (ldptr - ldtoptr < lptr - ltoptr)
- ltoptr++;
-
- while (ptr >= (const unsigned char *)lptr)
- *dptr-- = *ptr--;
-#if !defined(_ARCH_LOWCACHE)
- while (lptr >= ltoptr + (sizeof(int) * 8)) {
-#if !defined(_ARCH_USEINDEXING)
- *ldptr-- = *lptr--;
- *ldptr-- = *lptr--;
- *ldptr-- = *lptr--;
- *ldptr-- = *lptr--;
- *ldptr-- = *lptr--;
- *ldptr-- = *lptr--;
- *ldptr-- = *lptr--;
- *ldptr-- = *lptr--;
-#else /* !defined(_ARCH_USEINDEXING) */
- ldptr[0] = lptr[0];
- ldptr[-1] = lptr[-1];
- ldptr[-2] = lptr[-2];
- ldptr[-3] = lptr[-3];
- ldptr[-4] = lptr[-4];
- ldptr[-5] = lptr[-5];
- ldptr[-6] = lptr[-6];
- ldptr[-7] = lptr[-7];
- ldptr = &ldptr[-8];
- lptr = &lptr[-8];
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (lptr >= ltoptr)
- *ldptr-- = *lptr--;
- ptr = (const unsigned char *)lptr;
- dptr = (unsigned char *)ldptr;
- while (ptr >= toptr)
- *dptr-- = *ptr--;
- }
- }
-
-#endif /* _ARCH_INT_BITS == 8 */
-
- return dest;
-}
+++ /dev/null
-/* $Id: memset.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-void *memset(void *mem, int fill, size_t len)
-{
- register unsigned char *ptr, *toptr;
- u_int8_t byte = (u_int8_t)fill;
-
- ptr = toptr = mem;
- toptr += len;
-
-#if _ARCH_INT_BITS == 8
-
-#if !defined(_ARCH_LOWCACHE)
- while (ptr < toptr - 8) {
-#if !defined(_ARCH_USEINDEXING)
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
-#else /* !defined(_ARCH_USEINDEXING) */
- ptr[0] = byte;
- ptr[1] = byte;
- ptr[2] = byte;
- ptr[3] = byte;
- ptr[4] = byte;
- ptr[5] = byte;
- ptr[6] = byte;
- ptr[7] = byte;
- ptr += 8;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr < toptr)
- *ptr++ = byte;
-
-#else /* _ARCH_INT_BITS != 8 */
-
- if (len < 32) {
-#if !defined(_ARCH_LOWCACHE)
- while (ptr < toptr - 8) {
-#if !defined(_ARCH_USEINDEXING)
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
- *ptr++ = byte;
-#else /* !defined(_ARCH_USEDARRAYS) */
- ptr[0] = byte;
- ptr[1] = byte;
- ptr[2] = byte;
- ptr[3] = byte;
- ptr[4] = byte;
- ptr[5] = byte;
- ptr[6] = byte;
- ptr[7] = byte;
- ptr += 8;
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (ptr < toptr)
- *ptr++ = byte;
- } else {
- register unsigned int *lptr, *ltoptr, lword = 0;
-
- if (byte != 0) {
-#if _ARCH_INT_BITS == 16
- lword = ((byte << 8) & 0xff00) | (byte & 0x00ff);
-#elif _ARCH_INT_BITS == 32
- /* Creating a 16-bit word from the 8-bit one, then the 32-bit word
- * from the 16-bit one require less instructions.
- */
- register u_int16_t tmp;
-
- tmp = ((byte << 8) & 0xff00) | (byte & 0x00ff);
- lword = ((tmp << 16) & 0xffff0000) | (tmp & 0x0000ffff);
-#elif _ARCH_INT_BITS == 64
- /* Unlikely, since long is usually 64-bit on 64-bit archs, leaving
- * 32-bit ints.
- * Creating a 16-bit word from the 8-bit one, a 32-bit word from
- * the 16-bit one and a 64-bit word from the 32-bit one require
- * less instructions.
- */
- register u_int32_t tmp2;
- register u_int16_t tmp;
-
- tmp = ((byte << 8) & 0xff00) | (byte & 0x00ff);
- tmp2 = ((tmp << 16) & 0xffff0000) | (tmp & 0x0000ffff);
- lword = ((tmp2 << 32) & 0xffffffff00000000ULL) |
- (tmp2 & 0x00000000ffffffffULL);
-#endif
- }
-
- lptr = (unsigned int *)OALIGN_CEIL(ptr, int);
- ltoptr = (unsigned int *)OALIGN_FLOOR(toptr, int);
-
- while (ptr < (unsigned char *)lptr)
- *ptr++ = byte;
-#if !defined(_ARCH_LOWCACHE)
- while (lptr < ltoptr - (sizeof(int) * 8)) {
-#if !defined(_ARCH_USEINDEXING)
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
-#else /* !defined(_ARCH_USEINDEXING) */
- lptr[0] = lword;
- lptr[1] = lword;
- lptr[2] = lword;
- lptr[3] = lword;
- lptr[4] = lword;
- lptr[5] = lword;
- lptr[6] = lword;
- lptr[7] = lword;
- lptr = &lptr[8];
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (lptr < ltoptr)
- *lptr++ = lword;
- ptr = (unsigned char *)lptr;
- while (ptr < toptr)
- *ptr++ = byte;
- }
-
-#endif /* _ARCH_INT_BITS == 8 */
-
- return mem;
-}
+++ /dev/null
-/* $Id: pageclr.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-#include <port/support.h>
-
-
-
-/* Faster than memset(), but assumes that <mem> be aligned and that _PAGE_SIZE
- * is a multiple of sizeof(int).
- */
-void pageclr(void *mem, u_int32_t many)
-{
- register int *lptr, *ltoptr, lword = 0;
-
- lptr = (int *)mem;
- ltoptr = (int *)(((u_int8_t *)mem + (_PAGE_SIZE * many)));
-
-#if !defined(_ARCH_LOWCACHE)
- while (lptr < ltoptr - (sizeof(int) * 16)) {
-#if !defined(_ARCH_USEINDEXING)
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
- *lptr++ = lword;
-#else /* !defined(_ARCH_USEINDEXING) */
- lptr[0] = lword;
- lptr[1] = lword;
- lptr[2] = lword;
- lptr[3] = lword;
- lptr[4] = lword;
- lptr[5] = lword;
- lptr[6] = lword;
- lptr[7] = lword;
- lptr[8] = lword;
- lptr[9] = lword;
- lptr[10] = lword;
- lptr[11] = lword;
- lptr[12] = lword;
- lptr[13] = lword;
- lptr[14] = lword;
- lptr[15] = lword;
- lptr = &lptr[16];
-#endif /* !defined(_ARCH_USEINDEXING) */
- }
-#endif /* !defined(_ARCH_LOWCACHE) */
- while (lptr < ltoptr)
- *lptr++ = lword;
-}
+++ /dev/null
-/* $Id: straspl.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* Splits columns of a string delimited by spaces and/or tabs, and fills
- * char **argv with pointers to each column. Returns the number of columns
- * that could be filled in. The supplied string IS modified.
- */
-int straspl(char **argv, char *str, int maxcols)
-{
- register char *ptr, *ptr2;
- int col;
-
- for (ptr = str, col = 0; *ptr != '\0' && col < maxcols; ) {
- for (; *ptr == ' ' || *ptr == '\t'; ptr++) ;
- if (*ptr != '\0') {
- for (ptr2 = ptr; *ptr != '\0' && *ptr != ' ' && *ptr != '\t';
- ptr++) ;
- if (ptr != ptr2) {
- if (*ptr != '\0')
- *ptr++ = '\0';
- argv[col++] = ptr2;
- } else
- break;
- } else
- break;
- }
-
- return col;
-}
+++ /dev/null
-/* $Id: strchr.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-char *strchr(const char *str, int c)
-{
- for (; *str != '\0' && *str != c; str++) ;
-
- return (*str != '\0' ? (char *)str : NULL);
-}
+++ /dev/null
-/* $Id: strcmp.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-int strcmp(const char *s1, const char *s2)
-{
- for (; *s1 != '\0' && *s2 != '\0' && *s1 == *s2; s1++, s2++) ;
-
- return ((int)((const unsigned char)*s1 - (const unsigned char)*s2));
-}
+++ /dev/null
-/* $Id: strlen.c,v 1.2 2004/06/03 05:40:03 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-size_t strlen(const char *str)
-{
- register const char *ptr = str;
-
- /* Although this causes ptr to be increased even when 0 is reached,
- * compilers generally generate better code.
- * i.e. GCC2-m68k: tstb %a0@+ vs tstb %a0@ and addql #1, %a0
- * Matt
- */
- while (*ptr++ != '\0') ;
-
- /* Don't forget to substract 1 */
- return ((size_t)((ptr - 1) - str));
-}
+++ /dev/null
-/* $Id: strnchr.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-char *strnchr(const char *str, int c, size_t max)
-{
- if (max > 0) {
- register const char *toptr;
-
- for (toptr = str, toptr += max;
- str < toptr && *str != '\0' && (int)*str != c; str++) ;
-
- if (str == toptr || *str == '\0')
- return NULL;
-
- return (char *)str;
- }
-
- return NULL;
-}
+++ /dev/null
-/* $Id: strncmp.c,v 1.3 2004/06/03 05:40:04 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-int strncmp(const char *s1, const char *s2, size_t max)
-{
- if (max > 0) {
- register const char *toptr;
-
- for (toptr = s1, toptr += max; s1 < toptr && *s1 != '\0' &&
- *s2 != '\0' && *s1 == *s2; s1++, s2++) ;
-
- return (s1 < toptr ?
- ((int)((const unsigned char)*s1 - (const unsigned char)*s2)) :
- 0);
- }
-
- return 0;
-}
+++ /dev/null
-/* $Id: strnlen.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-size_t strnlen(const char *str, size_t max)
-{
- register const char *fptr, *tptr;
-
- for (fptr = tptr = str, tptr += max; fptr < tptr && *fptr != '\0'; fptr++)
- ;
-
- return ((size_t)(fptr - str));
-}
+++ /dev/null
-/* $Id: strnrchr.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-char *strnrchr(const char *str, int c, size_t max)
-{
- register const char *toptr, *found;
-
- for (found = NULL, toptr = str, toptr += max;
- str < toptr && *str != '\0'; str++) {
- if (*str == c)
- found = str;
- }
-
- return (char *)found;
-}
+++ /dev/null
-/* $Id: strrchr.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-char *strrchr(const char *str, int c)
-{
- register const char *found;
-
- for (found = NULL; *str != '\0'; str++) {
- if (*str == c)
- found = str;
- }
-
- return (char *)found;
-}
+++ /dev/null
-/* $Id: strspl.c,v 1.2 2004/06/03 05:40:04 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include <common/kernlib/string.h>
-
-
-
-/* Splits columns of a string delimited by sep, and fills
- * char **argv with pointers to each column. Returns the number of columns
- * that could be filled in. The supplied string IS modified. Note that two
- * contiguous separators cause empty entries to be filled in. An exception
- * consists of the last separator, which is ignored if nothing is found after
- * it.
- */
-int strspl(char **argv, char *str, int maxcols, char sep)
-{
- register char *ptr, *ptr2;
- int col;
-
- for (col = 0, ptr = str; *ptr != '\0' && col < maxcols; ) {
- for (ptr2 = ptr; *ptr != '\0' && *ptr != sep; ptr++) ;
- if (*ptr != '\0')
- *ptr++ = '\0';
- argv[col++] = ptr2;
- }
-
- return col;
-}
-
-
+++ /dev/null
-#!/bin/sh
-
-# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../makedefs.sh
-
-# Build kernlib
-show cd kernlib
-./make.sh
-show cd ..
-
-# Build kernel
-show cd kernel
-./make.sh
-show cd ..
+++ /dev/null
-/* $Id: types.h,v 1.6 2004/06/03 05:54:44 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef COMMON_TYPES_H
-#define COMMON_TYPES_H
-
-
-
-typedef unsigned char u_int8_t;
-typedef signed char int8_t;
-typedef unsigned short u_int16_t;
-typedef signed short int16_t;
-typedef unsigned long u_int32_t;
-typedef signed long int32_t;
-typedef unsigned long long u_int64_t;
-typedef signed long long int64_t;
-typedef int bool;
-typedef u_int32_t size_t;
-typedef int32_t ssize_t;
-#define NULL ((void *)0)
-#define TRUE /* CONSTCOND */(1)
-#define FALSE /* CONSTCOND */(0)
-
-/* For code-embedded copyright and CVS/RCS ID strings */
-#define COPYRIGHT(x) static const char _copyright[] = x
-#define RCSID(x) static const char _rcsid[] = x
-
-
-/* Useful to o-align a value relative to v (sizes and pointers) */
-#define OALIGN_CEIL(v, o) \
- ((((size_t)(v)) + (sizeof(o)) - 1) / (sizeof(o)) * (sizeof(o)))
-#define OALIGN_FLOOR(v, o) ((size_t)(v) - (((size_t)(v) % sizeof(o))))
-
-/* Useful to byte align a value with supplied size */
-#define BALIGN_CEIL(v, s) ((((size_t)(v)) + (s) - 1) / (s) * (s))
-#define BALIGN_FLOOR(v, s) ((size_t)(v) - (((size_t)(v) % (s))))
-
-/* Import necessary processor-specific optimization macros */
-#include <processor/support.h>
-/* For network and host byte order of 16-bit and 32-bit types */
-#if defined(_ARCH_LITTLE_ENDIAN)
-#define BYTEORDER_NETWORK16 _bswap16
-#define BYTEORDER_HOST16 _bswap16
-#define BYTEORDER_NETWORK32 _bswap32
-#define BYTEORDER_HOST32 _bswap32
-#elif defined(_ARCH_BIG_ENDIAN)
-#define BYTEORDER_NETWORK16(w) (w)
-#define BYTEORDER_HOST16(w) (w)
-#define BYTEORDER_NETWORK32(w) (w)
-#define BYTEORDER_HOST32(w) (w)
-#else
-error "Endian not specified (_ARCH_LITTLE_ENDIAN | _ARCH_BIG_ENDIAN)";
-#endif
-/* Ensure that _ARCH_INT_BITS was defined */
-#ifndef _ARCH_INT_BITS
-error "Bits in an int (_ARCH_INT_BITS) not specified (8, 16, 32, 64)";
-#endif
-
-/* common/kernel/device.h */
-typedef struct devicenode devicenode_t;
-typedef struct devicehandle device_t;
-typedef struct iorequest iorequest_t;
-
-/* common/kernel/exception.h */
-typedef u_int32_t hookid_t;
-typedef struct _int_hook hook_t;
-typedef struct _int_facility facility_t;
-
-/* common/kernel/memory.h */
-typedef struct page page_t;
-typedef struct mchunk mchunk_t;
-typedef struct pool pool_t;
-typedef struct ppool ppool_t;
-typedef struct pnode pnode_t;
-typedef struct mpool mpool_t;
-typedef struct mnode mnode_t;
-
-/* common/kernel/port.h */
-typedef struct port port_t;
-typedef struct message message_t;
-typedef struct mmessage mmessage_t;
-
-/* common/kernel/scheduler.h */
-typedef struct rwlock rwlock_t;
-
-/* common/kernel/signal.h */
-typedef int signum_t;
-typedef u_int32_t sigmask_t;
-
-/* common/kernel/task.h */
-typedef struct task task_t;
-typedef int8_t priority_t;
-
-/* common/kernlib/hash.h */
-typedef struct hashtable hashtable_t;
-typedef struct hashnode hashnode_t;
-
-
-#endif
+++ /dev/null
-/* $Id: config.h,v 1.5 2004/01/29 05:02:02 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef CONFIG_H
-#define CONFIG_H
-
-
-
-/* Flags which are possible to uncomment to compile in the kernel */
-
-#define DEBUG 4096
-#define STATISTICS
-
-
-
-#endif
+++ /dev/null
-#!/bin/sh
-
-# $Id: generic_makedefs.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-# These are various defaults which are used for the building process.
-# Change as required, and don't forget to create the ./port and ./processor
-# symbolic links to their respective directory.
-
-# Local non-cross building tools
-L_AR='ar qS'
-L_AS='as'
-L_CC='gcc'
-L_LD='ld'
-L_NM='nm'
-L_OBJDUMP='objdump'
-L_RANLIB='ranlib'
-L_STRIP='strip'
-L_CAT='cat'
-L_DD='dd'
-
-# Other general purpose tools
-L_RM='rm -f'
-L_LN='ln -s'
-L_SED='sed'
-L_ECHO='echo'
-L_LS='ls'
-
-show()
-{
- # Only output to stderr, not stdout
- $L_ECHO "$@" >&2
- $@
-}
-
-# Deletes all .o files in a directory
-# $1 = directory
-cleanlib()
-{
- show $L_RM $1/*.o
-}
+++ /dev/null
-#!/bin/sh
-
-# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ./generic_makedefs.sh
-
-usage()
-{
- $L_ECHO
- $L_ECHO 'Usage: ./make.sh -t <target>'
- $L_ECHO
- $L_ECHO 'Available targets:'
- $L_ECHO '- amiga'
- $L_ECHO
-}
-
-while getopts t: f; do
- case $f in
- t)
- case $OPTARG in
- amiga)
- PROCESSOR='processors/m68k'
- PORT='ports/amiga'
- ;;
- *)
- $L_ECHO "Unknown target $OPTARG"
- ;;
- esac
- ;;
- *)
- usage
- exit 0
- ;;
- esac
-done
-if [ -z $PORT ] || [ -z $PROCESSOR ]; then
- usage
- exit 0
-fi
-
-./clean.sh
-show $L_LN $PROCESSOR processor
-show $L_LN $PORT port
-show $L_LN $PORT/makedefs.sh makedefs.sh
-show export SRCDIR=`pwd`
-
-# XXX Is this a bug? As port/ symbolic link goes down two levels, I have to
-# cd back two levels?
-
-# Build processor-specific support code to processor/ar/*.a
-show cd processor
-show ./make.sh
-
-# Build port-specific support code to port/ar/*.a
-show cd ../../port
-show ./make.sh
-
-# Build the portable common code to common/kernel/ar/*.a,
-# common/kernlib/ar/*.a and XXX ???
-# and link to global ELF relocatable kernel object xisop.o
-show cd ../../common
-show ./make.sh
-
-# Let the port-specific boot code create the kernel image
-show cd ../port/boot
-show ./make.sh
-
-show cd ..
+++ /dev/null
-config_description=UAE
-x11.rom_path=./
-x11.floppy_path=./
-x11.hardfile_path=./
-x11.low_bandwidth=false
-x11.use_mitshm=false
-x11.hide_cursor=true
-32bit_blits=true
-use_gui=nowait
-use_debugger=false
-kickstart_rom_file=/home/mmondor/kick.rom
-floppy0=/home/mmondor/src/work/Xisop/src/ports/amiga/boot/xisop.adf
-floppy1=
-floppy2=
-floppy3=
-sound_output=none
-joyport0=mouse
-joyport1=kbd1
-kbd_lang=us
-immediate_blits=yes
-cpu_speed=max
-cpu_type=68000
-chipmem_size=2
-fastmem_size=4
-#filesystem=rw,System3.1:/data2/amiga/System3.1
-#filesystem=rw,Work:/data2/amiga/Work
-#filesystem=rw,ASM:/data2/amiga/asm
+++ /dev/null
-DOTuaerc ~/.uaerc used for testing and developping with UAE
- Amiga emulator
-
-xisop.adf Actual Amiga bootable Xisop floppy image
-
-image.bin Consists of the actual compiled kernel binary image
-image.o Useful for debugging, objdump -drw image.o
-bootf/bootf.bin Compiled floppy boot sector image
-bootf/kernel.bin Compiled floppy boot sector image + Xisop image
-
-config.h File to modify depending on your Amiga model and
- kernel size, stack size, etc.
+++ /dev/null
-/* $Id: bootf_c.c,v 1.2 2004/01/20 20:56:20 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* This used to be done by assembly code, it's however best to have a C
- * loader which could eventually perform fancy stuff, and relocation as well.
- * If the loader eventually gets too large to fit into the default boot block
- * size, this boot block can then simply serve to load another loader and
- * launch it.
- */
-
-
-
-#include <common/types.h>
-
-#include "../config.h"
-
-
-COPYRIGHT("\0\nXisop bootloader Copyright 2001-2003, Matthew Mondor, \
-All rights reserved.\n");
-
-
-
-/* CONFIGURATION */
-#define KERNSTART 1024 /* Start of kernel image on disk */
-#define READSIZE 4096 /* Block size for read/relocation (512 min) */
-
-
-
-#define MEMF_CHIP 0x0001
-#define MEMF_FAST 0x0002
-#define CMD_READ 2
-#define ALRT_NOMEM 0x00010001
-#define ALRT_RERR 0x14000001
-
-
-struct iostdreq {
- u_int8_t pad[28];
- u_int16_t command;
- u_int8_t flags;
- int8_t error;
- u_int32_t actual, length;
- void *data;
- u_int32_t offset;
-};
-
-
-void *AllocMem(long, long);
-void *AllocAbs(long, void *);
-void FreeMem(void *, long);
-long DoIO(struct iostdreq *);
-void Alert(long, void *);
-
-void load(struct iostdreq *);
-
-
-
-void load(struct iostdreq *ioreq)
-{
- void *buf = NULL, *readbuf = NULL;
- bool ok = FALSE;
-
- /* Allocate CHIP memory buffer to load disk blocks into */
- if ((readbuf = AllocMem(READSIZE, MEMF_CHIP)) != NULL) {
-
- if ((buf = AllocAbs(KERNSIZE + STACKSIZE, (void *)KERNADDR))
- == (void *)KERNADDR) {
- register u_int32_t offset, *addr, *toaddr;
-
- /* Read image from disk starting at KERNSTART, upto
- * KERNSTART + KERNSIZE, in blocks of READSIZE bytes, which we
- * relocate to our kernel buffer on the fly in KERNADDR.
- */
- for (offset = KERNSTART, addr = buf, toaddr = buf + KERNSIZE;
- addr < toaddr; offset += READSIZE) {
- register u_int32_t *raddr, *taddr;
-
- /* Read a block */
- ioreq->command = CMD_READ;
- ioreq->length = READSIZE;
- ioreq->data = readbuf;
- ioreq->offset = offset;
- if ((DoIO(ioreq)) != 0) {
- Alert(ALRT_RERR, load);
- for (;;) ;
- /* NOTREACHED */
- }
-
- /* Relocate block */
- for (raddr = readbuf, taddr = readbuf + READSIZE;
- raddr < taddr; *addr++ = *raddr++) ;
- }
- ok = TRUE;
- }
- FreeMem(readbuf, READSIZE);
- if (ok) {
- void (*code)(u_int32_t *, size_t);
-
- /* Jump to relocated code, passing it the supervisor stack */
- code = (void *)KERNADDR;
- code((u_int32_t *)STACKADDR, STACKSIZE);
- }
- }
-
- Alert(ALRT_NOMEM, load);
- for (;;) ;
- /* NOTREACHED */
-}
+++ /dev/null
-/* $Id: bootf_s.s,v 1.2 2004/01/20 20:56:20 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-ExecBase = 0x0004
-
-
-.globl _start, AllocMem, AllocAbs, FreeMem, DoIO, Alert
-
-
-.text
-
-| AmigaOS boot header (checksum should be calculated after assembling)
-| This is actually loaded at 0x0020b660, the ROM jumps at 0x0020b66c.
- .long 0x444f5300, 0x00000000, 0x00000000
-
-_start:
-
-| Loader code (startup leaves us with a6 to exec.lib and a1 to IOreq pointer)
-| Call our C load() function!
-|
- movel %a1, %sp@-
- bsrl load
-| NOTREACHED
- addql #4, %sp
- rts
-
-
-| Amiga library of required function stubs for load() C function
-
-| [d0]void * = [-198]AllocMem([d0]long bytes, [d1]long reqs)
-|
-AllocMem:
- moveml %a6/%d1-%d0, %sp@-
- moveal ExecBase:w, %a6
- movel %sp@(16), %d0
- movel %sp@(20), %d1
- jsr %a6@(-198)
- moveal %d0, %a0
- moveml %sp@+, %d0-%d1/%a6
- rts
-
-| [d0]void * = [-204]AllocAbs([d0]long bytes, [a1]void *addr)
-|
-AllocAbs:
- moveml %a1/%a6/%d0, %sp@-
- moveal ExecBase:w, %a6
- movel %sp@(16), %d0
- moveal %sp@(20), %a1
- jsr %a6@(-204)
- moveal %d0, %a0
- moveml %sp@+, %d0/%a6/%a1
- rts
-
-| void [-210]FreeMem([a1]memptr, [d0]bytesize)
-|
-FreeMem:
- moveml %a1/%a6/%d0, %sp@-
- moveal ExecBase:w, %a6
- moveal %sp@(16), %a1
- movel %sp@(20), %d0
- jsr %a6@(-210)
- moveml %sp@+, %d0/%a6/%a1
- rts
-
-| [d0] = [-456]DoIO([a1]struct IORequest *)
-|
-DoIO:
- moveml %a1/%a6, %sp@-
- moveal ExecBase:w, %a6
- moveal %sp@(12), %a1
- jsr %a6@(-456)
- moveml %sp@+, %a6/%a1
- rts
-
-| void [-108]Alert([d7]long alertNum,[a5]char *flags)
-|
-Alert:
- moveml %d7/%a5-%a6, %sp@-
- moveal ExecBase:w, %a6
- movel %sp@(16), %d7
- moveal %sp@(20), %a5
- jsr %a6@(-108)
- moveml %sp@+, %a6-%a5/%d7
- rts
+++ /dev/null
-OUTPUT_FORMAT("binary", "binary", "binary")
-OUTPUT_ARCH(m68k)
-
-ENTRY(_start)
-
-SECTIONS {
- . = 0x0020b660;
- .text : {
- *(.text)
- *(.rodata)
- *(.data)
- *(.bss)
- }
-}
+++ /dev/null
-#!/bin/sh
-
-# $Id: clean.sh,v 1.2 2004/01/20 20:56:20 mmondor Exp $
-
-. ../../../generic_makedefs.sh
-
-show $L_RM image.bin image.o init.o
-show $L_RM tools/aosbblock tools/dumpkern tools/config
-show $L_RM bootf/bootf_s.o bootf/bootf_c.o bootf/bootf.o bootf/bootf.bin
-show $L_RM bootf/kern.bin xisop.adf
+++ /dev/null
-/* $Id: config.h,v 1.1 2004/01/20 20:56:20 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* Until proper memory auto-detection is made, this file contains the general
- * information needed to setup the supervisor stack and kernel image location,
- * as well as the available RAM.
- * This currently assumes that 4 megs of FAST RAM are located from
- * 0x00200000 to 0x005fffff (Card 1, Zorro II), and that 2 megs of CHIP RAM
- * are found from 0x00001000 to 0x001fffff.
- * All these numbers should be a multiple of 4096 bytes.
- */
-
-
-
-/* CONFIGURATION */
-
-/* Size of kernel image to read from disk, in bytes */
-#define KERNSIZE 65536
-
-/* Size of kernel supervisor stack, in bytes */
-#define STACKSIZE 8192
-
-/* Boundaries of FAST RAM */
-#define FMEM_START 0x00200000
-#define FMEM_END 0x00600000
-
-/* Boundaries of CHIP RAM */
-#define CMEM_START 0x00001000
-#define CMEM_END 0x00200000
-
-
-/* These are useful results, used by boot/bootf/bootf_c.c, boot/init.c
- * and boot/tools/config.c
- */
-#define STACKADDR (FMEM_END - STACKSIZE)
-#define KERNADDR (STACKADDR - KERNSIZE)
-#define FPOOLADDR (FMEM_START)
-#define FPOOLSIZE (KERNADDR - FMEM_START - 1)
-#define CPOOLADDR (CMEM_START)
-#define CPOOLSIZE (CMEM_END - CMEM_START - 1)
+++ /dev/null
-OUTPUT_FORMAT("binary", "binary", "binary")
-OUTPUT_ARCH(m68k)
-
-ENTRY(_start)
-
-SECTIONS {
- .text : {
- *(.text)
- *(.rodata)
- *(.data)
- *(.bss)
- }
-}
+++ /dev/null
-/* $Id: init.c,v 1.7 2004/01/20 21:26:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* This code entry point consist of _start() which is called by the boot
- * loader. We setup the supervisor stack pointer and then jump to _init(),
- * which sets up the various port-specifics for Xisop, and finally launches
- * Xisop itself calling main(). See the Xisop documentation for more
- * information on what port-specific code has to provide in it's
- * initialization and support.h.
- */
-
-
-
-#include <common/types.h>
-#include <processors/m68k/support.h>
-#include <ports/amiga/support.h>
-#include <common/kernel/main.h>
-#include <common/kernel/memory.h>
-#include <common/kernel/exception.h>
-#include <common/kernel/syscall.h>
-#include <common/kernel/task.h>
-#include <common/kernel/port.h>
-
-#include "config.h"
-
-
-
-/* Entry point */
-void _start(u_int32_t *, size_t);
-
-/* Our initialization functions */
-static void _init(void);
-static void _disable_interrupts(void);
-static void _stop_floppy_motor(void);
-static void _init_memory(void);
-static void _init_syscalls(void);
-static void _enable_keyboard_interrupt(void);
-static void _enable_vblank_interrupt(void);
-static void _init_scheduler(void);
-static void _init_screen(void);
-
-/* C exception handlers */
-void _icatch1(u_int16_t);
-void _icatch2(u_int16_t);
-void _icatch3(u_int16_t);
-void _icatch4(u_int16_t);
-void _icatch5(u_int16_t);
-void _tcatch(int);
-void _ecatch(int);
-
-
-/* Port-specific port-initialization, to attach our port-specific shared
- * libraries and launch our port-specific tasks
- */
-struct initmsg {
- message_t node;
- u_int16_t color;
-};
-
-struct taskarg {
- u_int16_t color;
- u_int32_t rounds;
-};
-
-static int _task_color_server(void *, void *);
-static int _task_color_client(void *, void *);
-static void _keyboard_hook(hookid_t, int, void *);
-void _panic(u_int16_t);
-
-
-
-/* Used to ensure that only the first exception occurs */
-static _rlock_t elock;
-
-
-
-/* The role of this function is to go in supervisor mode, set the supervisor
- * stack, then call init().
- */
-void _start(u_int32_t *ssp, size_t sz) {
- _supervisor(ssp, sz, _init);
- /* NOTREACHED */
-}
-
-
-/* Main port-specific initialization */
-static void _init(void)
-{
- /* So that ecatch() cannot recurse */
- _rlock_init(&elock);
-
- _disable_interrupts();
- _stop_floppy_motor();
-
- memory_init(); /* MI function */
- _init_memory();
-
- facilities_init();
-
- _init_exceptions();
-
- _init_syscalls();
-
- _enable_keyboard_interrupt();
- _enable_vblank_interrupt();
- _init_scheduler();
- _init_screen();
-
- /* It is necessary to call this other MI function now, which internally
- * will enable interrupts when safe to do so, which should have been
- * disabled all along initialization.
- */
- xisop_init();
-
- /* We finally can switch to usermode and jump to MI main().
- * _usermode() m68k-specific function uses 1024 bytes on the
- * current supervisor stack to create a user stack, and jumps to
- * the specified function in user mode using that small stack.
- */
- _usermode(main);
-
- /* NOTREACHED */
-}
-
-
-static void _disable_interrupts(void)
-{
- /* Turn off scheduling, interrupts and DMA channels */
- Forbid();
- /* Disable interrupts */
- _splhigh();
- CUSTOM->INTENA = INTENA_CLRALL;
- CUSTOM->INTREQ = INTREQ_CLRALL;
- /* Disable DMA */
- CUSTOM->DMACON = DMACON_CLRALL;
- /* Disable CIA interrupt generation */
- CIA_A->ICR = CIA_ICR_CLRALL;
- CIA_B->ICR = CIA_ICR_CLRALL;
-}
-
-
-static void _stop_floppy_motor(void)
-{
- /* Stop floppy0 drive motor */
- CIA_B->PRB &= ~CIAB_PRB_FLOP0; /* Select */
- CIA_B->PRB |= CIAB_PRB_FMOTOR; /* Command */
-}
-
-
-static void _init_memory(void)
-{
- mchunk_t *mchunk;
-
- /* Initialize memory system, facilities_init() needs this to be done.
- * XXX Some effort should be made to better detect the available memory,
- * but at least it's easy to fix for each system type right now.
- * The file to modify consists of ../config.h
- */
- mchunk = mchunk_init((void *)FPOOLADDR, FPOOLSIZE);
- mchunk_attach(_MEM_FAST, mchunk);
- mchunk = mchunk_init((void *)CPOOLADDR, CPOOLSIZE);
- mchunk_attach(_MEM_CHIP, mchunk);
-}
-
-
-static void _init_syscalls(void)
-{
- CUSTOM->INTENA = INT_SETCLR | INT_SOFT;
-}
-
-
-static void _enable_keyboard_interrupt(void)
-{
- /* Enable keyboard interrupts */
- CIA_A->CRA = CIA_CRA_START | CIA_CRA_INMODE;
- CIA_A->ICR = CIA_ICR_SETCLR | CIA_ICR_SERDONE;
- CUSTOM->INTENA = INT_SETCLR | INT_CIAA;
-}
-
-
-static void _enable_vblank_interrupt(void)
-{
- /* Enable vertical blank interrupt */
- CUSTOM->INTENA = INT_SETCLR | INT_VBLANK;
-}
-
-
-/* Enable CIA-B TimerA interrupt for scheduler, which has high priority */
-static void _init_scheduler(void)
-{
- u_int8_t *p;
- u_int16_t v;
-
- /* Evaluate latch value to use for scheduler frequency */
- v = (u_int16_t)(CIA_COUNTSPEED / SCHEDTIMER_HZ);
- p = (u_int8_t *)&v;
- /* Stop timer and set latch */
- CIA_B->CRA = 0x00;
- CIA_B->TAHI = p[0];
- CIA_B->TALO = p[1];
- /* Start and order to load latch value, in contiguous mode */
- CIA_B->CRA = CIA_CRA_START | CIA_CRA_LOAD;
- CIA_B->ICR = CIA_ICR_SETCLR | CIA_ICR_TIMA0;
- /* Enable CIA-B interrupts */
- CUSTOM->INTENA = INT_SETCLR | INT_CIAB;
-}
-
-
-/* Setup our Amiga Xisop console screen */
-/* XXX I have two choices here. I could use a 16 color screen and emulate
- * ANSI-BBS colors, or just use a two color one and use my very small fonts.
- * in the second case, I could still support a few control codes.
- */
-static void _init_screen(void)
-{
- u_int16_t s;
-
- /* I need to allocate/initialize a bitmap/bitplane, as well as
- * chip memory for the copper list.
- */
- s = asplvblank();
- /* Screen size and position configuration */
- CUSTOM->DDFSTRT = 0x3C;
- CUSTOM->DDFSTOP = 0xD4;
- CUSTOM->DIWSTRT = 0x2C81;
- CUSTOM->DIWSTOP = 0xF4C1;
- CUSTOM->CLXCON = 0x0000;
- /* Enable bitplane and copper DMA */
- CUSTOM->DMACON = DMACON_SETCLR | DMACON_BLITTER /*| DMACON_COPPER*/;
- asplx(s);
-}
-
-
-/* These consist of the various interrupt handlers corresponding to each
- * of the 6 maskable hardware interrupt levels. Using _spl*() one can set the
- * current task level which may not be interrupted by every lower level.
- * <intreq> may be used to evaluate the cause of the interrupt.
- */
-
-/* ARGSUSED */
-void _icatch1(u_int16_t intreq)
-{
- _ipl_t x;
-
- x = _spl1();
- if (intreq & INT_SOFT) {
- /* Software interrupt */
- }
- if (intreq & INT_FLOPBLCK) {
- /* Floppy disk block done interrupt */
- facility_exechooks(_FACILITY_FLOPPYBLOCK, 0);
- }
- if (intreq & INT_SERTBE) {
- /* Serial RS-232 trasmit buffer empty interrupt */
- facility_exechooks(_FACILITY_SERIALTBE, 0);
- }
- _splx(x);
-}
-
-void _icatch2(u_int16_t intreq)
-{
- _ipl_t x;
-
- x = _spl2();
- if (intreq & INT_CIAA) {
- /* CIA-A or expansion bus pin 19 interrupt */
- u_int8_t icrmask = CIA_A->ICR;
-
- /* CIA-A TimerA available.
- * CIA-A TimerB used for keyboard.
- * CIA-A TOD counter available.
- */
- if (icrmask & CIA_ICR_TIMB0) {
- /* CIA-A TimerB reached 0. If used in continuous we only need
- * to react, otherwise we also should reload a latch value back.
- * This counts at a rate of 715909/second for NTSC and
- * 709379/second for PAL.
- */
- facility_exechooks(_FACILITY_CIATIMB0, 0);
- }
- if (icrmask & CIA_ICR_SERDONE) {
- /* Keyboard intput interrupt */
- u_int8_t c;
-
- c = CIA_A->SDR;
- facility_exechooks(_FACILITY_KEYBOARD, (int)c);
- }
- }
- _splx(x);
-}
-
-void _icatch3(u_int16_t intreq)
-{
- _ipl_t x;
-
- x = _spl3();
- if (intreq & INT_COPPER) {
- /* Copper processor generated interrupt */
- facility_exechooks(_FACILITY_COPPER, 0);
- }
- if (intreq & INT_VBLANK) {
- /* Vertical blank (raster) interrupt */
- facility_exechooks(_FACILITY_VBLANK, 0);
- }
- if (intreq & INT_BLITRDY) {
- /* Blitter done/ready interrupt */
- facility_exechooks(_FACILITY_BLITTERREADY, 0);
- }
- _splx(x);
-}
-
-/* ARGSUSED */
-void _icatch4(u_int16_t intreq)
-{
- _ipl_t x;
-
- x = _spl4();
- if (intreq & INT_AUDIO0) {
- /* DMA done for audio channel 0 interrupt */
- facility_exechooks(_FACILITY_AUDIO, 0);
- }
- if (intreq & INT_AUDIO1) {
- /* DMA done for audio channel 1 interrupt */
- facility_exechooks(_FACILITY_AUDIO, 1);
- }
- if (intreq & INT_AUDIO2) {
- /* DMA done for audio channel 2 interrupt */
- facility_exechooks(_FACILITY_AUDIO, 2);
- }
- if (intreq & INT_AUDIO3) {
- /* DMA done for audio channel 3 interrupt */
- facility_exechooks(_FACILITY_AUDIO, 3);
- }
- _splx(x);
-}
-
-/* ARGSUSED */
-void _icatch5(u_int16_t intreq)
-{
- _ipl_t x;
-
- x = _spl5();
- if (intreq & INT_SERRBF) {
- /* Serial RS-232 read buffer full interrupt */
- facility_exechooks(_FACILITY_SERIALRBF, 0);
- }
- if (intreq & INT_FLOPSYNC) {
- /* Floppy disk sync pattern found interrupt */
- facility_exechooks(_FACILITY_FLOPPYSYNC, 0);
- }
- _splx(x);
-}
-
-/* Level 6 handled by assembly code */
-
-
-/* And generic C trap handler to catch remaining trap vectors (0 and 1 are
- * used by the system calls and _yield(), respectively.
- */
-void _tcatch(int vector)
-{
- _ipl_t x;
-
- x = _splhigh(); /* XXX */
- facility_exechooks(_FACILITY_TRAP, vector);
- _splx(x);
-}
-
-
-/* A hack to display the exception vector number using raster lines */
-void _ecatch(int vector)
-{
- static u_int16_t ecolors[2] = {0x0F00, 0x0A00};
- register bool col = FALSE;
-
- if (_rlock_try(&elock)) {
- _splhigh();
- for (;;) {
- register int i;
-
- for (i = 0; i < vector; i++) {
- CUSTOM->COLOR[0] = ecolors[col];
- ldelay(2);
- CUSTOM->COLOR[0] = 0x0000;
- ldelay(2);
- }
- ldelay(8);
- col = !col;
- }
- }
- for (;;)
- _idle();
-}
-
-
-
-/* Function we supply to Xisop which calls it to allow us to initialize
- * our port-specific shared libraries and tasks.
- */
-void _port_init(void)
-{
- register task_t *task;
-
- if ((task = task_alloc(_task_color_server, NULL, NULL, 0, 4096, TF_KERNEL))
- != NULL)
- task_start(task);
- else
- _panic(0x0F00); /* Red */
-}
-
-
-
-/* These are our port-specific tasks */
-
-/* ARGSUSED */
-static int _task_color_server(void *res, void *args)
-{
- port_t *port;
-
- /* Create our public port */
- if ((port = port_create("COLOR")) != NULL) {
- struct taskarg targ[3];
- task_t *task;
- hookid_t kbdhook;
-
- /* Setup an interrupt handler (only for fun) */
- kbdhook = hook_attach(_FACILITY_KEYBOARD, 10, 0, _keyboard_hook, NULL);
-
- /* Start our tasks. As these require our port to communicate through,
- * note that we created it first.
- */
- targ[0].color = 0x00F0;
- targ[0].rounds = 6000;
- if ((task = task_alloc(_task_color_client, NULL, &targ[0], 0, 4096,
- TF_SHARED)) != NULL)
- task_start(task);
- targ[1].color = 0x00C0;
- targ[1].rounds = 4000;
- if ((task = task_alloc(_task_color_client, NULL, &targ[1], 0, 4096,
- TF_SHARED)) != NULL)
- task_start(task);
- targ[2].color = 0x0090;
- targ[2].rounds = 2000;
- if ((task = task_alloc(_task_color_client, NULL, &targ[2], 0, 4096,
- TF_SHARED)) != NULL)
- task_start(task);
-
- /* Main loop. All we do is listen for requests via our port, in the
- * form of messages. When we get one, display the requested color,
- * and reply. This task should be in STATE_WAIT most of the time,
- * and only awakens to answer requests then goes back to sleep.
- */
- for (;;) {
- port_t *ports[] = {
- port
- };
- register struct initmsg *msg = NULL;
-
- /* Black to show that we are sleeping */
- CUSTOM->COLOR[0] = 0x0000;
- ldelay(1);
- if ((port_wait(ports, 1, NULL)) == port) {
- /* We could use while () here instead of if (), however this
- * gives a better demonstration. When the screen eventually
- * gets purple, all tasks are sleeping (init, reaper and
- * this task, since our three children have died already).
- * If we use while (), we never go in sleep mode, since there
- * are several feeders and they are of equal priority than
- * us.
- */
- if ((msg = (struct initmsg *)port_get(port)) != NULL) {
- CUSTOM->COLOR[0] = msg->color;
- if (!port_reply((message_t *)msg))
- _panic(0x0F00); /* Red */
- }
- } else
- _panic(0x0FF0); /* Yellow */
- }
-
- port = port_destroy(port);
- }
-
- _panic(0x0FFF); /* White */
- /* NOTREACHED */
- return 0;
-}
-
-
-/* ARGSUSED */
-static int _task_color_client(void *res, void *args)
-{
- port_t *rport;
-
- /* Create our reply port */
- if ((rport = port_create(NULL)) != NULL) {
- port_t *sport;
-
- /* Locate task_init()'s public port */
- if ((sport = port_find("COLOR")) != NULL) {
- struct initmsg msg;
- port_t *ports[] = {
- rport
- };
- struct taskarg *targ = args;
- register u_int32_t rounds = targ->rounds;
-
- msg.color = targ->color;
- /* Send coloring requests via the public COLOR port.
- * For each request we wait for a reply, and then continue.
- * This task runs most of the time, only sleeping when waiting
- * for a result from the slave task.
- */
- for (;rounds > 0; rounds--) {
- if (port_send(sport, rport, (message_t *)&msg)) {
- if ((port_wait(ports, 1, NULL)) == rport) {
- /* Just get rid of messages since we don't need to
- * verify a result code.
- */
- port_flush(rport);
- } else
- _panic(0x0FF0); /* Yellow */
- } else
- _panic(0x0F00); /* Red */
- }
- }
-
- rport = port_destroy(rport);
- }
-
- return 0;
-}
-
-
-/* Used in the case of a fatal error we catch */
-void _panic(u_int16_t color)
-{
- sched_disable();
- sys_int_disable();
- for (;;) {
- CUSTOM->COLOR[0] = color;
- ldelay(2);
- CUSTOM->COLOR[0] = 0x0000;
- ldelay(2);
- }
- /* NOTREACHED */
-}
-
-
-/* Mainly used as a test for now */
-/* ARGSUSED */
-static void _keyboard_hook(hookid_t hookid, int reason, void *udata)
-{
- register u_int16_t col = 0;
-
- /* Just display a color corresponding to the keyboard code */
- col |= reason; col <<= 8; col |= reason;
- CUSTOM->COLOR[0] = col;
-}
+++ /dev/null
-#!/bin/sh
-
-# $Id: make.sh,v 1.2 2004/01/20 20:56:20 mmondor Exp $
-
-. ../../../makedefs.sh
-
-# Create tools
-show $L_CC -Wall -o tools/aosbblock tools/aosbblock.c
-show $L_CC -Wall -o tools/dumpkern tools/dumpkern.c
-show $L_CC -Wall -o tools/config tools/config.c
-KERNADDR=$(tools/config)
-
-# Create kernel image
-show $C_COMPC -I$SRCDIR init.c
-A=`$L_LS ../../../common/kernel/ar/*.a ../ar/*.a ../../../processor/ar/*.a ../../../common/kernlib/ar/*.a`
-show $C_LD -nostdlib -e _start -Ttext $KERNADDR -o image.o init.o $A
-show $C_LD -T image_script.ld -Ttext $KERNADDR -nostdlib -o image.bin init.o $A
-
-# Create bootblock loader
-show $C_COMPS -I$SRCDIR -o bootf/bootf_s.o bootf/bootf_s.s
-show $C_COMPC -I$SRCDIR -o bootf/bootf_c.o bootf/bootf_c.c
-show $C_LD -nostdlib -r -Ttext 0x0020b660 -o bootf/bootf.o bootf/bootf_s.o bootf/bootf_c.o
-show $C_LD -T bootf/bootf_script.ld -nostdlib -o bootf/bootf.bin bootf/bootf.o
-show tools/aosbblock bootf/bootf.bin
-
-# Create bootable floppy image
-show $L_CAT bootf/bootf.bin image.bin >bootf/kern.bin
-show $L_DD if=/dev/zero of=xisop.adf bs=901120 count=1
-show tools/dumpkern xisop.adf bootf/kern.bin
-
-$L_ECHO ===
-$L_ECHO The floppy image should be $SRCDIR/ports/amiga/boot/xisop.adf
-$L_ECHO ===
+++ /dev/null
-/* $Id: aosbblock.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Fixes a bootblock to be 1024 bytes in size, and sets it's AmigaOS checksum
- */
-
-
-
-#include <sys/types.h>
-#include <stdio.h>
-
-
-
-int main(int, char **);
-u_int32_t checksum(u_int32_t *, int);
-
-
-
-int main(int argc, char **argv)
-{
- FILE *fh;
- u_int32_t lblock[256];
- int i;
- char *block = (char *)lblock;
-
- if (argc == 2) {
-
- if ((fh = fopen(argv[1], "rb"))) {
- /* First make sure to 0 pad block, then load block */
- for (i = 0; i < 256; i++)
- lblock[i] = 0;
- fread(block, 1, 1024, fh);
- fclose(fh);
- } else {
- printf("could not open file \"%s\" for reading\n", argv[1]);
- exit(-1);
- }
-
- /* Calculate block checksum and fix it */
- lblock[1] = 0;
- lblock[1] = htonl(0xFFFFFFFF - checksum(lblock, 256));
-
- /* Write back file over old one */
- if ((fh = fopen(argv[1], "wb"))) {
- fwrite(block, 1, 1024, fh);
- fclose(fh);
- } else {
- printf("could not open file \"%s\" for writing\n", argv[1]);
- exit(-1);
- }
-
- } else {
- printf("usage: aosbblock <file>\n");
- exit(-1);
- }
-
- exit(0);
-}
-
-
-u_int32_t checksum(u_int32_t *block, int size)
-{
- u_int32_t sum, lastsum;
- int i;
-
- for (sum = 0, i = 0; i < size; i++) {
- lastsum = sum;
- sum += ntohl(block[i]);
- if (sum < lastsum)
- ++sum;
- }
-
- return sum;
-}
+++ /dev/null
-/* $Id: config.c,v 1.1 2004/01/20 20:56:20 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* This program is useful to know the proper address to use in image_script.ld
- * and make.sh for building the kernel image.
- */
-
-
-
-#include <stdio.h>
-#include "../config.h"
-
-
-
-int main(void);
-
-
-
-int main(void)
-{
- printf("0x%08x\n", KERNADDR);
- /*
- printf("Supervisor stack location (0x%08x bytes): 0x%08x\n",
- STACKSIZE, STACKADDR);
- printf("Kernel image location (0x%08x bytes): 0x%08x\n",
- KERNSIZE, KERNADDR);
- printf("Usable multipurpose RAM: (0x%08x bytes) 0x%08x\n",
- POOLSIZE, POOLADDR);
- */
-
- return 0;
-}
+++ /dev/null
-/* $Id: dumpkern.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* Simple program to allow to dump a kernel image over an existing file
- * (ususally to serve as a virtual floppy for bochs or to create
- * floppy image files), without changing the size of the file.
- */
-
-
-
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdio.h>
-
-
-
-#define BUFSIZE 4096
-
-
-
-int main(int, char **);
-
-
-
-int main(int argc, char **argv)
-{
- if (argc == 3) {
- int ffd, kfd;
-
- if ((ffd = open(argv[1], O_RDWR)) != -1) {
- if ((kfd = open(argv[2], O_RDONLY)) != -1) {
- char buf[BUFSIZE];
- size_t len;
-
- while ((len = read(kfd, buf, BUFSIZE)) > 0)
- if ((write(ffd, buf, len)) != len) {
- fprintf(stderr, "Error writing\n");
- break;
- }
- close(kfd);
- } else fprintf(stderr, "Cannot open kernel file '%s'\n", argv[2]);
- close(ffd);
- } else fprintf(stderr, "Cannot open disk file '%s'\n", argv[1]);
- } else fprintf(stderr, "Usage: dumpkern <file> <kernel>\n");
-
- exit(0);
-}
+++ /dev/null
-#!/bin/sh
-
-# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../generic_makedefs.sh
-
-show $L_RM support_s.o support_c.o ar/*.a
+++ /dev/null
-#!/bin/sh
-
-# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../makedefs.sh
-
-show $C_COMPS -I$SRCDIR support_s.s
-show $C_COMPC -I$SRCDIR support_c.c
-show $C_AR ar/support.a support_s.o support_c.o
-show $C_RANLIB ar/support.a
+++ /dev/null
-#!/bin/sh
-
-# $Id: makedefs.sh,v 1.2 2003/12/27 00:01:55 mmondor Exp $
-
-# These are various defaults which are used for the building process.
-# Change as required, and don't forget to create the ./port and ./processor
-# symbolic links to their respective directory.
-
-# Local non-cross building tools
-L_AR='ar qS'
-L_AS='as'
-L_CC='gcc'
-L_LD='ld'
-L_NM='nm'
-L_OBJDUMP='objdump'
-L_RANLIB='ranlib'
-L_STRIP='strip'
-L_CAT='cat'
-L_DD='dd'
-
-# Cross building tools (set to same values as L_* if wanted)
-C_PREFIX='/usr/src/tools/bin'
-C_AR="$C_PREFIX/m68k--netbsdelf-ar qS"
-C_AS="$C_PREFIX/m68k--netbsdelf-as"
-C_CC="$C_PREFIX/m68k--netbsdelf-gcc"
-C_LD="$C_PREFIX/m68k--netbsdelf-ld"
-C_NM="$C_PREFIX/m68k--netbsdelf-nm"
-C_OBJDUMP="$C_PREFIX/m68k--netbsdelf-objdump"
-C_RANLIB="$C_PREFIX/m68k--netbsdelf-ranlib"
-C_STRIP="$C_PREFIX/m68k--netbsdelf-strip"
-
-# Other general purpose tools
-L_RM='rm -f'
-L_LN='ln -s'
-L_SED='sed'
-L_ECHO='echo'
-L_LS='ls'
-
-# Command to compile a C module using the cross-compiler
-#C_COMPC="$C_CC -Wall -ffreestanding -nostdinc -m68000 -O2 -fno-function-cse -fomit-frame-pointer -c"
-C_COMPC="$C_CC -Wall -ffreestanding -nostdinc -m68000 -O2 -fno-function-cse -fomit-frame-pointer -c"
-# And to compile an assembly module using the cross-assembler
-C_COMPS="$C_CC -Wall -ffreestanding -nostdinc -m68000 -c"
-
-show()
-{
- # Only output to stderr, not stdout
- $L_ECHO "$@" >&2
- $@
-}
-
-# Useful to compile all .c and .s files in a directory to .o modules
-# $1 = directory
-buildlib()
-{
- for i in `$L_LS $1/*.s 2>/dev/null`; do
- DEST=`$L_ECHO $i | $L_SED 's/\.s/\.o/'`
- show $C_COMPS -I$SRCDIR -o $DEST $i
- done
- for i in `$L_LS $1/*.c 2>/dev/null`; do
- DEST=`$L_ECHO $i | $L_SED 's/\.c/\.o/'`
- show $C_COMPC -I$SRCDIR -o $DEST $i
- done
-}
-
-# Deletes all .o files in a directory
-# $1 = directory
-cleanlib()
-{
- show $L_RM $1/*.o
-}
+++ /dev/null
-/* $Id: support.h,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2001-2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * This code was not derived from original AmigaOS headerfiles, but rather
- * made comparing informations from various books I already had at home,
- * such as "Mapping the Amiga", "La Bible de l'Amiga", and "Amiga Intern".
- *
- * Matt
- */
-
-
-
-#ifndef AMIGA_SUPPORT_H
-#define AMIGA_SUPPORT_H
-
-
-
-#include <common/types.h>
-
-
-
-/* Adapt this as required for NTSC or PAL Amigas */
-
-/* CIA TimerA and TimerB count this amount of ticks per second */
-/* NTSC */
-/*#define CIA_COUNTSPEED 715909*/
-/* PAL */
-#define CIA_COUNTSPEED 709379
-
-
-
-/* The Amiga has two CIA chips, each CIA supports two 8-bit parallel ports
- * and one serial port. Each CIA chip also has two timers. Amiga also has
- * various custom chips (Agnus, Denise, Paula, Ramsey, the copper parallel
- * coprocessor)...
- *
- * The use of the CIA timers and counters is as follows:
- * - CIA-A TOD counter available
- * - CIA-A TimerA used for keyboard timing
- * - CIA-A TimerB available
- * - CIA-B TOD counter available
- * - CIA-B TimerA available, we use it for the Xisop scheduler interrupt.
- * - CIA-B TimerB available
- *
- * Xisop kernel itself may not need to use all those registers, however
- * devices may, and will be able to use this same headerfile.
- */
-
-#define CIA_A ((volatile CIA *)0x00BFE001)
-#define CIA_B ((volatile CIA *)0x00BFD000)
-#define CUSTOM ((volatile CUST *)0x00DFF000)
-
-
-/* Each of the two CIAs have this structure (8520 chip as mapped in Amiga) */
-typedef struct CIA {
- volatile u_int8_t PRA; /* Parallel port register A */
- u_int8_t pad1[255];
- volatile u_int8_t PRB; /* Parallel port register B */
- u_int8_t pad2[255];
- volatile u_int8_t DDRA; /* Parallel port A data direction register */
- u_int8_t pad3[255];
- volatile u_int8_t DDRB; /* Parallel port B data direction register */
- u_int8_t pad4[255];
- volatile u_int8_t TALO; /* Timer A lower 8 bits */
- u_int8_t pad5[255];
- volatile u_int8_t TAHI; /* Timer A upper 8 bits */
- u_int8_t pad6[255];
- volatile u_int8_t TBLO; /* Timer B lower 8 bits */
- u_int8_t pad7[255];
- volatile u_int8_t TBHI; /* Timer B upper 8 bits */
- u_int8_t pad8[255];
- volatile u_int8_t E_LSB; /* Event counter bits 0-7 */
- u_int8_t pad9[255];
- volatile u_int8_t E_MID; /* Event counter bits 8-15 */
- u_int8_t pad10[255];
- volatile u_int8_t E_MSB; /* Event counter bits 16-23 */
- u_int8_t pad11[255 + 256];
- volatile u_int8_t SDR; /* Serial port data register */
- u_int8_t pad12[255];
- volatile u_int8_t ICR; /* Interrupt control register */
- u_int8_t pad13[255];
- volatile u_int8_t CRA; /* Control register A */
- u_int8_t pad14[255];
- volatile u_int8_t CRB; /* Control register B */
- u_int8_t pad15[255];
-} CIA;
-
-/* Here are defined common CIA register bits for code clarity */
-#define CIA_ICR_TIMA0 (1 << 0)
-#define CIA_ICR_TIMB0 (1 << 1)
-#define CIA_ICR_TODALRM (1 << 2)
-#define CIA_ICR_SERDONE (1 << 3)
-#define CIA_ICR_SIGFLAG (1 << 4)
-#define CIA_ICR_CIAINT (1 << 7)
-#define CIA_ICR_SETCLR (1 << 7)
-#define CIA_ICR_CLRALL 0x7F
-
-#define CIA_CRA_START (1 << 0)
-#define CIA_CRA_PBON (1 << 1)
-#define CIA_CRA_OUTMODE (1 << 2)
-#define CIA_CRA_RUNMODE (1 << 3)
-#define CIA_CRA_LOAD (1 << 4)
-#define CIA_CRA_INMODE (1 << 5)
-#define CIA_CRA_SPMODE (1 << 6)
-
-#define CIA_CRB_START (1 << 0)
-#define CIA_CRB_PBON (1 << 1)
-#define CIA_CRB_OUTMODE (1 << 2)
-#define CIA_CRB_RUNMODE (1 << 3)
-#define CIA_CRB_LOAD (1 << 4)
-#define CIA_CRB_INMODE (1 << 5 | 1 << 6)
-#define CIA_CRB_ALARM (1 << 7)
-
-/* These are Amiga-specific and are different for each of the two CIAs */
-#define CIAA_PRA_MOVL (1 << 0) /* Memory overlay bit */
-#define CIAA_PRA_LED (1 << 1) /* Pwr LED & aud low pass filt stat */
-#define CIAA_PRA_FCHNG (1 << 2) /* Floppy disk change */
-#define CIAA_PRA_FRONLY (1 << 3) /* Floppy read-only */
-#define CIAA_PRA_FTRK0 (1 << 4) /* Floppy disk track 0 */
-#define CIAA_PRA_FRDY (1 << 5) /* Floppy disk ready for commands */
-#define CIAA_PRA_FIRE0 (1 << 6) /* Button 1 Port 1 pressed */
-#define CIAA_PRA_FIRE1 (1 << 7) /* Button 1 port 2 pressed */
-
-#define CIAA_PRB_CDATA 0xFF /* Centronics data pins/bits */
-
-#define CIAB_PRA_PBUSY (1 << 0) /* Centronics BUSY */
-#define CIAB_PRA_POUT (1 << 1) /* Centronics paper out */
-#define CIAB_PRA_PSEL (1 << 2) /* Centronics select */
-#define CIAB_PRA_SDSR (1 << 3) /* RS-232 Data Set Ready */
-#define CIAB_PRA_SCTS (1 << 4) /* RS-232 Clear To Send */
-#define CIAB_PRA_SDCD (1 << 5) /* RS-232 Carrier Detect */
-#define CIAB_PRA_SRTS (1 << 6) /* RS-232 Request To Send */
-#define CIAB_PRA_SDTR (1 << 7) /* RS-232 Data Terminal Ready */
-
-#define CIAB_PRB_FHSTEP (1 << 0) /* Floppy head step */
-#define CIAB_PRB_FHDIR (1 << 1) /* Floppy head direction */
-#define CIAB_PRB_FSIDE (1 << 2) /* Floppy side */
-#define CIAB_PRB_FLOP0 (1 << 3) /* Select Floppy drive 0 */
-#define CIAB_PRB_FLOP1 (1 << 4) /* Select Floppy drive 1 */
-#define CIAB_PRB_FLOP2 (1 << 5) /* Select Floppy drive 2 */
-#define CIAB_PRB_FLOP3 (1 << 6) /* Select Floppy drive 3 */
-#define CIAB_PRB_FMOTOR (1 << 7) /* Floppy motor status */
-
-
-/* Useful union to represent 32-bit address split into two 16-bit registers */
-typedef union AADDR {
- struct {
- void *addr; /* Always 32-bit on m68k */
- } full;
- struct {
- u_int16_t addr_h; /* Bits 16-31 */
- u_int16_t addr_l; /* Bits 0-15 */
- } parial;
-} AADDR;
-
-/* Each of the 4 native 8-bit Amiga channels are controled with this */
-typedef struct AUDCHAN {
- AADDR AUDLC; /* Address of audio data */
- u_int16_t AUDLEN; /* Length of audio data */
- u_int16_t AUDPER; /* Period duration */
- u_int16_t AUDVOL; /* Channel volume */
- u_int16_t AUDDAT; /* Audio data (to D/A converter) */
- u_int16_t pad1[2]; /* Unused */
-} AUDCHAN;
-
-/* Sprite information */
-typedef struct SPR {
- u_int16_t SPRPOS; /* Sprite start position (vert. and horiz.) */
- u_int16_t SPRCTL; /* Control register and vertical stop */
- u_int16_t SPRDATA; /* Data register A (to RGB output) */
- u_int16_t SPRDATB; /* Data register B (to RGB output) */
-} SPR;
-
-
-/* And, the Amiga custom chips registers. */
-typedef struct CUST {
- u_int16_t BLTDDAT; /* Blitter output data (Blitter to RAM) */
- u_int16_t DMACONR; /* Read DMA control register */
- u_int16_t VPOSR; /* MSB of vertical position */
- u_int16_t VHPOSR; /* Vertical and horizontal beam position */
- u_int16_t DSKDATR; /* Dist read data (disk to RAM) */
- u_int16_t JOY0DAT; /* Joystick/Mouse position game port 0 */
- u_int16_t JOY1DAT; /* Joystick/Mouse position game port 1 */
- u_int16_t CLXDAT; /* Collision register */
- u_int16_t ADKCONR; /* Read audio/disk control register */
- u_int16_t POT0DAT; /* Read potentiometer game port 0 */
- u_int16_t POT1DAT; /* Read potentiometer game port 1 */
- u_int16_t POTGOR; /* Read potentiometer port data */
- u_int16_t SERDATR; /* Read serial port and status */
- u_int16_t DSKBYTR; /* Read disk data byte and status */
- u_int16_t INTENAR; /* Read interrupt enable */
- u_int16_t INTREQR; /* Read interrupt request */
- AADDR DSKPTR; /* Disk DMA address */
- u_int16_t DSKLEN; /* Disk DMA block length */
- u_int16_t DSKDAT; /* Disk write data (RAM to disk) */
- u_int16_t REFPTR; /* Refresh counter */
- u_int16_t VPOSW; /* Write MSB of vertical beam position */
- u_int16_t VHPOSW; /* Write vertical and horizontal beam position */
- u_int16_t COPCON; /* Copper control register */
- u_int16_t SERDAT; /* Write serial data and stop bits */
- u_int16_t SERPER; /* Serial port control register and baud rate */
- u_int16_t POTGO; /* Write potentiometer port data and start bit */
- u_int16_t JOYTEST; /* Write in both mouse counters */
- u_int16_t STREQU; /* Horizontal sync with VBlank and equal frame */
- u_int16_t STRVBL; /* Horizontal sync wuth vertical blank */
- u_int16_t STRHOR; /* Horizontal synchronization signal */
- u_int16_t STRLONG; /* Long horizontal line marker */
- /* Following can also be accessed by the copper processor if COPCON=1 */
- u_int16_t BLTCON0; /* Blitter control register 0 */
- u_int16_t BLTCON1; /* Blitter control register 1 */
- u_int16_t BLTAFWM; /* Mask for first data word from A */
- u_int16_t BLTALWM; /* Mask for last data word from A */
- AADDR BLTCPTR; /* Address of source data C */
- AADDR BLTBPTR; /* Address of source data B */
- AADDR BLTAPTR; /* Address of source data A */
- AADDR BLTDPTR; /* Address of destination data D */
- u_int16_t BLTSIZE; /* Start bit and size of blitter window */
- u_int16_t BLTCON0L; /* Like BLTCON0, bits 0-7 */
- u_int16_t BLTSIZEV; /* Windth of blitter window */
- u_int16_t BLTSIZEH; /* Height of blitter window */
- u_int16_t BLTCMOD; /* Blitter modulo for source data C */
- u_int16_t BLTBMOD; /* Blitter modulo for source data B */
- u_int16_t BLTAMOD; /* Blitter modulo for source data A */
- u_int16_t BLTDMOD; /* Blitter modulo for destination data D */
- u_int16_t pad1[4]; /* Unused */
- u_int16_t BLTCDAT; /* Blitter source data register C */
- u_int16_t BLTBDAT; /* Blitter source data register B */
- u_int16_t BLTADAT; /* Blitter source data register A */
- u_int16_t pad2[3]; /* Unused */
- u_int16_t DENISEID; /* Chip identification from Denise */
- u_int16_t DSKSYNC; /* Disk sync pattern */
- /* The following registers can always be written to by the Copper */
- AADDR COP1LPTR; /* Address of first copper list */
- AADDR COP2LPTR; /* Address of second copper list */
- u_int16_t COPJMP1; /* Jump to start of first copper list */
- u_int16_t COPJMP2; /* Jump to start of second copper list */
- u_int16_t COPINS; /* Copper command register */
- u_int16_t DIWSTRT; /* Upper left corner of display window */
- u_int16_t DIWSTOP; /* Lower right corner of display window */
- u_int16_t DDFSTRT; /* Start of bitplane DMA (horiz. pos.) */
- u_int16_t DDFSTOP; /* End of bitplane DMA (horiz. pos.) */
- u_int16_t DMACON; /* Write DMA control register */
- u_int16_t CLXCON; /* Write collision control register */
- u_int16_t INTENA; /* Write interrupt enable */
- u_int16_t INTREQ; /* Write interrupt request */
- u_int16_t ADKCON; /* Audio, disk abd UART control register */
- AUDCHAN AUDCH[4]; /* Four digital audio channels */
- AADDR BPLPTR[6]; /* Six bitplane addresses */
- u_int16_t pad3[4]; /* Unused */
- u_int16_t BPLCON0; /* Bitplane control register 0 */
- u_int16_t BPLCON1; /* Bitplane control register 1 (scroll values) */
- u_int16_t BPLCON2; /* Bitplane control register 2 (priority control) */
- u_int16_t BPLCON3; /* Bitplane control register 3 */
- u_int16_t BPL1MOD; /* Bitplane modulo for uneven planes */
- u_int16_t BPL2MOD; /* Bitplane modulo for even planes */
- u_int16_t pad4[2]; /* Unused */
- u_int16_t BPLDAT[6];/* Bitplane data (to RGB output) */
- u_int16_t pad5[2]; /* Unused */
- AADDR SPRDATPTR[8]; /* Sprite data registers */
- SPR SPR[8]; /* Sprite control registers */
- u_int16_t COLOR[32];/* Color pallete registers (color table) */
- u_int16_t HTOTAL; /* Clock count per line (VARBEAM=1) */
- u_int16_t HSSTOP; /* H-sync stop position */
- u_int16_t HBSTRT; /* H-blank start position */
- u_int16_t HBSTOP; /* H-blank stop position */
- u_int16_t VTOTAL; /* Number of lines per picture */
- u_int16_t VSSTOP; /* V-sync stop line */
- u_int16_t VBSTRT; /* V-blank start line */
- u_int16_t VBSTOP; /* V-blank stop line */
- u_int16_t SPRHSTRT; /* UHRES sprite start line */
- u_int16_t SPRHSTOP; /* UHRES sprite stop line */
- u_int16_t BPLHSTRT; /* UHRES bitplane start line */
- u_int16_t BPLHSTOP; /* UHRES bitplane stop line */
- u_int16_t HHPOSW; /* Write DUAL-mode column counter */
- u_int16_t HHPOSR; /* Read DUAL-mode column counter */
- u_int16_t BEAMCON0; /* Raster beam control register */
- u_int16_t HSSTRT; /* H-sync start position */
- u_int16_t VSSTRT; /* V-sync start position */
- u_int16_t HCENTER; /* H-pos. of V-sync in interlace mode */
- u_int16_t DIWHIGH; /* Screen window, upper bits for start/stop */
- u_int16_t BPLHMOD; /* UHRES bitplane modulo */
- AADDR SPRHPTR; /* UHRES sprite pointer */
- AADDR BPLHPTR; /* UHRES bitplane pointer */
- u_int16_t pad6[7]; /* Unused */
-} CUST;
-
-/* And again some bits definitions for code clarity */
-#define DMACON_AUDIO0 (1 << 0)
-#define DMACON_AUDIO1 (1 << 1)
-#define DMACON_AUDIO2 (1 << 2)
-#define DMACON_AUDIO3 (1 << 3)
-#define DMACON_AUDIO (1 << 0 | 1 << 1 | 1 << 2 | 1 << 3)
-#define DMACON_FLOPPY (1 << 4)
-#define DMACON_SPRITE (1 << 5)
-#define DMACON_BLITTER (1 << 6)
-#define DMACON_COPPER (1 << 7)
-#define DMACON_BITPLANE (1 << 8)
-#define DMACON_MASTER (1 << 9)
-#define DMACON_PRIORITY (1 << 10)
-#define DMACON_ZEROS (1 << 13)
-#define DMACON_BLITTING (1 << 14)
-#define DMACON_SETCLR (1 << 15)
-#define DMACON_CLRALL 0x07FF
-
-#define ADKCON_0MODVOL1 (1 << 0)
-#define ADKCON_1MODVOL2 (1 << 1)
-#define ADKCON_2MODVOL3 (1 << 2)
-#define ADKCON_3OFF (1 << 3)
-#define ADKCON_0MODPER1 (1 << 4)
-#define ADKCON_1MODPER2 (1 << 5)
-#define ADKCON_2MODPER3 (1 << 6)
-#define ADKCON_3OFF2 (1 << 7)
-#define ADKCON_FLOP_GCR (1 << 8)
-#define ADKCON_GCR_SYNC (1 << 9)
-#define ADKCON_SYNC (1 << 10)
-#define ADKCON_UARTBRK (1 << 11)
-#define ADKCON_FLOP_MFM (1 << 12)
-#define ADKCON_SETCLR (1 << 15)
-
-/* Most of these are shared by INTREQ, INTREQR, INTENA, INTENAR */
-#define INT_SERTBE (1 << 0)
-#define INT_FLOPBLCK (1 << 1)
-#define INT_SOFT (1 << 2)
-#define INT_CIAA (1 << 3)
-#define INT_COPPER (1 << 4)
-#define INT_VBLANK (1 << 5)
-#define INT_BLITRDY (1 << 6)
-#define INT_AUDIO0 (1 << 7)
-#define INT_AUDIO1 (1 << 8)
-#define INT_AUDIO2 (1 << 9)
-#define INT_AUDIO3 (1 << 10)
-#define INT_AUDIO (1 << 7 | 1 << 8 | 1 << 9 | 1 << 10)
-#define INT_SERRBF (1 << 11)
-#define INT_FLOPSYNC (1 << 12)
-#define INT_CIAB (1 << 13)
-#define INT_LEVEL6 (1 << 14) /* INTREQR */
-#define INT_MASTER (1 << 14) /* INTENA */
-#define INT_SETCLR (1 << 15) /* INTENA & INTREQ */
-#define INTENA_CLRALL 0x3FFF
-#define INTREQ_CLRALL 0x7FFF
-
-
-
-
-/* Stuff we need to initialize before calling Xisop's main() */
-
-/*
-#define MEMF_CHIP 0x0001
-#define MEMF_FAST 0x0002
-
-void *OpenLibrary(char *, long);
-void CloseLibrary(void *);
-long AvailMem(long);
-void *AllocMem(long, long);
-void *AllocAbs(long, void *);
-void FreeMem(void *, long);
-void Permit(void);
-void Enable(void);
-void *SuperState(void);
-void UserState(void *);
-
-void jdelay(u_int32_t);
-u_int32_t atime(void);
-*/
-void ldelay(u_int32_t);
-
-void Alert(long, void *);
-void Forbid(void);
-void Disable(void);
-void _supervisor(u_int32_t *, size_t, void (*)(void));
-
-/* Set priority level */
-u_int16_t asplvblank(void);
-u_int16_t asplsched(void);
-/* Set priority level exit */
-void asplx(u_int16_t);
-
-
-
-/* Things we export for Xisop
- * --------------------------
- */
-
-
-/* Memory management */
-
-/* Our preferred page size. As Xisop does not support MMU, any size which is
- * resonable and a multiple of 16 can be used.
- */
-#define _PAGE_SIZE 4096
-
-/* Definitions we need for mpool_t */
-#define _MPOOLS 7
-#define _MPOOLSTART 16
-#define _MPOOLSTEP 1
-
-/* Memory types we setup */
-enum _memtypes {
- _MEM_FAST = 0,
- _MEM_CHIP,
- _MEM_MAX
-};
-
-/* Scheduler timer rate */
-#define SCHEDTIMER_HZ 200 /* 4 times the vertical blank rate for now */
-
-/* Interrupt facilities we provide */
-enum _facilities {
- _FACILITY_SCHEDTIMER = 0,
- _FACILITY_VBLANK,
- _FACILITY_FLOPPYSYNC,
- _FACILITY_FLOPPYBLOCK,
- _FACILITY_SERIALRBF,
- _FACILITY_SERIALTBE,
- _FACILITY_AUDIO,
- _FACILITY_BLITTERREADY,
- _FACILITY_COPPER,
- _FACILITY_CIATIMB0,
- _FACILITY_KEYBOARD,
- _FACILITY_TRAP,
- _FACILITY_MAX
-};
-
-
-/* Functions we provide */
-void _init_exceptions(void);
-void _syscall(u_int32_t, void *, void *);
-void _yield(void *);
-void _port_init(void);
-void _panic(u_int16_t); /* XXX */
-
-
-
-#endif
+++ /dev/null
-/* $Id: support_c.c,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <common/types.h>
-#include "support.h"
-
-
-
-/* These functions are similar to the _spl()/_splx() and spl()/splx() ones
- * although they work directly with the Amiga hardware INTENA register,
- * thus only disabling the specified interrupt (rather than setting a level).
- * XXX Problem with this is that it's absolutely architecture-dependant.
- * The general purpose spl() functions should be common to the various hardware
- * Xisop supports. Hmm but the devices will be architecture dependant as well;
- * unless kernel provides some basics to access some common things like
- * RS-232 and console I/O.
- */
-
-u_int16_t asplvblank(void)
-{
- u_int16_t old;
-
- old = CUSTOM->INTENAR;
- CUSTOM->INTENA = INT_VBLANK;
-
- return old;
-}
-
-/* Note: also prevents other CIA-B interrupts meanwhile */
-u_int16_t asplsched(void)
-{
- u_int16_t old;
-
- old = CUSTOM->INTENAR;
- CUSTOM->INTENA = INT_CIAB;
-
- return old;
-}
-
-void asplx(u_int16_t old)
-{
- CUSTOM->INTENA = old | INT_SETCLR;
-}
+++ /dev/null
-/* $Id: support_s.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include "support.h"
-
-
-| exec.library base
-ExecBase = 0x0004
-| Various Amiga custom chips registers
-VPOSR = 0x00DFF004
-VHPOSR = 0x00DFF006
-TODHI = 0x00BFEA01
-TODMID = 0x00BFE901
-TODLO = 0x00BFE801
-INTREQR = 0x00DFF01E
-INTREQ = 0x00DFF09C
-INTENAR = 0x00DFF01C
-INTENA = 0x00DFF09A
-COLOR0 = 0x00DFF180
-CIA_ICR = 0x00BFDD00
-
-
-.globl Alert, Forbid, Disable, AvailMem, AllocMem, AllocAbs, FreeMem
-.globl _supervisor, _init_exceptions, _syscall, _yield
-.globl jdelay, ldelay, atime
-
-.text
-
-
-| AmigaOS minimal library. We include here the various functions which the
-| kernel initialization may need.
-
-
-| void [-108]Alert([d7]long alertNum,[a5]char *flags)
-|
-Alert:
- moveml %d7/%a5-%a6, %sp@-
- moveal ExecBase:w, %a6
- movel %sp@(16), %d7
- moveal %sp@(20), %a5
- jsr %a6@(-108)
- moveml %sp@+, %a6-%a5/%d7
- rts
-
-
-| void [-132]Forbid(void)
-|
-Forbid:
- movel %a6, %sp@-
- moveal ExecBase:w, %a6
- jsr %a6@(-132)
- moveal %sp@+, %a6
- rts
-
-
-/*
-| void [-120]Disable(void)
-|
-Disable:
- movel %a6, %sp@-
- moveal ExecBase:w, %a6
- jsr %a6@(-120)
- moveal %sp@+, %a6
- rts
-
-
-| [d0]long = [-216]Availmem([d1]long reqs)
-|
-AvailMem:
- moveml %a6/%d1, %sp@-
- moveal ExecBase:w, %a6
- movel %sp@(12), %d1
- jsr %a6@(-216)
- moveml %sp@+, %d1/%a6
- rts
-
-
-| [d0]void * = [-198]AllocMem([d0]long bytes, [d1]long reqs)
-|
-AllocMem:
- moveml %a6/%d0-%d1, %sp@-
- moveal ExecBase:w, %a6
- movel %sp@(16), %d0
- movel %sp@(20), %d1
- jsr %a6@(-198)
- moveal %d0, %a0
- moveml %sp@+, %d1-%d0/%a6
- rts
-
-
-| [d0]void * = [-204]AllocAbs([d0]long bytes, [a1]void *addr)
-|
-AllocAbs:
- moveml %a1/%a6/%d0, %sp@-
- moveal ExecBase:w, %a6
- movel %sp@(16), %d0
- moveal %sp@(20), %a1
- jsr %a6@(-204)
- moveal %d0, %a0
- moveml %sp@+, %d0/%a6/%a1
- rts
-
-
-| void [-210]FreeMem([a1]memptr, [d0]bytesize)
-|
-FreeMem:
- moveml %a1/%a6/%d0, %sp@-
- moveal ExecBase:w, %a6
- moveal %sp@(16), %a1
- movel %sp@(20), %d0
- jsr %a6@(-210)
- moveml %sp@+, %d0/%a6/%a1
- rts
-*/
-
-
-| void _supervisor(u_int32_t *sp, size_t sz, void (*func)(void))
-| Allows to gain m68k supervisor access, and sets up the supervisor stack
-| pointer to the specified address. The supplied entry point function is
-| then called. It is expected to never return, we thus don't need to worry
-| about the registers we modify, except for A7/SSP.
-|
-_supervisor:
- moveal %sp@(4), %a1 | SSP to set
- movel %sp@(8), %d1 | Size to add
- moveal %sp@(12), %a2 | Function to call
- moveal ExecBase:w, %a6
- jsr %a6@(-150) | AmigaOS SuperState() exec.library call
- moveal %a1, %a7 | Set SSP
- addal %d1, %a7 | Jump to end of stack buffer
- jsr %a2@ | Call famous function
- rts | Should never be reached.
-
-
-| void _init_exceptions(void)
-| Called from C to initialize interrupts, traps and exception vectors
-|
-_init_exceptions:
- clrl _interrupt_depth
- bsrw trap_init
- bsrw int_init
- bsrw except_init
- rts
-
-
-| Other Amiga-specific assembler routines. The following deal with exceptions,
-| traps and interrupts.
-
-
-| void trap_init(void)
-| Setup our 15 trap vectors
-|
-trap_init:
- moveml %a0/%a1, %sp@-
- moveal #0x80, %a0
- lea %pc@(trap_catch0), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch1), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch2), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch3), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch4), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch5), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch6), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch7), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch8), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch9), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch10), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch11), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch12), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch13), %a1
- movel %a1, %a0@+
- lea %pc@(trap_catch14), %a1
- movel %a1, %a0@
- moveml %sp@+, %a1/%a0
- rts
-
-
-| And our trap handlers
-
-| This trap is special as it handles the backend for the _syscall() function
-| which prototype is as follows:
-| void _syscall(u_int32_t code, void *res, void *args)
-| _syscall() just puts the arguments onto d0, a0 and a1 registers, and causes
-| a trap 0. Our purpose is to copy those arguments on the supervisor
-| stack, call _scatch(), free our arguments from the supervisor stack and
-| return from the exception. The _syscall() caller will free it's own arguments
-| from the user stack.
-|
-trap_catch0:
- addql #1, _interrupt_depth
-
- | Backup registers on supervisor stack
- | _syscall backed up d0 and a0-a1 as it modifies them to store the
- | registers for us.
- |
- moveml %d0-%d7/%a0-%a6, %sp@-
-
- | Move registers which _syscall() setup for us into the stack and
- | call void _scatch(u_int32_t, void *, void *).
- |
- movel %a1, %sp@-
- movel %a0, %sp@-
- movel %d0, %sp@-
- bsrl _scatch
- lea %sp@(12), %sp | faster than addl #12, %sp
-
- | Restore registers we backed up
- |
- moveml %sp@+, %a6-%a0/%d7-%d0
- subql #1, _interrupt_depth
- rte
-
-
-| This trap is also special in that it triggers an immediate context switch.
-| Used to implement _yield(). It is implemented very similarly to the scheduler
-| timer interrupt handler which forces task preemption.
-|
-trap_catch1:
- addql #1, _interrupt_depth
-
- | First save SR and change it so that we be uninterruptible
- |
- movew %sr, %sp@-
- movew #0x2700, %sr
-
- | Save current CPU context to root->curctx
- |
- movel %a0, %sp@- | Save A0 internally as we need it
- movel %usp, %a0
- movel %a0, %sp@- | And USP
- lea root, %a0 | Load root->curctx _ctx_t *
- moveal %a0@(44), %a0
- lea %a0@(70), %a0 | Position now after struct's SR
-
- | Save SR, predecrementing. Normally stored at %sp@, but we saved
- | 10 bytes in the stack, and are using %sp@(10).
- movew %sp@(10), %a0@-
- | Save PC from SS, which is normally stored at %sp@(2), but we saved
- | 10 bytes in the stack, so are using %sp@(12).
- movel %sp@(12), %a0@- | Save PC, from SS
- moveml %a1-%a6/%d0-%d7, %a0@- | Save A6-A1, D7-D0
- movel %sp@+, %a0@- | Save USP/A7 from backup
- movel %sp@+, %a0@- | Save A0 from backup
-
- | Now call schedule(), passing the supplied argument we were passed
- |
- movel _yieldparam, %sp@-
- bsrl schedule
- addql #4, %sp
-
- | Load context from root->curctx, which may have changed
- |
- lea root, %a0 | Load root->curctx _ctx_t *
- moveal %a0@(44), %a0
- addql #4, %a0 | Skip A0 for now as we use it
- movel %a0@+, %a1
- movel %a1, %usp | Restore USP/A7
- moveml %a0@+, %d7-%d0/%a6-%a1 | Restore D0-D7, A1-A6
- | Load PC from context and save in SP. Normally it would be %sp@(2)
- | but there remains 2 bytes we saved in the stack and are using %sp@(4)
- movel %a0@+, %sp@(4) | PC
- | Restore SR, making sure that supervisor mode bit is set. Normally
- | stored at %sp@, but we saved 2 bytes on the stack, so %sp@(2).
- movew %a0@, %sp@(2)
- moveal %a0@(-68), %a0 | Restore A0
-
- | Restore normal SR
- |
- movew %sp@+, %sr
-
- | Return from exception, but to PC we loaded
- |
- subql #1, _interrupt_depth
- rte
-
-
-| These trap handlers consist of a backend for the C _tcatch() function.
-|
-trap_catch2:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 0
- braw 1f
-trap_catch3:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 1
- braw 1f
-trap_catch4:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 2
- braw 1f
-trap_catch5:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 3
- braw 1f
-trap_catch6:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 4
- braw 1f
-trap_catch7:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 5
- braw 1f
-trap_catch8:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 6
- braw 1f
-trap_catch9:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 7
- braw 1f
-trap_catch10:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 8
- braw 1f
-trap_catch11:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 9
- braw 1f
-trap_catch12:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 10
- braw 1f
-trap_catch13:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 11
- braw 1f
-trap_catch14:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 12
-1:
- | Call our C function: void _tcatch(int);
- |
- bsrl _tcatch
- addql #4, %sp
- moveml %sp@+, %a6-%a0/%d7-%d0
- subql #1, _interrupt_depth
- rte
-
-
-
-| C frontend to catch interrupts. Because of the way Amiga works with INTREQ
-| it is important that they verify and reset their cause bits. This is easily
-| done when dispatching all interrupt levels to a main single C function, with
-| the interrupt level passed as an argument, and leave it do all the work.
-| This is what was previously done here. However, leaving the C function do
-| all the work implied wasting some CPU cycles. For instance, the level is
-| already known via the handler being called, when we still let the C code
-| perform that condition checking again. Moreover, INTREQ handling wasn't
-| particularly optimized properly (using GCC 2.95.3 with m68k output).
-| As some interrupt levels may be generated very frequently it is best to
-| optimize this as much as possible.
-| So we separated the handlers and let the assembler frontend handle INTREQ.
-| For most interrupt handlers, We call a C function handler per interrupt
-| level:
-| void _icatch<n>(u_int16_t intreqmask)
-| where <intreqmask> is obtained from INTREQR, and we make sure to reset
-| the bits of the mask in INTREQ. It would also be possible to add some more
-| assembly here if wanted to serve special interrupts faster, like RS-232 I/O.
-| NOTE: GCC requires a 32-bit stack entry even for a 16-bit argument.
-
-
-| void int_init(void)
-| Setup our 6 interrupt vectors. Amiga does not use m68k interrupt level 7
-| and they are non-maskable (and hence are of no use to us anyways).
-|
-int_init:
- moveml %a0-%a1, %sp@-
- moveal #0x64, %a0
- lea %pc@(int_catch1), %a1
- movel %a1, %a0@+
- lea %pc@(int_catch2), %a1
- movel %a1, %a0@+
- lea %pc@(int_catch3), %a1
- movel %a1, %a0@+
- lea %pc@(int_catch4), %a1
- movel %a1, %a0@+
- lea %pc@(int_catch5), %a1
- movel %a1, %a0@+
- lea %pc@(int_catch6), %a1
- movel %a1, %a0@
- moveml %sp@+, %a1-%a0
- rts
-
-
-| These handlers call the various C _icatch<n>() functions
-int_catch1:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- movew INTREQR, %sp@-
- clrw %sp@-
- bsrl _icatch1
- addql #4, %sp
- movew #0x0007, INTREQ
- moveml %sp@+, %a6-%a0/%d7-%d0
- subql #1, _interrupt_depth
- rte
-int_catch2:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- movew INTREQR, %sp@-
- clrw %sp@-
- bsrl _icatch2
- addql #4, %sp
- movew #0x0008, INTREQ
- moveml %sp@+, %a6-%a0/%d7-%d0
- subql #1, _interrupt_depth
- rte
-int_catch3:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- movew INTREQR, %sp@-
- clrw %sp@-
- bsrl _icatch3
- addql #4, %sp
- movew #0x0070, INTREQ
- moveml %sp@+, %a6-%a0/%d7-%d0
- subql #1, _interrupt_depth
- rte
-int_catch4:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- movew INTREQR, %sp@-
- clrw %sp@-
- bsrl _icatch4
- addql #4, %sp
- movew #0x0780, INTREQ
- moveml %sp@+, %a6-%a0/%d7-%d0
- subql #1, _interrupt_depth
- rte
-int_catch5:
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- movew INTREQR, %sp@-
- clrw %sp@-
- bsrl _icatch5
- addql #4, %sp
- movew #0x1800, INTREQ
- moveml %sp@+, %a6-%a0/%d7-%d0
- subql #1, _interrupt_depth
- rte
-
-
-| This interrupt handler is special as it also comports CIAB timer A interrupt,
-| which is used by Xisop for the preeptive scheduler. We thus handle it
-| especially, entirely in assembly.
-| Unlike for other interrupt levels, we not just simply save and restore all
-| registers. We store the current context into root->curctx, call schedule(),
-| then load the context from root->curctx.
-| This executes on the Supervisor Stack, and the User Stack Pointer as such
-| is not modified except in context switches.
-|
-int_catch6:
- addql #1, _interrupt_depth
-
- | Raise level to 7
- |
- movew %sr, %sp@-
- movew #0x2700, %sr
-
- | Save D0 as we need it
- |
- movel %d0, %sp@-
-
- | if (!(intreq & INT_CIAB)) goto 1
- |
- movew INTREQR, %d0
- btst #13, %d0
- beqs 1f
-
- | if (icrmask & CIA_ICR_TIMA0) goto 2
- |
- moveb CIA_ICR, %d0
- btst #0, %d0
- bnes 2f
-1:
- | Not the CIAB Timer A interrupt, Restore D0 and end
- |
- movel %sp@+, %d0
- braw 4f
-2:
- | Restore D0 and continue
- |
- movel %sp@+, %d0
-
- | Tasks are expected to not run in supervisor mode.
- | We have syscalls (including sys_call() one allowing to execute
- | arbitrary code in supervisor mode), and public interrupt facilities
- | (to which they can attach hooks) for them which should suffice.
- | Moreover, system calls are uninterruptible by the scheduler when
- | running.
-
- | Store current user context into root->curctx buffer
- |
- movel %a0, %sp@- | Save A0 internally as we need it
- movel %usp, %a0
- movel %a0, %sp@- | And USP
- lea root, %a0 | Load root->curctx _ctx_t *
- moveal %a0@(44), %a0
- lea %a0@(70), %a0 | Position now after struct's SR
- | Save SR, predecrementing. Normally stored at %sp@, but we saved
- | 10 bytes in the stack, and are using %sp@(10).
- movew %sp@(10), %a0@-
- | Save PC from SS, which is normally stored at %sp@(2), but we saved
- | 10 bytes in the stack, so are using %sp@(12).
- movel %sp@(12), %a0@- | Save PC, from SS
- moveml %a1-%a6/%d0-%d7, %a0@- | Save A6-A1, D7-D0
- movel %sp@+, %a0@- | Save USP/A7 from backup
- movel %sp@+, %a0@- | Save A0 from backup
-
- | Execute _FACILITY_SCHEDTIMER hooks
- |
- clrl %sp@-
- pea 0 | _FACILITY_SCHEDTIMER
- bsrl facility_exechooks
- addql #8, %sp
-
- | The following ensures to only perform a context switch if not
- | currently executing a system call trap or other interrupt. The
- | reason we need this is that the scheduler interrupt has a very
- | high priority (6), which can actually interrupt most other
- | exception handlers.
- |
- | if (_interrupt_depth != 1) goto 3
- |
- cmpil #1, _interrupt_depth
- bnes 3f
-
- | Call schedule(), which internally handles scheduling, and can
- | modify root->curctx and root->curtask
- |
- pea 0
- bsrl schedule
- addql #4, %sp
-
-3:
- | Load back root->curctx into the current user CPU context
- |
- lea root, %a0 | Load root->curctx _ctx_t *
- moveal %a0@(44), %a0
- addql #4, %a0 | Skip A0 for now as we use it
- movel %a0@+, %a1
- movel %a1, %usp | Restore USP/A7
- moveml %a0@+, %d7-%d0/%a6-%a1 | Restore D0-D7, A1-A6
- | Load PC from context and save in SP. Normally it would be %sp@(2)
- | but there remains 2 bytes we saved in the stack and are using %sp@(4)
- movel %a0@+, %sp@(4) | PC
- | Restore SR, making sure that supervisor mode bit is set. Normally
- | stored at %sp@, but we saved 2 bytes on the stack, so %sp@(2).
- movew %a0@, %sp@(2)
- moveal %a0@(-68), %a0 | Restore A0
-
-4:
- | Exit point
- |
- | Reset INTREQ like for other levels interrupt handlers
- |
- movew #0x2000, INTREQ
-
- | Restore Interrupt Priority Level
- |
- movew %sp@+, %sr
-
- | Return using the %sp@(2) address from SS
- |
- subql #1, _interrupt_depth
- rte
-
-
-
-| void except_init(void)
-| Setup special exception handlers.
-|
-except_init:
- moveml %a0-%a1, %sp@-
- moveal #0x8, %a0
- lea %pc@(except_catch1), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch2), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch3), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch4), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch5), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch6), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch7), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch8), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch9), %a1
- movel %a1, %a0@+
- lea %pc@(except_catch10), %a1
- movel %a1, %a0@
- moveal #0x3c, %a0
- lea %pc@(except_catch11), %a1
- movel %a1, %a0@
- moveal #0x60, %a0
- lea %pc@(except_catch12), %a1
- movel %a1, %a0@
- moveal #0x7c, %a0
- lea %pc@(except_catch13), %a1
- movel %a1, %a0@
- moveml %sp@+, %a1-%a0
- rts
-
-
-| Our exception handlers. Internally calling _ecatch() C function.
-|
-except_catch1: | Bus error
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 1
- braw 1f
-except_catch2: | Address error
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 2
- braw 1f
-except_catch3: | Illegal operation
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 3
- braw 1f
-except_catch4: | Division by zero
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 4
- braw 1f
-except_catch5: | CHK
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 5
- braw 1f
-except_catch6: | TRAPV
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 6
- braw 1f
-except_catch7: | Privilege violation
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 7
- braw 1f
-except_catch8: | Trace
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 8
- braw 1f
-except_catch9: | Line A Emu
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 9
- braw 1f
-except_catch10: | Line F Emu
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 10
- braw 1f
-except_catch11: | Uninitialized interrupt vector
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 11
- braw 1f
-except_catch12: | Unjustified interrupt vector
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 12
- braw 1f
-except_catch13: | NMI ?
- addql #1, _interrupt_depth
- moveml %d0-%d7/%a0-%a6, %sp@-
- pea 13
-1:
- | Call our C function: void _ecatch(int);
- bsrl _ecatch
- addql #4, %sp
- moveml %sp@+, %a6-%a0/%d7-%d0
- subql #1, _interrupt_depth
- rte
-
-
-
-| void _syscall(int function, void *res, void *args)
-| C interface to Xisop syscalls. Because of the C prototype, the three
-| arguments are already pushed unto the user stack before calling _syscall().
-| We thus place them into registers d0, a0 and a1, and cause a trap 0,
-| which resumes execution at trap_catch0 in supervisor mode.
-|
-_syscall:
- | Save registers we modify
- moveml %d0/%a0-%a1, %sp@-
-
- | Prepare arguments
- movel %sp@(16), %d0
- moveal %sp@(20), %a0
- moveal %sp@(24), %a1
- | Poof! Through the gate
- trap #0
-
- | Restore registers
- moveml %sp@+, %a1-%a0/%d0
- rts
-
-
-| void _yield(task_t *)
-| Allows a task to immediately return control to the scheduler, allowing
-| other tasks some CPU time immediately. The optional task_t pointer argument
-| permits to suggest a task to run soon again to the scheduler, and should
-| consist of another STATE_READY task.
-| We use a special static buffer to hold this value because we do not want
-| to taint the registers before the context gets saved. Moreover, if we tried
-| to save and restore registers from the stack, we probably would be stealing
-| bytes from the stack of another task which probably was preempted rather than
-| interrupted using _yield() the last time.
-| I tried pushing the argument on the stack and having the other side check
-| it in via the User Stack Pointer, but it failed for some reason.
-|
-_yield:
- | Use a static buffer since we're not supposed to modify any registers
- | and that we can't save them on the stack since at return we won't
- | have the same stack, obviously.
- movel %sp@(4), _yieldparam
- trap #1
- rts
-
-
-
-| These consist of various delay functions. These are not particularly
-| useful in multitasking environments since they hug the CPU in loops,
-| but are great for testing purposes.
-
-
-/*
-| void jdelay(long jiffies)
-| Waits until completion of the current frame (vblank), 1/60th of a second
-| for NTSC and 1/50th for PAL, by checking current hardware raster position.
-| Not ideal as using an interrupt, but works. j stands for jiffy, a frame.
-|
-jdelay:
- moveml %d0-%d1, %sp@-
- movel %sp@(12), %d0
-1:
- movel VPOSR, %d1 | Read both VPOSR and VHPOSR at once
- andil #0x0001FF00, %d1 | Mask out the vertical beam position
- bnes 1b
- dbf %d0, 1b
- moveml %sp@+, %d1-%d0
- rts
-*/
-
-
-| void ldelay(long lines)
-| Similar to the above function but waits approximately for the start of the
-| next video raster line.
-|
-ldelay:
- moveml %d0-%d1, %sp@-
- movel %sp@(12), %d0
-1: movew VHPOSR, %d1
- andiw #0x000F, %d1
- bnes 1b
- dbf %d0, 1b
- moveml %sp@+, %d1-%d0
- rts
-
-
-*/
-| Time related functions.
-
-
-| u_int32_t atime(void)
-| Obtains current time in 50/60Hz resolution, from CIA-A TOD counter
-|
-atime:
- clrl %d0
- moveb TODHI, %d0
- lsll #4, %d0
- lsll #4, %d0
- moveb TODMID, %d0
- lsll #4, %d0
- lsll #4, %d0
- moveb TODLO, %d0
- rts
-*/
-
-
-.data
-.align 4
-
-| This counter is used to count the depth of interrupts, so that the scheduler
-| interrupt, which runs at a very high priority, only switches state if the
-| depth is 1. All interrupt and trap handlers increase this variable at
-| startup and decrease it at exit.
-|
-_interrupt_depth:
- .long 0
-
-| This consists of the parameter which is passed to _yield(), because we cannot
-| save registers on the stack and expect the same values to be loaded back
-| after causing trap #1, because obviously the context (and SP) may have
-| changed.
-|
-_yieldparam:
- .long 0
+++ /dev/null
-#!/bin/sh
-
-# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../makedefs.sh
-
-show $C_COMPS -I$SRCDIR support.s
-show $C_COMPS udivsi3.s
-show $C_COMPS mulsi3.s
-show $C_AR ar/support.a support.o udivsi3.o mulsi3.o
-show $C_RANLIB ar/support.a
+++ /dev/null
-/* $Id: support.h,v 1.3 2004/01/30 07:01:24 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef PROCESSOR_I386_H
-#define PROCESSOR_I386_H
-
-
-
-#include <common/types.h>
-
-
-
-#define _LITTLE_ENDIAN
-
-typedef struct _ctx {
- void *esp, *ebp, *eip; /* eip == PC, esp = stack top */
- u_int32_t eax, ebx, ecx, edx;
- u_int32_t esi, edi;
- u_int32_t eflags;
- u_int32_t es, fs, gs, ds;
-} volatile _ctx_t;
-
-typedef volatile int32_t _lock_t;
-typedef int32_t _rlock_t;
-
-
-
-void _lock_init(_lock_t *);
-void _lock_acquire(_lock_t *);
-void _lock_release(_lock_t *);
-bool _lock_try(_lock_t *);
-bool _lock_available(_lock_t *);
-
-void _rlock_init(_rlock_t *);
-void _rlock_acquire(_rlock_t *);
-void _rlock_release(_rlock_t *);
-bool _rlock_try(_rlock_t *);
-bool _rlock_available(_rlock_t *);
-
-u_int16_t _bswap16(u_int16_t);
-u_int32_t _bswap32(u_int32_t);
-
-void _ctx_init(_ctx_t *, u_int32_t *, size_t, void *);
-
-void _idle(void);
-
-
-
-#endif
+++ /dev/null
-/* $Id: support.s,v 1.4 2004/01/30 07:29:14 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-.globl _spl, _splx
-.globl _lock_init, _lock_acquire, _lock_try, _lock_available, _lock_release
-.globl _rlock_init, _rlock_acquire, _rlock_try, _rlock_release, _rlock_available
-.globl _ctx_init
-.globl setjmp, longjmp
-.globl _idle
-.globl _bswap16, _bswap32
-
-.text
-
-
-/* _spl_t _spl(_spl_t) _spl_t = ?XXX?
-*/
-_spl:
-/*XXX*/
- ret
-
-
-_lock_init:
- mov 0x4(%esp,1), %eax
- movl $0x0, (%eax)
- ret
-
-_lock_acquire:
- mov 0x4(%esp,1), %edx
- mov $0x1, %eax
- lea 0x0(%esi), %esi
-1: xchg %eax, (%edx)
- test %eax, %eax
- jne 1b
- ret
-
-_lock_release:
- mov 0x4(%esp,1), %eax
- movl $0x0, (%eax)
- ret
-
-_lock_try:
- mov 0x4(%esp,1), %edx
- mov $0x1, %eax
- xchg %eax, (%edx)
- test %eax, %eax
- je 1f
- xor %eax, %eax
- ret
-1: mov $0x1, %eax
- ret
-
-_lock_available:
- mov 0x4(%esp,1), %eax
- cmpl $0x0, (%eax)
- je 1f
- xor %eax, %eax
- ret
-1: mov $0x1, %eax
- ret
-
-
-_rlock_init:
- mov 0x4(%esp,1), %eax
- movl $0x0, (%eax)
- ret
-
-_rlock_acquire:
- mov 0x4(%esp,1), %eax
- incl (%eax)
- ret
-
-_rlock_release:
- mov 0x4(%esp,1), %eax
- decl (%eax)
- ret
-
-_rlock_try:
- mov 0x4(%esp,1), %eax
- incl (%eax)
- cmpl $0x1, (%eax)
- je 1f
- decl (%eax)
- xor %eax, %eax
- ret
-1: mov $0x1, %eax
- ret
-
-_rlock_available:
- mov 0x4(%esp,1), %eax
- cmpl $0x0, (%eax)
- je 1f
- xor %eax, %eax
- ret
-1: mov $0x1, %eax
- ret
-
-
-_bswap16:
- mov 0x4(%esp,1), %eax
- ror $0x8, %ax
- ret
-
-_bswap32:
- mov 0x4(%esp,1), %eax
- ror $0x8, %ax
- ror $0x10, %eax
- ror $0x8, %ax
- ret
-
+++ /dev/null
-#!/bin/sh
-
-# $Id: clean.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../generic_makedefs.sh
-
-cleanlib math
-show $L_RM support.o ar/support.a
+++ /dev/null
-#!/bin/sh
-
-# $Id: make.sh,v 1.1 2003/10/26 21:19:57 mmondor Exp $
-
-. ../../makedefs.sh
-
-show $C_COMPS -I$SRCDIR support.s
-buildlib math
-show $C_AR ar/support.a support.o math/*.o
-show $C_RANLIB ar/support.a
+++ /dev/null
-Some math functions are required by GCC m68k compiled code. Those were obtained
-from the NetBSD kernel m68k library and adapted. Their BSD license file headers
-were unchanged, however.
+++ /dev/null
-/* $Id: divsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*-
- * Copyright (c) 2002 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Matthew Fredette.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-.globl __divsi3
-
-
-
-| NB: this requires that __udivsi3 preserve %a0:
-__divsi3:
- movel %sp@(4), %d1 | load the dividend
- bpls 1f
- negl %sp@(4) | store abs(dividend)
-1: movel %sp@(8), %d0 | load the divisor
- bpls 2f
- negl %sp@(8) | store abs(divisor)
-2: eorl %d1, %d0
- bpls 3f | branch if sgn(divisor) == sgn(dividend)
- moveal %sp@+, %a0 | pop return address
- pea %pc@(Lret) | push our return address
-3: jmp __udivsi3
-Lret: negl %d0 | negate quotient
- jmp %a0@
+++ /dev/null
-/* $Id: modsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*-
- * Copyright (c) 2002 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Matthew Fredette.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-.globl __modsi3
-
-
-
-| NB: this requires that __udivsi3 preserve %a0 and return
-| the modulus in %d1:
-__modsi3:
- moveal %sp@+, %a0 | pop return address
- movel %sp@(4), %d1 | load the divisor
- bpls 1f
- negl %sp@(4) | store abs(divisor)
-1: movel %sp@, %d0 | load the dividend
- pea %pc@(Lret) | push our return address
- bpls 2f
- negl %sp@(4) | store abs(dividend)
- subql #2, %sp@ | adjust return address
-2: jmp __udivsi3
- negl %d1 | negate modulus
-Lret: movel %d1, %d0 | move modulus into %d0
- jmp %a0@
+++ /dev/null
-/* $Id: mulsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*-
- * Copyright (c) 2002 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Matthew Fredette.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-.globl __mulsi3
-
-__mulsi3:
- movew %sp@(6), %d0
- moveal %d0, %a0 | save B
- muluw %sp@(8), %d0 | %d0 holds B * C
- movew %sp@(10), %d1
- moveal %d1, %a1 | save D
- muluw %sp@(4), %d1 | %d1 holds A * D
- addw %d1, %d0 | %d0 holds (B * C) + (A * D)
- swap %d0
- clrw %d0 | %d0 holds ((B * C) + (A * D)) << 16
- exg %a0, %d0 | restore B
- movel %a1, %d1 | restore D
- muluw %d1, %d0 | %d0 holds B * D
- addl %a0, %d0 | final result
- rts
+++ /dev/null
-/* $Id: udivsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*-
- * Copyright (c) 2002 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Matthew Fredette.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-.globl __udivsi3
-
-__udivsi3:
- movel %d2, %sp@- | save %d2
- movel %sp@(12), %d0 | load divisor
- movel %sp@(8), %d1 | load dividend
-
-| first, we divide the divisor and dividend by two until
-| the divisor fits into 16 bits:
-1: cmpil #0x10000, %d0
- bcss 2f
- lsrl #1, %d0
- lsrl #1, %d1
- bras 1b
-2:
-
-| now we can do the divide. to avoid overflow, we have to
-| do the divide in two parts, high and low, and add the
-| results together:
- movew %d1, %d2 | save low(dividend)
- clrw %d1
- swap %d1 | %d1 = dividend >> 16
- divuw %d0, %d1 | do the high divide
- moveal %d1, %a1 | save high divide result
- movew %d2, %d1 | concat(remainder, low(dividend))
- divuw %d0, %d1 | do the low divide
- movel %a1, %d0 | recover high divide result
- swap %d0
- clrw %d0 | %d0 = finished high divide result
- andil #0xffff, %d1 | %d1 = finished low divide result
- addl %d1, %d0 | %d0 = quotient guess
-
-| the quotient we have so far is only a guess. the divide we
-| did above was really the divide of some dividendB by some
-| divisorB, where the following hold:
-|
-| (dividend - divisor) <= dividendB <= dividend
-| (divisor / 2) < divisorB <= divisor
-|
-| so our guess quotient cannot be less than our real desired
-| quotient. however, it might be one too big.
-|
-| to adjust this quotient, we multiply it by the original
-| divisor and subtract the result from the original dividend.
-| if the result is nonnegative, our guessed quotient was
-| correct, and the subtraction result is our remainder.
-| if the result is negative, our guessed quotient was one
-| too big, and the subtraction result plus the original
-| divisor is our remainder.
-|
-| as in mulsi3, we have to do the multiply in stages to avoid
-| overflow:
-
- movel %sp@(12), %d2 | load divisor
- swap %d2
- movel %d0, %d1
- muluw %d2, %d1 | high(divisor) * low(guess)
- moveal %d1, %a1 | save high(divisor) * low(guess)
- swap %d2
- movel %d0, %d1
- swap %d1
- muluw %d2, %d1 | low(divisor) * high(guess)
- addl %a1, %d1
- swap %d1
- clrw %d1 | %d1 = finished high multiply result
- moveal %d2, %a1 | save original divisor
- muluw %d0, %d2 | low(guess) * low(divisor)
- addl %d1, %d2 | %d2 = guess * divisor
-
- movel %sp@(8), %d1 | load original dividend
- subl %d2, %d1 | subtract
- bccs 3f
- subql #1, %d0 | adjust quotient
- addl %a1, %d1 | adjust remainder
-3: movel %sp@+, %d2 | restore %d2
- rts
+++ /dev/null
-/* $Id: umodsi3.s,v 1.1 2003/10/26 21:19:57 mmondor Exp $ */
-
-/*-
- * Copyright (c) 2002 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Matthew Fredette.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-.globl __umodsi3
-
-
-
-| NB: this requires that __udivsi3 preserve the %a0
-| register, and that it returns the modulus in %d1:
-__umodsi3:
- moveal %sp@+, %a0 | pop the return address
- jsr __udivsi3
- movel %d1, %d0 | move the modulus into %d0
- jmp %a0@ | return
+++ /dev/null
-/* $Id: support.h,v 1.6 2004/06/03 05:44:05 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Derived from informations from the "Mise en oeuvre du 68000" book.
- * The spl name was inspired from NetBSD SPL(9) man page. The code for
- * these is in support.s
- */
-
-
-
-#ifndef PROCESSOR_M68K_H
-#define PROCESSOR_M68K_H
-
-
-
-#include <common/types.h>
-
-
-
-/* Definitions for optimizations and network byte order support */
-#define _ARCH_BIG_ENDIAN
-#define _ARCH_INT_BITS 32
-/*#define _ARCH_LOWCACHE*/
-/*#define _ARCH_USEINDEXING*/
-
-
-
-/* Abstract datatypes we must provide */
-
-/* A CPU context which can also hold the state of user tasks.
- * Also used as buffer area for setjmp()/longjmp() functions.
- * Note that USP (User Stack Pointer) is actually SP/A7, as seen
- * from supervisor code, which SP/A7 stack is set to SSP (Supervisor
- * Stack Pointer). This means that from supervisor code the special
- * usp related instructions must be used to manipulate the current
- * user stack pointer, but that from userspace this simply is sp which
- * can be manipulated without supervisor access (like in setjmp()/longjmp()).
- */
-typedef struct _ctx {
- void *a0, *usp;
- u_int32_t d0, d1, d2, d3, d4, d5, d6, d7;
- void *a1, *a2, *a3, *a4, *a5, *a6;
- void *pc;
- u_int16_t sr;
- u_int16_t padding;
-} volatile _ctx_t;
-
-/* A simple lock */
-typedef volatile u_int8_t _lock_t;
-/* A recursive lock */
-typedef int32_t _rlock_t;
-
-/* An interrupt priority level context */
-typedef u_int16_t _ipl_t;
-
-
-
-/* Set interrupt level control functions */
-_ipl_t _spl(u_int16_t);
-#define _spl0() _spl(0x2000)
-#define _spl1() _spl(0x2100)
-#define _spl2() _spl(0x2200)
-#define _spl3() _spl(0x2300)
-#define _spl4() _spl(0x2400)
-#define _spl5() _spl(0x2500)
-#define _spl6() _spl(0x2600)
-#define _spl7() _spl(0x2700)
-#define _splhigh() _spl(0x2700)
-void _splx(_ipl_t);
-
-/* Atomic locking functions. These are SMP safe. */
-void _lock_init(_lock_t *);
-void _lock_acquire(_lock_t *);
-void _lock_release(_lock_t *);
-bool _lock_try(_lock_t *);
-bool _lock_available(_lock_t *);
-
-/* Atomic recursive locking functions. The same number of release operations
- * must be performed for the lock to become available again, and there can
- * be any number of concurrent lockers.
- */
-void _rlock_init(_rlock_t *);
-void _rlock_acquire(_rlock_t *);
-void _rlock_release(_rlock_t *);
-bool _rlock_try(_rlock_t *);
-bool _rlock_available(_rlock_t *);
-
-/* Efficient byte order conversion functions */
-u_int16_t _bswap16(u_int16_t);
-u_int32_t _bswap32(u_int32_t);
-
-/* Function to create a new CPU context for a task */
-void _ctx_init(_ctx_t *, u_int32_t *, size_t, void *);
-
-/* Function to put the CPU in idle mode until next interrupt occurs.
- * Restricted to supervisor mode.
- */
-void _idle(void);
-
-/* Useful to call Xisop main() from port-specific initialization code */
-void _usermode(int (*)(void));
-
-
-
-#endif
+++ /dev/null
-/* $Id: support.s,v 1.4 2004/01/30 07:08:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-.globl _spl, _splx
-.globl _lock_init, _lock_acquire, _lock_try, _lock_available, _lock_release
-.globl _rlock_init, _rlock_acquire, _rlock_try, _rlock_release, _rlock_available
-.globl _bswap16, _bswap32
-.globl _ctx_init
-.globl setjmp, longjmp
-.globl _idle, _usermode
-
-.text
-
-
-| _spl_t _spl(_spl_t) _spl_t == u_int16_t
-|
-_spl: movew %sr, %d0
- movew %sp@(6), %sr
- rts
-
-
-| void _splx(_spl_t oldstate)
-|
-_splx: movew %sp@(6), %sr
- rts
-
-
-| void _lock_init(_lock_t *lock) _lock_t == u_int8_t
-| Used to create/init new locks
-|
-_lock_init:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- clrb %a0@
- moveal %sp@+, %a0
- rts
-
-
-| void _lock_acquire(_lock_t *lock)
-| Blocks current CPU in a loop until lock is obtained
-|
-_lock_acquire:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
-1: tas %a0@
- bnes 1b
- moveal %sp@+, %a0
- rts
-
-
-| bool _lock_try(_lock_t *lock)
-| Attempts to obtain lock, but returns immediately with TRUE/FALSE result
-|
-_lock_try:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- tas %a0@
- beqs 1f
- clrl %d0
- moveal %sp@+, %a0
- rts
-1:
- moveq #1, %d0
- moveal %sp@+, %a0
- rts
-
-
-| bool _lock_available(_lock_t *lock)
-| Returns 1/TRUE if the lock is currently free, or 0/FALSE if it is locked.
-| Note that this should not be used to attempt to obtain the lock using two
-| steps (see _lock_try() above for this). It is useful to simply know if
-| it is locked, nothing else. Useful for the scheduler on/off switch.
-|
-_lock_available:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- btst #7, %a0@
- bnes 1f
- moveq #1, %d0
- moveal %sp@+, %a0
- rts
-1:
- clrl %d0
- moveal %sp@+, %a0
- rts
-
-
-| void _lock_release(_lock_t *lock)
-| Releases a previously acquired lock
-|
-_lock_release:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- clrb %a0@
- moveal %sp@+, %a0
- rts
-
-
-| void _rlock_init(_rlock_t *rlock) _rlock_t = int32_t
-| Initializes a recursive lock
-|
-_rlock_init:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- clrl %a0@
- moveal %sp@+, %a0
- rts
-
-
-| void _rlock_acquire(_rlock_t *rlock)
-| Acquires the lock, recursively
-|
-_rlock_acquire:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- addql #1, %a0@
- moveal %sp@+, %a0
- rts
-
-
-| void _rlock_release(_rlock_t *rlock)
-| Releases the lock. _rlock_acquire() and _rlock_release() instances must pair
-| for the lock to eventually be available again.
-|
-_rlock_release:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- subql #1, %a0@
- moveal %sp@+, %a0
- rts
-
-
-| bool _rlock_try(_rlock_t *rlock)
-| Atomically acquires the lock like _rlock_acquire(), but returns TRUE if
-| we then are the only locker. Otherwise, releases the lock back and return
-| FALSE. Obviously, if we are the only locker, the _rlock_t counter will be
-| one (1). This allows to perform an atomic
-| _rlock_available() + _rlock_acquire() pair.
-|
-_rlock_try:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- moveq #1, %d0
- addql #1, %a0@
- cmpl %a0@, %d0
- beqs 1f
- subql #1, %a0@
- clrl %d0
-1: moveal %sp@+, %a0
- rts
-
-
-| bool _rlock_available(_rlock_t *rlock)
-| Returns TRUE if the lock is available, or if FALSE if there are any lockers.
-|
-_rlock_available:
- movel %a0, %sp@-
- moveal %sp@(8), %a0
- clrl %d0
- cmpl %a0@, %d0
- bnes 1f
- moveq #1, %d0
-1: moveal %sp@+, %a0
- rts
-
-
-| u_int16_t _bswap16(u_int16_t)
-| Swaps the order of the two bytes held in the supplied 16-bit word
-|
-_bswap16:
- movew %sp@(6), %d0
- rorw #8, %d0
- rts
-
-
-| u_int32_t _bswap32(u_int32_t)
-| Reverses the order of the four bytes held in the supplied 32-bit word
-| First cause a 16-bit byte swap, then perform a 16-bit word swap within the
-| 32-bit word, and then perform another 16-bit byte swap.
-|
-_bswap32:
- movel %sp@(4), %d0
- rorw #8, %d0
- swap %d0
- rorw #8, %d0
- rts
-
-
-| void _ctx_init(_ctx_t *, u_int32_t *sp, size_t sz, void *pc)
-| Creates a new CPU context which will use specified SP and PC pointers.
-| Note that SP must be pointing at end of stack area, as it grows upwards.
-|
-_ctx_init:
- moveml %a0/%d0, %sp@- | Save a0/d0
- moveal %sp@(12), %a0 | _ctx_t *
- clrl %a0@+ | A0
- movel %sp@(16), %a0@ | A7/USP u_int32_t *
- movel %sp@(20), %d0 | size_t
- addl %d0, %a0@+ | A7/USP
- clrl %a0@+ | D0
- clrl %a0@+ | D1
- clrl %a0@+ | D2
- clrl %a0@+ | D3
- clrl %a0@+ | D4
- clrl %a0@+ | D5
- clrl %a0@+ | D6
- clrl %a0@+ | D7
- clrl %a0@+ | A1
- clrl %a0@+ | A2
- clrl %a0@+ | A3
- clrl %a0@+ | A4
- clrl %a0@+ | A5
- clrl %a0@+ | A6
- movel %sp@(24), %a0@+ | PC void *
- clrl %a0@ | SR + padding
-| clrw %a0@+ | SR
-| clrw %a0@ | 16-bit 32-bit padding
- moveml %sp@+, %d0/%a0 | Restore d0/a0
- rts
-
-
-| int setjmp(jmp_buf);
-| We can manipulate SP/USP/A7 from user state without problems. We do not need
-| to manipulate SR. We can safely run in usermode.
-| The supplied jmp_buf is actually a pointer to a _ctx_t. We must return 0
-| normally, but must return the value supplied to longjmp() when it is called.
-| To do this, we zero D0, save context to jmp_buf, and return. longjmp() will
-| load that context back and set D0 to the wanted value before returning.
-|
-setjmp:
- movel %a0, %sp@- | Save A0 (4 bytes) to current stack
- moveal %sp@(8), %a0 | Pointer to _ctx_t
- clrl %d0 | Default return value to 0
- lea %a0@(66), %a0 | Pos now after pc
- movel %sp@(6), %a0@- | PC norm at %sp@(2), we saved 4 bytes
- moveml %a1-%a6/%d0-%d7, %a0@- | Save A6-A1, D7-D0
- movel %sp, %a0@ | Save A7/USP (without saved 4 bytes)
- addql #4, %a0@-
- movel %sp@+, %a0@- | Restore A0 and save it
- rts
-
-
-| void longjmp(jmp_buf, int)
-| No need to save registers since we are loading the supplied state.
-| We technically never return, we however make sure to load D0 (which
-| will become setjmp() return value) with the supplied value argument.
-|
-longjmp:
- moveal %sp@(4), %a0 | _ctx_t pointer
- movel %sp@(8), %d0 | Return value for setjmp()
- addql #4, %a0 | Now at usp
- moveal %a0@+, %sp | Restore context stack pointer
- addql #4, %a0 | Now at d1
- moveml %a0@+, %d7-%d1/%a6-%a1 | Load registers from context
- movel %a0@, %sp@(2) | Restore return PC
- moveal %a0@(-68), %a0 | Restore A0
- rts | Resume at setjmp() saved PC
-
-
-| void _idle(void)
-| Puts CPU in sleep mode until next interrupt occurs. Useful to not use 100%
-| CPU time and overheat when all tasks are idle. Also saves alot of power
-| on battery powered systems. Restricted to supervisor mode.
-|
-_idle:
- movew %sr, %sp@-
- stop #0x2000
- movew %sp@+, %sr
- rts
-
-
-| void _usermode(int (*)(void));
-| Useful for port-specific init code to call Xisop main()
-| Permits to switch from supervisor mode to usermode and jump to the specified
-| function. The current supervisor stack is used to setup the user stack (US).
-| The user stack we create is 1024 bytes. Obviously, the SS should have enough
-| room for this. The function we jump to is expected to only perform minor
-| initialization, like to start the first Xisop task, with it's own stack.
-| Used to call Xisop's main() function from init.c
-|
-_usermode:
- moveal %sp@(4), %a0 | Address to jump to
- movel %sp, %a1
- lea %sp@(-1024), %sp
- movel %a1, %usp
- andiw #0xdfff, %sr | Switch to usermode
- jmp %a0@
+++ /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.46 2006/11/26 05:58:55 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
- *
- * 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.46 2006/11/26 05:58:55 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)
- stderr.write("No more free appserv slots\n");
-
- 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 */ stderr.write(this.berrorStr[this.berror]
- + "\n");
- 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 */ stderr.write(
- this.berrorStr[this.berror] + "\n");
- 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) {
- stderr.write(x +
- " at lseek()\n");
- }
- }
- }
- }
- }
- if (res == undefined) {
- res = new HTTPReply(200, 'OK', mimetype);
- this.transfer_size = Math.abs(st.st_size);
- }
-
- /*
- * Flush HTTP header, sending it
- */
- res.flush(this, this.transfer_size);
-
- /*
- * Switch to outbound transfer mode.
- */
- this.transfer_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) {
- stderr.write(x + " in appserv_connect()\n");
- 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 = {};
-}
-
-PollSet.prototype = {
- add: function(fd)
- {
-
- if (this.count > 9999999)
- this.count = this.min + 1;
- fd.fdidx = this.count++;
- this.set[fd.fdidx] = fd;
- },
-
- remove: function(fd)
- {
-
- delete this.set[fd.fdidx];
- }
-};
-
-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) {
- stderr.write(x + " creating VHost object\n");
- }
- }
- /*
- * Attempt to link default_vhost to a VHost object
- */
- if (options.default_vhost == undefined) {
- stderr.write("No default_vhost property in options, exiting\n");
- exit();
- }
- if (vhosts_table[options.default_vhost.toLowerCase()] != undefined)
- default_vhost =
- vhosts_table[options.default_vhost.toLowerCase()];
- else {
- stderr.write('Default vhost ' + options.default_vhost +
- " unkonwn, exiting\n");
- exit();
- }
-
- /*
- * Populate mimetypes database.
- * XXX If this got very large, because the destination strings
- * are rather large, it might be good to use indexes or references
- * to them rather than provide the string for each extension.
- */
- for (i in mimetypes) {
- var i2, ext;
-
- for (i2 in mimetypes[i]) {
- ext = mimetypes[i][i2].toLowerCase();
- if (mimetypes_table[ext] != undefined) {
- stderr.write('Conflicting mime type ' +
- ext + ' -> ' + i + "\n");
- 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) {
- stderr.write(x + " preparing listening socket\n");
- }
- }
- if (pollset.count == 0)
- exit();
-
- /*
- * Reset pollset.min to avoid overwriting static descriptors
- */
- pollset.min = pollset.count;
-
- /*
- * 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.
- */
- fd.close();
- 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) {
- stderr.write(x + " for poll(2)\n");
- continue;
- }
-
- /*
- * Verify if a timeout occurred. Because our poll
- * implementation returns an array, its timeout event can be
- * checked against by verifying if the set is empty.
- */
- if (e.length == 0) {
- /* Timeout */
- continue;
- }
-
- /*
- * XXX We should perhaps check timeouts after, so that we
- * only need to query time once per loop?
- * We could only do this if we knew that all our next
- * processing is non-blocking, however. Otherwise we
- * would loose track of actual time.
- */
- old = cur;
- cur = (Date.parse(new Date) / 1000);
-
- /*
- * Verify if we should call the session gc, and if so, do so.
- * 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
- */
- if (efd.type == STYPE_LISTEN &&
- (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.');
- fd.close();
- continue;
- }
- /*
- * Setup client's initial state and
- * add FD to polling set
- */
- fd.state_http_init(cur);
- pollset.add(fd);
- } catch (x) {
- stderr.write(x + " at accept(2)\n");
- fd.close();
- }
- 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.4 2006/11/27 15:18:17 mmondor Exp $ */
-
-/* Configuration */
-var irc_channel = '#gurumeditation';
-var irc_servers = [
- '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.14 2006/11/30 18:32:48 mmondor Exp $ */
-
-var irc_version = '$Id: irclog.js,v 1.14 2006/11/30 18:32:48 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()] +
- 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;
- 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.8 2006/11/06 04:03:58 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 +
- '">');
- 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 + '"></a>' + "\n");
- }
- fh.write('<br><sub>Thumb gallery generated by: ' +
- '$Id: thumb.js,v 1.8 2006/11/06 04:03:58 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.7 2006/10/28 22:02:52 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.10 2006/10/28 22:02:52 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>
-
-
-
-/*
- * 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;
-} 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) {
- /* 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: 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
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html><head>
-<!-- $Id: contributors.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $
- Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED.
--->
-<title>Matthew Mondor's Software Site - Contributors</title>
-<link rel="shortcut icon" href="/favicon.ico">
-</head>
-
-<!-- Begin -->
-<body bgcolor="#f0f0ff" text="#000000" link="#3535c5" vlink="#700080">
-<table border="0" cellspacing="5" cellpadding="5" width="100%">
-<tbody><tr>
-
-<!-- Left column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Sections</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<p><font face="helvetica, arial" size="-1">
- <a href="index.html">Main</a> <br>
- <a href="software.html">Software</a> <br>
- <a href="donations.html">Donations</a> <br>
- <a href="contributors.html"><em>Contributors</em></a> <br>
- <a href="philosophy.html">Philosophy</a> <br>
- <a href="cvs.html">CVS</a> <br>
- <a href="projects.html">Projects</a> <br>
- <a href="mirrors.html">Mirrors</a> <br>
- <a href="mailto:mmondor@gobot.ca">Contact</a> <br>
-</font></td></tr></tbody></table>
-<br><img src="images/sigil.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</td></p>
-
-<!-- Middle column -->
-<td valign="top">
-<center>
-<font face="helvetica, arial" size="4">MMSoftware Contributors</font><br>
-</center>
-<font face="helvetica, arial">
-<p>
-These contributors generally consist of various mmsoftware users across the
-globe who are developing related third-party software, to add features such as
-frontends and configuration tools. They are free to release their software
-under their prefered licenses and conditions. The contributors maintain
-their own software themselves, so bug reports or suggestions should be sent
-directly to them. The content of their section become their own responsibility.
-To obtain sources for the official mmsoftware releases please visit the
-<a href="software.html">software</a> area.
-</p><p>
-I personally thank the contributors for their interest in my projects.
-</p>
-<table border="1" bgcolor="#d0d0f0" width="100%">
-<tr><td align="center">
-<a href="software/contributors/joostendorp/index.html">Jeroen Oostendorp</a>
-</td></tr>
-<tr><td align="center">
-<a href="software/contributors/ddemaggio/index.html">Daniel DeMaggio</a>
-</td></tr>
-<tr><td align="center">
-<a href="software/contributors/aschlett/index.html">Alexander Schlett</a>
-</td></tr>
-<tr><td align="center">
-<a href="software/contributors/jkbentzen/index.html">Jonas Koch Bentzen</a>
-</td></tr>
-<tr><td align="center">
-<a href="software/contributors/vigityan/index.html">Vahram Igityan</a>
-</td></tr>
-</table>
-</font>
-</td>
-
-<!-- Right column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Languages</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>English</em></a> <br>
- <a href="index_fr.html">French</a> <br>
-</font></td></tr></tbody></table></p>
-<p><font face="helvetica, arial" color="#000066"><b>Mirrors</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="http://mmondor.dynup.net/index.html">Canada</a> <br>
- <a href="http://gobot.accela.net/index.html"><nobr>United-States</nobr></a> <br>
- <a href="http://mmondor.oostendorp-ict.nl/index.html">Holland</a> <br
->
-</font></td></tr></tbody></table></td></p>
-
-<!-- End -->
-</tr><tr><td valign="bottom" align="left" colspan="3">
-<font face="helvetica, arial" size="-2">
-$Id: contributors.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $<br>
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED.
-</font></td></tr></tbody></table>
-</body></html>
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html><head>
-<!-- $Id: cvs.html,v 1.7 2003/12/10 20:33:58 mmondor Exp $
- Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED.
--->
-<title>Matthew Mondor's Software Site - CVS</title>
-<link rel="shortcut icon" href="/favicon.ico">
-</head>
-
-<!-- Begin -->
-<body bgcolor="#f0f0ff" text="#000000" link="#3535c5" vlink="#700080">
-<table border="0" cellspacing="5" cellpadding="5" width="100%">
-<tbody><tr>
-
-<!-- Left column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Sections</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<p><font face="helvetica, arial" size="-1">
- <a href="index.html">Main</a> <br>
- <a href="software.html">Software</a> <br>
- <a href="donations.html">Donations</a> <br>
- <a href="contributors.html">Contributors</a> <br>
- <a href="philosophy.html">Philosophy</a> <br>
- <a href="cvs.html"><em>CVS</em></a> <br>
- <a href="projects.html">Projects</a> <br>
- <a href="mirrors.html">Mirrors</a> <br>
- <a href="mailto:mmondor@gobot.ca">Contact</a> <br>
-</font></td></tr></tbody></table>
-<br><img src="images/sigil.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</td></p>
-
-<!-- Middle column -->
-<td valign="top">
-<center>
-<font face="helvetica, arial" size="4">MMSoftware CVS Repository</font><br>
-</center>
-<font face="helvetica, arial">
-<p>
-Note that the CVS repository contains the latest actual development tree
-and should not be used by the general public, it is mostly intended for
-developpers, maintainers and beta-testers. There also can be found some
-software which never have previously been released in the form of an archive.
-</p><p>
-The HTTP cvsweb interface to the CVS repository is no longer available at
-present time. However, read-only public CVS pserver access is provided.
-I highly suggest getting aquainted with the command-line cvs(1) utility
-for best results.
-</p><p>
-Those who want to access the repository via public pserver to access all
-of my BSD-style licensed open source software can use the following command:
-</p><p><font face="times" size="-1">
-% cvs -z6 -d:pserver:anoncvs@cvs.accela.net:/cvsroot co mmondor
-</font></p><p>
-To only obtain the Xisop kernel, <em>mmondor/Xisop</em> may be used as the
-module name. To only retreive the mmsoftware directory,
-<em>mmondor/mmsoftware</em> will be used.</p><p>
-The service is provided using the cvs(1), mmspawnd(8) and mmanoncvs(8)
-utilities as an alternative to the common setup using inetd(8). This
-ensures a secure public pserver setup which cannot affect the original
-repositories or the rest of the system if exploited.
-</font>
-</td>
-
-<!-- Right column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Languages</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>English</em></a> <br>
- <a href="index_fr.html">French</a> <br>
-</font></td></tr></tbody></table></p>
-<p><font face="helvetica, arial" color="#000066"><b>Mirrors</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="http://mmondor.dynup.net/index.html">Canada</a> <br>
- <a href="http://gobot.accela.net/index.html"><nobr>United-States</nobr></a> <br>
- <a href="http://mmondor.oostendorp-ict.nl/index.html">Holland</a> <br
->
-</font></td></tr></tbody></table></td></p>
-
-<!-- End -->
-</tr><tr><td valign="bottom" align="left" colspan="3">
-<font face="helvetica, arial" size="-2">
-$Id: cvs.html,v 1.7 2003/12/10 20:33:58 mmondor Exp $<br>
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED.
-</font></td></tr></tbody></table>
-</body></html>
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html><head>
-<!-- $Id: donations.html,v 1.6 2003/06/04 12:17:47 mmondor Exp $
- Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED.
--->
-<title>Matthew Mondor's Software Site - Donations</title>
-<link rel="shortcut icon" href="/favicon.ico">
-</head>
-
-<!-- Begin -->
-<body bgcolor="#f0f0ff" text="#000000" link="#3535c5" vlink="#700080">
-<table border="0" cellspacing="5" cellpadding="5" width="100%">
-<tbody><tr>
-
-<!-- Left column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Sections</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<p><font face="helvetica, arial" size="-1">
- <a href="index.html">Main</a> <br>
- <a href="software.html">Software</a> <br>
- <a href="donations.html"><em>Donations</em></a> <br>
- <a href="contributors.html">Contributors</a> <br>
- <a href="philosophy.html">Philosophy</a> <br>
- <a href="cvs.html">CVS</a> <br>
- <a href="projects.html">Projects</a> <br>
- <a href="mirrors.html">Mirrors</a> <br>
- <a href="mailto:mmondor@gobot.ca">Contact</a> <br>
-</font></td></tr></tbody></table>
-<br><img src="images/sigil.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</td></p>
-
-<!-- Middle column -->
-<td valign="top">
-<center>
-<font face="helvetica, arial" size="4">Donations to support MMSoftware</font><br>
-</center>
-<font face="helvetica, arial">
-<p>
-Free opensource software projects need the support of their users to evolve.
-Programmers spend alot of time trying to make the code efficient, bug-free,
-and user-friendly. In an area where security is a primary concern, like for
-public internet services, the time required to roll safe code is often higher
-than it would be for some other projects. There are design, coding, testing,
-debugging, and auditing sessions. And at times even restructuring when
-required to allow the software to be more manageable for the forthcoming new
-features it should integrate.
-</p><p>
-There are many ways one can use to support the project. One is to help it
-maintain public exposure by providing hosting and bandwidth for a mirror.
-This obviously only should be done on stable connections, it can then become
-useful for people to find a mirror closer to their location, and so that at
-all times at least one mirror remains up if others are ever temporarily down.
-The mmondor.gobot.ca DNS pool of addresses can then be updated once a day,
-verifying the availability and stability of the mirrors. An FTP account or
-other method can be used to mirror the site regularly.
-</p><p>
-Another way is to audit the software against potential security, portability
-issues and bugs, as well as test the software. Beta testers are welcome to
-track the CVS repository tree and to propose diff/patches whenever necessary.
-Also considered a donation is the time other programmers can put in
-contributing third party software related to the projects. Although this may
-not necessarily affect mmsoftware directly, some of that software may be very
-useful for many mmsoftware users.
-</p><p>
-It is also possible to sponsor the work on a particular opensource project or
-feature of one of the existing project by material and/or money donations.
-For example, one may want the software to support another UNIX-style system
-for which the software does not currently work for some reason, and provide
-the necessary hardware and/or software tools to allow to do it.
-</p><p>
-As we are mostly dealing with BSD licensed software it is also possible to
-hire us in order to develop a closed-source implementation with
-custom-specific needs of one of the existing projects. The customer would
-decide whether or not the new branch should eventually be donated, back to the
-main public repository, to fall back under the same BSD-license as well
-(possibly with an advertising clause in the license text).
-</p><p>
-Finally, a thing which is always encouraging and doesn't cost much, is to
-send positive feedback, via email or on freshmeat, about the features you
-enjoy in mmsoftware, if you decide to use it. Gifts are also welcome.
-</p><p>
-We appreciate your contribution to our common goal. We would like to assure
-you that we use it to the best of our efforts to help the project evolve.
-</p><p><font size="-1">
-Matthew Mondor
-</p>
-</font>
-</td>
-
-<!-- Right column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Languages</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>English</em></a> <br>
- <a href="index_fr.html">French</a> <br>
-</font></td></tr></tbody></table></p>
-<p><font face="helvetica, arial" color="#000066"><b>Mirrors</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="http://mmondor.dynup.net/index.html">Canada</a> <br>
- <a href="http://gobot.accela.net/index.html"><nobr>United-States</nobr></a> <br>
- <a href="http://mmondor.oostendorp-ict.nl/index.html">Holland</a> <br
->
-</font></td></tr></tbody></table></td></p>
-
-<!-- End -->
-</tr><tr><td valign="bottom" align="left" colspan="3">
-<font face="helvetica, arial" size="-2">
-$Id: donations.html,v 1.6 2003/06/04 12:17:47 mmondor Exp $<br>
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED.
-</font></td></tr></tbody></table>
-</body></html>
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html><head>
-<!-- $Id: index.html,v 1.9 2003/09/30 05:34:53 mmondor Exp $
- Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED.
--->
-<title>Matthew Mondor's Software Site - Main</title>
-<link rel="shortcut icon" href="/favicon.ico">
-</head>
-
-<!-- Begin -->
-<body bgcolor="#f0f0ff" text="#000000" link="#3535c5" vlink="#700080">
-<table border="0" cellspacing="5" cellpadding="5" width="100%">
-<tbody><tr>
-
-<!-- Left column -->
-<p><td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Sections</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>Main</em></a> <br>
- <a href="software.html">Software</a> <br>
- <a href="donations.html">Donations</a> <br>
- <a href="contributors.html">Contributors</a> <br>
- <a href="philosophy.html">Philosophy</a> <br>
- <a href="cvs.html">CVS</a> <br>
- <a href="projects.html">Projects</a> <br>
- <a href="mirrors.html">Mirrors</a> <br>
- <a href="mailto:mmondor@gobot.ca">Contact</a> <br>
-</font></td></tr></tbody></table>
-<br><img src="images/sigil.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</td></p>
-
-<!-- Middle column -->
-<td valign="top">
-<center>
-<font face="helvetica, arial" size="5">The MMSoftware Project</font><br>
-<a href="http://mmondor.gobot.ca">
-<img border="0" src="images/key.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</a>
-</center>
-<font face="helvetica, arial">
-<p>
-<b>NOTE:</b> The french version is not yet available. Additionally, only
-the master site currently works, mirrors should be up soon.
-</p><p>
-This site provides various opensource <a href="software.html">software</a>
-to anyone who find it useful. The projects mostly were written from scratch by
-Matthew Mondor and released under a BSD-style license, but we also host
-third-party software related to these projects, provided by other
-<a href="contributors.html">contributors</a> worldwide who wanted to make
-their related work freely available.
-</p><p>
-The hosting and bandwidth for the <a href="mirrors.html">mirrors</a> is
-provided by various <a href="donations.html">donators</a> accross the globe.
-Although we attempt to encourage contributors and donators, we try to keep
-this site as free from commercial ads as possible as a convenience to our
-users. The main mmsoftware project <a href="philosophy.html">philosophy</a>
-will describe our goals with more details.
-</p><p>
-Matthew Mondor can be contacted via email at
-<a href="mailto:mmondor@gobot.ca">mmondor@gobot.ca</a> for suggestions,
-contributions, donations, flames, thanks, business and bug reports.
-</p>
-</font>
-</td>
-
-<!-- Right column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Languages</b></font><br>
-<p><table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>English</em></a> <br>
- <a href="index_fr.html">French</a> <br>
-</font></td></tr></tbody></table></p>
-<p><font face="helvetica, arial" color="#000066"><b>Mirrors</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="http://mmondor.dynup.net/index.html">Canada</a> <br>
- <a href="http://gobot.accela.net/index.html"><nobr>United-States</nobr></a> <br>
- <a href="http://mmondor.oostendorp-ict.nl/index.html">Holland</a> <br
->
-</font></td></tr></tbody></table></td>
-
-<!-- End -->
-</tr><tr><td valign="bottom" align="left" colspan="3">
-<font face="helvetica, arial" size="-2">
-$Id: index.html,v 1.9 2003/09/30 05:34:53 mmondor Exp $<br>
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED.
-</font></td></tr></tbody></table>
-</body></html>
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html><head>
-<!-- $Id: mirrors.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $
- Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED.
--->
-<title>Matthew Mondor's Software Site - Mirrors</title>
-<link rel="shortcut icon" href="/favicon.ico">
-</head>
-
-<!-- Begin -->
-<body bgcolor="#f0f0ff" text="#000000" link="#3535c5" vlink="#700080">
-<table border="0" cellspacing="5" cellpadding="5" width="100%">
-<tbody><tr>
-
-<!-- Left column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Sections</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<p><font face="helvetica, arial" size="-1">
- <a href="index.html">Main</a> <br>
- <a href="software.html">Software</a> <br>
- <a href="donations.html">Donations</a> <br>
- <a href="contributors.html">Contributors</a> <br>
- <a href="philosophy.html">Philosophy</a> <br>
- <a href="cvs.html">CVS</a> <br>
- <a href="projects.html">Projects</a> <br>
- <a href="mirrors.html"><em>Mirrors</em></a> <br>
- <a href="mailto:mmondor@gobot.ca">Contact</a> <br>
-</font></td></tr></tbody></table>
-<br><img src="images/sigil.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</td></p>
-
-<!-- Middle column -->
-<td valign="top">
-<center>
-<font face="helvetica, arial" size="4">MMSoftware Mirrors</font><br>
-</center>
-<font face="helvetica, arial">
-<p>
-Here are the current site mirrors and their generous providers, appearing
-in setup chronological order (first setup to last):
-</p>
-<table border="1" bgcolor="#d0d0f0" width="100%">
-<tr><th>Location</th><th>Provider</th><th>URL</th></tr>
-<tr>
-<td align="center">United-States</td>
-<td align="center">Matthew J Backes</td>
-<td align="center"><font size="-1"><a href="http://gobot.accela.net">http://gobot.accela.net</a></font></td>
-</tr><tr>
-<td align="center">Holland</td>
-<td align="center">Jeroen Oostendorp</td>
-<td align="center"><font size="-1"><a href="http://mmondor.oostendorp-ict.nl">http://mmondor.oostendorp-ict.nl</a></font></td>
-</tr><tr>
-<td align="center">Canada</td>
-<td align="center">Ryan Werber</td>
-<td align="center"><font size="-1"><a href="http://mmondor.dynup.net">http://mmondor.dynup.net</a></font></td>
-</tr>
-</table>
-<p>
-If you would like to provide hosting space and bandwidth for a new mirror,
-this would consist of a service donation, please consult the
-<a href="donations.html">donations</a> area for more information about the
-requirements and suggestions.
-</p>
-</font>
-</td>
-
-<!-- Right column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Languages</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>English</em></a> <br>
- <a href="index_fr.html">French</a> <br>
-</font></td></tr></tbody></table></p>
-<p><font face="helvetica, arial" color="#000066"><b>Mirrors</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="http://mmondor.dynup.net/index.html">Canada</a> <br>
- <a href="http://gobot.accela.net/index.html"><nobr>United-States</nobr></a> <br>
- <a href="http://mmondor.oostendorp-ict.nl/index.html">Holland</a> <br
->
-</font></td></tr></tbody></table></td></p>
-
-<!-- End -->
-</tr><tr><td valign="bottom" align="left" colspan="3">
-<font face="helvetica, arial" size="-2">
-$Id: mirrors.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $<br>
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED.
-</font></td></tr></tbody></table>
-</body></html>
+++ /dev/null
-Because there is a general layout already for the site (left column with
-a main menu, center column with the content for the current page and
-right column for the language menu), I should design something which uses
-templates or such. I did so for the spes project a while back, but which
-dynamically generated the pages, using php.
-
-I want something with which I can simply type a make command for it to
-generate the static html pages. There would be a list of languages and
-for each the process would be automatically repeated, also. For each
-language, a left main menu and right language selection menu would be
-generated, and it's center column contents generated from a template.
-Example of a possible layout:
-
-build/
-build/lang/english
-build/lang/english/menu_main.txt
-build/lang/english/menu_lang.txt
-build/lang/english/page_index.txt
-build/lang/english/page_cvs.txt
-build/lang/english/page_software.txt
-build/lang/english/page_philosophy.txt
-build/lang/english/soft_descriptions.txt
-build/soft_releases.txt
-build/languages.txt
-htdocs/
-
-The files would be generated into htdocs/
-The soft_descriptions.txt file would hold a map of software name->short
-description -> long description, to be used by build/soft_releases.txt in
-lang/page_software.txt .
-build/languages would hold a list of available languages.
-
-The format of the manu_*.txt files would be as follows:
-
-"<Menu title>
-"<Filename>" "<Menu description>"
-
-I.E.
-
-"Sections"
-"index" "Main"
-"software" "Software"
-"donations" "Donations"
-"contributors" "Contributors"
-"philosophy" "Philosophy"
-"cvs" "CVS"
-"projects" "Projects"
-"mirrors" "Mirrors"
-"contact" "Contact"
-
-The filename will be suffixed with the language suffix in the htdocs/
-directory, and the file will be generated from the language/page_<filename>.txt
-file.
-
-The format of the languages.txt file would be as follows:
-
-<Suffix> <Directory>
-
-I.E.
-
-en english
-fr french
-ru russian
-
-The format of the page_*.txt files would be standard HTML, but which could
-contain special keywords. The only current special keyword consists of:
-.soft releases
-which should be beginning at the first column, as-is, on a single line.
-This keyword causes the soft_releases.txt file to be parsed with the
-language/soft_descriptions.txt, to create a table of all the available
-software.
-It is possible to do:
-.soft maintained
-.soft unmaintained
-etc.
-
-I also need a special command for mirrors table. And one for links.
-The one for links would automatically append the language suffix and
-html extension...
-
-.link <name> <show>
-.mirrors
-
-A similar system for FAQs:
-
-.faq <name>
-
-
-TODO
-====
-
-- Verify apache multilingual support to use the same convention for
- page storage...
+++ /dev/null
-# $Id: GNUmakefile,v 1.1 2004/04/30 00:05:53 mmondor Exp $
-
-MMLIBS := $(addprefix ../../../mmsoftware/mmlib/,mmpool.o mmlog.o \
-mmreadcfg.o mmstring.o mmhash.o)
-
-OBJS := mmsite.o
-
-CFLAGS += -Wall -DDEBUG
-
-
-all: mmsite
-
-%.o: %.c
- cc -c ${CFLAGS} -I. -I../../../mmsoftware/mmlib -o $@ $<
-
-mmsite: $(MMLIBS) $(OBJS)
- cc -o $@ $(OBJS) -lc $(MMLIBS)
-
-clean:
- rm -f mmsite $(OBJS) $(MMLIBS)
+++ /dev/null
-It was chosen to use a simple system based on text files which could be
-used with CVS rather than another type of database to build the site's
-HTML files from. This allows contributors to simply send a diff generated
-by CVS containing their update suggestions and additions. Also, generating
-a static site has advantages, allowing cacheing for efficiency by HTTP
-cache proxies, and easing the task of mirrors updating their copy of the site.
-
-This system makes it easy to maintain the list of available software, as
-well as to translate the site to other languages, while avoiding HTML
-formatting and linking bugs. It also enforces the site's general layout
-through all the site pages. The generated pages are made from an W3C validated
-template for HTML 4.0 Transitional.
-
-The default language causes the pages to have no special suffix, while
-the others will have the short description of the language appended before
-the '.html' extension.
+++ /dev/null
-- Although it is nice to have a few hash tables for fast lookup, some
- files need to be considered as sequencial lists and will be have to
- be processed accordingly (using a hash table causes the entries to
- become in an arbitrary order). Actually, most or all of the base files
- are lists, while language-specific ones are tables for fast lookup
- when mapping.
-
-- Work out a system for FAQs similar to the one for software.
-
-- Work out a system for news. This would be nice to announce releases etc
+++ /dev/null
-# First line consists of FAQ .title
-# Then following are .section <num> <title> for a section,
-# .question <num> <title> for a question. Each can be followed by
-# arbitrary HTML, and an index will be automatically generated.
-#
-.title "Frequently asked questions about mmftpd"
-This FAQ deals with most common problems which first time users encounter.
-.section "Installation"
-This section deals exclusively with mmftpd installation.
-.question "How do I compile mmftpd?"
-Answer to the question
-.question "Another question?"
-Another answer.
-.section "Configuration"
-This section deals with mmftpd configuration.
-.question "Blah??"
-Answer!?
+++ /dev/null
-# One line per entry, two columns:
-# <language> <description>
-#
-MENU Languages
-english English
-french French
+++ /dev/null
-MENU Sections
-index Main
-software Software
-donations Donations
-contributors Contributors
-philosophy Philosophy
-cvs CVS
-projects Projects
-mirrors Mirrors
-contact Contact
+++ /dev/null
-# One line per entry, two columns:
-# <name> <description>
-#
-MENU Mirrors
-canada Canada
-united-states United-States
-holland Holland
+++ /dev/null
-<p><b>NOTE:</b> The French version of this site is not yet available.
-Additionally, only the master site currently works, mirrors should
-be back up eventually.</p>
-<p>This site provides various open source
-.link software software
-to anyone who finds it useful. The projects mostly were written from
-scratch by Matthew Mondor and released under a BSD-style license,
-but we also host third-party software related to these projects,
-provided by other
-.link contributors contributors
-worldwide who wanted to make their related work freely available.</p>
-<p>The hosting and bandwidth for the
-.link mirrors mirrors
-is provided by various
-.link donations donators
-accross the globe. Although we attempt to encourage contributors and
-donators, we try to keep this site as free from commercial ads as
-possible as a convenience to our users. The main mmsoftware project
-.links philosophy philosophy will describe our goals with more details.</p>
-<p>Matthew Mondor can be contacted via email at
-<a href="mailto:mmondor@accela.net">mmondor@accela.net</a> for suggestions,
-contributions, donations, flames, thanks, business and bug reports.</p>
+++ /dev/null
-# Each project is separated by an empty line. The first line of a project
-# consists of it's name, the second line of it's short description, and
-# the following lines, in HTML format, of the long description, ending
-# with an empty line.
-#
-mmftpd
-Unprivileged virtual users FTP server
-Long multiline description follows in HTML, until empty line.
-So it continues...
-And still...
-
-ginseng-ftpd
-blah
-blah
-
+++ /dev/null
-/* $Id: mmsite.c,v 1.1 2004/04/30 00:05:53 mmondor Exp $ */
-
-/*
- * Copyright (C) 2003, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* This program builds the site in htdocs/. Please read mmsite.8 for more
- * information on the required layout. This is a helper to generate correct
- * HTML, translate the site into multiple languages and build the software
- * and faq indexes and tables, using static pages only. This then allows
- * acceleration using cacheing HTTP proxies and helps to more easily provide
- * a straightforward method for mirror providers.
- */
-
-
-
-/* HEADERS */
-
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <mmtypes.h>
-#include <mmstring.h>
-#include <mmpool.h>
-#include <mmlist.h>
-#include <mmhash.h>
-
-
-
-/* DEFINITIONS */
-
-/* The following system allows to load arbitrary tables from files. The
- * table field is only used for language map to the project descriptions.
- */
-#define COLS_MAX 4
-#define LINE_MAX 256
-
-typedef struct table_node {
- hashnode_t node;
- char line[LINE_MAX];
- char *columns[COLS_MAX];
- size_t lengths[COLS_MAX];
- hashtable_t *table;
-} table_node;
-
-/* And for linked lists, where order is necessary */
-typedef struct list_node {
- node_t node;
- char line[LINE_MAX];
- char *columns[COLS_MAX];
- size_t lengths[COLS_MAX];
- u_int32_t hash;
-} list_node;
-
-/* The following are definitions helping to use each of our known tables.
- * Let's start with the global ones:
- * --------------------------------
- */
-
-/* table_languages */
-enum columns_languages {
- LANGUAGES_NAME = 0,
- LANGUAGES_MAX
-};
-#define LANGUAGES_KEY LANGUAGES_NAME
-
-/* table_mirrors */
-enum columns_mirrors {
- MIRRORS_NAME = 0,
- MIRRORS_URL,
- MIRRORS_CONTRIBUTOR,
- MIRRORS_MAX
-};
-#define MIRRORS_KEY MIRRORS_URL
-
-/* table_pages */
-enum columns_pages {
- PAGES_NAME = 0,
- PAGES_MAX
-};
-#define PAGES_KEY PAGES_NAME
-
-/* table_software */
-enum columns_software {
- SOFTWARE_CATEGORY = 0,
- SOFTWARE_DIRECTORY,
- SOFTWARE_MAX
-};
-#define SOFTWARE_KEY SOFTWARE_CATEGORY
-
-/* For each of table_software, projects */
-enum columns_projects {
- PROJECTS_NAME = 0,
- PROJECTS_STATUS,
- PROJECTS_VERSION,
- PROJECTS_MAX
-};
-#define PROJECTS_KEY PROJECTS_NAME
-
-/* Following are the language-specific tables:
- * ------------------------------------------
- */
-
-/* table_menu_languages */
-enum columns_menu_languages {
- MENU_LANGUAGES_NAME = 0,
- MENU_LANGUAGES_DESCR
-};
-
-#define MENU_LANGUAGES_KEY MENU_LANGUAGES_NAME
-
-/* This structure is hold to hold nodes in project description tables */
-
-#define TDN_PROJECT_MAX 32
-#define TDN_DESCR_S_MAX 256
-#define TDN_DESCR_L_MAX 2048
-
-typedef struct table_descr_node {
- hashnode_t node;
- char project[TDN_PROJECT_MAX];
- char descr_s[TDN_DESCR_S_MAX];
- char descr_l[TDN_DESCR_L_MAX];
-} table_descr_node;
-
-
-
-/* PROTOTYPES */
-
-int main(void);
-
-static hashtable_t *table_load(const char *, unsigned int, unsigned int);
-static hashtable_t *table_descr_load(const char *);
-static hashtable_t *table_free(hashtable_t *);
-static list_t *list_load(const char *, unsigned int, unsigned int);
-static list_t *list_free(list_t *);
-
-
-
-/* GLOBALS */
-
-static pool_t table_pool, table_descr_pool, list_pool;
-static hashtable_t *table_languages = NULL, *table_descriptions = NULL;
-
-static list_t *list_software = NULL;
-
-
-
-/* CODE */
-
-int main(void)
-{
- /* First initialize our fast memory management pools */
- if (!pool_init(&table_pool, malloc, free, sizeof(table_node),
- 65536 / sizeof(table_node), 0, 0)) {
- (void) fprintf(stderr, "main() - pool_init(table_pool)\n");
- return EXIT_FAILURE;
- }
- if (!pool_init(&table_descr_pool, malloc, free, sizeof(table_descr_node),
- 65536 / sizeof(table_descr_node), 0, 0)) {
- (void) fprintf(stderr, "main() - pool_init(table_descr_pool)\n");
- return EXIT_FAILURE;
- }
- if (!pool_init(&list_pool, malloc, free, sizeof(list_node),
- 65536 / sizeof(list_node), 0, 0)) {
- (void) fprintf(stderr, "main() - pool_init(list_pool)\n");
- return EXIT_FAILURE;
- }
-
- /* Load global lists */
- if ((list_languages = list_load("site_languages.txt", LANGUAGES_MAX,
- LANGUAGES_KEY)) == NULL) {
- (void) fprintf(stderr, "main() - list_load(site_languages.txt)\n");
- return EXIT_FAILURE;
- }
- if ((list_mirrors = list_load("site_mirrors.txt", MIRRORS_MAX,
- MIRRORS_KEY)) == NULL) {
- (void) fprintf(stderr, "main() - list_load(site_mirrors.txt)\n");
- return EXIT_FAILURE;
- }
-
- /* Load required lists */
- if ((table_languages = table_load("site_languages.txt", LANGUAGES_MAX,
- LANGUAGES_KEY)) == NULL) {
- (void) fprintf(stderr, "main() - table_load(languages)\n");
- return EXIT_FAILURE;
- }
- if ((table_descriptions = table_descr_load(
- "english/soft_descriptions.txt")) == NULL) {
- (void) fprintf(stderr, "main() - table_descr_load(descriptions)\n");
- return EXIT_FAILURE;
- }
- if ((list_software = list_load("site_software.txt", SOFTWARE_MAX,
- SOFTWARE_KEY)) == NULL) {
- (void) fprintf(stderr, "main() - list_load(software)\n");
- return EXIT_FAILURE;
- } else {
- list_node *node;
-
- for (node = DLIST_TOP(list_software); node != NULL;
- node = DLIST_NEXT(node))
- (void) printf("'%s|%s'\n", node->columns[SOFTWARE_CATEGORY],
- node->columns[SOFTWARE_DIRECTORY]);
- }
-
- {
- table_node *node;
-
- if ((node = (table_node *)hashtable_lookup(table_languages,
- "english", 7))
- != NULL)
- (void) printf("%s\n", node->columns[LANGUAGES_NAME]);
- else
- (void) printf("'english' not found!\n");
- }
- {
- table_descr_node *node;
-
- if ((node = (table_descr_node *)hashtable_lookup(table_descriptions,
- "mmftpd", 6)) != NULL) {
- (void) printf("Project: %s\n", node->project);
- (void) printf("Descr_s: %s\n", node->descr_s);
- (void) printf("Descr_l: %s", node->descr_l);
- } else
- (void) printf("'mmftpd' not found!\n");
- }
-
- table_languages = table_free(table_languages);
- return EXIT_SUCCESS;
-}
-
-
-/* Loads a hash table from the specified text file which holds one entry
- * per line and <cols> expected columns each. Links each entry to the table
- * using <key> column as the lookup key. Returns the hashtable_t pointer on
- * success, or NULL.
- */
-static hashtable_t *table_load(const char *file, unsigned int cols,
- unsigned int key)
-{
- hashtable_t *table = NULL;
- table_node *node = NULL;
- FILE *fh;
-
- if (cols >= COLS_MAX || key >= COLS_MAX) {
- (void) fprintf(stderr, "table_load(%s) - cols|key >= COLS_MAX!\n",
- file);
- return NULL;
- }
-
- if ((fh = fopen(file, "r")) != NULL) {
- if ((table = malloc(sizeof(hashtable_t))) != NULL) {
- if (hashtable_init(table, HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR,
- malloc, free, mm_memcmp, hashtable_hash, TRUE)) {
- for (;;) {
- int i;
- size_t len;
-
- if (node == NULL && (node = (table_node *)pool_alloc(
- &table_pool, FALSE)) == NULL) {
- (void) fprintf(stderr,
- "table_load(%s) - pool_alloc()\n",
- file);
- break;
- }
- if (fgets(node->line, LINE_MAX - 1, fh) == NULL) {
- node = (table_node *)pool_free((pnode_t *)node);
- break;
- }
- len = mm_strlen(node->line);
- if (len > 0 && node->line[len - 1] == '\n')
- len--;
- if (len > 0 && node->line[len - 1] == '\r')
- len--;
- node->line[len] = '\0';
- if (*node->line == '#' || *node->line == '\0')
- continue;
- if (mm_strasplq(node->columns, node->line, cols) != cols) {
- (void) fprintf(stderr,
- "table_load(%s) - Invalid line '%s'\n",
- file, node->line);
- break;
- }
- for (i = 0; i < cols; i++)
- node->lengths[i] = mm_strlen(node->columns[i]);
- node->table = NULL;
- if (!hashtable_link(table, (hashnode_t *)node,
- node->columns[key], node->lengths[key])) {
- (void) fprintf(stderr,
- "table_load(%s) - link key '%s'\n",
- file, node->columns[key]);
- break;
- }
- node = NULL;
- }
- if (node != NULL)
- table = table_free(table);
- } else {
- (void) fprintf(stderr, "table_load(%s) - hashtable_init()\n",
- file);
- free(table);
- table = NULL;
- }
- } else
- (void) fprintf(stderr, "table_load(%s) - malloc(table)\n",
- file);
- (void) fclose(fh);
- }
-
- return table;
-}
-
-/* This function loads another type of file-based table. For each entry,
- * the first line consists of the project name, the second of the short
- * project description, and the following lines of the long description
- * with allowed embedded HTML for emphasis, ending with an empty line.
- */
-static hashtable_t *table_descr_load(const char *file)
-{
- hashtable_t *table = NULL;
- table_descr_node *node = NULL;
- FILE *fh;
- char line[TDN_DESCR_S_MAX];
-
- if ((fh = fopen(file, "r")) != NULL) {
- if ((table = malloc(sizeof(hashtable_t))) != NULL) {
- if (hashtable_init(table, HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR,
- malloc, free, mm_memcmp, hashtable_hash, TRUE)) {
- int field = 0;
- size_t descr_l_len = 0, project_len = 0;
-
- for (;;) {
- size_t len;
-
- if (node == NULL) {
- if ((node = (table_descr_node *)pool_alloc(
- &table_descr_pool, FALSE)) == NULL) {
- (void) fprintf(stderr,
- "table_descr_load(%s) - pool_alloc()\n",
- file);
- break;
- }
- /* New entry */
- field = 0;
- descr_l_len = project_len = 0;
- }
- if (fgets(line, TDN_DESCR_S_MAX - 1, fh) == NULL) {
- if (field == 0)
- node = (table_descr_node *)pool_free(
- (pnode_t *)node);
- else
- (void) fprintf(stderr, "table_descr_load(%s) - \
-Incomplete entry for '%s'\n", file, node->project);
- break;
- }
- len = mm_strlen(line);
- if (len > 0 && line[len - 1] == '\n')
- len--;
- if (len > 0 && line[len - 1] == '\r')
- len--;
- line[len] = '\0';
- if (*line == '#' && field == 0)
- continue;
- if (len == 0) {
- /* Empty line, ignore if between two entries,
- * consider it as entry termination if we filled
- * them all, generate an error otherwise.
- */
- if (field == 0)
- continue;
- else if (field == 2) {
- /* Link entry and force new entry creation */
- if (!hashtable_link(table, (hashnode_t *)node,
- node->project, project_len)) {
- (void) fprintf(stderr, "table_descr_load(%s) \
-- hashtable_link(%s)\n", file, node->project);
- break;
- }
- node = NULL;
- continue;
- } else {
- (void) fprintf(stderr, "table_descr_load(%s) - \
-Incomplete entry for '%s'\n", file, node->project);
- break;
- }
- }
- if (field == 0) {
- /* Project field entry */
- if (len > TDN_PROJECT_MAX - 1) {
- (void) fprintf(stderr, "table_descr_load(%s) - \
-Project field exceeds %d for '%s'\n", file, TDN_PROJECT_MAX - 1,
- node->project);
- break;
- }
- mm_memcpy(node->project, line, len + 1);
- project_len = len;
- field = 1;
- continue;
- } else if (field == 1) {
- /* Short description entry */
- if (len > TDN_DESCR_S_MAX - 1) {
- (void) fprintf(stderr, "table_descr_load(%s) - \
-Short description field exceeds %d for '%s'\n", file, TDN_DESCR_S_MAX - 1,
- node->project);
- break;
- }
- mm_memcpy(node->descr_s, line, len + 1);
- field = 2;
- continue;
- } else if (field == 2) {
- /* Long description entry */
- if (descr_l_len + len + 1 > TDN_DESCR_L_MAX - 1) {
- (void) fprintf(stderr, "table_descr_load(%s) - \
-Long description field exceeds %d for '%s'\n", file, TDN_DESCR_L_MAX - 1,
- node->project);
- break;
- }
- mm_memcpy(&node->descr_l[descr_l_len], line, len);
- descr_l_len += len;
- node->descr_l[descr_l_len++] = '\n';
- continue;
- }
- }
- if (node != NULL)
- table = table_free(table);
- } else {
- (void) fprintf(stderr,
- "table_descr_load(%s) - hashtable_init()\n",
- file);
- free(table);
- table = NULL;
- }
- } else
- (void) fprintf(stderr, "table_descr_load(%s) - malloc(table)\n",
- file);
- (void) fclose(fh);
- }
-
- return table;
-}
-
-static hashtable_t *table_free(hashtable_t *table)
-{
- if (table != NULL) {
- (void) hashtable_destroy(table, TRUE);
- free(table);
- }
-
- return NULL;
-}
-
-
-static list_t *list_load(const char *file, unsigned int cols, unsigned int key)
-{
- list_t *list = NULL;
- list_node *node = NULL;
- FILE *fh;
-
- if (cols >= COLS_MAX || key >= COLS_MAX) {
- (void) fprintf(stderr, "list_load(%s) - cols|key >= COLS_MAX!\n",
- file);
- return NULL;
- }
-
- if ((fh = fopen(file, "r")) != NULL) {
- if ((list = malloc(sizeof(list_t))) != NULL) {
- DLIST_INIT(list);
- for (;;) {
- int i;
- size_t len;
-
- if (node == NULL && (node = (list_node *)pool_alloc(&list_pool,
- FALSE)) == NULL) {
- (void) fprintf(stderr,
- "list_load(%s) - pool_alloc()\n",
- file);
- break;
- }
- if (fgets(node->line, LINE_MAX - 1, fh) == NULL) {
- node = (list_node *)pool_free((pnode_t *)node);
- break;
- }
- len = mm_strlen(node->line);
- if (len > 0 && node->line[len - 1] == '\n')
- len--;
- if (len > 0 && node->line[len - 1] == '\r')
- len--;
- node->line[len] = '\0';
- if (*node->line == '#' || *node->line == '\0')
- continue;
- if (mm_strasplq(node->columns, node->line, cols) != cols) {
- (void) fprintf(stderr,
- "list_load(%s) - Invalid line '%s'\n",
- file, node->line);
- break;
- }
- for (i = 0; i < cols; i++)
- node->lengths[i] = mm_strlen(node->columns[i]);
- node->hash = hashtable_hash(node->columns[key],
- node->lengths[key]);
- DLIST_APPEND(list, (node_t *)node);
- node = NULL;
- }
- if (node != NULL)
- list = list_free(list);
- } else
- (void) fprintf(stderr, "list_load(%s) - malloc(list)\n",
- file);
- (void) fclose(fh);
- }
-
- return list;
-}
-
-static list_t *list_free(list_t *list)
-{
- if (list != NULL) {
- node_t *node, *tnode;
-
- for (node = DLIST_TOP(list); node != NULL; node = tnode) {
- tnode = DLIST_NEXT(node);
- (void) pool_free((pnode_t *)node);
- }
- free(list);
- }
-
- return NULL;
-}
+++ /dev/null
-# General configuration options about the site
-
-TITLE "Matthew Mondor's Software Site"
-BGCOLOR #f0f0ff
-TEXT #000000
-LINK #3535c5
-VLINK #700080
-MENU_BGCOLOR #d0d0f0
-MENU_TEXT #000066
-FONT "helvetica, arial"
-HTDOCS ../htdocs
-DEFAULT_LANG en
+++ /dev/null
-# One FAQ entry per line, one column:
-# <faq>
-#
-mmftpd
-mmmail
-mmstatd
+++ /dev/null
-# One language description per line, one column:
-# <language>
-#
-english
-french
+++ /dev/null
-# One line per entry, three columns:
-# <name> <URL> <provider>
-#
-canada http://mmondor.dynup.net/ "Ryan Werber"
-united-states http://gobot.accela.net/ "Matthew J Backes"
-holland http://mmondor.oostendorp-ict.nl/ "Jeroen Oostendorp"
+++ /dev/null
-# One entry per line, one column:
-# <name>
-#
-index
-software
-donations
-contributors
-philosophy
-cvs
-projects
-mirrors
-contact
+++ /dev/null
-# Each software category, one entry per line, two columns:
-# <category> <location>
-#
-stable software/stable
-development software/devl
-old software/old
+++ /dev/null
-# One project per line, three columns per project:
-# <projname> <status> <version>
-#
-mmftpd devl 1.0
+++ /dev/null
-# One project per line, three columns per project:
-# <projname> <status> <version>
-#
-ginseng-ftpd devl 1.0
+++ /dev/null
-# One project per line, three columns per project:
-# <projname> <status> <version>
-#
-ginseng-ftpd devl 1.0
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html><head>
-<!-- $Id: philosophy.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $
- Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED.
--->
-<title>Matthew Mondor's Software Site - Philosophy</title>
-<link rel="shortcut icon" href="/favicon.ico">
-</head>
-
-<!-- Begin -->
-<body bgcolor="#f0f0ff" text="#000000" link="#3535c5" vlink="#700080">
-<table border="0" cellspacing="5" cellpadding="5">
-<tbody><tr>
-
-<!-- Left column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Sections</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<p><font face="helvetica, arial" size="-1">
- <a href="index.html">Main</a> <br>
- <a href="software.html">Software</a> <br>
- <a href="donations.html">Donations</a> <br>
- <a href="contributors.html">Contributors</a> <br>
- <a href="philosophy.html"><em>Philosophy</em></a> <br>
- <a href="cvs.html">CVS</a> <br>
- <a href="projects.html">Projects</a> <br>
- <a href="mirrors.html">Mirrors</a> <br>
- <a href="mailto:mmondor@gobot.ca">Contact</a> <br>
-</font></td></tr></tbody></table>
-<br><img src="images/sigil.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</td></p>
-
-<!-- Middle column -->
-<td valign="top">
-<center>
-<font face="helvetica, arial" size="4">Philosophy behind mmsoftware</font><br>
-</center>
-<font face="helvetica, arial">
-<p>
-Several primary goals were the basis to write my suite of public internet
-services. One consisted of a challenge to learn UNIX APIs deeply at the time
-I decided to write my own. This is no longer the case, but probably that the
-most driving factor still has to do with my need to run such services.
-</p><p>
-Of course there exists a variety of solutions for each of the standard
-protocols. However, most of them seemed historical, based on code which made
-it though the years through extreme amounts of patching and kludges. This
-generally results in solutions which are hard to configure, and have gone
-though a long history of security-related issues. Most of them even support
-obsolete protocols, or experimental ones which never have been widely used.
-</p><p>
-Moreover, several of them were subject to strict licensing issues. Writing
-my own software I have the full rights to do whatever I want with it, as
-well as eventually distribute closed-source special editions commercially.
-But I didn't want to be the only one to be able to do this, hence I decided
-to release my software under a BSD-style license (Berkeley Source Distribution
-license), allowing others to modify it and distribute it under source or
-binary form as wanted, only requireing them to add an aknowledgement to their
-product documentation:
-</p><p><font face="times">
- This product includes software written by Matthew Mondor.
-</font></p><p>
-It however obviously is encouraged to report to me the various bug fixes
-or enhancement patch so that the main publicly available tree also evolve.
-</p><p>
-Considering security aspects, most traditional UNIX internet services software
-ran as the superuser, and required standard UNIX users to be added for each
-remote user. I strongly beleive that most administrators enjoy being able to
-only create virtual users rather than real UNIX shell ones for each of their
-users. My daemons therefore only allow virtual users. Moreover, none of them
-require to run as the superuser after their initial startup. Most of them
-have support for chroot(2) as well, for administrators who need it.
-</p><p>
-These are public services afterall. I personally use them to contribute
-software and services which are often free of charge, and would find it quite
-discouraging if my services were exploited and servers virtually destroyed.
-I think that many administrators are in the same position and would like to
-run safe services.
-</p><p>
-Special care is taken when writing this software to avoid memory leaks (which
-could be a threat to service uptime and autonomy), and to check every
-error condition possible. As the services should remain up, system resources
-error conditions are treated in a safe way; The current operation is cancelled
-to not execute code which requires these new resources to be allocated,
-previous allocated ones are released if any (but not all) were obtained for
-the current operation as well, and the service keeps running as usual. This
-also implies using I/O operations which can timeout where required. Also,
-libc functions with uncertain delay periods get executed by an asynchroneous
-parallel system when userspace threading systems are used, to prevent locking
-the main process, allowing service to remain fluid among the multiple
-simultaneous connections being served. Some effort was also made to keep the
-code clean.
-</p><p>
-C was chosen to write these for several reasons. I am well used to it, having
-used it for many years, was a primary one. A second worthwhile reason is that
-the UNIX, POSIX and BSD APIs are intended for C programs. It is also possible
-for the programmer to optimize C code decently rather than only relying on
-compiler effeciency to do it, gaining speed advantage without having to use
-less maintainable and unportable assembly. When well written, C programs are
-also portable. C++ could have suited, but would result in generally less
-efficient code. I also like to control my own use of memory useage, object
-allocation and garbage collection, hence it was not worth it to use C++.
-Where possible, ANSI-C compliance is observed.
-</p><p>
-Matthew Mondor
-</p>
-</font>
-</td>
-
-<!-- Right column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Languages</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>English</em></a> <br>
- <a href="index_fr.html">French</a> <br>
-</font></td></tr></tbody></table></p>
-<p><font face="helvetica, arial" color="#000066"><b>Mirrors</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="http://mmondor.dynup.net/index.html">Canada</a> <br>
- <a href="http://gobot.accela.net/index.html"><nobr>United-States</nobr></a> <br>
- <a href="http://mmondor.oostendorp-ict.nl/index.html">Holland</a> <br>
-</font></td></tr></tbody></table></td></p>
-
-<!-- End -->
-</tr><tr><td valign="bottom" align="left" colspan="3">
-<font face="helvetica, arial" size="-2">
-$Id: philosophy.html,v 1.5 2003/06/04 12:17:47 mmondor Exp $<br>
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED.
-</font></td></tr></tbody></table>
-</body></html>
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html><head>
-<!-- $Id: projects.html,v 1.6 2003/06/04 12:17:47 mmondor Exp $
- Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED.
--->
-<title>Matthew Mondor's Software Site - Projects</title>
-<link rel="shortcut icon" href="/favicon.ico">
-</head>
-
-<!-- Begin -->
-<body bgcolor="#f0f0ff" text="#000000" link="#3535c5" vlink="#700080">
-<table border="0" cellspacing="5" cellpadding="5" width="100%">
-<tbody><tr>
-
-<!-- Left column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Sections</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<p><font face="helvetica, arial" size="-1">
- <a href="index.html">Main</a> <br>
- <a href="software.html">Software</a> <br>
- <a href="donations.html">Donations</a> <br>
- <a href="contributors.html">Contributors</a> <br>
- <a href="philosophy.html">Philosophy</a> <br>
- <a href="cvs.html">CVS</a> <br>
- <a href="projects.html"><em>Projects</em></a> <br>
- <a href="mirrors.html">Mirrors</a> <br>
- <a href="mailto:mmondor@gobot.ca">Contact</a> <br>
-</font></td></tr></tbody></table>
-<br><img src="images/sigil.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</td></p>
-
-<!-- Middle column -->
-<td valign="top">
-<center>
-<font face="helvetica, arial" size="4">Various Projects and Background</font><br>
-</center>
-<font face="helvetica, arial">
-<p>
-Here first follows an overview of my computer-related background
-</p><p>
-A brief overview of my computer-related background: I touched my first computer
-at the age of 8, an Apple][+, on which I immediately wanted to code games,
-mostly of the text type. I then learned 6802 assembly, as AppleSoft BASIC was
-pretty much useless, and Z80 assembly, which was available through a
-bridgeboard. I then wrote disk utilities (backup, data retreival).
-</p><p>
-I then at my great joy had my first Amiga when I was 15, on which I first used
-mildly AmigaBASIC. Of course a year later I was learning C, and using the RKMS
-wrote AstralPortal (Voice/Fido/Data Modem software) in the few following years.
-I then did some 2d vector graphics using 68000 assembly, C and sometimes
-BlitzBasic (which accepted direct assembly instructions and had nice blit
-routines. Some playing with ARexx was then done. The last thing I developed
-on Amiga was audio sample manipulation and compression utilities in C, along
-with a programmable binaural/hemisync braintrainer, and some custom GUI
-library. Another latest project was a control server which allowed several
-RS232 terminals to be synchronized with a main multitasking server application
-with abstract tty-specific code translation. What a great box that was.
-</p><p>
-Then finally, I decided to get an i386DX/40/8megs although it was already
-deprecated, and started doing some Turbo-C and 8088 assembly on MS-DOS,
-and wrote trivial 2d 8-bit functions library along with a graphic
-block-oriented editor for game programming. Some scrolling, sprite and
-collision code was added, and it was a ready system to write games, but none
-were ever written using it, but demos. As I always was avoiding windows since,
-but had to at least confront it for a while, I finally bought an AMD-K6-2/450
-and learned some Win32 API, tried various compilers with GUI frontends. I
-unfortunately was pretty discouraged after a year or so, and was not
-programming anything useful; Until I made a discovery... Linux 2.0.35 (RH6)
-in December 1999.
-</p><p>
-I used RedHat 6 for a few months, getting aquainted with GCC (which fortunately
-had the same base interface than DICE dcc from Matt Dillon which I was using
-on the Amiga). And, glibc info/man pages. Linux finally made me discover that
-a PC could finally be worth it, as much as Amiga was. I soon found that on
-an AmigaOS shell I couldn't remember the commands, erroneously typing unix
-ones. The switch was made. I then tried Debian Potato, which of course made
-me forget RH for good, and eventually did my own distribution, Ginseng, a
-security and server related distribution. Between Debian and Ginseng I started
-to write a few meaningful TCP server-oriented utilities. I then discovered
-NetBSD.
-</p><p>
-Obviously I then had found my ideal OS, which I am still heavily using today
-on all my systems. I finally was free from glibc, which was everyday getting
-more bloated without true additionnal functionallity.
-I tried other BSD variants and was more convinced that NetBSD still was my
-choice. My current publically available software was written using it.
-Current projects include mmmail, which is now being totally redesigned from
-scratch (for v2), mmftpd, Xisop (a portable non-SMP multitasking preemptive
-microkernel similar to AmigaOS), an arbitrary math library and C-like
-simplified tokernizer/interpretor language for secure distributed network
-clients, along with their distributed server counterpart. I mostly emphasize
-my projects on security (good tactics as running virtual services as
-non-privileged users, encryption, etc are useful).
-</p><p>
-I also use and test various tunnelling/VPN solutions, am working on a secure
-NFS alternative, and various work-related projects. I try to eventually
-provide easy solutions to common administration nightmares such as sendmail
-administration, localized central data servers and similar tasks. Some work
-has also been done on a state-persistant secure HTTP authentication protocol
-where unique cookies are exchanged between each page under SSL, and their
-origin/expiration verified on the server-side, possibly that an ssl-httpd will
-eventually become available for safe remote administration of mmmail, mmftpd
-and other server daemons I write.
-</p><p>
-I often don't mind to redesign existing systems, re-invent them or be
-inspired by their concepts, when a happy result can be obtained afterwards.
-Other non-computer related interests exist, including swimming, martial
-arts, meditation (often a necessity for relaxation), international foods,
-microbrewered and rare dark, rich beers, especially the stout type.
-Writing also has always been a great passion all my life, from adventure
-novels (unpublished) to technical documentation (some only has been made
-available publically). And music. What a great way to express soul states,
-composing and producing various music types, especially Fusion-Jazz remains
-a never-ending story.
-</p><p>
-Matt
-</p>
-</font>
-</td>
-
-<!-- Right column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Languages</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>English</em></a> <br>
- <a href="index_fr.html">French</a> <br>
-</font></td></tr></tbody></table></p>
-<p><font face="helvetica, arial" color="#000066"><b>Mirrors</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="http://mmondor.dynup.net/index.html">Canada</a> <br>
- <a href="http://gobot.accela.net/index.html"><nobr>United-States</nobr></a> <br>
- <a href="http://mmondor.oostendorp-ict.nl/index.html">Holland</a> <br
->
-</font></td></tr></tbody></table></td></p>
-
-<!-- End -->
-</tr><tr><td valign="bottom" align="left" colspan="3">
-<font face="helvetica, arial" size="-2">
-$Id: projects.html,v 1.6 2003/06/04 12:17:47 mmondor Exp $<br>
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED.
-</font></td></tr></tbody></table>
-</body></html>
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
-<html><head>
-<!-- $Id: software.html,v 1.15 2003/12/12 15:58:39 mmondor Exp $
- Copyright (c) 2002-2003 Matthew Mondor, ALL RIGHTS RESERVED.
--->
-<title>Matthew Mondor's Software Site - Software</title>
-<link rel="shortcut icon" href="/favicon.ico">
-</head>
-
-<!-- Begin -->
-<body bgcolor="#f0f0ff" text="#000000" link="#3535c5" vlink="#700080">
-<table border="0" cellspacing="5" cellpadding="5" width="100%">
-<tbody><tr>
-
-<!-- Left column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Sections</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<p><font face="helvetica, arial" size="-1">
- <a href="index.html">Main</a> <br>
- <a href="software.html"><em>Software</em></a> <br>
- <a href="donations.html">Donations</a> <br>
- <a href="contributors.html">Contributors</a> <br>
- <a href="philosophy.html">Philosophy</a> <br>
- <a href="cvs.html">CVS</a> <br>
- <a href="projects.html">Projects</a> <br>
- <a href="mirrors.html">Mirrors</a> <br>
- <a href="mailto:mmondor@gobot.ca">Contact</a> <br>
-</font></td></tr></tbody></table>
-<br><img src="images/sigil.jpg" alt="Image Copyright (c) 2002-2003, Matthew Mondor">
-</td></p>
-
-<!-- Middle column -->
-<td valign="top">
-<center>
-<font face="helvetica, arial" size="5">The Software</font><br>
-</center>
-<font face="helvetica, arial">
-<p>
-This page contains the various official mmsoftware sources. The third-party
-software provided by the contributors can be found in the
-<a href="contributors.html">contributors</a> area. Access to the current
-official mmsoftware sources CVS repository is available (via pserver). Details
-about CVS can be found under the <a href="cvs.html">CVS</a> section. It is
-also possible to obtain the source archives of the old releases for reference
-only, in the software archive: <a href="software/">software/</a>.
-</p><p><center><b>Featured software:</b></center></p><p>
-This software is actively maintained and in constant development. This also
-means that bug reports are taken into consideration as fast as
-possible and new version released accordingly, shortly after bug discovery,
-when a suitable solution has been implemented to solve the issue.
-</p>
-</font>
-<table border="1" bgcolor="#d0d0f0" width="100%">
-<font face="helvetica, arial">
-<tr>
-<th><a href="software/devl/mmftpd-0.0.17.tgz">mmftpd</a></th>
-<td align="center">0.0.17</td>
-<td align="center">Devl</td>
-<td align="center">
-<a href="software/misc/mmftpd-changelog.txt">ChangeLog</a></td>
-<td align="center">
-<a href="software/devl/mmftpd-0.0.17.tgz.md5.txt">MD5</a></td>
-</tr>
-<tr><td align="center" colspan="5">
-Unprivileged virtual users FTP server
-</td></tr>
-<tr><td bgcolor="#c6e0e0" colspan="5"><font face="helvetica, arial" size="-1">
-<p>
-mmftpd was written from scratch from the ground up, and consists of a
-featureful yet very security-aware FTP server. It is released under the
-terms and conditions of the BSD license with advertizing clause to keep
-credits to the author. It ensures to run under a single unprivileged user,
-and provides FTP virtual users, as opposed to traditional UNIX/system ones,
-which each appear jailed in their home directory using extensive path sanity
-checking. It optionally also can chroot(2) at program startup for the whole
-service and all users to be setup under a real alternative root jail.
-Runs great on BSD and Linux systems and is fairly small in size. Well
-documented via UNIX manual pages.
-</p><p>
-The server can limit the connection rate from an address and also has
-bandwidth traffic shaping capabilities for both control and data ports,
-on a per-connection and global basis. It also can limit the connections
-on several factors (maximum number of addresses, number of allowed
-connections per address and how many simultaneous connections from the same
-FTP user to accept). Client hostname resolving is optional for speed, and
-is performed using asynchroneous methods when enabled.
-</p><p>
-For each FTP user a number of permission parameters can be customized,
-including the maximum upload and download speed, umask, rights to
-change umask, upload, modify, maximum home directory tree size (even safe
-among multiple simultaneous connections of the same user). Various techniques
-were implemented to prevent common Denial Of Service attempts.
-</p><p>
-Various statistics are reliably maintained using the mmstat facility,
-and the administrator can set the verbosity of wanted events and statistics
-to be output via syslog. Moreover it uses efficient custom I/O buffering
-around filedescriptors for speed. Users are stored in a configuration file
-with their permissions, using native crypt(3) password hashes
-(both MD5 and DES), so that it is possible to create virtual mmftpd users
-from system ones using the same password hashes. The server options are
-configured via a second configuration file.
-</font></td></tr>
-</table><br><table border="1" bgcolor="#d0d0f0" width="100%">
-<th><a href="software/devl/mmmail-0.0.23.tgz">mmmail</a></th>
-<td align="center">0.0.23</td>
-<td align="center">Devl</td>
-<td align="center">
-<a href="software/misc/mmmail-changelog.txt">ChangeLog</a></td>
-<td align="center">
-<a href="software/devl/mmmail-0.0.23.tgz.md5.txt">MD5</a></td>
-</tr>
-<tr><td align="center" colspan="5">
-Unprivileged virtual SMTP+POP3 server suite using MySQL
-</td></tr>
-<tr><td bgcolor="#c6e0e0" colspan="5"><font face="helvetica, arial" size="-1">
-<p>
-mmmail was written from scratch from the ground up, and consists of a
-suite of SMTP and POP3 servers which can fully run under a unprivileged
-user. It is released under the terms and conditions of the BSD license with
-advertizing clause to keep credits to the author. It provides virtual mail
-users, contrary to traditional UNIX/system ones. It optionally supports
-chroot(2) at program startup to enclose itself into a jail. It runs great
-on both BSD and Linux systems and is fairly small in size. Well documented
-via UNIX manual pages.
-</p><p>
-The servers can limit their clients connection rate on a per-address basis,
-and also support bandwidth traffic shaping capabilities in both directions
-(connection-specific and global ones). It also can limit the connections
-on several factors (maximum number of addresses, number of allowed
-connections per address and how many simultaneous connections from the same
-POP3 user to accept). Client hostname resolving is optional for speed, and
-is performed using asynchroneous methods when enabled. Moreover, the
-administrator may decide weither to check for HELO and/or MAIL MX DNS
-records before accepting SMTP mail from a client. Use of HELO may be
-disabled or enforced. Various techniques were implemented to prevent common
-Denial Of Service attempts.
-</p><p>
-For each mail user/password pair can exist several email addresses at several
-virtual hosts, and aliasing is supported to map unexisting addresses to others,
-even optionally using wildcard pattern matching. The administrator can also
-set the address and/or hostname masks through which mail with empty FROM
-address is sent. Each mailbox can be set customizeable quotas (mailbox total
-size and total number of messages). All storage (users, mailboxes, and mail
-itself) uses MySQL, which permits a global database server to be used, even
-remotely. A persistant connection is established and maintained with the
-server by each daemon, and re-established if ever necessary.
-</p><p>
-Various statistics are reliably maintained using the mmstat facility,
-and the administrator can set the verbosity of wanted events and statistics
-to be output via syslog. Moreover it uses efficient custom I/O buffering
-around filedescriptors for speed. Each daemon is configured through it's
-own configuration file. User password hashes are stored in native crypt(3)
-format (both MD5 and DES work) so it is possible to translate system users
-to mmmail virtual ones keeping the same hash.
-Also, mmpop3d supports unstandard POP3 PAGE command (better than TOP) which
-was especially implemented for human POP3 clients.
-</p><p>
-Unfortunately, support for relaying will only be added for mmmail2, a future
-re-implementation of mmmail which also should support multiple authentication
-and storage methods, as well as many other features including IMAP.
-If you are interested in knowing more about mmmail2 ongoing engineering
-(diagrams and documentation), and to make suggestions, you can download this
-<a href="software/devl/mmmail-design.pdf">mmmail-design.pdf</a> document.
-</td></tr>
-</table><br>
-<table border="1" bgcolor="#d0d0f0" width="100%">
-<th><a href="software/devl/mmstatd-0.0.8.tgz">mmstatd</a></th>
-<td align="center">0.0.8</td>
-<td align="center">Devl</td>
-<td align="center">
-<a href="software/misc/mmstatd-changelog.txt">ChangeLog</a></td>
-<td align="center">
-<a href="software/devl/mmstatd-0.0.8.tgz.md5.txt">MD5</a></td>
-</tr>
-<tr><td align="center" colspan="5">
-Statistics maintenance server daemon and client library
-</td></tr>
-<tr><td bgcolor="#c6e0e0" colspan="5"><font face="helvetica, arial" size="-1">
-<p>
-mmstatd was originally written for mmftpd and mmmail to asynchroneously
-maintain statistical counters in an efficient manner. I however also
-release it separately as it is used by some other people in their projects.
-These include an IRC network services system which although using db4 for
-most data storage uses mmstat(3) library for various statistics counters.
-</p><p>
-It does not require any additional libraries, and provides a simple API
-for applications to either update counters or query statistics. The update
-requests are done using an AF_LOCAL/UNIX datagram to the mmstatd service's
-log socket, similarly to the way syslog(3) works. Statistics are queried
-via an AF_LOCAL/UNIX stream on another mmstatd socket. Permissions for access
-to both sockets can be different.
-</p><p>
-The service consists of a librarian and logger. The logger accumulates
-requests filling a recovery log, while the librarian processes those logs
-asynchroneously and syncs the disk database with them from time to time.
-In case of a crash, the recovery logs which weren't synchronized yet to
-disk are used to recover from the crash. Provided with it is a utility
-to query and update statistics from the shell as well.
-</td></tr>
-</table><br><p><center><b>Unmaintained software:</b></center></p>
-<p>
-This section contains software which works but which I am no longer
-maintaining. These are Linux-specific and are getting old. I keep them
-here because a fair amount of people find these handy and download
-them.
-</p>
-<table border="1" bgcolor="#d0d0f0" width="100%">
-<th><a href="software/stable/mmtcpfwd-0.1.0.tar.gz">mmtcpfwd</a></th>
-<td align="center">0.1.0</td>
-<td align="center">Stable</td>
-<td align="center">
-<a href="software/misc/mmtcpfwd-changelog.txt">ChangeLog</a></td>
-<td align="center">
-<a href="software/stable/mmtcpfwd-0.1.0.tar.gz.md5.txt">MD5</a></td>
-</tr>
-<tr><td align="center" colspan="5">
-Port forwarder, MASQ fake identd and FTP proxy for Linux
-</td></tr>
-<tr><td bgcolor="#c6e0e0" colspan="5"><font face="helvetica, arial" size="-1">
-<p>
-Written from scratch, consists of a superserver daemon made of two parts,
-one process running as the superuser (to perform tasks like modifying
-firewall rules) and the other running as an unprivileged user performing
-all the work. This privilege separation system is quite effective for
-a secure setup. Linux 2.2 specific (can work with 2.4+ but without the
-transparent proxying support). Released under the terms of the GPL
-(GNU Public License).
-</p><p>
-mmtcpfwd provides transparent TCP/IP connection proxying from a MASQ enabled
-gateway to other machines, including FTP connections, via a special userspace
-passive FTP connections proxy supporting PASV, LPSV and EPSV, and masking the
-actual FTP server LAN address by supplying the gateway's address instead.
-Supports SMP where hardware permits. Uses a main configuration file to
-configure all it's parameters. Also provides an optional random UNIX-ident
-protocol daemon, allowing masqueraded connections behind the gateway to
-use services requireing identd to run.
-</p><p>
-For each forwarded port, a non-privileged process is setup to listen to
-and forward multiple connections to their configured destination. This
-destination can consist of a remote or local host, and it is possible to
-make it resolve IP address by hostname or to specify absolute IP addresses
-for speed.
-</p><p>
-Several interesting techniques are implemented to counter Denial of Service
-attacks: Total number of forwarded simultanious connections per port can be
-set, as well as per address. Also permits to deny automatically an IP address
-overflowing connections with a threshold, in which case each active connection
-from that address are cleanly closed before applying the firewall deny rule,
-executing a command of our choice. Allows to fake services like portsentry to
-immediately DENY an IP address that connects, sending a configurable message
-before closing and applying the DENY rule. Can also automatically undeny IP
-addresses of offendants after a certain amount of minutes, or indefinitely.
-As many IP addresses to never deny as we want can be specified.
-</p><p>
-Uses syslog logging, to keep log of connections/bytes transfered, elapsed
-time, etc... Hostnames can be resolved or not, on a per-port basis.
-Supports kernel's IP Transparent Proxying support to fake client's IP address
-when forwarding. Aimed towards security as much as possible.
-Fairly small, executable around 30k only.
-</p>
-</font></td></tr>
-</table><br><table border="1" bgcolor="#d0d0f0" width="100%">
-<th><a href="software/devl/ginseng-ftpd-1.6.tar.gz">ginseng-ftpd</a></th>
-<td align="center">1.6</td>
-<td align="center">Devl</td>
-<td align="center">
-<a href="software/misc/ginseng-ftpd-changelog.txt">ChangeLog</a></td>
-<td align="center">
-<a href="software/devl/ginseng-ftpd-1.6.tar.gz.md5.txt">MD5</a></td>
-</tr>
-<tr><td align="center" colspan="5">
-Security-enhanced Linux ftpd based on bsd-ftpd (NetBSD)
-</td></tr>
-<tr><td bgcolor="#c6e0e0" colspan="5"><font face="helvetica, arial" size="-1">
-<p>
-This server originally consisted of a port of BSD-FTPd to Linux, and various
-custom features were added which I personally needed at the time. Since I
-wrote mmftpd which much better suits my needs I no longer maintain
-ginseng-ftpd. It's still available though, for people who need to run FTP
-services with real users (where security is generally not that much of a
-concern). An FTP server allowing use of standard UNIX system users obviously
-required to run as the superuser. Run mmftpd if you need better security.
-Was released under the terms of the BSD license.
-</p><p>
-I here describe a list of the various changes that I made on the original
-BSD-FTPd code: The popular recently discovered single-byte vulnerability of
-bsd-ftpd was fixed, some better sanity checking around seteuid(),
-setegid() and fork() was added. Was fixed against the recursive LIST/NLST
-problems which alot of FTP servers are vulnerable to, including BSD-FTPd
-at the time. Now only requires a single configuration file (/etc/ftpusers)
-for all account options, and users MUST be present in it to be allowed
-FTP access (contrary to traditional behavior). The configuration file now
-uses one user per line, and one column per user configurable option.
-</p><p>
-Support for read-only accounts, umask specification and homedir total size
-limits on a per/user basis and number of connections per user was added,
-note that the tree size quotas are only safe if only one simultaneous logins
-of that user are allowed. Shared memory and semaphores would have been
-required otherwise which would have strongly impacted performance.
-This is not the case for mmftpd where threads are used and quotas are
-safe no matter what. The server was modified to only accept to be launched
-by the superuser.
-</p><p>
-The following command switches/parameters were added when starting the
-daemon: -q to not display ftpd type/version to clients, -n to not resolve
-hostnames for speed, -x to allow masquerading actual LAN IP address to
-0.0.0.0 for passive replies (not that not all clients will work, I
-recommend using mmtcpfwd passive FTP proxying to masquerade those to
-the actual gateway's IP address instead, where all clients will work.
-Finally, -q was added to specify which port to listen to.
-</p>
-</font></td></tr>
-</table>
-</font>
-</td>
-
-<!-- Right column -->
-<td valign="top" bgcolor="#d0d0f0">
-<font face="helvetica, arial" color="#000066"><b>Languages</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="index.html"><em>English</em></a> <br>
- <a href="index_fr.html">French</a> <br>
-</font></td></tr></tbody></table></p>
-<p><font face="helvetica, arial" color="#000066"><b>Mirrors</b></font><br>
-<table border="0" cellspacing="4" cellpadding="2"><tbody><tr><td>
-<font face="helvetica, arial" size="-1">
- <a href="http://mmondor.dynup.net/index.html">Canada</a> <br>
- <a href="http://gobot.accela.net/index.html"><nobr>United-States</nobr></a> <br>
- <a href="http://mmondor.oostendorp-ict.nl/index.html">Holland</a> <br
->
-</font></td></tr></tbody></table></td></p>
-
-<!-- End -->
-</tr><tr><td valign="bottom" align="left" colspan="3">
-<font face="helvetica, arial" size="-2">
-$Id: software.html,v 1.15 2003/12/12 15:58:39 mmondor Exp $<br>
-This site Copyright (c) 2002-2003, Matthew Mondor, ALL RIGHTS RESERVED.
-</font></td></tr></tbody></table>
-</body></html>
+++ /dev/null
-Attempts to generate enough rnd(4) entropy on NetBSD systems using
-CGD for encrypted swap. Using it on a laptop.
-
-This implentation uses pseudo-random reads on a hard disk device
-in order to hopefully cause the rnd(4) device (which is among other
-devices fed by the wd* and sd* devices) to be fed entropy caused
-by unstability features of the heads.
-
-The program can be launched before starting the encrypted builds,
-then mounting the wanted CGD devices can be made, which shouldn't
-cause a lockup anymore while cgdconfig(8) creates random the
-cryptography keys with /dev/random. The program can then be killed.
+++ /dev/null
-/* $Id: entropy_disk.c,v 1.5 2006/09/21 20:11:16 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006 Pulsar-Zone, Reg.
- * All rights reserved.
- *
- * Written by Matthew Mondor for Pulsar-Zone, Reg.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials 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 for the NetBSD Project by
- * Pulsar-Zone, Reg.
- * 4. The name of Pulsar-Zone, Reg. may not be used to endorse
- * or promote products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY PULSAR-ZONE, REG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PULSAR-ZONE, REG
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Strategy:
- *
- * 1) First obtain wd* or sd* devices using sysctl(3) which could be used to
- * produce entropy. Error and exit if no such devices can be used.
- * 2) Report our PID and detach, allowing SIGTERM to kill us.
- * 3) In an endless loop, read various arbitrary blocks of random lenghts and
- * at random positions from random disks. We take care to toggle the
- * reading direction often enough to give the heads an opportunity to seek
- * in both directions.
- * 4) Upon receiving a SIGTERM signal, close the disk devices and exit.
- *
- * To obtain rather decent random values, we use the 4.2BSD random(3) PRNG
- * but seed it using /dev/urandom. This prevents sucking up too much entropy
- * from rnd(4), which we're designed to fill rather than waste, afterall.
- * We periodically reseed the PRNG with /dev/urandom during the main loop as
- * well.
- *
- * XXX Consider using arc4random(3) instead. However, possible problems could
- * occur using it: it could void rnd(4) entropy, especially that at least
- * 1024 bytes of the arcfour keystream should be discarded to be random
- * enough.
- *
- * XXX Possibly add an option to tell the daemon to automatially exit after a
- * certain number of seconds or minutes elapsed. We would use setitimer(2)
- * to receive a SIGALRM, acting the same as for SIGTERM upon its reception.
- *
- * XXX Make lseek(2)/read(2) errors silent via compile time option to not
- * disclose any information in case of disk problems for a final version of
- * this program.
- */
-
-#include <sys/mman.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/queue.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-
-
-/*
- * Minimum block size to read (could be disk dependent but it doesn't matter
- * much for this task.
- */
-#define BLOCK_SIZE 16384
-/*
- * Maximum number of BLOCK_SIZE blocks to read in a single read(2)
- */
-#define BLOCK_MULTIPLE 16
-/*
- * Minimum and maximum number of reads to perform in a single direction before
- * switching direction. If these are too low the disk head thrashing could be
- * excessive.
- */
-#define DIRECTION_MIN 256
-#define DIRECTION_MAX 4096
-/*
- * Minimum and maximum reseeding of PRNG from urandom(4) frequency (in reads).
- * If we set these this too low, the entropy we're trying to accumulate in
- * rnd(4) is sucked out as we create it, causing us to loop indefinitely for
- * nothing.
- */
-#define RESEED_MIN 32
-#define RESEED_MAX 256
-
-enum direction {
- DIR_FORWARD = 0,
- DIR_BACKWARDS = 1
-};
-
-typedef struct drive_entry {
- SLIST_ENTRY(drive_entry) chain;
- char *name;
- off_t max, pos;
- int fd;
- int dir, dircnt;
-} disk_t;
-
-SLIST_HEAD(slisthead, drive_entry);
-
-#define BALIGN_CEIL(v, s) ((((size_t)(v)) + (s) - 1) / (s) * (s))
-
-
-
-int main(int, char **);
-
-static int prng_init(void);
-static void prng_reseed(void);
-static void prng_close(void);
-static void signal_handler(int);
-static int detach(const char *);
-static void disk_open(const char *);
-static int disks_open(void);
-static void disks_close(void);
-static void *buffer_alloc(size_t);
-static void buffer_free(void *, size_t);
-
-
-
-static char *pidfile = "/var/run/entropy_disk.pid";
-static int maxdisks = 4;
-
-static struct slisthead disks_list = SLIST_HEAD_INITIALIZER(drive_entry);
-static int ndisks = 0;
-static disk_t **disks_array = NULL;
-
-static int urandom = -1;
-static int reseed = 0;
-
-static size_t pagesize = 0;
-
-static int run = 1;
-
-
-
-int
-main(int argc, char **argv)
-{
- char c;
- void *readbuf;
- int ret = EXIT_FAILURE;
-
- setprogname(argv[0]);
-
- /* Parse commad line arguments */
- while ((c = getopt(argc, argv, "p:n:?")) != -1) {
- switch (c) {
- case 'p':
- pidfile = optarg;
- break;
- case 'n':
- maxdisks = strtol(optarg, NULL, 10);
- if (maxdisks < 1 || maxdisks > 100) {
- (void) fprintf(stderr,
- "<maxdisks> must be between 1 and 100\n");
- return EXIT_FAILURE;
- }
- break;
- case '?':
- /* FALLTHROUGH */
- default:
- (void) fprintf(stderr,
- "Usage: %s [-p <pidfile>] [-n <maxdisks>]\n",
- getprogname());
- return EXIT_FAILURE;
- }
- argc -= optind;
- argv += optind;
- }
-
- /*
- * Initialization
- */
- if (prng_init() != 0)
- return EXIT_FAILURE;
-
- if (disks_open() != 0)
- return EXIT_FAILURE;
-
- if ((readbuf = buffer_alloc(BLOCK_SIZE * BLOCK_MULTIPLE)) == NULL)
- goto end;
- if (detach(pidfile) != 0)
- goto end;
-
- /* Main loop */
- while (run) {
- disk_t *e;
- int blocks;
- unsigned long diff;
-
- /* Choose a disk */
- e = disks_array[random() % ndisks];
-
- /* Choose a block size */
- blocks = random() % BLOCK_MULTIPLE;
-
- /*
- * Choose a position relative to a previous one and doesn't
- * exceed e->max (which already accounts for the block size
- * and is already BLOCK_SIZE aligned). Our new position must
- * also be BLOCK_SIZE aligned.
- */
- diff = BALIGN_CEIL(random() % (e->max / DIRECTION_MIN),
- BLOCK_SIZE);
- if (e->dir == DIR_BACKWARDS) {
- if ((e->pos -= diff) < 0)
- e->dir = DIR_FORWARD;
- }
- if (e->dir == DIR_FORWARD)
- e->pos = BALIGN_CEIL((e->pos + diff) % (e->max + 1),
- BLOCK_SIZE);
- if (--e->dircnt < 0) {
- e->dir = (e->dir == DIR_FORWARD ?
- DIR_BACKWARDS : DIR_FORWARD);
- e->dircnt = DIRECTION_MIN +
- (random() % (DIRECTION_MAX - DIRECTION_MIN));
- }
-
- /* Seek and read block(s) */
- if (lseek(e->fd, e->pos, SEEK_SET) != -1) {
- if (read(e->fd, readbuf, blocks * BLOCK_SIZE) !=
- blocks * BLOCK_SIZE)
- (void) fprintf(stderr,
- "read(%s, %llu, %u): %s\n",
- e->name, e->pos, blocks * BLOCK_SIZE,
- strerror(errno));
- } else
- (void) fprintf(stderr,
- "lseek(%s, %llu): %s\n",
- e->name, e->pos, strerror(errno));
-
- /* Sleep for a slight random delay */
- (void) usleep(random() % 1000);
-
- /* Reseed PRNG out of /dev/urandom periodically */
- prng_reseed();
- }
-
- ret = EXIT_SUCCESS;
- /* FALLTHROUGH */
-
-end:
- /* Cleanup */
- if (readbuf != NULL)
- buffer_free(readbuf, BLOCK_SIZE * BLOCK_MULTIPLE);
- (void) unlink(pidfile);
- disks_close();
- prng_close();
-
- return ret;
-}
-
-/*
- * Seed 4.2BSD random(3) using rnd(4)'s /dev/urandom
- */
-static int
-prng_init(void)
-{
- unsigned long seed;
-
- if ((urandom = open("/dev/urandom", O_RDONLY)) == -1) {
- (void) fprintf(stderr,
- "open(/dev/urandom): %s\n", strerror(errno));
- goto err;
- }
- if (read(urandom, &seed, sizeof(seed)) != sizeof(seed)) {
- (void) fprintf(stderr,
- "read(/dev/urandom): %s\n", strerror(errno));
- goto err;
- }
-
- srandom(seed);
- reseed = RESEED_MIN + (random() % (RESEED_MAX - RESEED_MIN));
-
- return 0;
-
-err:
- if (urandom != -1) {
- (void) close(urandom);
- urandom = -1;
- }
-
- return -1;
-}
-
-static void
-prng_reseed(void)
-{
- unsigned long seed;
-
- if (--reseed < 1) {
- if (read(urandom, &seed, sizeof(seed)) != sizeof(seed)) {
- (void) fprintf(stderr,
- "read(/dev/urandom): %s\n", strerror(errno));
- }
- srandom(seed);
- reseed = RESEED_MIN + (random() % (RESEED_MAX - RESEED_MIN));
- }
-}
-
-static void
-prng_close(void)
-{
-
- if (urandom != -1) {
- (void) close(urandom);
- urandom = -1;
- }
-}
-
-/*
- * Called upon reception of SIGTERM
- */
-static void
-signal_handler(int sig)
-{
-
- switch (sig) {
- case SIGTERM: /* FALLTHROUGH */
- default:
- run = 0;
- }
-}
-
-/*
- * Become a background daemon and write our PID file.
- * Returns 0 on success or -1 on error.
- */
-static int
-detach(const char *pidfile)
-{
- pid_t pid;
- int fd;
- struct sigaction act;
-
- if ((pid = fork()) == -1)
- return -1;
- if (pid != 0)
- exit(EXIT_SUCCESS);
-
- /* Create PID file */
- if ((fd = open(pidfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
- char str[16];
-
- (void) snprintf(str, 15, "%d\n", getpid());
- if (write(fd, str, strlen(str)) == -1 || close(fd) == -1) {
- (void) fprintf(stderr,
- "Error writing PID file [%s]: %s\n",
- pidfile, strerror(errno));
- return -1;
- }
- } else {
- (void) fprintf(stderr,
- "Error creating PID file [%s]: %s\n",
- pidfile, strerror(errno));
- return -1;
- }
-
- /* Be paranoid, redirect our stdio file descriptors to null(4) */
- (void) setsid();
- (void) chdir("/");
- if ((fd = open("/dev/null", O_RDWR)) != -1) {
- (void) dup2(fd, STDIN_FILENO);
- (void) dup2(fd, STDOUT_FILENO);
- /* Keep stderr */
- if (fd > STDERR_FILENO)
- (void) close(fd);
- }
-
- /* Set our SIGTERM handler and ignore some signals */
- act.sa_handler = signal_handler;
- act.sa_flags = 0;
- (void) sigemptyset(&act.sa_mask);
- (void) sigaction(SIGTERM, &act, NULL);
- act.sa_handler = SIG_IGN;
- (void) sigaction(SIGTTOU, &act, NULL);
- (void) sigaction(SIGTTIN, &act, NULL);
- (void) sigaction(SIGTSTP, &act, NULL);
-
- return 0;
-}
-
-/*
- * Attempts to open specified disk device and on success adds a new disk_t
- * entry to the disks_list. Logs any errors to stderr.
- */
-static void
-disk_open(const char *dname)
-{
- disk_t *e;
- char devname[16];
-
- if ((e = malloc(sizeof(disk_t))) == NULL) {
- (void) fprintf(stderr,
- "malloc(%d): %s\n", sizeof(disk_t), strerror(errno));
- goto err;
- }
- if ((e->name = strdup(dname)) == NULL) {
- (void) fprintf(stderr,
- "strdup(%d): %s\n", strlen(dname), strerror(errno));
- goto err;
- }
-
- (void) snprintf(devname, 16, "/dev/r%sd", dname);
- if ((e->fd = open(devname, O_RDONLY)) == -1) {
- (void) fprintf(stderr,
- "open(%s): %s\n", devname, strerror(errno));
- goto err;
- }
-
- /*
- * Although we could use an ioctl(2) to read the label of the device,
- * using lseek(2) is simpler. If it generates head movement, it's all
- * for the better.
- */
- if (lseek(e->fd, 0, SEEK_END) == -1) {
- (void) fprintf(stderr,
- "lseek(%s): %s\n", devname, strerror(errno));
- goto err;
- }
- if ((e->max = lseek(e->fd, 0, SEEK_SET)) == -1) {
- (void) fprintf(stderr,
- "lseek(%s): %s\n", devname, strerror(errno));
- goto err;
- }
- /*
- * Substract so that any position is valid for reading up to
- * BLOCK_SIZE * BLOCK_MULTIPLE bytes. We also ensure that size
- * is always a multiple of BLOCK_SIZE.
- */
- e->max = BALIGN_CEIL(e->max - (BLOCK_SIZE * BLOCK_MULTIPLE),
- BLOCK_SIZE);
-
- /* Set a random but valid start position, BLOCK_SIZE aligned. */
- e->pos = BALIGN_CEIL(((off_t)random() * (off_t)random()) %
- (e->max + 1), BLOCK_SIZE);
-
- /* And a random direction/count */
- e->dir = random() % 2;
- e->dircnt = DIRECTION_MIN +
- (random() % (DIRECTION_MAX - DIRECTION_MIN));
-
- SLIST_INSERT_HEAD(&disks_list, e, chain);
- ndisks++;
- return;
-
-err:
- if (e != NULL) {
- if (e->name != NULL)
- free(e->name);
- if (e->fd != -1)
- (void) close(e->fd);
- free(e);
- }
-}
-
-/*
- * Queries sysctl(3) for wd* and sd* drive names and opens the devices.
- * Returns 0 on success or -1 on failure.
- */
-static int
-disks_open(void)
-{
- int mib[] = { CTL_HW, HW_DISKNAMES };
- char buf[1024], *cptr, *sptr;
- size_t size = sizeof(buf);
-
- /* Query local disks */
- if (sysctl(mib, 2, buf, &size, NULL, 0) != 0) {
- (void) fprintf(stderr,
- "Error querying sysctl(3) for disk names: %s\n",
- strerror(errno));
- goto err;
- }
-
- /* Open for each sd(4) or wd(4) disk reported, up to maxdisks */
- /* XXX We actually could use strsep(3) easily here as well */
- for (cptr = buf, ndisks = 0; ndisks < maxdisks; ) {
- for (; *cptr != '\0' && *cptr == ' '; cptr++) ;
- if (*cptr == '\0')
- break;
- sptr = cptr;
- for (; *cptr != '\0' && *cptr != ' '; cptr++) ;
- if (*cptr == '\0')
- break;
- *cptr++ = '\0';
- if ((sptr[0] == 's' || sptr[0] == 'w') &&
- sptr[1] == 'd')
- disk_open(sptr);
- }
-
- if (ndisks == 0) {
- (void) fprintf(stderr,
- "No sd(4) or wd(4) disks to generate entropy from\n");
- goto err;
- }
-
- /* Initialize fast index array */
- if ((disks_array = malloc(sizeof(disk_t *) * ndisks)) == NULL) {
- (void) fprintf(stderr,
- "malloc(%d): %s\n", sizeof(disk_t *) * ndisks,
- strerror(errno));
- goto err;
- }
- {
- int i = 0;
- disk_t *e;
-
- SLIST_FOREACH(e, &disks_list, chain)
- disks_array[i++] = e;
- }
-
- return 0;
-
-err:
- disks_close();
-
- return -1;
-}
-
-/*
- * Closes any open drive devices if any.
- */
-static void
-disks_close(void)
-{
- disk_t *e;
-
- if (disks_array != NULL)
- free(disks_array);
-
- while (!SLIST_EMPTY(&disks_list)) {
- e = SLIST_FIRST(&disks_list);
- SLIST_REMOVE_HEAD(&disks_list, chain);
- if (e->fd != -1)
- (void) close(e->fd);
- if (e->name != NULL)
- free(e->name);
- free(e);
- }
-
- ndisks = 0;
-}
-
-/*
- * Allocate a wired memory buffer.
- */
-static void *
-buffer_alloc(size_t size)
-{
- void *buf = NULL;
-
- if (pagesize == 0) {
- if ((pagesize = (size_t)sysconf(_SC_PAGESIZE)) == -1) {
- perror("sysconf(_SC_PAGESIZE)");
- goto err;
- }
- }
-
- size = (size_t)BALIGN_CEIL(size, pagesize);
-
- if ((buf = mmap(NULL, size, PROT_WRITE, MAP_ANON, -1, 0)) ==
- MAP_FAILED) {
- (void) fprintf(stderr,
- "mmap(%d): %s\n", size, strerror(errno));
- buf = NULL;
- goto err;
- }
- if (mlock(buf, size) == -1) {
- perror("mlock()");
- goto err;
- }
- if (madvise(buf, size, MADV_SEQUENTIAL) == -1) {
- perror("madvise()");
- goto err;
- }
-
- return buf;
-
-err:
- if (buf != NULL) {
- (void) munlock(buf, size);
- (void) munmap(buf, size);
- }
-
- return NULL;
-}
-
-/*
- * Free a previously allocated wired buffer.
- */
-static void
-buffer_free(void *buf, size_t size)
-{
-
- assert(pagesize != 0 && buf != NULL);
-
- size = (size_t)BALIGN_CEIL(size, pagesize);
-
- (void) memset(buf, 0, size);
- (void) munlock(buf, size);
- (void) munmap(buf, size);
-}
+++ /dev/null
-$Id: README,v 1.3 2006/07/22 04:47:43 mmondor Exp $
-
-Note that this was a test tree only. The current code which is in use
-can be found under /cvsroot/mmondor/mmsoftware/js/
-
-Thanks,
-Matt
+++ /dev/null
-/* $Id: copy.js,v 1.2 2005/07/01 13:11:08 mmondor Exp $ */
-
-/*
- * Test our read() and write() methods, mapping to unix read(2) and write(2).
- * Seems to be working great so far.
- */
-
-src = '/tmp/src.bin'
-dst = '/tmp/dst.bin'
-
-err = new FD();
-err.set(FD.STDERR_FILENO);
-
-srcfd = new FD();
-dstfd = new FD();
-
-try {
- srcfd.open(src, FD.O_RDONLY);
- dstfd.open(dst, FD.O_WRONLY | FD.O_CREAT);
-
- var data;
-
- while (data = srcfd.read(16384)) {
- if (data.length == 0)
- break;
- err.put('Copied ' + data.length + " bytes block\n");
- dstfd.write(data);
- }
-
- srcfd.close();
- dstfd.fdatasync();
- dstfd.close();
-} catch (x) {
- err.put(x + "\n");
-}
-
-err.close();
+++ /dev/null
-/* $Id: fd.js,v 1.5 2005/02/10 10:34:25 mmondor Exp $ */
-
-var out = new FD();
-out.set(FD.STDOUT_FILENO);
-
-try {
-
- var fd = new FD();
-
- {
- var size;
-
- fd.open("/tmp/test.txt", FD.O_WRONLY | FD.O_CREAT);
- size = fd.put("Yo!\n");
- out.put('Wrote bytes=' + size + ' to file="' + fd.path +
- '", fd=' + fd.fd + ', created with mode=' + fd.mode +
- "\n");
- fd.close();
- }
-
- {
- var buf;
-
- fd.open("/tmp/test.txt", FD.O_RDONLY);
- while (buf = fd.get())
- out.put(buf);
- fd.close();
- }
-
-} catch (x) {
- out.put(x + "\n");
-}
-
-out.put("done\n");
-out.close();
+++ /dev/null
-/* $Id: objects.js,v 1.7 2005/11/25 07:38:35 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Primarily written in JavaScript for easy and quick prototyping/testing.
- * May eventually be written in C afterwards if need be, but the game might
- * actually be implemented in JavaScript too depending on future decisions.
- */
-
-/*
- * Simple but rather realistic implementation of items and containers.
- * We observe containers maximum allowed volume and weight, container neck
- * size, as well as expandable containers, such as cloth bags which can expand
- * to eventually fill their parent container too. We allow nestled
- * containers. We also add the concept of arbitrary marks which a user might
- * add to objects to differenciate them. Containers can be open or closed,
- * and obviously need to be open for items to be added/removed from them.
- * We flag containers as such to accept either only solids or liquids, or
- * both. We could consider flowing sand or salt as a liquid in this case.
- *
- * XXX
- * - For liquids, we might need to provide a special object or flag for them
- * to be able to have a volume/weight ratio, so that we could perform
- * actions easily such as fill bottles/bags, etc.
- * - The code dealing with the container parents, weights and expanding volume
- * containers needs to throughly be tested, both for success and failing
- * cases of all kinds.
- * - XXX FIXME XXX
- * When removing items from a container, the container's weight becomes 0,
- * as well as its volume if it is expandable.
- */
-
-
-
-/*
- * Describes a game item or object.
- */
-
-/* Used so that every item has a unique index ID */
-var unique = 0;
-
-/* Content types */
-const CT_SOLID = (1 << 0);
-const CT_LIQUID = (1 << 1);
-/* And container type flag */
-const CT_EXPAND = (1 << 2);
-/* If item cannot be taken */
-const CT_FIXED = (1 << 3);
-
-function Item(name, description, volume, weight, ctype)
-{
- this.name = name;
- this.description = description;
- this.volume = volume;
- this.weight = weight;
- this.ctype = ctype;
-
- this.marks = [];
-
- this.index = ++unique;
-}
-
-Item.prototype = {
- isContainer: function()
- {
- return false;
- },
-
- getVolume: function()
- {
- return this.volume;
- },
-
- getWeight: function()
- {
- return this.weight;
- },
-
- mark: function(mark)
- {
- this.mark.push(mark);
- }
-};
-
-
-/*
- * And an object to handle container Items, inheriting the Item object.
- */
-
-const E_OK = 0;
-const E_TOOLARGE = "Item too large";
-const E_TOOHEAVY = "Item too heavy";
-const E_FIXED = "Item is well fixed and cannot move";
-const E_BADTYPE = "Item is not of proper type (solid vs liquid)";
-const E_SELF = "Item could not penetrate itself";
-const E_NONE = "Item not in container";
-const E_ALREADY = "Item already in container";
-const E_CLOSED = "Container is closed";
-
-function ContainerItem(name, description, volume, weight, ctype,
- con_neck, con_volume_max, con_weight_max, con_ctype)
-{
- this.base = Item;
- this.base(name, description, volume, weight, ctype);
-
- this.con_neck = con_neck;
- this.con_volume_max = con_volume_max;
- this.con_weight_max = con_weight_max;
- this.con_volume_cur = this.con_weight_cur = 0;
- this.con_ctype = con_ctype;
-
- this.con_items = {};
- this.con_items_cnt = 0;
- this.con_open = false;
-}
-
-ContainerItem.prototype = {
- isContainer: function()
- {
- return true;
- },
-
- open: function()
- {
- this.con_open = true;
- },
-
- close: function()
- {
- this.con_open = false;
- },
-
- getVolume: function()
- {
- /*
- * Objects which expand, such as bags, need to report proper
- * volume relating to the volume of the objects they hold.
- * Objects which do not expand, such as boxes or bottles,
- * need to always report the same volume.
- */
- return ((this.con_ctype & CT_EXPAND) != 0) ?
- this.volume + this.con_volume_cur :
- this.volume;
- },
-
- getWeight: function()
- {
- return this.weight + this.con_weight_cur;
- },
-
- items: function()
- {
- return this.con_items_cnt;
- },
-
- add: function(item)
- {
- /* We can't accept fixed items */
- if ((item.ctype & CT_FIXED) != 0)
- return E_FIXED;
-
- /* Can only add items to open container */
- if (!this.con_open)
- return E_CLOSED;
-
- /* We can't add ourself in :) */
- if (item.index == this.index)
- return E_SELF;
-
- /* Nor items which are already inside */
- if (this.con_items[item.index] != undefined)
- return E_ALREADY;
-
- /* We only accept items of proper type */
- if ((this.con_ctype & item.ctype) == 0)
- return E_BADTYPE;
-
- /* We can only hold objects which are small enough */
- if (this.con_neck < item.getVolume())
- return E_TOOLARGE;
- if (this.con_volume_cur + item.getVolume() >
- this.con_volume_max)
- return E_TOOLARGE;
-
- /* We can only hold objects which are light enough */
- if (this.con_weight_cur + item.getWeight() >
- this.con_weight_max)
- return E_TOOHEAVY;
-
- this.con_volume_cur += item.getVolume();
- this.con_weight_cur += item.getWeight();
-
- /*
- * The weight has to propagate to parent containers.
- * The volume also has, in the case where containers
- * are expandable.
- * We need to observe the maximum allowed volume and weight
- * of all parent containers as well.
- * We need to climb through the parents list to do this.
- * Moreover, we need to actually reverse any changes made
- * to the parents in case of any volume/weight exceeded.
- */
- var err = E_OK;
- for (var o = this.con_parent; o != undefined;
- o = o.con_parent) {
- if (o.getWeight() + this.getWeight() >
- o.con_weight_max) {
- err = E_TOOHEAVY;
- break;
- }
- o.con_weight_cur += this.getWeight();
- if ((o.con_ctype & CT_EXPAND) != 0) {
- if (o.getVolume() + this.getVolume() >
- o.con_volume_max) {
- err = E_TOOLARGE;
- break;
- }
- o.con_volume_cur += this.getVolume();
- }
- }
- if (err != E_OK) {
- for (var t = this.con_parent; t != o;
- t = t.con_parent) {
- t.con_weight -= this.getWeight();
- if ((t.con_ctype & CT_EXPAND) != 0)
- t.con_volume_cur -= this.getVolume();
- }
- if (err == E_TOOLARGE)
- o.con_weight_cur -= this.getWeight();
-
- this.con_volume_cur -= item.getVolume();
- this.con_weight_cur -= item.getWeight();
-
- return err;
- }
-
- if (item.isContainer())
- item.con_parent = this;
- this.con_items[item.index] = item;
- this.con_items_cnt++;
-
- return E_OK;
- },
-
- remove: function(item)
- {
- /* Can only remove items from open container */
- if (!this.con_open)
- return E_CLOSED;
-
- /* We can only remove items which are really inside */
- if (this.con_items[item.index] == undefined)
- return E_NONE;
-
- /*
- * Climb up our parents containers list to update them
- */
- for (var o = this.con_parent; o != undefined;
- o = o.con_parent) {
- o.con_weight_cur -= this.getWeight();
- if ((o.con_ctype & CT_EXPAND) != 0)
- o.con_volume_cur -= this.getVolume();
- }
-
- if (item.isContainer())
- delete item.con_parent;
- this.con_weight_cur -= item.getWeight();
- this.con_volume_cur -= item.getVolume();
- delete this.con_items[item.index];
- this.con_items_cnt--;
-
- return E_OK;
- },
-
- inventory: function(level)
- {
- print(' - ' + level +
- ': name=' + this.name +
- ' volume=' + this.getVolume() +
- ' weight=' + this.getWeight());
-
- if (!this.con_open)
- return 'already_open!';
-
- level++;
- for (var i in this.con_items) {
- /*
- if (this.con_items[i] == undefined)
- continue;
- */
- print(level +
- ': name=' + this.con_items[i].name +
- ' volume=' + this.con_items[i].getVolume() +
- ' weight=' + this.con_items[i].getWeight());
- if (this.con_items[i].isContainer())
- this.con_items[i].inventory(level);
- }
- level--;
- }
-};
-
-
-
-/*
- * Some testing in order
- */
-
-/* Create an item */
-var i = new Item('a ring', 'the one ring', 5, 5, CT_SOLID);
-
-/* Create a container item */
-var c = new ContainerItem('a bag', 'a soft cloth bag', 5, 5, CT_SOLID,
- 10, 15, 20, CT_SOLID | CT_EXPAND);
-
-/* Create a second container item */
-var c2 = new ContainerItem('another bag', 'another soft cloth bag', 5, 5,
- CT_SOLID, 10, 15, 20, CT_SOLID | CT_EXPAND);
-
-print();
-c2.inventory(0);
-print();
-
-c.open();
-print(c.add(i));
-c.close();
-c2.open();
-print(c2.add(c));
-c2.close();
-
-c.open();
-c2.open();
-c2.inventory(0);
-c.remove(i);
-c2.remove(c);
-
-print();
-c2.inventory(0);
+++ /dev/null
-/* $Id: httpd.js,v 1.74 2005/12/17 00:13:12 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Little example showing the versatility of ECMAScript, provided with a
- * custom library for BSD socket support, using SpiderMonkey.
- * This tiny HTTPd allows simultaneous concurrent client connections without
- * using multiple threads or processes. Must be ran through
- * ../../src/js-server.
- *
- * Configuration options can be found in options.js
- *
- * XXX Possibly that with close var semantics changes or such, we could
- * support Keep-Alive for HTTP/1.1 connections. This however is not a
- * priority at current time.
- *
- * TODO:
- * - There is a problem if the remote socket closes when we're sending
- * alot of data, poll(2) apparently doesn't always save us from this
- * despite checking POLLHUP/POLLERR events. A SIGPIPE signal is sent
- * to the process, and we must be able to handle some common signals.
- * This would also allow an opportunity for applications to register
- * server cleanup handlers when SIGTERM is received, i.e. to save to
- * disk in-memory cached data, etc.
- * We probably should ensure to block the signal when executing the
- * user function for an occurred signal...
- * - Read http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
- * and see if we meet conformance, adjust as needed.
- * - We might want to check Accept-Language: for multilingual sites...
- * - See what to do for HEAD and PUT
- * - Possibly limit rate of connections per address like I did in
- * mmftpd/mmsmtpd/mmpop3d/mmspawnd, a requested feature of 3s4i.
- * If enabling this, it probably should be per-vhost configurable.
- * I'm not sure HTTP protocol is well suited to this type of limiting,
- * some testing will be required.
- * - Provide a function to scripts to redirect to another page.
- * - We might want vhost-specific default login page, and provide
- * an easy means in the server to have non-authenticated users automatically
- * redirected to that page using server-side magic. A special session
- * variable should be used for this, and registered when the user logins.
- * - Similar to the above, but should be special provision for scripts to
- * easily specify their required access level, which must be met by a
- * currently logged in user, which is otherwise redirected to an
- * application-specific page.
- * - Implement logging
- * - It might be nice to implement a minimal database-like facility, where
- * for performance we could log changes, and only sync to disk once in a
- * while, discarding obsolete logs... JSON would be used
- * - Enhance the JS shell to report errors better
- * - We should have support for easy storage and safe export of files,
- * like I implemented for ascpi.com. This is useful for instance to
- * store and post user profile images and related thumbnail, etc.
- */
-
-
-
-/*
- * Server identification
- */
-SERVER_VERSION = 'mmondor_js_httpd/0.0.1 (NetBSD)';
-SERVER_CVSID = '$Id: httpd.js,v 1.74 2005/12/17 00:13:12 mmondor Exp $';
-
-
-
-/*
- * Open standard error FD for diagnostics output
- */
-err = new FD();
-err.set(FD.STDERR_FILENO);
-
-
-
-/*
- * Import needed functionality from external modules, since #include is
- * missing :)
- */
-function file_read(file)
-{
- var contents = '';
- var data;
- var fd;
-
- try {
- /*
- * If we were provided with a filename, rather than a
- * filedescriptor object, open the filename. Otherwise,
- * read from the specified FD.
- */
- if (typeof file != 'object') {
- fd = new FD();
- fd.open(file, FD.O_RDONLY);
- } else
- fd = file;
- } catch (x) {
- err.put(x + " at file_read()\n");
- }
-
- try {
- for (;;) {
- data = fd.read(65536);
- if (data.length == 0)
- break;
- contents += data;
- }
- } catch (x) {
- err.put(x + " at file_read()\n");
- }
-
- fd.close();
-
- return contents;
-}
-
-try {
- eval(file_read('options.js')); /* Configuration */
-} catch (x) {
- err.put(x + " while reading options file\n");
- exit();
-}
-eval(file_read('string.js')); /* startsWith()/endsWith() */
-eval(file_read('ml.js')); /* MLTag object for HTML generation */
-eval(file_read('root.js')); /* Root object for virtual chroot(2) */
-
-
-
-/*
- * Client states (enumeration)
- */
-const STATE_TRANSFER_READ = 1;
-const STATE_TRANSFER_WRITE = 2;
-
-
-
-/*
- * Quick lookup object from virtual hosts names and aliases to VHost objects
- */
-var vhosts_table = {};
-var default_vhost = undefined;
-
-/*
- * The VHost object
- */
-function VHost(o)
-{
- /*
- * A few properties are definitely required
- */
- if (o.name == undefined || o.root == undefined)
- throw ('name and root properties missing from vhost');
- o.name = o.name.toLowerCase();
- this.name = o.name;
-
- if (o.scripts != undefined && o.scripts == true) {
- this.scripts = true;
- this.globals = {};
- }
-
- /*
- * Create VHost object
- */
- try {
- this.htdocs_root = new Root(o.root);
- } catch (x) {
- throw (x);
- }
-
- if (o.charset != undefined)
- this.charset = o.charset;
- if (o.session_exp != undefined)
- this.session_exp = o.session_exp;
-
- /*
- * Link object to vhosts table
- */
- if (vhosts_table[o.name] != undefined)
- throw ('Conflicting vhost name: ' + o.name);
- vhosts_table[o.name] = this;
-
- if (o.aliases != undefined) {
- for (i in o.aliases) {
- o.aliases[i] = o.aliases[i].toLowerCase();
- if (vhosts_table[o.aliases[i]] != undefined)
- throw ('Conflicting vhost alias: ' +
- o.aliases[i]);
- vhosts_table[o.aliases[i]] = this;
- }
- }
-}
-
-
-
-/*
- * The Session object allows to maintain persistent session variables among
- * multiple client queries. We are using a cookie with the unique session ID
- * to link those queries together into a session.
- */
-
-var o = new FD();
-o.set(FD.STDOUT_FILENO);
-
-var session_randfd = new FD();
-try {
- session_randfd.open('/dev/urandom', FD.O_RDONLY);
-} catch (x) {
- err.put(x + " while attempting to open /dev/urandom\n");
- exit();
-}
-
-var session_charlist = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
- 'abcdefghijklmnopqrstuvwxyz' +
- '0123456789-_';
-
-var sessions_table = {};
-
-/*
- * To be called at regular but spaced intervals,
- * destroys expired session objects.
- */
-function session_gc(time)
-{
- var i;
-
- for (i in sessions_table) {
- if (sessions_table[i].expires <= time)
- delete sessions_table[i];
- }
-}
-
-function Session(time, exp)
-{
- var rval;
- var i;
-
- try {
- rval = session_randfd.read(options.sess_id_size);
- } catch (x) {
- err.put(x + " while attempting to read from /dev/urandom\n");
- }
- this.sessid = '';
- for (i = 0; i < options.sess_id_size; i++)
- this.sessid +=
- session_charlist.charAt(rval.charCodeAt(i) & 0x3f);
-
- this.variables = {};
- this.expires = time + exp;
- sessions_table[this.sessid] = this;
-}
-
-
-
-/*
- * For the mime types database
- */
-
-var mimetypes_table = {};
-
-
-
-/*
- * And our pre-evaluated scripts cache
- */
-var jso_cache = {};
-
-function JSO(path, time, script)
-{
- this.path = path;
- this.time = time;
- this.script = script;
-
- jso_cache[this.path] = this;
-}
-
-
-
-/*
- * The HTTPReply object allows to cache addHeader() and addContent() requests,
- * and to eventually flush the whole reply to an arbitrary FD afterwards.
- */
-
-/*
- * Constructor
- */
-function HTTPReply(code, desc, type)
-{
- this.code = code;
- this.desc = desc;
- this.headers = [];
- this.contents = [];
- this.type = type;
-
- /*
- * Insert our standard headers.
- */
- this.gmttime = (new Date()).toGMTString();
- this.headers.push('Date: ' + this.gmttime);
- this.headers.push('Server: ' + SERVER_VERSION);
- this.headers.push('Connection: close');
- this.headers.push('Accept-Ranges: bytes');
-}
-/*
- * HTTPReply prototype object
- */
-HTTPReply.prototype = {
- setType: function(type)
- {
-
- this.type = type;
- },
-
- addHeader: function(data)
- {
-
- this.headers.push(data);
- },
-
- addNoCacheHeaders: function()
- {
-
- this.headers.push('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
- this.headers.push('Last-Modified: ' + this.gmttime);
- this.headers.push('Cache-Control: no-cache, must-revalidate');
- this.headers.push('Pragma: no-cache');
- },
-
- addContent: function(data)
- {
-
- this.contents.push(data);
- },
-
- flush: function(fd, size)
- {
- var headers = '';
- var contents = '';
- var i;
-
- for (i = 0; i < this.contents.length; i++)
- contents += this.contents[i];
-
- this.headers.push('Content-Length: ' +
- (size == null ? contents.length : size));
- headers += 'HTTP/1.1 ' + this.code + ' ' + this.desc + "\r\n";
- for (i = 0; i < this.headers.length; i++)
- headers += this.headers[i] + "\r\n";
- if (this.type != null)
- headers += 'Content-Type: ' + this.type + "\r\n";
- headers += "\r\n";
-
- try {
- if (!fd.http_old_get)
- fd.bwrite(headers + contents);
- else
- fd.bwrite(contents);
- } catch (x) {}
- }
-}
-
-/*
- * Generates an error page and sends it to specified FD object.
- */
-function http_error(fd, code, desc, ldesc)
-{
- var res = new HTTPReply(code, desc, 'text/html; charset=' +
- options.default_charset);
- res.addNoCacheHeaders();
-
- res.addContent('<html><head><title>' + code + ' ' + desc +
- '</title></head><body><h1>' + code + ' ' + desc + '</h1>' +
- '<p>' + ldesc + '</p><br>');
-
- res.addContent(fd.httpDebug());
-
- res.addContent('<br><sub>' + SERVER_VERSION + '<br>' + SERVER_CVSID +
- '</sub></body></html>');
-
- res.flush(fd, null);
- delete res;
-}
-
-
-
-/*
- * Add new methods to the FD prototype object (JavaScript is great like that).
- * We need to add these properties one by one to the prototype object of FD,
- * since we wouldn't want to override its default prototype, just expand it.
- */
-
-/*
- * Handles request query processing, to be assigned to FD.process() while in
- * the request query state. This then invokes parseRequest() which may
- * either send an HTTP response and/or cause a switch to another state.
- */
-function process_query(time)
-{
- var len;
- var done = false;
- var close = false;
- var idx;
- var data;
-
- /*
- * If we reach a request of 32768 bytes, stop reading.
- * If there is no request terminating line within
- * that buffer, close connection.
- *
- * On read error, close connection.
- *
- * If we do find request terminating line, parse query
- * to modify state accordingly, and let the parsing
- * method decide if we'll close the connection.
- */
- len = 32768 - this.request_data.length;
- if (len < 1) {
- done = true;
- close = true;
- } else {
- try {
- data = this.read(len);
- if (data.length == 0)
- close = true;
- this.request_data += data;
- this.updateTimeout(time);
- } catch (x) {
- if (this.errno != Errno.EAGAIN)
- close = true;
- }
- }
- if ((idx = this.request_data.indexOf("\r\n\r\n")) != -1)
- done = true;
- else if (!this.checked_old &&
- (idx = this.request_data.indexOf("\r\n")) != -1) {
- var w;
-
- this.checked_old = true;
- if ((w = (this.request_data.substr(0, idx)).split(' ')).
- length == 2 && w[0] == 'GET') {
- /*
- * Old-style HTTP request, we need to respond
- * immediately in this case without any headers.
- */
- done = true;
- }
- }
-
- if (done && !close) {
- /*
- * Store remaining of buffer after "\r\n" so that we may
- * be able to process POST for instance, without needing
- * to read one char at a time here.
- * Then call our request parsing function.
- */
- idx += 4;
- this.bread_buffer = this.request_data.substr(idx);
- this.request_data = this.request_data.substr(0, idx);
- close = this.parseRequest(time);
- }
-
- return close;
-}
-
-function process_post(time)
-{
- var close = false;
- var len;
- var data;
-
- if (this.post_data.length < this.http_content_length) {
- len = this.http_content_length - this.post_data.length;
- if (len > options.readbuf_size)
- len = options.readbuf_size;
- try {
- data = this.read(len);
- if (data.length == 0)
- close = true;
- this.post_data += data;
- this.updateTimeout(time);
- } catch (x) {
- if (this.errno != Errno.EAGAIN)
- close = true;
- }
- } else
- close = this.parsePost(time);
-
- return close;
-}
-
-/*
- * Handles transfer processing, to be assigned to FD.process() while in the
- * transfer state.
- */
-function process_transfer(time)
-{
- var close = false;
-
- /*
- * STATE_TRANSFER_READ specifies that we're reading to the
- * buffer from transfer_src, or writing from the buffer
- * to transfer_dst (STATE_TRANSFER_WRITE). We can do
- * both steps one after another if possible.
- * On EAGAIN errors, simply pass our turn to go back to
- * polling. On EOF reading from either end, exit transfer
- * state after writing to the other and syncing/closing,
- * and order to close client descriptor.
- * We take care not to transfer more than this.transfer_size bytes
- * outbound.
- * XXX We might also want to observe it for inbound, where it could
- * be set to the user-provided Content-Length... for PUT ?
- */
- if (this.transfer_state == STATE_TRANSFER_READ) {
- if (this == this.transfer_src) {
- /* Reading from client */
- try {
- this.transfer_data =
- this.read(options.readbuf_size);
- if (this.transfer_data.length == 0)
- this.transfer_eof = true;
- this.transfer_state = STATE_TRANSFER_WRITE;
- this.updateTimeout(time);
- } catch (x) {
- if (this.errno != Errno.EAGAIN) {
- close = true;
- try {
- this.transfer_dst.fdatasync();
- } catch (x) {}
- try {
- this.transfer_dst.close();
- } catch (x) {}
- }
- }
- } else {
- /* Reading from file */
- var bufsiz = this.transfer_size;
- if (bufsiz > options.readbuf_size)
- bufsiz = options.readbuf_size;
- try {
- if (bufsiz > 0)
- this.transfer_data =
- this.transfer_src.read(bufsiz);
- else
- this.transfer_data = '';
- if (this.transfer_data.length == 0)
- this.transfer_eof = true;
- this.transfer_size -=
- this.transfer_data.length;
- this.transfer_state = STATE_TRANSFER_WRITE;
- } catch (x) {
- close = true;
- try {
- this.transfer_src.close();
- } catch (x) {}
- }
- }
- }
- if (this.transfer_state == STATE_TRANSFER_WRITE) {
- if (this == this.transfer_dst) {
- /* Writing to client */
- if (this.transfer_eof)
- close = true;
- else {
- try {
- this.bwrite(this.transfer_data);
- this.transfer_state =
- STATE_TRANSFER_READ;
- this.updateTimeout(time);
- } catch (x) {
- if (this.errno != Errno.EAGAIN)
- close = true;
- }
- }
- if (close) {
- try {
- this.transfer_src.close();
- } catch (x) {}
- }
- } else {
- /* Writing to file */
- if (this.transfer_eof)
- close = true;
- else {
- try {
- this.transfer_dst.write(
- this.transfer_data);
- this.transfer_state =
- STAT_TRANSFER_READ;
- } catch (x) {
- close = true;
- }
- }
- if (close) {
- try {
- this.transfer_dst.fdatasync();
- } catch (x) {}
- try {
- this.transfer_dst.close();
- } catch (x) {}
- }
- }
- }
-
- return close;
-}
-
-/*
- * Updates input timeout expiration time for this FD.
- * To be passed current time in seconds since epoch.
- */
-FD.prototype.updateTimeout = function(time)
-{
- /* Update input timeout expiration time */
- this.expires = time + options.io_timeout;
-}
-
-/*
- * Reset FD to a consistent known state, to use after accept(2).
- * To be passed current time in seconds since epoch, and unique id to be used
- * as an index.
- */
-FD.prototype.init = function(time, idx)
-{
- /* Not a bound socket */
- this.bound = false;
- /* Initial state */
- this.transfer_state = STATE_TRANSFER_READ;
- this.transfer_src = this.transfer_dst = null;
- this.transfer_eof = false;
- this.transfer_data = '';
- /* Empty request string */
- this.request_data = '';
- /* Unique index */
- this.index = idx;
- /*
- * Events interested in. FD.POLLOUT to be eventually used instead in
- * transfer state if transfering from server to client.
- */
- this.events = FD.POLLIN;
- /*
- * Default handler to process this FD, the request state one.
- * To be changed to the transfer one as needed for transfer state.
- */
- this.process = process_query;
- /* Initial input timeout */
- this.updateTimeout(time);
- /* For process_request()/process_post() */
- this.bread_buffer = this.bwrite_buffer = '';
- /*
- * Flag used to speed up request parsing related to old HTTP requests
- */
- this.checked_old = false;
-
- /*
- * Note that fcntl(2) and setsockopt(2) flags applied to the bound
- * descriptors are inherited to their children sockets at accept(2).
- * We therefore can save a few syscalls here.
- * We are non-blocking already, for instance.
- */
-}
-
-/*
- * To be called once our client request has been read, to decide what action
- * to perform and modify state accordingly.
- */
-FD.prototype.parseRequest = function(time)
-{
- var close = valid = false;
- var evil_browser = evil_os = false;
- var vhost = '';
- var lines;
- var words;
- var i;
- var sessid, sess;
-
- this.http_protocol = '';
- this.http_method = '';
- this.http_vhost = undefined;
- this.http_path = '';
- this.http_vars_get = {};
- this.http_vars_post = {};
- this.http_vars_cookies = {};
- this.http_agent = '';
- this.http_content_length = -1;
- this.http_modified_since = undefined;
- this.http_sessid = undefined;
- this.http_vars_session = {};
- this.http_range = undefined;
- this.http_old_get = false;
-
- /* Split request lines */
- lines = this.request_data.split("\r\n");
-
- /* Verify if first line has a request which seems valid */
- if (lines.length > 0) {
- words = lines[0].split(' ');
- if (words.length == 2 && words[0] == 'GET') {
- this.http_method = words[0];
- this.http_path = words[1];
- this.http_protocol = 'HTTP/1.1';
- this.http_old_get = true;
- valid = true;
- } else if (words.length == 3) {
- if ((words[0] == 'GET' || words[0] == 'POST' ||
- words[0] == 'PUT') && (words[2] == 'HTTP/1.0' ||
- words[2] == 'HTTP/1.1')) {
- this.http_method = words[0];
- this.http_path = words[1];
- this.http_protocol = words[2];
- valid = true;
- }
- }
- }
-
- /*
- * Parse header for interesting lines.
- */
- if (valid && !this.http_old_get) {
- for (i in lines) {
- words = lines[i].split(' ');
- if (words[0] == 'Host:' && words.length == 2) {
- var i2;
-
- if ((i2 = words[1].indexOf(':')) != -1)
- words[1] = words[1].substr(0, i2);
- vhost = words[1];
- } else if (words[0] == 'Cookie:') {
- words = (lines[i].substr(8)).split('=');
- if (words.length == 2)
- property_add(this.http_vars_cookies,
- words[0], words[1]);
- } else if (words[0] == 'User-Agent:') {
- this.http_agent = lines[i].substr(12);
- if (options.ban_msie == true &&
- this.http_agent.indexOf('MSIE') != -1)
- evil_browser = true;
- if (options.ban_windows == true &&
- this.http_agent.indexOf('Windows') != -1)
- evil_os = true;
- } else if (words[0] == 'Content-Length:' &&
- words.length == 2)
- this.http_content_length = words[1].valueOf();
- else if (words[0] == 'If-Modified-Since:')
- this.http_modified_since = Math.round(
- Date.parse(lines[i].substr(19)) / 1000);
- else if (words[0] == 'Range:')
- this.http_range = lines[i].substr(7);
- }
- }
-
- /*
- * If no Host: line set the vhost, set the default one.
- * If there is a vhost set, but is unknown, also set the default one.
- * Also set the name of the vhost to the actual vhost name despite
- * it possibly being resolved through an alias.
- */
- if (vhost == '' || vhosts_table[vhost] == undefined)
- vhost = options.default_vhost;
- this.http_vhost = vhosts_table[vhost.toLowerCase()];
-
- if (this.http_old_get && this.http_vhost.scripts) {
- /*
- * We only allow old style GET request on static vhosts
- */
- this.bwrite('<html><head>' +
- '<title>Unsupported Old-Style Request</title></head>' +
- '<body><h1>Unsupported Old-Style Request</h1><p>' +
- 'Your browser sent an old-style HTTP request, which ' +
- 'this server does not support on dynamic content ' +
- 'virtual hosts. Use an HTTP/1.0 or HTTP/1.1 compliant ' +
- 'client to continue.</p></body></html>' + "\r\n");
- return true;
- }
-
- /*
- * Filter out definitely invalid requests
- */
- if (!valid) {
- http_error(this, 400, 'Bad Request',
- 'Your browser sent an invalid HTTP query.');
- return true;
- }
-
- /*
- * Block out evil Microsoft products. Start reporting the OS
- * so that an unwanted windows user doesn't install another
- * browser to then realize that his OS is banned nevertheless :)
- */
- if (evil_os) {
- http_error(this, 666, 'Evil Operating System Banished!',
- 'Your Operating System is evil born.<br>At least ' +
- '<b>upgrade</b> to a <a href="http://netbsd.org">' +
- 'decent</a> OS to survive on these grounds.<br>');
- return true;
- } 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 true;
- }
-
- /*
- * Verify if client sent a session cookie, and if it consists of a
- * valid one, load session data.
- */
- if (this.http_vhost.scripts &&
- (sessid = this.http_vars_cookies['SessionID']) != undefined &&
- (sess = sessions_table[sessid]) != undefined &&
- sess.expires > time) {
- this.http_sessid = sessid;
- this.http_vars_session = sess.variables;
- }
-
- /*
- * Fill in associative array with any GET-style supplied
- * variables (as part of the URL). We want this even for POST method.
- */
- if ((i = this.http_path.lastIndexOf('?')) != -1) {
- var v;
- var t;
-
- v = this.http_path.substr(i + 1);
- /*
- * Note: substring() and substr() are semantically different
- */
- this.http_path = this.http_path.substring(0, i);
- words = v.split('&');
- for (i in words) {
- t = words[i].split('=');
- if (t.length == 2)
- property_add(this.http_vars_get, t[0], t[1]);
- }
- }
-
- /*
- * Switch to POST parsing mode if needed.
- * This will then call this.parsePost() rather than this function,
- * which will also invoke this.httpRespond().
- */
- if (this.http_method == 'POST' && this.http_content_length != -1) {
- /*
- * First obtain buffer stored by process_query() for us,
- * if any. If the full post_data was found in it, simply
- * call the parsing function immediately, which will return
- * with close status after invking httpRespond().
- */
- this.post_data = '';
- if (this.bread_buffer.length > 0) {
- this.post_data += this.bread_buffer;
- this.bread_buffer = '';
- }
- if (this.post_data.length < this.http_content_length) {
- /* Go back to polling, activating process_post */
- this.post_data = '';
- this.process = process_post;
- return false;
- } else
- return this.parsePost(time);
- }
-
- if (!close)
- close = this.httpRespond(time);
-
- return close;
-}
-
-/*
- * After reading post data, allows to parse it down into variables
- */
-FD.prototype.parsePost = function(time)
-{
- var words;
- var i;
- var t;
-
- /*
- * We need to strip "\n", "\r" and "\r\n" which may be sent by broken
- * clients.
- */
- this.post_data = this.post_data.replace(/\n/g, '');
- this.post_data = this.post_data.replace(/\r/g, '');
-
- words = this.post_data.split('&');
- for (i in words) {
- t = words[i].split('=');
- if (t.length == 2)
- property_add(this.http_vars_post, t[0], t[1]);
- }
-
- delete this.post_data;
-
- return this.httpRespond(time);
-}
-
-/*
- * Finally respond to client request
- */
-FD.prototype.httpRespond = function(time)
-{
- var path, fd, st, res, ext, mimetype, i, sess, size;
-
- /*
- * Verify if requested path is valid
- */
- path = this.http_vhost.htdocs_root.valid_virtual(this.http_path);
- if (!path) {
- http_error(this, 403, 'Permission Denied',
- 'You do not have the permission to access this ' +
- 'resource.');
- return true;
- }
-
- /*
- * We now want to verify if the client sent a valid session cookie.
- * If it didn't, we present it with a page with meta-tag reload
- * instructions to attempt to reload the originally supplied URL,
- * and with a new session cookie.
- */
- if (this.http_vhost.scripts == true && this.http_sessid == undefined) {
- var doc, sess;
-
- doc = new HTTPReply(200, 'OK',
- 'text/html; charset=' + options.default_charset);
- doc.addNoCacheHeaders();
-
- sess = new Session(time,
- (this.http_vhost.session_exp != undefined ?
- this.http_vhost.session_exp :
- options.default_session_exp));
- sess.variables.count = 0;
- doc.addHeader('Set-Cookie: SessionID=' + sess.sessid +
- '; expires=' +
- (new Date(sess.expires * 1000)).toGMTString() +
- '; path=/');
- doc.addContent('<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>');
- doc.flush(this, null);
-
- return true;
- }
- if (this.http_sessid != undefined)
- this.http_vars_session.count++;
-
-
- /*
- * Attempt to find requested file.
- * If it consists of a directory, attempt to first find index.html in
- * it, then index.jso in it, fail with error 403 if none can be found.
- * Otherwise, continue forward keeping the descriptor open, and the
- * stat information already filled in.
- */
- fd = new FD();
-
- try {
- fd.open(path.real, FD.O_RDONLY);
- st = fd.fstat();
- } catch (x) {
- http_error(this, 404, 'Not Found',
- path.virtual + ' could not be found.');
- return true;
- }
-
- if ((st.st_mode & FD.S_IFDIR) != 0) {
- var error = false;
-
- fd.close();
- try {
- fd.open(path.real + '/index.html', FD.O_RDONLY);
- st = fd.fstat();
- path.real += '/index.html';
- path.virtual += '/index.html';
- } catch (x) {
- try {
- fd.open(path.real + '/index.jso',
- FD.O_RDONLY);
- st = fd.fstat();
- path.real += '/index.jso';
- path.virtual += '/index.jso';
- } catch (x) {
- error = true;
- }
- }
- if (error) {
- http_error(this, 403, 'Permission Denied',
- 'You do not have the permission to access the ' +
- 'resource ' + path.virtual);
- return true;
- }
- }
-
- /*
- * Only continue if file is a regular file
- */
- if ((st.st_mode & FD.S_IFREG) == 0) {
- fd.close();
- http_error(this, 403, 'Permission Denied',
- 'You do not have the permission to access the resource ' +
- path.virtual);
- return true;
- }
-
- /*
- * Extract file extension, as well as its mime type.
- */
- if ((i = path.virtual.lastIndexOf('.')) != -1) {
- ext = (path.virtual.substr(i + 1)).toLowerCase();
- if (ext.lastIndexOf('/') == -1 &&
- mimetypes_table[ext] != undefined)
- mimetype = mimetypes_table[ext];
- else
- mimetype = options.default_mimetype;
- }
- mimetype += '; charset=' + (this.http_vhost.charset == undefined ?
- options.default_charset : this.http_vhost.charset);
-
- /*
- * JavaScript Object (JSO) files are processed especially.
- * We interpret them, providing them with an environment to
- * access the needed resources (get/post/cookie/session variables,
- * as well as the document object they can use).
- * We flush the document once the script returns, or fire up
- * an error if something fails.
- */
- if (ext == 'jso') {
-
- /* Create document object */
- var obj = new HTTPReply(200, 'OK', mimetype);
- obj.addNoCacheHeaders();
-
- /* XXX Add other objects to export as necessary */
- obj.get = this.http_vars_get;
- obj.post = this.http_vars_post;
- obj.cookie = this.http_vars_cookies;
- obj.session = this.http_vars_session;
- obj.global = this.http_vhost.globals;
-
- /*
- * Check if object in our cache already and file not modified
- * since cached entry. Reuse function then. Otherwise,
- * load in function from file, store a cache entry for it
- * and launch it.
- */
-
- var data, s, o;
-
- if (((o = jso_cache[path.real]) != undefined) &&
- o.time == st.st_mtime) {
- obj.script = o.script;
- fd.close();
- } else {
- try {
- /* file_read() closes fd for us */
- data = file_read(fd);
- s = 'obj.script = function() {' + data + '}';
- eval(s);
- o = new JSO(path.real, st.st_mtime,
- obj.script);
- } catch (x) {
- err.put(x + ' evaluating script ' + path.real
- + "\n");
- http_error(this, 500, 'Internal Server Error',
- 'Please try again later.');
- return true;
- }
- }
-
- try {
- if (obj.script != undefined)
- obj.script();
- obj.flush(this, null);
- } catch (x) {
- err.put(x + ' executing script ' + path.real + "\n");
- http_error(this, 500, 'Internal Server Error',
- 'Please try again later.');
- }
-
- delete obj;
- return true;
- }
-
- /*
- * If client only wanted the document if it wasn't modified since
- * a certain time, report that it wasn't if it is the case.
- */
- if (this.http_modified_since != undefined &&
- st.st_mtime <= this.http_modified_since) {
- fd.close();
- res = new HTTPReply(304, 'Not Modified', null);
- res.flush(this, null);
- return true;
- }
-
- /*
- * If client only requested a range of bytes of the file, verify if
- * the range is valid, and if so, arrange to only transfer the
- * requested part of the file.
- * In any other case, simply transfer the whole file.
- */
- if (this.http_range != undefined) {
- var w;
-
- if (this.http_range.startsWith('bytes'))
- this.http_range = this.http_range.substr(5);
- if (this.http_range.length > 0 &&
- this.http_range.charAt(0) == '=')
- this.http_range = this.http_range.substr(1);
- if ((w = this.http_range.split('-')).length == 2) {
- var from, to;
-
- if (w[0] == '')
- w[0] = 0;
- if (w[1] == '')
- w[1] = st.st_size - 1;
- from = Number(w[0]);
- to = Number(w[1]);
-
- if (from >= 0 && to < st.st_size && to >= from) {
- res = new HTTPReply(206, 'Partial Content',
- mimetype);
- res.addHeader('Content-Range: bytes ' +
- from + '-' + to + '/' + st.st_size);
- this.transfer_size = Math.abs((to - from) + 1);
- if (from > 0) {
- try {
- fd.lseek(from, FD.SEEK_SET);
- } catch(x) {
- err.put(x + " at lseek()\n");
- }
- }
- }
- }
- }
- if (res == undefined) {
- res = new HTTPReply(200, 'OK', mimetype);
- this.transfer_size = Math.abs(st.st_size);
- }
-
- /*
- * Flush HTTP header, sending it
- */
- res.flush(this, this.transfer_size);
-
- /*
- * Switch to outbound transfer mode.
- */
- this.transfer_src = fd;
- this.transfer_dst = this;
- this.process = process_transfer;
- this.events = FD.POLLOUT;
-
- /*
- * Return with close=false, to delegate operations to
- * process_transfer().
- */
- return false;
-}
-
-FD.prototype.httpDebug = function()
-{
- table = new MLTag('table', true);
- table.addAttr('width', '100%');
- table.addAttr('border', '1');
-
- var tr = new MLTag('tr', true);
- var td = new MLTag('td', true);
- td.addAttr('width', '10%');
- td.addAttr('align', 'right');
- td.addContent('You are:');
- tr.addContent(td);
- td = new MLTag('td', true);
- td.addAttr('width', '90%');
- td.addContent(this.client_addr + ':' + this.client_port);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('You sent:');
- tr.addContent(td);
- td = new MLTag('td', true);
- var font = new MLTag('font', true);
- font.addAttr('size', '-3');
- pre = new MLTag('pre', true);
- pre.addContent(this.request_data);
- font.addContent(pre);
- td.addContent(font);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('GET vars:');
- tr.addContent(td);
- td = new MLTag('td', true);
- var font = new MLTag('font', true);
- font.addAttr('size', '-3');
- pre = new MLTag('pre', true);
- pre.addContent(uneval(this.http_vars_get));
- font.addContent(pre);
- td.addContent(font);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('POST vars:');
- tr.addContent(td);
- td = new MLTag('td', true);
- var font = new MLTag('font', true);
- font.addAttr('size', '-3');
- pre = new MLTag('pre', true);
- pre.addContent(uneval(this.http_vars_post));
- font.addContent(pre);
- td.addContent(font);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('Cookies:');
- tr.addContent(td);
- td = new MLTag('td', true);
- var font = new MLTag('font', true);
- font.addAttr('size', '-3');
- pre = new MLTag('pre', true);
- pre.addContent(uneval(this.http_vars_cookies));
- font.addContent(pre);
- td.addContent(font);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('VHost:');
- tr.addContent(td);
- td = new MLTag('td', true);
- td.addContent(this.http_vhost.name);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('Path:');
- tr.addContent(td);
- td = new MLTag('td', true);
- td.addContent(this.http_path);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('Mod-Since:');
- tr.addContent(td);
- td = new MLTag('td', true);
- td.addContent(this.http_modified_since);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('SessID:');
- tr.addContent(td);
- td = new MLTag('td', true);
- td.addContent(this.http_sessid);
- tr.addContent(td);
- table.addContent(tr);
-
- tr = new MLTag('tr', true);
- td = new MLTag('td', true);
- td.addAttr('align', 'right');
- td.addContent('Sess vars:');
- tr.addContent(td);
- td = new MLTag('td', true);
- var font = new MLTag('font', true);
- font.addAttr('size', '-3');
- pre = new MLTag('pre', true);
- pre.addContent(uneval(this.http_vars_session));
- font.addContent(pre);
- td.addContent(font);
- tr.addContent(td);
- table.addContent(tr);
-
- return table.toStr(0);
-}
-
-/*
- * Since we are using nonblocking mode, it is possible for write(2) to return
- * a short count, in which case we must be able to resume writing the
- * remaining buffer after poll(2). There are currently two write paths,
- * HTTPResponse.flush() and process_transfer(). Both can use this function
- * instead of write(2). The main poll(2) based loop can then handle buffer
- * flushing. However, we first attempt to immediately write as much as we can
- * before queuing what needs to be written again.
- * Like write(2), this function can throw an exception on error, including
- * for EAGAIN.
- */
-FD.prototype.bwrite = function(data)
-{
- var size;
-
- if ((size = this.write(data)) == data.length)
- return data.length;
-
- this.bwrite_buffer += data.substr(size);
- return size;
-}
-
-/*
- * Called by the main loop to know if there exists queued data, and to write
- * it out if needed. Returns an object with two properties, done which if
- * true tells the caller that there is no more data to flush, and close
- * which if true means that an error occurred in which case connection should
- * be closed.
- */
-FD.prototype.bwrite_flush = function()
-{
- var size;
- var obj = new Object();
-
- obj.done = obj.close = false;
-
- if (this.bwrite_buffer.length == 0) {
- obj.done = true;
- return obj;
- }
-
- b = true;
- try {
- size = this.write(this.bwrite_buffer);
- this.bwrite_buffer = this.bwrite_buffer.substr(size);
- if (this.bwrite_buffer.length == 0)
- obj.done = true;
- } catch (x) {
- if (this.errno != Errno.EAGAIN)
- obj.close = true;
- }
-
- return obj;
-}
-
-/*
- * Verifies if property name ends with [], which considers it as an array of
- * values which are then pushed into that array.
- * Sets the property normally otherwise.
- */
-function property_add(obj, prop, val)
-{
- prop = unescape(prop.replace(/\+/g, ' '));
- val = unescape(val.replace(/\+/g, ' '));
-
- if (prop.endsWith('[]')) {
- /* Array entry */
- prop = prop.substring(0, prop.length - 2);
- if (obj[prop] == undefined)
- obj[prop] = [];
- obj[prop].push(val);
- } else
- obj[prop] = val;
-}
-
-
-
-/*
- * To know how many connections we are currently serving and limit them
- */
-var counters_connections = 0;
-var counters_addresses = [];
-
-function counters_inc(fd)
-{
-
- if (counters_connections >= options.max_connections)
- return false;
-
- var addr = fd.client_addr;
- if ((addrcnt = counters_addresses[addr]) != undefined &&
- addrcnt >= options.max_connections_addr)
- return false;
-
- if (addrcnt == undefined)
- addrcnt = 1;
- else
- addrcnt++;
- counters_addresses[addr] = addrcnt;
-
- return true;
-}
-
-function counters_dec(fd)
-{
- var addr = fd.client_addr;
- if ((--counters_addresses[addr]) == 0)
- delete counters_addresses[addr];
-
- counters_connections--;
-}
-
-
-
-/*
- * Main program
- */
-function main() {
- var i;
- var sess_gc_secs = 0;
-
- /*
- * Populate vhosts database
- */
- for (i in vhosts) {
- try {
- var v = new VHost(vhosts[i]);
- } catch (x) {
- err.put(x + " creating VHost object\n");
- }
- }
- /*
- * Attempt to link default_vhost to a VHost object
- */
- if (options.default_vhost == undefined) {
- err.put("No default_vhost property in options, exiting\n");
- exit();
- }
- if (vhosts_table[options.default_vhost.toLowerCase()] != undefined)
- default_vhost =
- vhosts_table[options.default_vhost.toLowerCase()];
- else {
- err.put('Default vhost ' + options.default_vhost +
- " unkonwn, exiting\n");
- exit();
- }
-
- /*
- * Populate mimetypes database.
- * XXX If this got very large, because the destination strings
- * are rather large, it might be good to use indexes or references
- * to them rather than provide the string for each extension.
- */
- for (i in mimetypes) {
- var i2, ext;
-
- for (i2 in mimetypes[i]) {
- ext = mimetypes[i][i2].toLowerCase();
- if (mimetypes_table[ext] != undefined) {
- err.put('Conflicting mime type ' +
- ext + ' -> ' + i + "\n");
- continue;
- }
- mimetypes_table[ext] = i;
- }
- }
- if (mimetypes_table['jso'] == undefined)
- mimetypes_table['jso'] = 'text/html';
-
- /*
- * Create polling array set
- */
- var set = [];
-
- /*
- * Initialize server
- */
- for (i in listen) {
- var fd;
-
- try {
- fd = new FD();
- fd.socket(FD.AF_INET, FD.SOCK_STREAM, 0);
- fd.bind(listen[i].address, listen[i].port);
-
- fd.setsockopt(FD.SO_REUSEADDR, 1);
- fd.setsockopt(FD.SO_LINGER, -1);
- fd.setsockopt(FD.SO_KEEPALIVE, 1);
- fd.setsockopt(FD.TCP_NODELAY, 1);
-
- fd.fcntl(FD.F_SETFL, FD.O_NONBLOCK);
- fd.listen(options.max_connections);
- /*
- * Mark socket as bound one and add it to
- * polling set
- */
- fd.events = FD.POLLIN;
- fd.bound = true;
- set.push(fd);
- } catch (x) {
- err.put(x + " preparing listening socket\n");
- }
- }
- if (set.length == 0)
- exit();
-
- /*
- * Used for unique index associated to each FD for efficient removal
- * from polling set when closing connection
- */
- var count = listen.length + 1;
-
- /*
- * Main loop
- */
- for (;;) {
- /*
- * Determine next to expire FD event, so that we can sleep
- * as long as possible. Also get rid of expired requests.
- */
- var cur = Date.parse(new Date) / 1000;
- var exp = cur + 3600;
- var old;
- for (i in set) {
- var fd;
-
- if ((fd = set[i]) == undefined || fd.bound == true)
- continue;
- if (fd.expires <= cur) {
- /*
- * Request timeout expired for this
- * descriptor, we must close it.
- */
- fd.close();
- counters_dec(fd);
- delete set[fd.index];
- delete fd;
- continue;
- }
- if (fd.expires < exp)
- exp = fd.expires;
- }
- exp -= cur;
-
- /*
- * Poll our set of descriptors for events
- */
- var e;
- try {
- e = FD.poll(set, exp * 1000);
- } catch (x) {
- err.put(x + " for poll(2)\n");
- continue;
- }
-
- /*
- * Verify if a timeout occurred. Because our poll
- * implementation returns an array, its timeout event can be
- * checked against by verifying if the set is empty.
- */
- if (e.length == 0) {
- /* Timeout */
- continue;
- }
-
- /*
- * XXX We should perhaps check timeouts after, so that we
- * only need to query time once per loop?
- * We could only do this if we knew that all our next
- * processing is non-blocking, however. Otherwise we
- * would loose track of actual time.
- */
- old = cur;
- cur = (Date.parse(new Date) / 1000);
-
- /*
- * Verify if we should call the session gc, and if so, do so.
- */
- sess_gc_secs += cur - old;
- if (sess_gc_secs >= options.sess_gc_interval) {
- sess_gc_secs = 0;
- session_gc(cur);
- }
-
- /*
- * Run through set of descriptors with pending events
- */
- for (i in e) {
- if (e[i] == undefined)
- continue;
-
- /*
- * If descriptor is a bound one, attempt to accept
- * the new client connection
- */
- if (e[i].bound == true &&
- (e[i].revents & FD.POLLIN) != 0) {
- var fd;
-
- try {
- fd = e[i].accept();
- if (!counters_inc(fd)) {
- http_error(fd, 403.9,
- 'Too Many Connections',
- 'Your browser has exceeded its maximum ' +
- 'allowed number of concurrent ' +
- 'connections.');
- fd.close();
- delete fd;
- } else {
- /*
- * Setup client's initial
- * state and add FD to polling
- * set
- */
- if (++count > 999999)
- count = listen.length
- + 1;
- fd.init(cur, count);
- set[count] = fd;
- }
- } catch (x) {
- err.put(x + " at accept(2)\n");
- }
- continue;
- }
-
- /*
- * Not a new connection event
- */
- var close = false;
-
- /*
- * Close connection on error conditions,
- * Call the FD's process function on interesting
- * events, which will tell when we should drop the
- * client.
- */
- if ((e[i].revents & (FD.POLLHUP | FD.POLLERR)) != 0)
- close = true;
- else if ((e[i].revents & (FD.POLLIN | FD.POLLOUT))
- != 0) {
- /*
- * Flush output queue if any and possible,
- * since we're in non-blocking mode writes
- * can yield short counts.
- */
- var o = e[i].bwrite_flush();
-
- if (o.close == true)
- close = true;
- else if (o.done == true)
- close = e[i].process(cur);
- }
-
- if (close) {
- try {
- e[i].close();
- } catch (x) {}
- counters_dec(e[i]);
- delete set[e[i].index];
- delete e[i];
- }
- }
- }
-
- /* NOTREACHED */
- err.close();
-}
-
-main();
-/* NOTREACHED */
-exit(0);
+++ /dev/null
-/* $Id: ml_clean.js,v 1.4 2005/07/12 13:09:07 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-/*
- * Useful object to use for HTML tags. <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.4 2005/07/12 13:09:07 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-/*
- * Useful object to use for HTML tags. <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: options.js,v 1.17 2005/12/12 09:55:45 mmondor Exp $ */
-
-var options = {
- max_connections: 32,
- max_connections_addr: 4,
- io_timeout: 60,
- readbuf_size: 65536,
- default_vhost: "chat.pulsar-zone.net",
- default_mimetype: "application/octet-stream",
- default_charset: "us-ascii",
- default_session_exp: 1800,
- sess_gc_interval: 600,
- sess_id_size: 64,
- ban_msie: false,
- ban_windows: false
-};
-
-/* Address:port combinations to listen to */
-var listen = [
- {
- address: "127.0.0.1",
- port: 8080
- },
- {
- address: "192.168.1.12",
- port: 8080
- }
-];
-
-var vhosts = [
- /* Default virtual host */
- {
- name: "chat.pulsar-zone.net",
- root: "/home/mmondor/jswww/chat",
- scripts: true,
- charset: 'iso-8859-1',
- session_exp: 14400
- },
-
- /* Dynamic application virtual host for ascpi.com */
- {
- name: "ascpi.com",
- aliases: [ "www.ascpi.com", "ascpi.hal.xisop",
- "ascpi.hal" ],
- root: "/home/mmondor/jswww/ascpi.com"
- },
-
- /* Static only test virtual host */
- {
- name: "test.localhost",
- root: "/home/mmondor/jswww/welcome"
- }
-];
-
-var mimetypes = {
- 'text/html': [ "html", "htm", "dhtml",
- "jso" ],
- 'text/plain': [ "txt" ],
- 'text/css': [ "css" ],
- 'application/x-xpinstall': [ "xpi" ],
- 'application/vnd.mozilla.xul+xml': [ "xul" ],
- 'text/rdf': [ "rdf" ],
- 'application/pdf': [ "pdf" ],
- 'application/postscript': [ "ps" ],
- 'application/x-tar': [ "tar" ],
- 'application/x-gzip': [ "gz" ],
- 'application/x-bzip2': [ "bz2" ],
- 'application/zip': [ "zip" ],
- 'application/x-javascript': [ "js" ],
- 'application/x-c': [ "c", "h", "cpp", "cc" ],
- 'application/x-sh': [ "sh" ],
- 'application/x-shockwave-flash': [ "swf" ],
- 'application/xml': [ "xml" ],
- 'application/xml-dtd': [ "dtd" ],
- 'image/jpg': [ "jpg", "jpeg" ],
- 'image/png': [ "png" ],
- 'image/gif': [ "gif" ],
- 'image/x-icon': [ "ico" ],
- 'video/mpeg': [ "mpeg", "mpg" ],
- 'video/quicktime': [ "mov" ],
- 'video/x-msvideo': [ "asf", "asx", "wmv", "avi" ]
-};
+++ /dev/null
-/* $Id: root.js,v 1.1 2005/07/07 00:11:43 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Implementation of a virtual chroot(2) system.
- */
-
-
-
-const PATH_MAX = 255;
-
-
-
-function Root(root)
-{
-
- if (!(this.root = this.valid_path(root)))
- throw('Invalid root path "' + root + '"!');
- this.cwd = '/';
-}
-
-Root.prototype = {
-
- /*
- * Quick lookup table of valid characters within pathnames.
- * Currently 'a'-'z', 'A'-'Z', '0'-'9', '.', '/', '-', '_'
- */
- valid_char_table: [
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- ],
-
- /*
- * Base function to return formatted valid path, or false.
- * Returned path will always begin with '/' and not have any
- * trailing '/'. Multiple '/' are also collapsed into one.
- */
- valid_path: function(path)
- {
- var i, t, l, p, c, code;
-
- p = l = '/';
- for (i = 0, t = path.length; i < t; i++) {
- c = path.charAt(i);
- if (l == '/') {
- /* Collapse multiple '/' */
- if (c == '/')
- continue;
- /* Prohibit '.' at start of element */
- if (c == '.')
- return false;
- }
- /* Validate chars */
- code = c.charCodeAt(0);
- if (code != code & 0xff ||
- this.valid_char_table[code] == 0)
- return false;
- p += c;
- l = c;
- }
- /* Strip last '/' if any, unless only one */
- if (c == '/' && p.length > 1)
- p = p.substr(0, p.length - 1);
-
- /* If exceeding PATH_MAX, return false */
- if (p.length > PATH_MAX)
- return false;
-
- return p;
- },
-
- /*
- * Returns version of provided path which points to the parent
- * directory if possible. Returns false otherwise.
- * Should only be used with paths first copied using valid_path().
- * Can typically be used with the cwd.
- */
- parent: function(path)
- {
- var i;
-
- /* First make sure that path starts with '/' */
- if (path.length < 2 || path.charAt(0) != '/')
- return false;
-
- /* Strip trailing '/' chars */
- for (i = path.length - 1; i >= 0 && path.charAt(i) == '/';
- i--) ;
- if (i <= path.length)
- path = path.substr(0, i + 1);
-
- /*
- * Locate previous '/', strip everything after it, including
- * it, except in the case where it is the only remaining,
- * where path must remain '/'.
- */
- if ((i = path.lastIndexOf('/')) == 0)
- i = 1;
- return path.substring(0, i);
- },
-
- /*
- * This function should always be called when processing user-supplied
- * paths. The application should then only trust the object it
- * returns. Returns false if the path is invalid.
- * Otherwise, returns an object, with the following properties:
- *
- * <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 2005/07/11 01:50:56 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Extension to the standard String object to support startsWith() and
- * endsWith() very useful methods.
- */
-
-
-
-String.prototype.startsWith = function(str)
-{
- var t = str.length;
-
- if (this.length >= t && this.substr(0, t) == str)
- return true;
-
- return false;
-}
-
-String.prototype.endsWith = function(str)
-{
- var t1 = str.length;
- var t2 = this.length;
-
- if (t2 >= t1 && this.substr(t2 - t1, t1) == str)
- return true;
-
- return false;
-}
+++ /dev/null
-/* $Id: ml.js,v 1.4 2005/06/27 18:21:21 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-const ident_str = ' ' +
- ' ';
-
-function indent(level)
-{
- if (level == null)
- return false;
-
- return ident_str.substr(0, level);
-}
-
-function wrap(str, level)
-{
- if (str == null || level == null)
- return false;
-
- var len = str.length;
- var newstr = indent(level);
- var newlen = level;
- var i;
-
- /*
- * Could find index to space and use substring as necessary instead of
- * looping through every character. Of course, this could also all be
- * implemented using C native code.
- */
- for (i = 0; i < len; i++) {
- if (str.charAt(i) == ' ' && newlen > 70) {
- newstr += "\n" + indent(level);
- newlen = level;
- continue;
- }
- newstr += str.charAt(i);
- newlen++;
- }
-
- return newstr;
-}
-
-
-
-function MLAttr(attr, value)
-{
- /* We allow null value */
- if (attr == null)
- return false;
-
- this.attr = attr;
- this.value = value;
-}
-
-
-
-/*
- * XXX Using prototypes to inherit functions would be ideal for speed,
- * probably, to avoid having to redeclare the functions in the constructor for
- * every new object instance. However, I seemed to have problems when using
- * that method, mostly related to the constructor of the object no longer
- * being tracable.
- */
-
-function MLTag(attr, close)
-{
- if (attr == null || close == null)
- return false;
-
- this.attr = attr;
- this.close = close;
- this.attributes = new Array();
- this.attributes_count = 0;
- this.contents = new Array();
- this.contents_count = 0;
-
- this.addAttr = function(attr, value)
- {
- if (attr == null)
- return false;
-
- this.attributes[this.attributes_count++] =
- new MLAttr(attr, value);
- }
-
- this.addContent = function(item)
- {
- if (item == null)
- return false;
-
- this.contents[this.contents_count++] = item;
- }
-
- this.toStr = function(level)
- {
- if (level == null)
- return false;
-
- var str = '';
- var i, attrlen, len, a;
-
- str += indent(level) + '<' + this.attr;
- attrlen = this.attr.length;
- len = level + attrlen + 1;
- for (i = 0; i < this.attributes_count; i++) {
- a = this.attributes[i];
-
- if (len > 70) {
- len = level + attrlen + 1;
- str += "\n" + indent(len);
- }
- str += ' ' + a.attr;
- len += attrlen + 1;
- if (a.value != null) {
- var value = a.value;
- str += '="' + value + '"';
- len += value.length + 3;
- }
- }
- str += ">\n";
-
- if (this.close)
- level++;
-
- for (i = 0; i < this.contents_count; i++) {
- a = this.contents[i];
-
- if (typeof a == 'object')
- str += a.toStr(level);
- else
- str += wrap(a, level) + "\n";
- }
-
- if (this.close)
- str += indent(--level) + '</' + this.attr + ">\n";
-
- return str;
- }
-}
-
-
-
-/*
- * Now test our functionality
- */
-
-out = new FD();
-out.set(FD.STDOUT_FILENO);
-
-for (i = 0; i < 1000; i++) {
-
-var table = new MLTag('table', true);
-table.addAttr('border', 0);
-table.addAttr('width', '100%');
-
-var tr = new MLTag('tr', true);
-
-var td = new MLTag('td', true);
-td.addAttr('bgcolor', '#000000');
-
-var p = new MLTag('p', true);
-p.addContent('Hello');
-
-td.addContent(p);
-tr.addContent(td);
-table.addContent(tr);
-
-out.put(table.toStr(8));
-
-//out.put(uneval(table));
-
-}
+++ /dev/null
-/* $Id: ml2.js,v 1.3 2005/06/27 19:45:22 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * XXX Using prototypes to inherit functions would be ideal for speed,
- * probably, to avoid having to redeclare the functions in the constructor for
- * every new object instance. However, I seemed to have problems when using
- * that method, mostly related to the constructor of the object no longer
- * being tracable. At worse, we could define our methods as MLTag_foo() and
- * in the constructor function just assign them to the proper properties...
- * The currently used method at least has advantage of being cleaner to read.
- */
-
-/*
- * Useful object to use for HTML tags. <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)
-{
- if (tag == null || close == null)
- return false;
-
- /*
- * A few utility functions used by our methods later on.
- * Adding these here prevents namespace pollution.
- */
-
- /* Returns a string with requested number of spaces */
- const indent_str = ' ' +
- ' ';
- this.indent = function(level)
- {
- if (level == null)
- return false;
-
- return indent_str.substr(0, level);
- }
-
- /*
- * Wraps specified string at 70 columns and observing specified
- * indentation level
- */
- this.wrap = function(str, level)
- {
- if (str == null || level == null)
- return false;
-
- var len = str.length;
- var newstr = this.indent(level);
- var newlen = level;
-
- /*
- * Could find index to space and use substring as necessary
- * instead of looping through every character. Of course,
- * this could also all be implemented using C native code.
- */
- for (var i = 0; i < len; i++) {
- var c = str.charAt(i);
-
- if (c == ' ' && newlen > 70) {
- newstr += "\n" + this.indent(level);
- newlen = level;
- continue;
- }
- newstr += c;
- newlen++;
- }
-
- return newstr;
- }
-
-
- /*
- * MLTag object properties
- */
- this.tag = tag;
- this.close = close;
- this.attributes = new Object();
- this.contents = new Array();
- this.contents_count = 0;
-
- /*
- * Add specified attribute with optional associated value to this tag
- */
- this.addAttr = function(attr, value)
- {
- if (attr == null)
- return false;
-
- eval('this.attributes.' + attr + ' = \'' + value + '\'');
- }
-
- /*
- * Add arbitrary content in sequencial order to this tag's body
- */
- this.addContent = function(item)
- {
- if (item == null)
- return false;
-
- this.contents[this.contents_count++] = item;
- }
-
- /*
- * Returns a string consisting of this tag along with its body and
- * optional corresponding closing tag
- */
- this.toStr = function(level)
- {
- if (level == null)
- return false;
-
- /* Open opening tag */
- var str = this.indent(level) + '<' + this.tag;
-
- /*
- * Store these immediately for performance since we use those
- * values several times later on, also saves typing
- */
- var taglen = this.tag.length;
- var len = level + taglen + 1;
- var attributes = this.attributes;
-
- /* Add each of our attributes with their optional values */
- for (var attr in attributes) {
- var value;
-
- if (len > 70) {
- len = level + taglen + 1;
- str += "\n" + this.indent(len);
- }
- str += ' ' + attr;
- len += taglen + 1;
- if ((value = attributes[attr]) != null) {
- str += '="' + value + '"';
- len += value.length + 3;
- }
- }
- /* Close opening tag */
- str += ">\n";
-
- /* Increase our indent level for content */
- level++;
-
- /*
- * Now add all our contents, if any, which may consist of
- * other MLTag objects or arbitrary String objects. Use
- * recursion to process MLTag ones, which may also have their
- * respective bodies and closing tags.
- */
- for (var i = 0; i < this.contents_count; i++) {
- var a = this.contents[i];
-
- if (typeof a == 'object')
- str += a.toStr(level);
- else
- str += this.wrap(a, level) + "\n";
- }
-
- /*
- * This tag required closing, so add corresponding closing tag
- * and decrease indent level.
- */
- level--;
- if (this.close)
- str += this.indent(level) + '</' + this.tag + ">\n";
-
- return str;
- }
-}
-
-
-
-/*
- * Now test our functionality
- */
-
-out = new FD();
-out.set(FD.STDOUT_FILENO);
-
-for (i = 0; i < 1000; i++) {
-
-var table = new MLTag('table', true);
-table.addAttr('border', 0);
-table.addAttr('width', '100%');
-
-var tr = new MLTag('tr', true);
-
-var td = new MLTag('td', true);
-td.addAttr('bgcolor', '#000000');
-
-var p = new MLTag('p', true);
-p.addContent('Hello');
-
-td.addContent(p);
-tr.addContent(td);
-table.addContent(tr);
-
-out.put(table.toStr(8));
-
-//out.put(uneval(table));
-
-}
+++ /dev/null
-/* $Id: ml3.js,v 1.3 2005/06/27 19:45:22 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * XXX Using prototypes to inherit functions would be ideal for speed,
- * probably, to avoid having to redeclare the functions in the constructor for
- * every new object instance. However, I seemed to have problems when using
- * that method, mostly related to the constructor of the object no longer
- * being tracable. At worse, we could define our methods as MLTag_foo() and
- * in the constructor function just assign them to the proper properties...
- * The currently used method at least has advantage of being cleaner to read.
- */
-
-/*
- * Useful object to use for HTML tags. <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)
-{
- if (tag == null || close == null)
- return false;
-
- /*
- * A few utility functions used by our methods later on.
- * Adding these here prevents namespace pollution.
- */
-
- /* Returns a string with requested number of spaces */
- const indent_str = ' ' +
- ' ';
- this.indent = function(level)
- {
- if (level == null)
- return false;
-
- return indent_str.substr(0, level);
- }
-
- /*
- * Wraps specified string at 70 columns and observing specified
- * indentation level
- */
- this.wrap = function(str, level)
- {
- if (str == null || level == null)
- return false;
-
- var len = str.length;
- var newstr = this.indent(level);
- var newlen = level;
-
- /*
- * Could find index to space and use substring as necessary
- * instead of looping through every character. Of course,
- * this could also all be implemented using C native code.
- */
- for (var i = 0; i < len; i++) {
- var c = str.charAt(i);
-
- if (c == ' ' && newlen > 70) {
- newstr += "\n" + this.indent(level);
- newlen = level;
- continue;
- }
- newstr += c;
- newlen++;
- }
-
- return newstr;
- }
-
-
- /*
- * MLTag object properties
- */
- this.tag = tag;
- this.close = close;
- this.attributes = new Array(); /* Associative */
- this.contents = new Array();
- this.contents_count = 0;
-
- /*
- * Add specified attribute with optional associated value to this tag
- */
- this.addAttr = function(attr, value)
- {
- if (attr == null)
- return false;
-
- this.attributes[attr] = value;
- }
-
- /*
- * Add arbitrary content in sequencial order to this tag's body
- */
- this.addContent = function(item)
- {
- if (item == null)
- return false;
-
- this.contents[this.contents_count++] = item;
- }
-
- /*
- * Returns a string consisting of this tag along with its body and
- * optional corresponding closing tag
- */
- this.toStr = function(level)
- {
- if (level == null)
- return false;
-
- /* Open opening tag */
- var str = this.indent(level) + '<' + this.tag;
-
- /*
- * Store these immediately for performance since we use those
- * values several times later on, also saves typing
- */
- var taglen = this.tag.length;
- var len = level + taglen + 1;
- var attributes = this.attributes;
-
- /* Add each of our attributes with their optional values */
- for (var attr in attributes) {
- var value;
-
- if (len > 70) {
- len = level + taglen + 1;
- str += "\n" + this.indent(len);
- }
- str += ' ' + attr;
- len += taglen + 1;
- if ((value = attributes[attr]) != null) {
- str += '="' + value + '"';
- len += value.length + 3;
- }
- }
- /* Close opening tag */
- str += ">\n";
-
- /* Increase our indent level for content */
- level++;
-
- /*
- * Now add all our contents, if any, which may consist of
- * other MLTag objects or arbitrary String objects. Use
- * recursion to process MLTag ones, which may also have their
- * respective bodies and closing tags.
- */
- for (var i = 0; i < this.contents_count; i++) {
- var a = this.contents[i];
-
- if (typeof a == 'object')
- str += a.toStr(level);
- else
- str += this.wrap(a, level) + "\n";
- }
-
- /*
- * This tag required closing, so add corresponding closing tag
- * and decrease indent level.
- */
- level--;
- if (this.close)
- str += this.indent(level) + '</' + this.tag + ">\n";
-
- return str;
- }
-}
-
-
-
-/*
- * Now test our functionality
- */
-
-out = new FD();
-out.set(FD.STDOUT_FILENO);
-
-for (i = 0; i < 1000; i++) {
-
-var table = new MLTag('table', true);
-table.addAttr('border', 0);
-table.addAttr('width', '100%');
-
-var tr = new MLTag('tr', true);
-
-var td = new MLTag('td', true);
-td.addAttr('bgcolor', '#000000');
-
-var p = new MLTag('p', true);
-p.addContent('Hello');
-
-td.addContent(p);
-tr.addContent(td);
-table.addContent(tr);
-
-out.put(table.toStr(8));
-
-//out.put(uneval(table));
-
-}
+++ /dev/null
-/* $Id: ml4.js,v 1.5 2005/06/27 19:45:22 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-/*
- * Useful object to use for HTML tags. <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)
-{
- if (tag == null || close == null)
- return false;
-
- /*
- * MLTag object properties
- */
- this.tag = tag;
- this.close = close;
- this.attributes = {}; /* Object with associative attributes */
- this.contents = []; /* Array object */
-}
-
-/*
- * Force creation of prototype object
- */
-MLTag('prototype', false);
-
-/*
- * A few utility functions used by our methods later on.
- * Adding these here prevents namespace pollution.
- */
-
-MLTag.prototype.indent_str = ' ' +
- ' ';
-/* Returns a string with requested number of spaces */
-MLTag.prototype.indent = function(level)
-{
- if (level == null)
- return false;
-
- return this.indent_str.substr(0, level);
-}
-
-/*
- * Wraps specified string at 70 columns and observing specified
- * indentation level
- */
-MLTag.prototype.wrap = function(str, level)
-{
- if (str == null || level == null)
- return false;
-
- var len = str.length;
- var newstr = this.indent(level);
- var newlen = level;
-
- /*
- * Could find index to space and use substring as necessary
- * instead of looping through every character. Of course,
- * this could also all be implemented using C native code.
- */
- for (var i = 0; i < len; i++) {
- var c = str.charAt(i);
-
- if (c == ' ' && newlen > 70) {
- newstr += "\n" + this.indent(level);
- newlen = level;
- continue;
- }
- newstr += c;
- newlen++;
- }
-
- return newstr;
-}
-
-/*
- * Add specified attribute with optional associated value to this tag
- */
-MLTag.prototype.addAttr = function(attr, value)
-{
- if (attr == null)
- return false;
-
- this.attributes[attr] = value;
-}
-
-/*
- * Add arbitrary content in sequencial order to this tag's body
- */
-MLTag.prototype.addContent = function(item)
-{
- if (item == null)
- return false;
-
- this.contents.push(item);
-}
-
-/*
- * Returns a string consisting of this tag along with its body and
- * optional corresponding closing tag
- */
-MLTag.prototype.toStr = function(level)
-{
- if (level == null)
- return false;
-
- /* Open opening tag */
- var str = this.indent(level) + '<' + this.tag;
-
- /*
- * Store these immediately for performance since we use those
- * values several times later on, also saves typing
- */
- var taglen = this.tag.length;
- var len = level + taglen + 1;
- var attributes = this.attributes;
-
- /* Add each of our attributes with their optional values */
- for (var attr in attributes) {
- var value;
-
- if (len > 70) {
- len = level + taglen + 1;
- str += "\n" + this.indent(len);
- }
- str += ' ' + attr;
- len += taglen + 1;
- if ((value = attributes[attr]) != null) {
- str += '="' + value + '"';
- len += value.length + 3;
- }
- }
- /* Close opening tag */
- str += ">\n";
-
- /* Increase our indent level for content */
- level++;
-
- /*
- * Now add all our contents, if any, which may consist of
- * other MLTag objects or arbitrary String objects. Use
- * recursion to process MLTag ones, which may also have their
- * respective bodies and closing tags.
- */
- for (var i = 0; i < this.contents.length; i++) {
- var a = this.contents[i];
-
- if (typeof a == 'object')
- str += a.toStr(level);
- else
- str += this.wrap(a, level) + "\n";
- }
-
- /*
- * This tag required closing, so add corresponding closing tag
- * and decrease indent level.
- */
- level--;
- if (this.close)
- str += this.indent(level) + '</' + this.tag + ">\n";
-
- return str;
-}
-
-
-
-/*
- * Now test our functionality
- */
-
-out = new FD();
-out.set(FD.STDOUT_FILENO);
-
-for (i = 0; i < 1000; i++) {
-
-var table = new MLTag('table', true);
-table.addAttr('border', 0);
-table.addAttr('width', '100%');
-
-var tr = new MLTag('tr', true);
-
-var td = new MLTag('td', true);
-td.addAttr('bgcolor', '#000000');
-
-var p = new MLTag('p', true);
-p.addContent('Hello');
-
-td.addContent(p);
-tr.addContent(td);
-table.addContent(tr);
-
-out.put(table.toStr(8));
-
-//out.put(uneval(table));
-
-}
+++ /dev/null
-/* $Id: ml5.js,v 1.3 2005/06/27 19:45:22 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-/*
- * Useful object to use for HTML tags. <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)
-{
- /*
- if (tag == null || close == null)
- return false;
- */
-
- /*
- * MLTag object properties
- */
- this.tag = tag;
- this.close = close;
- this.attributes = {}; /* Object with associative attributes */
- this.contents = []; /* Array object */
-}
-
-/*
- * Define prototype of the MLTag object.
- */
-MLTag.prototype = {
- /* A string of spaces to be used by indent() */
- indent_str: ' ' +
- ' ',
-
- /* Returns a string with requested number of spaces */
- indent: function(level)
- {
- /*
- if (level == null)
- return false;
- */
-
- return this.indent_str.substr(0, level);
- },
-
- /*
- * Wraps specified string at 70 columns and observing specified
- * indentation level
- */
- wrap: function(str, level)
- {
- /*
- if (str == null || level == null)
- return false;
- */
-
- var len = str.length;
- var newstr = this.indent(level);
- var newlen = level;
-
- /*
- * Could find index to space and use substring as necessary
- * instead of looping through every character. Of course,
- * this could also all be implemented using C native code.
- */
- for (var i = 0; i < len; i++) {
- var c = str.charAt(i);
-
- if (c == ' ' && newlen > 70) {
- newstr += "\n" + this.indent(level);
- newlen = level;
- continue;
- }
- newstr += c;
- newlen++;
- }
-
- return newstr;
- },
-
- /*
- * Add specified attribute with optional associated value to this tag
- */
- addAttr: function(attr, value)
- {
- /*
- if (attr == null)
- return false;
- */
-
- this.attributes[attr] = value;
- },
-
- /*
- * Add arbitrary content in sequencial order to this tag's body
- */
- addContent: function(item)
- {
- /*
- if (item == null)
- return false;
- */
-
- this.contents.push(item);
- },
-
- /*
- * Returns a string consisting of this tag along with its body and
- * optional corresponding closing tag
- */
- toStr: function(level)
- {
- /*
- if (level == null)
- return false;
- */
-
- /* Open opening tag */
- var str = this.indent(level) + '<' + this.tag;
-
- /*
- * Store these immediately for performance since we use those
- * values several times later on, also saves typing
- */
- var taglen = this.tag.length;
- var len = level + taglen + 1;
- var attributes = this.attributes;
-
- /* Add each of our attributes with their optional values */
- for (var attr in attributes) {
- var value;
-
- if (len > 70) {
- len = level + taglen + 1;
- str += "\n" + this.indent(len);
- }
- str += ' ' + attr;
- len += taglen + 1;
- if ((value = attributes[attr]) != null) {
- str += '="' + value + '"';
- len += value.length + 3;
- }
- }
- /* Close opening tag */
- str += ">\n";
-
- /* Increase our indent level for content */
- level++;
-
- /*
- * Now add all our contents, if any, which may consist of
- * other MLTag objects or arbitrary String objects. Use
- * recursion to process MLTag ones, which may also have their
- * respective bodies and closing tags.
- */
- for (var i = 0; i < this.contents.length; i++) {
- var a = this.contents[i];
-
- if (typeof a == 'object')
- str += a.toStr(level);
- else
- str += this.wrap(a, level) + "\n";
- }
-
- /*
- * This tag required closing, so add corresponding closing tag
- * and decrease indent level.
- */
- level--;
- if (this.close)
- str += this.indent(level) + '</' + this.tag + ">\n";
-
- return str;
- }
-}
-
-
-
-
-/*
- * Now test our functionality
- */
-
-out = new FD();
-out.set(FD.STDOUT_FILENO);
-
-for (i = 0; i < 1000; i++) {
-
-var table = new MLTag('table', true);
-table.addAttr('border', 0);
-table.addAttr('width', '100%');
-
-var tr = new MLTag('tr', true);
-
-var td = new MLTag('td', true);
-td.addAttr('bgcolor', '#000000');
-
-var p = new MLTag('p', true);
-p.addContent('Hello');
-
-td.addContent(p);
-tr.addContent(td);
-table.addContent(tr);
-
-out.put(table.toStr(8));
-
-//out.put(uneval(table));
-
-}
+++ /dev/null
-/* $Id: ml6.js,v 1.1 2005/11/16 00:35:01 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-/*
- * Useful object to use for HTML tags. <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)
- {
-
- /* Open opening tag */
- var str = this.indent_str.substr(0, level) + '<' + this.tag;
-
- /*
- * Store these immediately for performance since we use those
- * values several times later on, also saves typing
- */
- var taglen = this.tag.length;
- var len = level + taglen + 1;
- var attributes = this.attributes;
-
- /* Add each of our attributes with their optional values */
- for (var attr in attributes) {
- var value;
-
- if (len > 70) {
- len = level + taglen + 1;
- str += "\n" + this.indent_str.substr(0, len);
- }
- str += ' ' + attr;
- len += taglen + 1;
- if ((value = attributes[attr]) != null) {
- str += '="' + value + '"';
- len += value.length + 3;
- }
- }
- /* Close opening tag */
- str += ">\n";
-
- /* Increase our indent level for content */
- level++;
-
- /*
- * Now add all our contents, if any, which may consist of
- * other MLTag objects or arbitrary String objects. Use
- * recursion to process MLTag ones, which may also have their
- * respective bodies and closing tags.
- */
- for (var i = 0; i < this.contents.length; i++) {
- var a = this.contents[i];
-
- if (typeof a == 'object')
- str += a.toStr(level);
- else
- str += this.wrap(a, level) + "\n";
- }
-
- /*
- * This tag required closing, so add corresponding closing tag
- * and decrease indent level.
- */
- level--;
- if (this.close)
- str += this.indent_str.substr(0, level) + '</' +
- this.tag + ">\n";
-
- return str;
- }
-}
-
-
-
-
-/*
- * Now test our functionality
- */
-
-out = new FD();
-out.set(FD.STDOUT_FILENO);
-
-for (i = 0; i < 1000; i++) {
-
-var table = new MLTag('table', true);
-table.addAttr('border', 0);
-table.addAttr('width', '100%');
-
-var tr = new MLTag('tr', true);
-
-var td = new MLTag('td', true);
-td.addAttr('bgcolor', '#000000');
-
-var p = new MLTag('p', true);
-p.addContent('Hello');
-
-td.addContent(p);
-tr.addContent(td);
-table.addContent(tr);
-
-out.put(table.toStr(8));
-
-//out.put(uneval(table));
-
-}
+++ /dev/null
-/* $Id: parse.js,v 1.1 2005/11/16 00:36:58 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-/*
- * Useful object to use for HTML tags. <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 = 0; i < this.contents.length; i++) {
- var a = this.contents[i];
-
- if (typeof a == 'object')
- str += a.toStr(0);
- else
- str += a;
- }
-
- /*
- * If this tag required closing, do so
- */
- if (this.tag != null && this.close)
- str += '</' + 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;
-}
-
-
-
-/*
- * This is a parsing test.
- *
- * For now, we want to simply accept patterns in the form %x{...}, where
- * there may be arbitrary data within the { }, and function %x must be
- * performed on it. For instance, %b{some text} would denote this text
- * as bold. However, we should also allow %b{some text %i{more text}} and
- * such. Because %, { and } characters are special, we should support
- * escaping with \. Because \ is special for escaping, we should support
- * \\ meaning a single \. Unclosed } should either generate an error,
- * or we might want to automatically consider them all closed at the end
- * of the user provided string.
- *
- * XXX One level currently seems to work, but there are recursive problems!
- * Also, we are loosing a space.
- */
-
-function tokenize(str, ctag, i)
-{
- var t, c;
- var escaped = false;
- var tags = new MLTag(null, false);
- var ss = '';
-
- if (ctag != undefined) {
- if (str.length < i || str.charAt(i) != '{')
- return tags;
- i++;
- } else
- i = 0;
-
- for (t = str.length; i < t; i++) {
- c = str.charAt(i);
-
- /* Handle '\' escaping, valid for the whole string */
- if (escaped &&
- (c == '\\' || c == '%' || c == '{' || c == '}')) {
- ss += c;
- escaped = false;
- continue;
- }
- if (c == '\\') {
- escaped = true;
- continue;
- }
-
- /*
- * Special character which delimits commands strings. Since
- * we are recursively called we must detect this condition and
- * exit just like for the end of string.
- */
- if (c == '{' || c == '}' && ctag == undefined) {
- ss += c;
- continue;
- }
- if (c == '}') {
-// i++; /* XXX */
- break;
- }
-
- /*
- * Command mode. Make sure that we do have a command name,
- * followed with required '{'. If an invalid command, simply
- * allow enclosed string to be litteral. For valid commands,
- * create the required tag and recurse into ourselves on the
- * enclosed string in '{' and '}'.
- */
- /* Command mode */
- if (c == '%') {
- var ntag, o;
-
- if (/*ctag != undefined ||*/ i > t - 1 ||
- str.charAt(i + 2) != '{') {
- ss += c;
- continue;
- }
-
- /* Flush current string if any */
- if (ss.length > 0) {
- tags.addContent(ss);
- ss = '';
- }
-
- switch (str.charAt(i + 1)) {
- case 'b':
- ntag = new MLTag('b', true);
- break;
- case 'i':
- ntag = new MLTag('i', true);
- break;
- case 'e':
- ntag = new MLTag('em', true);
- break;
- case 'S':
- ntag = new MLTag('sup', true);
- break;
- case 's':
- ntag = new MLTag('sub', true);
- break;
- case 't':
- ntag = new MLTag('tt', true);
- break;
- case 'h':
- ntag = new MLTag('h', true);
- break;
- case 'p':
- ntag = new MLTag('p', true);
- break;
- case 'n':
- ntag = new MLTag('br', false);
- break;
- case 'A':
- ntag = new MLTag('a', true);
- break;
- case 'I':
- ntag = new MLTag('img', true);
- break;
- default:
- ntag = new MLTag(null, false);
- }
-
- o = tokenize(str, ntag, i + 2);
- i = o.i;
- /* XXX */
- ntag.addContent(o.tags.toStr(0));
- tags.addContent(ntag);
- continue;
- }
-
- ss += c;
- }
-
- /*
- * Now that we gathered command-enclosed string, take care of special
- * cases for <a> and <img>, and add resulting content.
- */
- if (ctag != undefined) {
- switch (ctag.tag) {
- case 'a':
- ctag.addAttr('href', ss);
- break;
- case 'img':
- ctag.addAttr('src', ss);
- break;
- }
- if (ss.length > 0) {
- ctag.addContent(ss);
- ss = '';
- }
- tags.addContent(ctag);
- }
- if (ss.length > 0)
- tags.addContent(ss);
-
- /*
- * Return object with current index in string, as well as new tag
- * container
- */
- var obj = {};
- obj.i = i;
- obj.tags = tags;
-
- return obj;
-}
-
-
-//print((tokenize('Some %b{enclosed} string', undefined, 0)).tags.toStr(0));
-print((tokenize('Some %b{enclo%i{s}ed} string', undefined, 0)).tags.toStr(0));
-//print(uneval((tokenize('Some %b{enclo%i{s}ed} string', undefined, 0)).tags));
+++ /dev/null
-/* $Id: poll_test.js,v 1.3 2005/02/13 09:02:06 mmondor Exp $ */
-
-function events(e)
-{
- s = new String();
-
- if (e & FD.POLLIN)
- s += 'POLLIN ';
- if (e & FD.POLLOUT)
- s += 'POLLOUT ';
- if (e & FD.POLLERR)
- s += 'POLLERR ';
- if (e & FD.POLLHUP)
- s += 'POLLHUP ';
- if (e & FD.POLLNVAL)
- s += 'POLLNVAL ';
-
- return s;
-}
-
-output = new FD();
-output.set(FD.STDOUT_FILENO);
-
-try {
-
- input = new FD();
- input.set(FD.STDIN_FILENO);
-
- set = new Array();
-
- /*
- * This is interesting, because we allow normal arrays, either dense
- * using set.push(), or sparse using set[n] = fd, but we also allow
- * associative array elements such as set['string'] = fd. The
- * returned set only holds FD objects for which events occurred. One
- * can run through that resulting set using for (v in a) ... format,
- * but it is also possible to verify the associative string name to
- * see if it is in the set. As usual with poll(2), POLLHUP, POLLERR
- * and POLLNVAL are always possible even for descriptor objects with
- * their events property set to 0. The default events property for
- * an FD object is also 0. The user can then verify the revents
- * property of each FD object in the result set, and take appropriate
- * I/O action or processing.
- *
- * Moreover, JavaScript being a very dynamic language, one can add
- * properties or methods to individual FD objects as wanted, and of
- * course can use those to say, process events of the descriptors
- * returned in the set with pending events. This means that a common
- * method name with different functionality added to each FD object
- * permits a main events loop to transparently execute the wanted code
- * using, for instance, fd.process(). In a case where two types of
- * common events, input and output need to be processed differently
- * with such a method, one can even define new methods as a prototype
- * for base custom FD objects, which can be automatically inherited
- * in shared mode by all children objects derived from the prototype
- * object.
- */
-
- /*
- * Set interesting events mask for our FD objects to monitor
- */
- input.events = FD.POLLIN;
- output.events = 0;
-
- /*
- * Add our FD objects to an array
- */
- set.push(input);
- set.push(output);
-
- /*
- * Invoke poll(2) on descriptors in our FD objects array, sleep for a
- * maximum of 5000 milliseconds (5 seconds). Retreive result set of
- * FD objects with pending events into rset.
- */
- rset = FD.poll(set, 5000);
-
- /*
- * Display rset contents
- */
- output.put('Set size: ' + rset.length + "\n");
- for (var i in rset) {
- output.put(i + ': type=' + rset[i].constructor.name + ' fd=' +
- rset[i].fd + ' revents=' + events(rset[i].revents) + "\n");
- }
-
- input.close();
-
-} catch (x) {
- output.put(x + "\n");
-}
-
-output.close();
+++ /dev/null
-/* $Id: sock_httpd.js,v 1.8 2005/06/28 00:19:03 mmondor Exp $ */
-
-/*
- * Little example showing the versatility of ECMAScript, provided with a
- * custom library for BSD socket support, using SpiderMonkey.
- * This tiny HTTPd allows simultaneous concurrent client connections without
- * using multiple threads or processes. Must be ran through ../src/test,
- * compiled with ../src/classes/js_fd.[ch] support.
- *
- * We support a maximum of 4 concurrent connections per IP address, as well
- * as a total maximum of 32 concurrent connections by default.
- * Change MAX_CONNECTIONS and MAX_CONNECTIONS_ADDR as needed.
- *
- * We also support per-connection request timeouts, defaulting to 60 seconds.
- * Change REQUEST_TIMEOUT as wanted.
- * Note that this timeout consists of a total timeout for the connection,
- * rather than an actual input timeout (which would require to reset the
- * expiration time everytime user input occurs). Using the current timeout
- * method suits most embedded applications, but would not be adequate to
- * support large file transfers or the like (connection would timeout
- * during a long transfer). However, if we supported this kind of long
- * file transfers, because we are single-threaded, we would need two states
- * in our machine, one where small requests take place, and another
- * specialized for ongoing file transfers. Although this could be
- * implemented, it was not a required functionality for this demonstration.
- */
-
-
-
-/*
- * Configuration
- */
-const MAX_CONNECTIONS = 32;
-const MAX_CONNECTIONS_ADDR = 4;
-const REQUEST_TIMEOUT = 60;
-
-
-
-/*
- * Open standard error FD for diagnostics output
- */
-err = new FD();
-err.set(FD.STDERR_FILENO);
-
-
-/*
- * Initialize server
- */
-try {
- sock = new FD();
- sock.socket(FD.AF_INET, FD.SOCK_STREAM, 0);
- sock.bind('127.0.0.1', 1337);
- sock.listen(MAX_CONNECTIONS);
-} catch (x) {
- err.put(x + "\n");
- exit();
-}
-
-
-/*
- * Create polling array set and insert bound socket in it
- */
-var set = [];
-sock.events = FD.POLLIN;
-set['bind'] = sock;
-
-
-
-/*
- * Used for unique index associated to each FD for efficient removal from
- * polling set when closing connection
- */
-var count = 0;
-
-
-/*
- * To know how many connections we are currently serving and limit them
- */
-var connections = 0;
-var addresses = [];
-
-function counters_inc(fd)
-{
- if (connections >= MAX_CONNECTIONS)
- return false;
-
- var addr = fd.client_addr;
- if ((addrcnt = addresses[addr]) != undefined &&
- addrcnt >= MAX_CONNECTIONS_ADDR)
- return false;
-
- if (addrcnt == undefined)
- addrcnt = 1;
- else
- addrcnt++;
- addresses[addr] = addrcnt;
-
- return true;
-}
-
-function counters_dec(fd)
-{
- var addr = fd.client_addr;
- if ((--addresses[addr]) == 0)
- delete addresses[addr];
-
- connections--;
-}
-
-
-/*
- * Handles client request and replies back
- */
-function handle_request(fd)
-{
- try {
- fd.put(
- "HTTP/1.1 200\r\n" +
- "Content-type: text/html\r\n" +
- "Server: js/1\r\n" +
- "Connection: close\r\n" +
- "\r\n" +
- "<HTML><HEAD><TITLE>Detected!</TITLE></HEAD>\n" +
- "<BODY><PRE>\n" +
- "You are: " + fd.client_addr + ":" + fd.client_port +
- "\n" + "ID: " + fd.index + "\n\n" +
- "You sent:\n" + fd.request + "\n" +
- "</PRE>\n" + "<P>Tracing in progress...</P>\n" +
- "</BODY></HTML>\r\n");
- } catch (x) {}
-}
-
-
-/*
- * Main server loop
- */
-for (;;) {
- try {
-
- /*
- * Determine next to expire FD event, so that we can sleep
- * as long as possible. Also get rid of expired requests.
- */
- var cur = Date.parse(new Date) / 1000;
- var exp = cur + 3600;
- for (i in set) {
- var fd;
-
- if ((fd = set[i]) == undefined || i == 'bind')
- continue;
- if (fd.expires <= cur) {
- /*
- * Request timeout expired for this
- * descriptor, we must close it.
- */
- fd.close();
- counters_dec(fd);
- delete set[fd.index];
- delete fd;
- continue;
- }
- if (fd.expires < exp)
- exp = fd.expires;
- }
- exp -= cur;
-
- /*
- * Poll our set of descriptors for events
- */
- var e = FD.poll(set, exp * 1000);
- /*
- * Verify if a timeout occurred. Because our poll
- * implementation returns an array, its timeout event can be
- * checked against by verifying if the set is empty. However,
- * associative-array/object-attributes are not accounted
- * properly with 'length' in JS, so also test fo the case of
- * the 'bind' entry.
- */
- if (e.length == 0 && e['bind'] == undefined) {
- /* Timeout */
- continue;
- }
-
- /*
- * Process occurred events. First handle new connections,
- * if any.
- */
- if (e['bind'] != undefined) {
- /*
- * New connection, accept it
- */
- var fd = sock.accept();
-
- if (!counters_inc(fd)) {
- fd.put("HTTP/1.1 403.9\r\n" +
- "Connection: close\r\n\r\n");
- fd.close();
- delete fd;
- } else {
- /*
- * Associate a new string with FD object
- * which will serve to hold the client request
- */
- fd.request = '';
- /*
- * We add this custom property to efficiently
- * be able to remove FD from polling set later
- * on
- */
- count++;
- fd.index = count;
- /*
- * Set its interesting polling event to
- * POLLIN
- */
- fd.events = FD.POLLIN;
- /*
- * Also set request expiration time
- */
- fd.expires = (Date.parse(new Date) / 1000) +
- REQUEST_TIMEOUT;
- /*
- * Add descriptor to polling set
- */
- set[count] = fd;
- }
- }
-
- /*
- * Run through set of descriptors with pending events
- */
- for (i in e) {
- if (e[i] == undefined || i == 'bind')
- continue;
- if ((e[i].revents & (FD.POLLHUP | FD.POLLERR)) != 0) {
- /*
- * Unexpected connection loss, close and
- * remove from polling set.
- */
- e[i].close();
- counters_dec(e[i]);
- delete set[e[i].index];
- delete e[i];
- } else if ((e[i].revents & FD.POLLIN) != 0) {
- var l, close = false;
-
- /*
- * New data to read from this FD, add to
- * request string, verify if we should answer
- * and close connection.
- * We also limit the maximum request length.
- */
- if ((l = e[i].get()) != null) {
- if (e[i].length > 32768)
- close = true;
- else
- e[i].request += l;
- if (e[i].request.search("\r\n\r\n")
- != -1)
- close = true;
- } else
- close = true;
-
- if (close) {
- /*
- * Answer and close connection
- */
- handle_request(e[i]);
- e[i].close();
- counters_dec(e[i]);
- /*
- * Nice feature, we can hint the
- * GC about the fact that we no
- * longer refer to, or want these,
- * which saves RAM since it avoids
- * caching many old useless objects.
- */
- delete set[e[i].index];
- delete e[i];
- }
- }
- }
- } catch (x) {
- err.put(x + "\n");
- }
-}
-
-
-/* NOTREACHED */
-
-sock.close();
-err.close();
+++ /dev/null
-/* $Id: sock_listen.js,v 1.4 2005/04/20 08:50:06 mmondor Exp $ */
-
-err = new FD();
-err.set(FD.STDERR_FILENO);
-
-try {
- sock = new FD();
- sock.socket(FD.AF_INET, FD.SOCK_STREAM, 0);
- sock.bind('127.0.0.1', 1337);
- sock.listen(10);
-} catch (x) {
- err.put(x + "\n");
-}
-
-var req = '';
-for (;;) {
- try {
- fd = sock.accept();
- try {
- req = '';
- while (req.length < 32768) {
- if ((l = fd.get()) == null)
- break;
- req += l;
- if (l.search("\r\n\r\n"))
- break;
- }
- /*
- * Note: because I was asked a few times about this,
- * the following is a joke :) we actually don't care
- * about the client and won't traceroute, we only
- * want client connections to test the program.
- */
- fd.put("HTTP/1.1 200\r\nServer: js/1\r\n" +
- "Connection: close\r\n" + "\r\n");
- fd.put("<HTML><HEAD><TITLE>Detected!</TITLE></HEAD>" +
- "<BODY><PRE>\n");
- fd.put("You are: " + fd.client_addr + ":" +
- fd.client_port + "\n\nYou sent:\n" + req + "\n");
- fd.put("</PRE>\n" + "<P>Tracing in progress...</P>\n" +
- "</BODY></HTML>\r\n");
- } catch (y) {
- err.put(y + "\n");
- }
- fd.close();
- } catch (x) {
- err.put(x + "\n");
- }
-}
-
-sock.close();
-err.close();
+++ /dev/null
-/* $Id: test.js,v 1.1 2004/12/27 11:16:15 mmondor Exp $ */
-
-
-/*
- * The following test should not succeed on API class
- */
-
-/* {{ */
-
-/*
-function test(s)
-{
- API.print(s);
-}
-
-API.test = test;
-API.test("Successfully added method to API class!\n");
-API.test2 = "Successfully added property to API class!\n";
-API.test(API.test2 + "\n");
-*/
-
-/* }} */
-
-
-
-/*
- * These tests should succeed, however.
- */
-
-API.print("API.property1 = " + API.property1 + "\n");
-API.print("API.property2 = " + API.property2 + "\n");
-API.print("API.property3 = " + API.property3 + "\n\n");
-
-/*
- * XXX
- * These fail, even though these are permanent and read/write properties.
- * JS_SealObject() seems to be converting all read/write properties to
- * report an error if setProperty() method is called. However, interestingly
- * enough, read/only properties report no error. However, they obviously
- * are not modified despite attempts to assign them new values.
- */
-API.property1 = 'Hello';
-API.property2 = 911;
-
-/*
- * Following statement should fail, but is simply internally ignored at least
- * (the setProperty() method is not called).
- */
-API.property3 = 'READONLY!';
-
-API.print("API.property1 = " + API.property1 + "\n");
-API.print("API.property2 = " + API.property2 + "\n");
-API.print("API.property3 = " + API.property3 + "\n\n");
-
-
-
-/*
- * Perform a test loop, during which callMe() should be called by the
- * application code, via the break callback handler function.
- */
-for (i = 0; i < 3; i++) {
- API.print("We are the: " + Date() + "\n");
- API.print("NetBSD Kernel size is " +
- (API.fileSize("/netbsd") / 1024 / 1024) + "MB\n");
- API.print("END\n");
-}
-API.print("\n");
-
-
-
-/*
- * Will be called by our application if we create it
- */
-function callMe(n)
-{
- API.print("CallMe(" + n + ")!\n");
-}
-
-
-
-/*
- * Interesting feature where we can return a value explicitely, optionally
- */
-10
+++ /dev/null
-/* $Id: test2.js,v 1.4 2005/02/05 08:07:35 mmondor Exp $ */
-
-var file;
-
-try {
- file = new API.File("/etc/hosts");
- API.print("File '" + file.path + "' is loaded (" + file.size +
- ") bytes.\n");
- file.release();
-} catch (x) {
- API.print("Exception: " + x + "\n");
- exit();
-}
-
-API.print("Finishing...\n");
+++ /dev/null
-function Employee() {
- this.name = "";
- this.dept = "general";
-}
-
-function Manager() {
- this.reports = [];
-}
-Manager.prototype = new Employee;
-
-function WorkerBee() {
- this.projects = [];
-}
-WorkerBee.prototype = new Employee;
-
-function SalesPerson() {
- this.dept = "sales";
- this.quota = 100;
-}
-SalesPerson.prototype = new WorkerBee;
-
-function Engineer() {
- this.dept = "engineering";
- this.machine = "";
-}
-Engineer.prototype = new WorkerBee;
-
-t = Engineer;
-var eng
-eng = new t();
-API.print(typeof eng + ', ' + eng.constructor.name);
-API.print("\n");
-
-/* Interesting test. Dynamically create properties i0 - i9 */
-list = new Object;
-for (i = 0; i < 10; i++) {
- c = 'list.i' + i + ' = ' + i * i;
- eval(c);
-}
-/* And print the values of our i0 - i9 previously created properties. */
-for (i = 0; i < 10; i++) {
- c = 'API.print(\'' + 'list.i' + i + ' = \' + list.i' + i + ' + "\\n")';
- eval(c);
-}
-
-API.print("\nEND\n");
+++ /dev/null
-# $Id: GNUmakefile,v 1.9 2006/07/17 08:55:18 mmondor Exp $
-
-#CFLAGS += -g
-CFLAGS += -Wall
-
-JS_CFLAGS := $(shell spidermonkey-config -dc)
-JS_LDFLAGS := $(shell spidermonkey-config -dl)
-
-PG_CFLAGS := $(shell pg_config --cppflags)
-PG_LDFLAGS := $(shell pg_config --ldflags)
-PG_LDFLAGS += -lpq
-
-OBJS := $(addprefix classes/,js_fd.o js_errno.o js_signal.o js_pgsql.o)
-OBJS += js-server.o
-
-CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) -Iclasses -Wall
-LDFLAGS += $(JS_LDFLAGS) $(PG_LDFLAGS)
-
-
-all: js-server
-
-%.o: %.c
- cc -c ${CFLAGS} -o $@ $<
-
-js-server: $(OBJS)
- cc -o $@ -lc ${LDFLAGS} $(OBJS)
-
-clean:
- rm -f js-server $(OBJS)
+++ /dev/null
-/* $Id: js_errno.c,v 1.3 2005/12/12 09:55:15 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Basic UNIX errno services for ECMAScript
- */
-
-
-
-#include <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_sm_strerror(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-
-
-
-/* Actual class parameters */
-static JSClass errno_class = {
- "Errno", 0, JS_PropertyStub, JS_PropertyStub,
- JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,
- JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
-};
-
-/* Provided static methods */
-static JSFunctionSpec errno_smethods[] = {
- { "strerror", errno_sm_strerror, 1, 0, 0 },
- { NULL, NULL, 0, 0, 0 }
-};
-
-/*
- * Provided static properties.
- * We use these to provide ECMAScript with the ability to use system-specific
- * standard C constant macros without us having to tidiously map them
- * individually, or to require other scripts to be used as headers to define
- * them. Another possibility would have been to supply these parameters as
- * string, but this would have required even slower remapping because of the
- * parsing and string comparisions.
- * We only include those which we consider necessary for now, others may be
- * added easily as needed, provided that they are added in all three maps.
- * I might perhaps develop macros and/or functions to map all these easily
- * from a single map.
- */
-
-struct property_spec {
- const char *name;
- int value;
-};
-
-#define SP(n) \
- { #n, n }
-
-static struct property_spec errno_sprops[] = {
- SP(EPERM),
- SP(ENOENT),
- SP(EINTR),
- SP(EIO),
- SP(ENXIO),
- SP(EBADF),
- SP(EACCES),
- SP(ENOTBLK),
- SP(EBUSY),
- SP(EEXIST),
- SP(EXDEV),
- SP(ENODEV),
- SP(ENOTDIR),
- SP(EISDIR),
- SP(EINVAL),
- SP(ENFILE),
- SP(EMFILE),
- SP(ENOTTY),
- SP(ETXTBSY),
- SP(EFBIG),
- SP(ENOSPC),
- SP(ESPIPE),
- SP(EROFS),
- SP(EMLINK),
- SP(EPIPE),
- SP(EAGAIN),
- SP(EINPROGRESS),
- SP(EALREADY),
- SP(ENOTSOCK),
- SP(EDESTADDRREQ),
- SP(EMSGSIZE),
- SP(EPROTOTYPE),
- SP(EPROTONOSUPPORT),
- SP(EOPNOTSUPP),
- SP(EPFNOSUPPORT),
- SP(EAFNOSUPPORT),
- SP(EADDRINUSE),
- SP(EADDRNOTAVAIL),
- SP(ENETDOWN),
- SP(ENETUNREACH),
- SP(ENETRESET),
- SP(ECONNABORTED),
- SP(ECONNRESET),
- SP(ENOBUFS),
- SP(EISCONN),
- SP(ENOTCONN),
- SP(ESHUTDOWN),
- SP(ETIMEDOUT),
- SP(ECONNREFUSED),
- SP(ELOOP),
- SP(ENAMETOOLONG),
- SP(EHOSTDOWN),
- SP(EHOSTUNREACH),
- SP(ENOTEMPTY),
- SP(EDQUOT),
- SP(ESTALE),
- SP(ENOLCK),
- SP(ENOSYS),
- SP(EFTYPE),
- SP(ENOMSG),
- SP(ENOTSUP),
- SP(ECANCELED),
- SP(EBADMSG),
- SP(ENODATA),
- SP(ETIME),
-
- { NULL, 0 }
-};
-
-#undef SP
-
-
-
-/*
- * Class control functions
- */
-
-JSObject *
-js_InitErrnoClass(JSContext *cx, JSObject *obj)
-{
- JSObject *proto, *ctor;
- struct property_spec *sp;
-
- if ((proto = JS_InitClass(cx, obj, NULL, &errno_class, NULL,
- 0, NULL, NULL, NULL, errno_smethods)) == NULL) {
- (void) fprintf(stderr, "Error initializing Errno class\n");
- return NULL;
- }
-
- /* Create static properties */
- if ((ctor = JS_GetConstructor(cx, proto)) == NULL) {
- (void) fprintf(stderr, "Errno: JS_GetConstructor == NULL\n");
- return NULL;
- }
- for (sp = errno_sprops; sp->name != NULL; sp++) {
- if (JS_DefineProperty(cx, ctor, sp->name,
- INT_TO_JSVAL(sp->value), NULL, NULL,
- JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) {
- (void) fprintf(stderr,
- "Errno: Error defining property %s\n", sp->name);
- return NULL;
- }
- }
-
- return proto;
-}
-
-
-
-/*
- * Static properties functions
- */
-
-
-
-/*
- * Static methods
- */
-
-static JSBool
-errno_sm_strerror(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- int error;
- JSString *string;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- return JS_FALSE;
- }
- if (!JSVAL_IS_INT(*argv)) {
- QUEUE_EXCEPTION("Argument not an integer");
- return JS_FALSE;
- }
- error = (int)JSVAL_TO_INT(*argv);
-
- if ((string = JS_NewStringCopyZ(cx, strerror(error))) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- return JS_FALSE;
- }
-
- *rval = STRING_TO_JSVAL(string);
-
- return JS_TRUE;
-}
+++ /dev/null
-/* $Id: js_errno.h,v 1.2 2005/07/19 19:25:44 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-#ifndef JSERRNO_H
-#define JSERRNO_H
-
-extern JSObject *js_InitErrnoClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $Id: js_fd.c,v 1.42 2006/07/11 10:25:51 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Basic UNIX filedescriptor and BSD sockets services for ECMAScript
- */
-
-/*
- * XXX TODO XXX
- * - Possibly create a byte buffer type
- * - Add path based restrictions to open()
- * - (maybe) add restrictions to bind()
- * - Either add stat() or provide equivalent properties
- * - Enhance exception reports. Function name should be provided, and
- * errno should be displayed when wanted. Perhaps that jsfd->error could
- * also be set automatically when an exception is generated which involves
- * errno.
- * - Check JS_ReportError().
- * - Moving finalizer freeing and close() freeing code to a common function
- * might be a good idea.
- * - Add getnameinfo()/getaddrinfo() ? Or should we transparently allow this
- * through properties?
- * - Add send()/sendto()/sendmsg(), recv()/recvfrom()/recvmsg() ...
- * - It is possible that we need to verify that obj is instance of FD in all
- * methods, perhaps. I.E. consider an FD method assigned on another object.
- * - Add hostname to address and address to hostname resolution facilities
- * - Maybe also add properties for local end address/port of socket
- * - Would be nice to experiment with a fork(2) heh. If so, if we allow
- * fcntl(2) close-on-exec flag, we should make sure to mark FD objects as
- * closed too for an execve(2) wrapper.
- * - popen(2), would probably need to use custom execve(2) wrapper above...
- * - Add support to easily restrict an application's right to functions.
- * Path and mode sanity checking functions should also be written and their
- * parameters set on a per-application basis.
- * - Maybe virtual chdir(2)
- * - A stdio FILE extension object might be nice, with stuff like fdopen() to
- * create one... This would be most useful for buffered lined based input
- * and output. Exporting mmfd library to js might also be nice perhaps,
- * either as alternative or addition.
- * - mmap(2) - how could I do this without some byte class and associated
- * methods? Seems way tricky.
- * - lstat(2), fstat(2)
- * - dup2()
- * - rename(2), unlink(2) etc would be useful, but we need another class
- * for this (maybe a VFS static class?) Maybe even something calling
- * execve(2) and fork(2), those primitives... popen(3) also.
- * - Also opendir(3) and friends wrapper...
- */
-
-
-
-#include <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 <unistd.h>
-#include <poll.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.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,
- JSAT_DOUBLE,
- JSAT_STRING,
- JSAT_OBJECT
-};
-
-
-/* Prototypes */
-static JSBool fd_constructor(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static void fd_finalize(JSContext *, JSObject *);
-
-static JSBool fd_getProperty(JSContext *, JSObject *, jsval, jsval *);
-static JSBool fd_setProperty(JSContext *, JSObject *, jsval, jsval *);
-
-static JSBool fd_m_open(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_set(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_close(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_truncate(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool fd_m_put(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_get(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_socket(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_connect(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_bind(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_listen(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_accept(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_shutdown(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool fd_m_setsockopt(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool fd_m_getsockopt(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool fd_m_fcntl(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_write(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_read(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_fdatasync(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool fd_m_lseek(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_fchmod(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_flock(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_m_fstat(JSContext *, JSObject *, uintN, jsval *, jsval *);
-
-static JSBool fd_sm_poll(JSContext *, JSObject *, uintN, jsval *, jsval *);
-static JSBool fd_sm_poll_mkset(JSContext *, jsval *, jsval *, void *);
-
-static JSBool object_iterate(JSContext *, JSObject *, void *,
- JSBool (*)(JSContext *, jsval *, jsval *, void *));
-static int fd_path_allow(const char *);
-static mode_t fd_mode_allow(mode_t);
-static int fd_flags_allow(int);
-static jsfd_t * fd_methods_args_check(JSContext *, JSObject *, const char *,
- int, int, jsval *, int);
-
-
-
-/* Actual class parameters */
-static JSClass fd_class = {
- "FD", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub,
- fd_getProperty, fd_setProperty, JS_EnumerateStub, JS_ResolveStub,
- JS_ConvertStub, fd_finalize
-};
-
-enum fd_methods_args_enum {
- FDMA_SET,
- FDMA_CLOSE,
- FDMA_TRUNCATE,
- FDMA_GET,
- FDMA_SOCKET,
- FDMA_CONNECT,
- FDMA_BIND,
- FDMA_LISTEN,
- FDMA_ACCEPT,
- FDMA_SHUTDOWN,
- FDMA_SETSOCKOPT,
- FDMA_GETSOCKOPT,
- FDMA_FCNTL,
- FDMA_READ,
- FDMA_WRITE,
- FDMA_FDATASYNC,
- FDMA_LSEEK,
- FDMA_FCHMOD,
- FDMA_FLOCK,
- FDMA_FSTAT,
- FDMA_MAX
-};
-
-static int fd_methods_args_array[FDMA_MAX][6] = {
- { 1, JSAT_INTEGER }, /* SET */
- { 0 }, /* CLOSE */
- { 1, JSAT_DOUBLE }, /* TRUNCATE */
- { 0 }, /* GET */
- { 3, JSAT_INTEGER, JSAT_INTEGER, JSAT_INTEGER },/* SOCKET */
- { 2, JSAT_STRING, JSAT_INTEGER }, /* CONNECT */
- { 2, JSAT_STRING, JSAT_INTEGER }, /* BIND */
- { 1, JSAT_INTEGER }, /* LISTEN */
- { 0 }, /* ACCEPT */
- { 1, JSAT_INTEGER }, /* SHUTDOWN */
- { 2, JSAT_INTEGER, JSAT_INTEGER }, /* SETSOCKOPT */
- { 1, JSAT_INTEGER }, /* GETSOCKOPT */
- { 2, JSAT_INTEGER, JSAT_INTEGER }, /* FCNTL */
- { 1, JSAT_INTEGER }, /* READ */
- { 1, JSAT_STRING }, /* WRITE */
- { 0 }, /* FDATASYNC */
- { 2, JSAT_DOUBLE, JSAT_INTEGER }, /* LSEEK */
- { 1, JSAT_INTEGER }, /* FCHMOD */
- { 1, JSAT_INTEGER }, /* FLOCK */
- { 0 } /* FSTAT */
-};
-
-/* Provided methods/functions */
-static JSFunctionSpec fd_methods[] = {
- { "open", fd_m_open, 0, 0, 0 }, /* Variable 2-3 parameters */
- { "set", fd_m_set, 1, 0, 0 },
- { "close", fd_m_close, 0, 0, 0 },
- { "truncate", fd_m_truncate, 1, 0, 0 },
- { "put", fd_m_put, 1, 0, 0 },
- { "get", fd_m_get, 0, 0, 0 },
- { "socket", fd_m_socket, 3, 0, 0 },
- { "connect", fd_m_connect, 2, 0, 0 },
- { "bind", fd_m_bind, 2, 0, 0 },
- { "listen", fd_m_listen, 1, 0, 0 },
- { "accept", fd_m_accept, 0, 0, 0 },
- { "shutdown", fd_m_shutdown, 1, 0, 0 },
- { "setsockopt", fd_m_setsockopt, 2, 0, 0 },
- { "getsockopt", fd_m_getsockopt, 1, 0, 0 },
- { "fcntl", fd_m_fcntl, 2, 0, 0 },
- { "read", fd_m_read, 1, 0, 0 },
- { "write", fd_m_write, 1, 0, 0 },
- { "fdatasync", fd_m_fdatasync, 0, 0, 0 },
- { "lseek", fd_m_lseek, 2, 0, 0 },
- { "fchmod", fd_m_fchmod, 1, 0, 0 },
- { "flock", fd_m_flock, 1, 0, 0 },
- { "fstat", fd_m_fstat, 0, 0, 0 },
- { NULL, NULL, 0, 0, 0 }
-};
-
-/* Provided static methods */
-static JSFunctionSpec fd_smethods[] = {
- { "poll", fd_sm_poll, 2, 0, 0 },
- { NULL, NULL, 0, 0, 0 }
-};
-
-/* Provided properties */
-enum fd_props {
- FD_P_PATH = 0,
- FD_P_FD,
- FD_P_MODE,
- FD_P_EVENTS,
- FD_P_REVENTS,
- FD_P_ERRNO,
- FD_P_CLIENT_ADDR,
- FD_P_CLIENT_PORT,
- FD_P_MAX
-};
-
-static JSPropertySpec fd_properties[FD_P_MAX + 1] = {
- { "path", FD_P_PATH, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL },
- { "fd", FD_P_FD, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL },
- { "mode", FD_P_MODE, JSPROP_ENUMERATE | JSPROP_READONLY, NULL, NULL },
- { "events", FD_P_EVENTS, JSPROP_ENUMERATE, NULL, NULL },
- { "revents", FD_P_REVENTS, JSPROP_ENUMERATE | JSPROP_READONLY, NULL,
- NULL },
- { "errno", FD_P_ERRNO, JSPROP_ENUMERATE | JSPROP_READONLY, NULL,
- NULL },
- { "client_addr", FD_P_CLIENT_ADDR, JSPROP_ENUMERATE | JSPROP_READONLY,
- NULL, NULL },
- { "client_port", FD_P_CLIENT_PORT, JSPROP_ENUMERATE | JSPROP_READONLY,
- NULL, NULL },
- { NULL, 0, 0, NULL, NULL }
-};
-
-/*
- * Provided static properties.
- * We use these to provide ECMAScript with the ability to use system-specific
- * standard C constant macros without us having to tidiously map them
- * individually, or to require other scripts to be used as headers to define
- * them. Another possibility would have been to supply these parameters as
- * string, but this would have required even slower remapping because of the
- * parsing and string comparisions.
- * We only include those which we consider necessary for now, others may be
- * added easily as needed, provided that they are added in all three maps.
- * I might perhaps develop macros and/or functions to map all these easily
- * from a single map.
- */
-
-struct property_spec {
- const char *name;
- int value;
-};
-
-#define SP(n) \
- { #n, n }
-
-static struct property_spec fd_sprops[] = {
- SP(STDIN_FILENO),
- SP(STDOUT_FILENO),
- SP(STDERR_FILENO),
-
- SP(O_RDONLY),
- SP(O_WRONLY),
- SP(O_RDWR),
- SP(O_APPEND),
- SP(O_CREAT),
- SP(O_TRUNC),
- SP(O_NONBLOCK),
-
- SP(POLLIN),
- SP(POLLRDNORM),
- SP(POLLRDBAND),
- SP(POLLPRI),
- SP(POLLOUT),
- SP(POLLWRNORM),
- SP(POLLWRBAND),
- SP(POLLERR),
- SP(POLLHUP),
- SP(POLLNVAL),
-
- SP(SHUT_RD),
- SP(SHUT_WR),
- SP(SHUT_RDWR),
-
- SP(AF_INET),
- SP(SOCK_STREAM),
- SP(SOCK_DGRAM),
-
- SP(SO_REUSEADDR),
- SP(SO_REUSEPORT),
- SP(SO_KEEPALIVE),
- SP(SO_DONTROUTE),
- SP(SO_LINGER),
- SP(SO_BROADCAST),
- SP(SO_OOBINLINE),
- SP(SO_SNDBUF),
- SP(SO_RCVBUF),
- SP(SO_SNDLOWAT),
- SP(SO_RCVLOWAT),
- SP(SO_SNDTIMEO),
- SP(SO_RCVTIMEO),
- SP(SO_TIMESTAMP),
- SP(SO_TYPE),
- SP(SO_ERROR),
- SP(TCP_NODELAY),
-
- SP(F_SETFL),
- SP(F_GETFL),
-
- SP(SEEK_SET),
- SP(SEEK_CUR),
- SP(SEEK_END),
-
- SP(S_IRWXU),
- SP(S_IRUSR),
- SP(S_IWUSR),
- SP(S_IXUSR),
- SP(S_IRWXG),
- SP(S_IRGRP),
- SP(S_IXGRP),
- SP(S_IRWXO),
- SP(S_IROTH),
- SP(S_IWOTH),
- SP(S_IXOTH),
- SP(S_ISUID),
- SP(S_ISGID),
- SP(S_ISVTX),
- SP(S_IFMT),
- SP(S_IFIFO),
- SP(S_IFCHR),
- SP(S_IFDIR),
- SP(S_IFBLK),
- SP(S_IFREG),
- SP(S_IFLNK),
- SP(S_IFSOCK),
- SP(S_IFWHT),
- SP(UF_NODUMP),
- SP(UF_IMMUTABLE),
- SP(UF_APPEND),
- SP(UF_OPAQUE),
- SP(SF_ARCHIVED),
- SP(SF_IMMUTABLE),
- SP(SF_APPEND),
-
- SP(LOCK_SH),
- SP(LOCK_EX),
- SP(LOCK_NB),
- SP(LOCK_UN),
-
- { NULL, 0 }
-};
-
-#undef SP
-
-
-
-/*
- * Miscelaneous static globals
- */
-
-/* XXX Should be initialized at main process startup ideally */
-static int tcp_proto = -1;
-static char *read_charbuf = NULL;
-static size_t read_charbuf_size = 0;
-
-
-
-/*
- * Class control functions
- */
-
-JSObject *
-js_InitFDClass(JSContext *cx, JSObject *obj)
-{
- JSObject *proto, *ctor;
- struct property_spec *sp;
-
- if ((proto = JS_InitClass(cx, obj, NULL, &fd_class, fd_constructor,
- 0, fd_properties, fd_methods, NULL, fd_smethods))
- == NULL) {
- (void) fprintf(stderr, "Error initializing FD class\n");
- return NULL;
- }
-
- /* Create static properties. Should probably be a function. */
-
- if ((ctor = JS_GetConstructor(cx, proto)) == NULL) {
- (void) fprintf(stderr, "FD: JS_GetConstructor == NULL\n");
- return NULL;
- }
- for (sp = fd_sprops; sp->name != NULL; sp++) {
- if (JS_DefineProperty(cx, ctor, sp->name,
- INT_TO_JSVAL(sp->value), NULL, NULL,
- JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) {
- (void) fprintf(stderr,
- "FD: Error defining property %s\n", sp->name);
- return NULL;
- }
- }
-
- return proto;
-}
-
-static JSBool
-fd_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd = NULL;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
-
- /*
- * IMPORTANT: We must verify if the caller attempts to execute us as a
- * normal function rather than as a constructor. Otherwise, the
- * caller can cause the interpreter to abort(3) in an assertion in
- * JS_SetPrivate()!
- */
- if (!JS_IsConstructing(cx)) {
- QUEUE_EXCEPTION("Constructor called as a function");
- goto err;
- }
-
- if ((jsfd = JS_malloc(cx, sizeof(jsfd_t))) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- goto err;
- }
-
- jsfd->fd = -1;
- jsfd->type = JSFD_NONE;
- jsfd->events = jsfd->revents = 0;
- jsfd->error = 0;
-
- if (!JS_SetPrivate(cx, obj, jsfd)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- if (jsfd != NULL)
- JS_free(cx, jsfd);
-
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
-}
-
-static void
-fd_finalize(JSContext *cx, JSObject *obj)
-{
- jsfd_t *jsfd;
-
- if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) != NULL) {
- /* Only close if not one of std descriptors */
- if (jsfd->fd != -1 && jsfd->type != JSFD_STD) {
- (void) close(jsfd->fd);
- jsfd->fd = -1;
- jsfd->type = JSFD_NONE;
- }
- if (jsfd->type == JSFD_FILE && jsfd->u.file.path != NULL) {
- JS_free(cx, jsfd->u.file.path);
- jsfd->u.file.path = NULL;
- }
- JS_free(cx, jsfd);
- (void) JS_SetPrivate(cx, obj, NULL);
- }
-}
-
-
-/*
- * Property functions
- */
-
-static JSBool
-fd_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- jsfd_t *jsfd;
- jsint p;
-
- if (!JSVAL_IS_INT(id))
- return JS_TRUE;
- p = (int)JSVAL_TO_INT(id);
-
- if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL)
- return JS_TRUE;
- if (jsfd->fd == -1)
- return JS_TRUE;
-
- switch (p) {
- case FD_P_PATH:
- if (jsfd->type == JSFD_FILE) {
- JSString *string;
-
- if ((string = JS_NewStringCopyZ(cx,
- jsfd->u.file.path)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- return JS_FALSE;
- }
- *vp = STRING_TO_JSVAL(string);
- }
- break;
- case FD_P_FD:
- *vp = INT_TO_JSVAL(jsfd->fd);
- break;
- case FD_P_MODE:
- if (jsfd->type == JSFD_FILE)
- *vp = INT_TO_JSVAL((int)jsfd->u.file.mode);
- break;
- case FD_P_EVENTS:
- *vp = INT_TO_JSVAL((int)jsfd->events);
- break;
- case FD_P_REVENTS:
- *vp = INT_TO_JSVAL((int)jsfd->revents);
- break;
- case FD_P_ERRNO:
- *vp = INT_TO_JSVAL(jsfd->error);
- break;
- case FD_P_CLIENT_ADDR:
- if (jsfd->type == JSFD_SOCKET) {
- char addr[16];
- JSString *string;
-
- if (inet_ntop(AF_INET, &jsfd->u.socket.caddr.sin_addr,
- addr, 15) == NULL) {
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
- if ((string = JS_NewStringCopyZ(cx, addr)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- return JS_FALSE;
- }
- *vp = STRING_TO_JSVAL(string);
- }
- break;
- case FD_P_CLIENT_PORT:
- if (jsfd->type == JSFD_SOCKET) {
- *vp = INT_TO_JSVAL((int)ntohs(jsfd->
- u.socket.caddr.sin_port));
- }
- break;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- jsfd_t *jsfd;
- jsint p;
-
- if (!JSVAL_IS_INT(id))
- return JS_TRUE;
- p = (int)JSVAL_TO_INT(id);
-
- if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL)
- return JS_TRUE;
- if (jsfd->fd == -1)
- return JS_TRUE;
-
- switch (p) {
- case FD_P_EVENTS:
- if (!JSVAL_IS_INT(*vp)) {
- QUEUE_EXCEPTION(
- "FD_P_EVENTS property requires an int");
- return JS_FALSE;
- }
- jsfd->events = (short)JSVAL_TO_INT(*vp);
- break;
- }
-
- return JS_TRUE;
-}
-
-
-/*
- * Static properties functions
- */
-
-
-/*
- * Method functions
- */
-
-static JSBool
-fd_m_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- int fd, flags;
- mode_t mode = 0644;
- char *bytes;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- /*
- * We use custom arguments checking here since we can accept both
- * 2 or 3.
- */
- if (argc < 2 || argc > 3) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- return JS_FALSE;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("First argument must be a string");
- return JS_FALSE;
- }
- if (!JSVAL_IS_INT(argv[1])) {
- QUEUE_EXCEPTION("Second argument must be an integer");
- return JS_FALSE;
- }
- if (argc == 3 && !JSVAL_IS_INT(argv[2])) {
- QUEUE_EXCEPTION("Third argument must be an integer");
- return JS_FALSE;
- }
- if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) {
- QUEUE_EXCEPTION("Null private data!");
- return JS_FALSE;
- }
- if (jsfd->type != JSFD_NONE) {
- QUEUE_EXCEPTION("Descriptor already open");
- return JS_FALSE;
- }
-
- if (argc == 3) {
- /*
- * Mode, supplied as an int.
- */
- mode = (mode_t)JSVAL_TO_INT(argv[2]);
- if ((mode = fd_mode_allow(mode)) == (mode_t)-1) {
- QUEUE_EXCEPTION("Mode not permitted");
- return JS_FALSE;
- }
- }
-
- /*
- * Flags, provided as an int.
- */
- flags = JSVAL_TO_INT(argv[1]);
- if ((flags = fd_flags_allow(flags)) == -1) {
- QUEUE_EXCEPTION("Flag not permitted");
- return JS_FALSE;
- }
-
- /* Path, provided as a string */
- if ((bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) {
- QUEUE_EXCEPTION("Internal error");
- return JS_FALSE;
- }
- /* Perform path sanity checking */
- if (fd_path_allow(bytes) == -1) {
- QUEUE_EXCEPTION("Invalid path");
- return JS_FALSE;
- }
- if ((jsfd->u.file.path = JS_strdup(cx, bytes)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- return JS_FALSE;
- }
-
- /* We can finally attempt to open(2) */
- if ((fd = open(bytes, flags, mode)) == -1) {
- jsfd->error = errno;
- /*
- * XXX strerror() seems to always need to load up the
- * nls table file, which is way silly for performance.
- * This is related to locale stuff, and should be able
- * to simply be disabled, even.
- * Since this event occurs often in httpd.js, let's just
- * output a fixed string for now.
- * I should actually fix NetBSD libc on this matter.
- */
-/* QUEUE_EXCEPTION(strerror(errno)); */
- QUEUE_EXCEPTION("open() error");
- JS_free(cx, jsfd->u.file.path);
- return JS_FALSE;
- }
-
- /* Success! */
- jsfd->fd = fd;
- jsfd->type = JSFD_FILE;
- jsfd->u.file.flags = flags;
- jsfd->u.file.mode = mode;
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_set(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- int32_t fd;
- jsfd_t *jsfd;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "set", FDMA_SET, argc,
- argv, JSFD_NONE)) == NULL)
- return JS_FALSE;
-
- fd = JSVAL_TO_INT(*argv);
- if (fd < STDIN_FILENO || fd > STDERR_FILENO) {
- QUEUE_EXCEPTION("Unknown standard descriptor");
- return JS_FALSE;
- }
- jsfd->fd = fd;
- jsfd->type = JSFD_STD;
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- int error;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "close", FDMA_CLOSE, argc,
- argv, JSFD_STD | JSFD_FILE | JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- if (jsfd->type == JSFD_STD) {
- jsfd->fd = -1;
- jsfd->type = JSFD_NONE;
- return JS_TRUE;
- }
-
- if (jsfd->type == JSFD_FILE) {
- if (jsfd->u.file.path != NULL) {
- JS_free(cx, jsfd->u.file.path);
- jsfd->u.file.path = NULL;
- }
- }
-
- error = close(jsfd->fd);
- jsfd->fd = -1;
- jsfd->type = JSFD_NONE;
-
- if (error == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_truncate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd;
- off_t size;
- jsdouble dsize;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "truncate", FDMA_TRUNCATE,
- argc, argv, JSFD_FILE)) == NULL)
- return JS_FALSE;
-
- if (!JS_ValueToNumber(cx, *argv, &dsize)) {
- QUEUE_EXCEPTION("Internal error");
- return JS_FALSE;
- }
- size = (off_t)dsize;
-
- if (ftruncate(jsfd->fd, size) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_put(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- JSString *str;
- char *bytes;
- ssize_t size;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- return JS_FALSE;
- }
-
- if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL)) == NULL) {
- QUEUE_EXCEPTION("Null private data!");
- return JS_FALSE;
- }
-
- if (jsfd->fd == -1) {
- QUEUE_EXCEPTION("Descriptor closed");
- return JS_FALSE;
- }
-
- /*
- * Instead of verifying if supplied value really is a JSString, and
- * using JSVAL_TO_STRING(), we convert the value to a string in this
- * case.
- */
- if ((str = JS_ValueToString(cx, *argv)) == NULL ||
- (bytes = JS_GetStringBytes(str)) == NULL) {
- QUEUE_EXCEPTION("Internal error");
- return JS_FALSE;
- }
-
- size = strlen(bytes);
- if ((size = write(jsfd->fd, bytes, size)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- *rval = INT_TO_JSVAL((int)size);
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_get(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- char bytes[4096];
- ssize_t size;
- JSString *string;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "get", FDMA_GET, argc,
- argv, JSFD_STD | JSFD_FILE | JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- if ((size = read(jsfd->fd, bytes, 4096)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
- if (size == 0)
- return JS_TRUE;
-
- if ((string = JS_NewStringCopyN(cx, bytes, size)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- return JS_FALSE;
- }
- *rval = STRING_TO_JSVAL(string);
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_socket(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- int domain, type, protocol, error;
- jsfd_t *jsfd;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "socket", FDMA_SOCKET,
- argc, argv, JSFD_NONE)) == NULL)
- return JS_FALSE;
-
- domain = (int)JSVAL_TO_INT(argv[0]);
- type = (int)JSVAL_TO_INT(argv[1]);
- protocol = (int)JSVAL_TO_INT(argv[2]);
-
- /* Sanity checking on currently supported protocols */
- if (domain != AF_INET || (type != SOCK_DGRAM && type != SOCK_STREAM)
- || protocol != 0) {
- QUEUE_EXCEPTION("Unsupported protocol");
- return JS_FALSE;
- }
-
- if ((error = socket(domain, type, protocol)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- jsfd->fd = error;
- jsfd->type = JSFD_SOCKET;
- jsfd->u.socket.domain = domain;
- jsfd->u.socket.type = type;
- jsfd->u.socket.protocol = protocol;
-
- return JS_TRUE;
-}
-
-/*
- * We currently make this rather simple; If the supplied string doesn't
- * consist of a valid IPv4 address, we simply attempt to resolve it, and on
- * success then attempt connection.
- */
-static JSBool
-fd_m_connect(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd;
- char *address;
- struct sockaddr_in sinaddr;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "connect", FDMA_CONNECT,
- argc, argv, JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- address = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
- if (inet_pton(AF_INET, address, &sinaddr.sin_addr) != 1) {
- struct hostent *h;
-
- /*
- * Not a valid IPv4 address, consider it as a hostname and
- * attempt to resolve it.
- * XXX Note: Not thread safe unless a global mutex/rwlock is
- * used. Should use getaddrinfo(3) instead. Especially if we
- * someday want to support other address families than
- * AF_INET.
- */
- if ((h = gethostbyname(address)) == NULL) {
- jsfd->error = errno;
- QUEUE_EXCEPTION("Invalid address or hostname");
- return JS_FALSE;
- }
- sinaddr.sin_addr.s_addr =
- ((struct in_addr *)h->h_addr_list[0])->s_addr;
- }
- sinaddr.sin_port = htons((in_port_t)JSVAL_TO_INT(argv[1]));
-
- if (connect(jsfd->fd, (struct sockaddr *)&sinaddr,
- sizeof(struct sockaddr_in)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_bind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- struct sockaddr_in sinaddr;
- char *address;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "bind", FDMA_BIND, argc,
- argv, JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- address = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
- if (inet_pton(AF_INET, address, &sinaddr.sin_addr) != 1) {
- QUEUE_EXCEPTION("Invalid IP address");
- return JS_FALSE;
- }
- sinaddr.sin_port = htons((in_port_t)JSVAL_TO_INT(argv[1]));
-
- if (bind(jsfd->fd, (struct sockaddr *)&sinaddr,
- sizeof(struct sockaddr_in)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_listen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "listen", FDMA_LISTEN,
- argc, argv, JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- if (listen(jsfd->fd, (int)JSVAL_TO_INT(*argv)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_accept(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd, *njsfd;
- int sock;
- struct sockaddr_in sinaddr;
- socklen_t socklen;
- JSObject *nobj;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "accept", FDMA_ACCEPT,
- argc, argv, JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- socklen = sizeof(struct sockaddr_in);
- if ((sock = accept(jsfd->fd, (struct sockaddr *)&sinaddr, &socklen))
- == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- /*
- * Success, create new FD object, fill it and return it.
- */
- if ((nobj = JS_ConstructObject(cx, &fd_class, obj, obj)) == NULL) {
- (void) close(sock);
- QUEUE_EXCEPTION("Out of resources");
- return JS_FALSE;
- }
- njsfd = JS_GetInstancePrivate(cx, nobj, &fd_class, NULL);
- njsfd->fd = sock;
- njsfd->type = JSFD_SOCKET;
- njsfd->u.socket.domain = jsfd->u.socket.domain;
- njsfd->u.socket.type = jsfd->u.socket.type;
- njsfd->u.socket.protocol = jsfd->u.socket.protocol;
- (void) memcpy(&njsfd->u.socket.caddr, &sinaddr,
- sizeof(struct sockaddr_in));
-
- *rval = OBJECT_TO_JSVAL(nobj);
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_shutdown(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "shutdown", FDMA_SHUTDOWN,
- argc, argv, JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- if (shutdown(jsfd->fd, (int)JSVAL_TO_INT(*argv)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-/*
- * XXX Not thread safe ATM as it uses a static int with test-and-set operation
- * to only query the protocol database once. This could be done at early
- * process initialization alternatively.
- * Unlike BSD/POSIX setsockopt(2), always requires a single integer value (-1
- * in the case of SO_LINGER to disable it, or the number of seconds to
- * linger to enable it).
- */
-static JSBool
-fd_m_setsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd;
- void *opt;
- struct linger l;
- int optname, level, optval;
- socklen_t optlen;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "setsockopt",
- FDMA_SETSOCKOPT, argc, argv, JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- optname = JSVAL_TO_INT(argv[0]);
- optval = JSVAL_TO_INT(argv[1]);
-
- /*
- * Work out special case for SO_LINGER
- */
- if (optname == SO_LINGER) {
- if (optval == -1) {
- l.l_onoff = 0;
- l.l_linger = 0;
- } else {
- l.l_onoff = 1;
- l.l_linger = optval;
- }
- opt = &l;
- optlen = sizeof(struct linger);
- } else {
- opt = &optval;
- optlen = sizeof(int);
- optval = (optval != 0 ? 1 : 0);
- }
-
- /*
- * And for TCP_NODELAY which must use tcp_proto as level
- */
- if (optname == TCP_NODELAY) {
- if (tcp_proto == -1) {
- struct protoent *pent;
-
- if ((pent = getprotobyname("TCP")) != NULL)
- tcp_proto = pent->p_proto;
- else
- tcp_proto = 4; /* Generally allright */
- }
- level = tcp_proto;
- } else
- level = SOL_SOCKET;
-
- if (setsockopt(jsfd->fd, level, optname, opt, optlen) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-/*
- * XXX Not thread safe ATM as it uses a static int with test-and-set operation
- * to only query the protocol database once. This could be done at early
- * process initialization alternatively.
- * Unlike BSD/POSIX getsockopt(2), always returns a single integer value (-1
- * in the case of SO_LINGER disabled, or the number of seconds assigned to
- * wait if enabled).
- */
-static JSBool
-fd_m_getsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd;
- void *opt;
- struct linger l;
- int i, optname, level, result;
- socklen_t optlen;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "getsockopt",
- FDMA_GETSOCKOPT, argc, argv, JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- optname = JSVAL_TO_INT(*argv);
-
- /*
- * Special case for SO_LINGER which expects a structure rather than an
- * integer
- */
- if (optname == SO_LINGER) {
- opt = &l;
- optlen = sizeof(struct linger);
- } else {
- opt = &i;
- optlen = sizeof(int);
- }
-
- /*
- * And for TCP_NODELAY which must use TCP protocol number rather than
- * SOL_SOCKET level
- */
- if (optname == TCP_NODELAY) {
- if (tcp_proto == -1) {
- struct protoent *pent;
-
- if ((pent = getprotobyname("TCP")) != NULL)
- tcp_proto = pent->p_proto;
- else
- tcp_proto = 4; /* Generally allright */
- }
- level = tcp_proto;
- } else
- level = SOL_SOCKET;
-
- if (getsockopt(jsfd->fd, level, optname, opt, &optlen) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- /*
- * To simplify the implementation, special case of SO_LINGER result;
- * We return -1 if lingering is disabled, or otherwise return the
- * number of seconds it should linger for maximum.
- */
- if (optname == SO_LINGER) {
- if (l.l_onoff != 0)
- result = l.l_linger;
- else
- result = -1;
- } else
- /*
- * These are booleans, so ensure proper return value despite
- * several implementations which return a mask rather than 0/1
- */
- result = (i != 0 ? 1 : 0);
-
- *rval = INT_TO_JSVAL(result);
-
- return JS_TRUE;
-}
-
-/*
- * Unlike POSIX fcntl(2), only currently supports F_GETFL and F_SETFL along
- * with O_NONBLOCK and O_APPEND. The flags argument is also mandatory, which
- * will serve as a result mask for F_GETFL or to set wanted flags using
- * F_SETFL. The previous flags are returned as usual (but will only ever
- * include 0, O_NONBLOCK and/or O_APPEND).
- */
-static JSBool
-fd_m_fcntl(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- int cmd, flags, error;
-
- *rval = INT_TO_JSVAL(0);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "fcntl", FDMA_FCNTL,
- argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- cmd = JSVAL_TO_INT(argv[0]);
- flags = JSVAL_TO_INT(argv[1]);
-
- if (cmd != F_GETFL && cmd != F_SETFL) {
- QUEUE_EXCEPTION("Unimplemented fcntl() command");
- return JS_FALSE;
- }
- flags &= (O_NONBLOCK | O_APPEND);
- if ((flags & O_NONBLOCK) == 0 && (flags & O_APPEND) == 0) {
- QUEUE_EXCEPTION("Unimplemented fcntl() flag");
- return JS_FALSE;
- }
-
- if (cmd == F_GETFL) {
- if ((error = fcntl(jsfd->fd, cmd, NULL)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
- error &= flags;
- } else {
- if ((error = fcntl(jsfd->fd, cmd, flags)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
- error &= (O_NONBLOCK | O_APPEND);
- }
-
- *rval = INT_TO_JSVAL(error);
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- size_t size;
- ssize_t rsize;
- JSString *string;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "read", FDMA_READ,
- argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- size = (size_t)JSVAL_TO_INT(*argv);
- if (size < 1) {
- QUEUE_EXCEPTION("read() requested size smaller than 1");
- return JS_FALSE;
- }
-
- /*
- * Ensure that our read buffer is ready, and of a large enough size
- * to accomodate read.
- */
- if (read_charbuf_size < size) {
- if (read_charbuf == NULL) {
- /* Never allocated yet, simply allocate */
- if ((read_charbuf = malloc(size)) == NULL) {
- QUEUE_EXCEPTION("Cannot allocate read buffer");
- return JS_FALSE;
- }
- } else {
- char *ptr;
-
- /* Buffer too small, attempt to increase it */
- if ((ptr = realloc(read_charbuf, size)) == NULL) {
- QUEUE_EXCEPTION(
- "Cannot reallocate read buffer");
- return JS_FALSE;
- }
- read_charbuf = ptr;
- }
- read_charbuf_size = size;
- }
-
- if ((rsize = read(jsfd->fd, read_charbuf, size)) == -1) {
- /*
- * XXX Should we really throw an exception, or simply return
- * an error? For instance, if using nonblocking mode and
- * expecting EAGAIN, would using an exception clubber
- * unnecessarily the code?
- */
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
- if (size == 0)
- return JS_TRUE;
-
- if ((string = JS_NewStringCopyN(cx, read_charbuf, rsize)) == NULL) {
- QUEUE_EXCEPTION("Couldn't allocate read result string");
- return JS_FALSE;
- }
- *rval = STRING_TO_JSVAL(string);
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- ssize_t rsize;
- JSString *str;
- char *bytes;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "write", FDMA_WRITE,
- argc, argv, JSFD_FILE | JSFD_SOCKET)) == NULL)
- return JS_FALSE;
-
- str = JSVAL_TO_STRING(*argv);
- if ((bytes = JS_GetStringBytes(str)) == NULL) {
- QUEUE_EXCEPTION("Internal error");
- return JS_FALSE;
- }
-
- if ((rsize = write(jsfd->fd, bytes, JS_GetStringLength(str))) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
- *rval = INT_TO_JSVAL((int)rsize);
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_fdatasync(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsfd_t *jsfd;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "fdatasync",
- FDMA_FDATASYNC, argc, argv, JSFD_FILE)) == NULL)
- return JS_FALSE;
-
- if (fdatasync(jsfd->fd) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_lseek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- off_t off, newoff;
- int whence;
- jsdouble doff;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "lseek", FDMA_LSEEK,
- argc, argv, JSFD_FILE)) == NULL)
- return JS_FALSE;
-
- if (!JS_ValueToNumber(cx, argv[0], &doff)) {
- QUEUE_EXCEPTION("Internal error");
- return JS_FALSE;
- }
- off = (off_t)doff;
- whence = JSVAL_TO_INT(argv[1]);
-
- if ((newoff = lseek(jsfd->fd, off, whence)) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- if (!JS_NewDoubleValue(cx, (jsdouble)newoff, rval)) {
- QUEUE_EXCEPTION("Internal error");
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_fchmod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- mode_t mode;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "fchmod", FDMA_FCHMOD,
- argc, argv, JSFD_FILE)) == NULL)
- return JS_FALSE;
-
- mode = (mode_t)JSVAL_TO_INT(*argv);
- if ((mode = fd_mode_allow(mode)) == (mode_t)-1) {
- QUEUE_EXCEPTION("Mode not permitted");
- return JS_FALSE;
- }
-
- if (fchmod(jsfd->fd, mode) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_flock(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- int op;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "flock", FDMA_FLOCK,
- argc, argv, JSFD_FILE)) == NULL)
- return JS_FALSE;
-
- op = JSVAL_TO_INT(*argv);
- if (flock(jsfd->fd, op) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-static JSBool
-fd_m_fstat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- jsfd_t *jsfd;
- struct stat st;
- JSObject *array = NULL;
- jsval val;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if ((jsfd = fd_methods_args_check(cx, obj, "fstat", FDMA_FSTAT,
- argc, argv, JSFD_FILE)) == NULL)
- return JS_FALSE;
-
- if (fstat(jsfd->fd, &st) == -1) {
- jsfd->error = errno;
- QUEUE_EXCEPTION(strerror(errno));
- return JS_FALSE;
- }
-
- /*
- * Note: We immediately link newly created objects to avoid GC
- * problems. For the simplicity of this task we don't need an
- * additional root to be created using JS_AddRoot(), since *rval
- * is already rooted. Moreover, the double objects we create are
- * immediately added as propery as well.
- */
-
- if ((array = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- goto err;
- }
- *rval = OBJECT_TO_JSVAL(array);
-
-#define DEFINE_INT_PROP(n, i) do { \
- val = INT_TO_JSVAL((int)(i)); \
- if (!JS_DefineProperty(cx, array, (n), val, NULL, NULL, \
- JSPROP_ENUMERATE)) { \
- QUEUE_EXCEPTION("Internal error!"); \
- goto err; \
- } \
-} while (/* CONSTCOND */0)
-
-#define DEFINE_DOUBLE_PROP(n, d) do { \
- if (!JS_NewDoubleValue(cx, (jsdouble)(d), &val)) \
- goto err; \
- if (!JS_DefineProperty(cx, array, (n), val, NULL, NULL, \
- JSPROP_ENUMERATE)) { \
- QUEUE_EXCEPTION("Internal error!"); \
- goto err; \
- } \
-} while (/* CONSTCOND */0)
-
- DEFINE_INT_PROP("st_dev", st.st_dev);
- DEFINE_INT_PROP("st_ino", st.st_ino);
- DEFINE_INT_PROP("st_mode", st.st_mode);
- DEFINE_INT_PROP("st_nlink", st.st_nlink);
- DEFINE_INT_PROP("st_uid", st.st_uid);
- DEFINE_INT_PROP("st_gid", st.st_gid);
- DEFINE_INT_PROP("st_rdev", st.st_rdev);
- DEFINE_DOUBLE_PROP("st_atime", st.st_atime);
- DEFINE_DOUBLE_PROP("st_mtime", st.st_mtime);
- DEFINE_DOUBLE_PROP("st_ctime", st.st_ctime);
- DEFINE_DOUBLE_PROP("st_size", st.st_size);
- DEFINE_DOUBLE_PROP("st_blocks", st.st_blocks);
- DEFINE_INT_PROP("st_blksize", st.st_blksize);
- DEFINE_INT_PROP("st_flags", st.st_flags);
- DEFINE_INT_PROP("st_gen", st.st_gen);
-
-#undef DEFINE_INT_PROP
-#undef DEFINE_DOUBLE_PROP
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-
-
-/*
- * Static methods
- */
-
-static JSBool
-fd_sm_poll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- struct poll_fds fds;
- int nfds, timeout, i;
- JSObject *array = NULL;
- jsint index;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if (argc != 2) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- return JS_FALSE;
- }
-
- /*
- * First make sure that user supplied object really consists of an
- * array.
- */
- if (!JSVAL_IS_OBJECT(argv[0]) ||
- !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
- QUEUE_EXCEPTION("First argument must be Array object");
- return JS_FALSE;
- }
-
- /*
- * Obtain timeout from argv[1]
- */
- if (!JSVAL_IS_INT(argv[1])) {
- QUEUE_EXCEPTION("Second argument must be timeout integer");
- return JS_FALSE;
- }
- timeout = (int)JSVAL_TO_INT(argv[1]);
-
- /*
- * Create our pollfd array, iterating through all FD objects of the
- * user-provided array object.
- */
- if ((fds.entries = malloc(sizeof(struct pollfd) * 16)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- goto err;
- }
- if ((fds.info = malloc(sizeof(struct poll_fdsi) * 16)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- goto err;
- }
- fds.count = 0;
- fds.size = 16;
- if (!object_iterate(cx, JSVAL_TO_OBJECT(argv[0]), &fds,
- fd_sm_poll_mkset))
- goto err;
-
- /*
- * Finally perform actual polling
- */
- if ((nfds = poll(fds.entries, fds.count, timeout)) == -1) {
- QUEUE_EXCEPTION(strerror(errno));
- goto err;
- }
-
- /*
- * Now set FD objects event field and create custom array object to
- * return to the caller, only holding entries for which events
- * occurred.
- * Link object immediately to avoid GC problems or needing
- * JS_AddRoot().
- */
- if ((array = JS_NewArrayObject(cx, 0, NULL)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- goto err;
- }
- *rval = OBJECT_TO_JSVAL(array);
-
- for (i = 0, index = 0; i < fds.count && nfds != 0; i++) {
- if (fds.entries[i].revents != 0) {
- nfds--;
- fds.info[i].jsfd->revents = fds.entries[i].revents;
- /*
- * Add an element if numeric index entry, or a
- * property if name based/associative entry.
- */
- if (fds.info[i].name == NULL) {
- if (!JS_DefineElement(cx, array, index++,
- fds.info[i].fdobj, NULL, NULL,
- JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- } else {
- if (!JS_DefineProperty(cx, array,
- fds.info[i].name, fds.info[i].fdobj, NULL,
- NULL, JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- }
- }
- }
-
- free(fds.entries);
- free(fds.info);
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
- if (fds.entries != NULL)
- free(fds.entries);
- if (fds.info != NULL)
- free(fds.info);
-
- return JS_FALSE;
-}
-
-static JSBool
-fd_sm_poll_mkset(JSContext *cx, jsval *id, jsval *val, void *udata)
-{
- struct poll_fds *fds = (struct poll_fds *)udata;
- JSObject *o;
- jsfd_t *jsfd;
-
- if (!JSVAL_IS_OBJECT(*val) ||
- !JS_InstanceOf(cx, (o = JSVAL_TO_OBJECT(*val)), &fd_class, NULL)) {
- QUEUE_EXCEPTION("Not FD object");
- return JS_FALSE;
- }
- if ((jsfd = JS_GetInstancePrivate(cx, o, &fd_class, NULL)) == NULL) {
- QUEUE_EXCEPTION("Null private data!");
- return JS_FALSE;
- }
-
- if (fds->count == fds->size) {
- void *ptr;
-
- /* Need to grow entries and names */
- if ((ptr = realloc(fds->entries,
- sizeof(struct pollfd) * fds->size * 2)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- return JS_FALSE;
- }
- fds->entries = ptr;
- if ((ptr = realloc(fds->info,
- sizeof(struct poll_fdsi) * fds->size * 2)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- return JS_FALSE;
- }
- fds->info = ptr;
- fds->size *= 2;
- }
-
- /*
- * Add new entry. If it's a property (associative array entry), also
- * fill in the name pointer which will be used to recreate the result
- * array with those names as well. We set the name to NULL for index
- * based array entries.
- */
- fds->entries[fds->count].fd = jsfd->fd;
- fds->entries[fds->count].events = jsfd->events;
- fds->entries[fds->count].revents = 0;
- if (JSVAL_IS_STRING(*id))
- fds->info[fds->count].name =
- JS_GetStringBytes(JSVAL_TO_STRING(*id));
- else
- fds->info[fds->count].name = NULL;
- fds->info[fds->count].fdobj = *val;
- fds->info[fds->count++].jsfd = jsfd;
-
- return JS_TRUE;
-}
-
-
-/*
- * Utility functions
- */
-
-/*
- * Was written to be able to iterate over all elements of an array object,
- * despite being an associated array or not, or a mix of both. Unfortunately
- * uses marked as private JSIdArray structure.
- * This was needed because arrays are using indexes, while associative arrays
- * are nothing more than an object with its properties. This function can
- * deal with both.
- */
-static JSBool
-object_iterate(JSContext *cx, JSObject *obj, void *udata,
- JSBool (*func)(JSContext *, jsval *, jsval *, void *))
-{
- JSIdArray *a;
- jsval id, val;
- char *name;
- jsint i;
- JSBool ret = JS_FALSE;
-
- if ((a = JS_Enumerate(cx, obj)) != NULL) {
- for (i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JSVAL_IS_STRING(id)) {
- /*
- * Property id is a string, attempt to
- * lookup its value by name.
- */
- name = JS_GetStringBytes(JSVAL_TO_STRING(id));
- if (!JS_LookupProperty(cx, obj, name, &val))
- continue;
- } else {
- /*
- * Property id is a number, attempt to
- * lookup its array element by index.
- */
- if (!JS_LookupElement(cx, obj,
- JSVAL_TO_INT(id), &val))
- continue;
- }
- if (!JSVAL_IS_VOID(val)) {
- if (!(ret = func(cx, &id, &val, udata)))
- break;
- }
- }
- JS_DestroyIdArray(cx, a);
- }
-
- return ret;
-}
-
-/*
- * Utility function return 0 if user supplied path should be allowed, or -1 if
- * it should be rejected (invalid, or permission denied). This can for
- * instance be used to restrict the program in a virtual chroot(2)-like jail.
- */
-/* ARGSUSED */
-static int
-fd_path_allow(const char *path)
-{
- /* XXX */
-
- return 0;
-}
-
-/* ARGSUSED */
-static mode_t
-fd_mode_allow(mode_t mode)
-{
- /* XXX */
-
- return mode;
-}
-
-/* ARGSUSED */
-static int
-fd_flags_allow(int flags)
-{
- /* XXX */
-
- return flags;
-}
-
-/*
- * Useful to ensure that a function's arguments are as expected, and to
- * retrieve the private data associated with the FD object. Implemented to
- * minimize code duplication among common functions.
- */
-static jsfd_t *
-fd_methods_args_check(JSContext *cx, JSObject *obj, const char *fun, int id,
- int argc, jsval *argv, int type)
-{
- int *p = fd_methods_args_array[id], i;
- char line[1024];
- jsfd_t *jsfd;
-
- if (*p != argc) {
- (void) snprintf(line, 1023,
- "%s() - Wrong number of arguments (%d), expected %d",
- fun, argc, *p);
- QUEUE_EXCEPTION(line);
- return NULL;
- }
-
- for (p++, i = 0; i < argc; i++) {
- switch (p[i]) {
- case JSAT_INTEGER:
- if (!JSVAL_IS_INT(argv[i])) {
- (void) snprintf(line, 1023,
- "%s() - argument #%d not an integer",
- fun, i + 1);
- QUEUE_EXCEPTION(line);
- return NULL;
- }
- break;
- case JSAT_DOUBLE:
- if (!JSVAL_IS_DOUBLE(argv[i]) &&
- !JSVAL_IS_INT(argv[i])) {
- (void) snprintf(line, 1023,
- "%s() - argument #%d not a double",
- fun, i + 1);
- QUEUE_EXCEPTION(line);
- return NULL;
- }
- break;
- case JSAT_STRING:
- if (!JSVAL_IS_STRING(argv[i])) {
- (void) snprintf(line, 1023,
- "%s() - argument #%d not a string",
- fun, i + 1);
- QUEUE_EXCEPTION(line);
- return NULL;
- }
- break;
- default:
- (void) snprintf(line, 1023,
- "%s() - Unexpected argument type #%d",
- fun, i + 1);
- QUEUE_EXCEPTION(line);
- return NULL;
- }
- }
-
- if ((jsfd = JS_GetInstancePrivate(cx, obj, &fd_class, NULL))
- == NULL) {
- (void) snprintf(line, 1023, "%s() - NULL private data!", fun);
- QUEUE_EXCEPTION(line);
- return NULL;
- }
-
- if (type == JSFD_NONE && jsfd->type != JSFD_NONE) {
- (void) snprintf(line, 1023,
- "%s() - Descriptor is already open",
- fun);
- QUEUE_EXCEPTION(line);
- return NULL;
- } else
- return jsfd;
-
- if ((jsfd->type & type) == 0) {
- (void) snprintf(line, 1023,
- "%s() - Descriptor is closed or of wrong type",
- fun);
- QUEUE_EXCEPTION(line);
- return NULL;
- }
-
- return jsfd;
-}
+++ /dev/null
-/* $Id: js_fd.h,v 1.4 2006/07/11 06:45:14 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-#ifndef JSFD_H
-#define JSFD_H
-
-#include <jsapi.h>
-
-extern JSObject *js_InitFDClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $Id: js_global.c,v 1.1 2006/07/22 03:43:09 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Provide a means to set and access global objects and global properties.
- * Basically we can store hashtable objects, which each can store arbitrary
- * String/String tuples, but using shared memory instead of a database.
- * Internally a hash table would also be used to index hash tables by String.
- * There would be a single synchronization lock around the system.
- * We would need to initially work with an allocated buffer of shared memory,
- * which only needed pages are used. For this, mmpool(3) could be used.
- * A pool of hash tables would be necessary, as well as one for the data
- * pair items. To be linked among those.
- *
- * I yet have to find a proper interface. We optionally could have stuff
- * like:
- *
- * Global.getProperty(table, property);
- * Global.setProperty(table, property, string);
- *
- * But would it also be possible to use lazy allocation such that this would
- * be possible, although of course enforcing the same internal behavior:
- *
- * Global.table.property would be read or set as necessary.
- *
- * Or:
- * table = new Global(tablename);
- * table.prop = 'string';
- * out.put(table.prop + "\n");
- */
-
-
-
-#include <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 03:43:09 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/09 00:30:30 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
+++ /dev/null
-/* $Id: js_mysql.h,v 1.1 2006/07/09 00:30:30 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-#ifndef JSMYSQL_H
-#define JSMYSQL_H
-
-extern JSObject *js_InitMySQLClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $Id: js_pgsql.c,v 1.17 2006/07/22 03:22:05 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * XXX TODO XXX
- * - Verify if JS_GetStringLength() really safe to continue using
- * - Perhaps provide simpler replacement functions for query functions
- * allowing separate parameters to be set (perhaps not necessary considering
- * that we can provide null to an array).
- * - All functions creating doubles or strings should check if NULL is
- * returned. Verify this.
- * - (maybe) make reentrant by causing optimization buffers to be part of
- * generated objects instances's private data (using structures as necessary
- * instead of simply wrapping around the native object's pointer
- * (actually PGconn object).
- * - 28.10. Notice Processing
- * Either place one(s) that use syslog(3) transparently, or somehow allow
- * the user to set a custom handler
- * - Large objects API
- * - Compare to PHP library to verify if missing any nice functions ideas
- * - See what to do about the following functions:
- * - PQgetssl() (returns an SSL object!)
- * - PQprint() (writes to a supplied FILE *)
- */
-
-
-
-#include <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 <js_pgsql.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 JSBool pg_sm_PQconndefaults(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pg_sm_PQconnectdb(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pg_sm_PQconnectStart(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pg_sm_PQresStatus(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pg_sm_PQunescapeBytea(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-
-static JSObject *js_InitPGconnClass(JSContext *, JSObject *);
-static JSBool pgconn_constructor(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static void pgconn_finalize(JSContext *, JSObject *);
-
-static JSBool pgconn_m_PQfinish(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQconnectPoll(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQreset(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQresetStart(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQresetPoll(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQdb(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQuser(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQpass(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQhost(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQport(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQtty(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQoptions(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQstatus(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQtransactionStatus(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQparameterStatus(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQprotocolVersion(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQserverVersion(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQerrorMessage(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQsocket(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQbackendPID(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQexec(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQsendQuery(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQexecParams2(JSContext *, JSObject *, uintN, jsval *,
- jsval *, int);
-static JSBool pgconn_m_PQexecParams(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQsendQueryParams(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQprepare2(JSContext *, JSObject *, uintN, jsval *,
- jsval *, int);
-static JSBool pgconn_m_PQprepare(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQsendPrepare(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQexecPrepared2(JSContext *, JSObject *, uintN,
- jsval *, jsval *, int);
-static JSBool pgconn_m_PQexecPrepared(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQsendQueryPrepared(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQmakeEmptyPGresult(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQescapeStringConn(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQescapeByteaConn(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQgetCancel(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQnotifies(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQgetResult(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQconsumeInput(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQisBusy(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQsetnonblocking(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQisnonblocking(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQflush(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQsetErrorVerbosity(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgconn_m_PQtrace(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQuntrace(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQputCopyData(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQputCopyEnd(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgconn_m_PQgetCopyData(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-
-static JSObject *js_InitPGresultClass(JSContext *, JSObject *);
-static JSBool pgresult_constructor(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static void pgresult_finalize(JSContext *, JSObject *);
-
-static JSBool pgresult_m_PQclear(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQresultStatus(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgresult_m_PQresultErrorMessage(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgresult_m_PQresultErrorField(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgresult_m_PQntuples(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQnfields(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQfname(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQfnumber(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQftable(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQftablecol(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQfformat(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQftype(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQfmod(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQfsize(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQbinaryTuples(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgresult_m_PQgetvalue(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQgetisnull(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQgetlength(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQcmdStatus(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQcmdTuples(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static JSBool pgresult_m_PQoidValue(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-
-static JSObject *js_InitPGcancelClass(JSContext *, JSObject *);
-static JSBool pgcancel_constructor(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-static void pgcancel_finalize(JSContext *, JSObject *);
-
-static JSBool pgcancel_m_PQfreeCancel(JSContext *, JSObject *, uintN,
- jsval *, jsval *);
-static JSBool pgcancel_m_PQcancel(JSContext *, JSObject *, uintN, jsval *,
- jsval *);
-
-
-
-/*
- * Static globals
- */
-
-/*
- * General purpose string buffer (note that this is not thread-safe).
- * Allows to optimize functions such as PQescapeStringConn().
- */
-static char *buffer = NULL;
-static size_t buffer_size = 0;
-static Oid *param_types = NULL;
-static char **param_values = NULL;
-static int *param_lengths = NULL;
-static int *param_formats = NULL;
-static int param_entries = 0;
-
-/* PG class */
-static JSClass pg_class = {
- "PG", 0, JS_PropertyStub, JS_PropertyStub,
- JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub,
- JS_ConvertStub, JS_FinalizeStub
-};
-
-/* Provided static methods */
-static JSFunctionSpec pg_smethods[] = {
- { "connDefaults", pg_sm_PQconndefaults, 0, 0, 0 },
- { "connectDb", pg_sm_PQconnectdb, 1, 0, 0 },
- { "connectStart", pg_sm_PQconnectStart, 1, 0, 0 },
- { "resStatus", pg_sm_PQresStatus, 1, 0, 0 },
- { "unescapeBytea", pg_sm_PQunescapeBytea, 2, 0, 0 },
- { NULL, NULL, 0, 0, 0 }
-};
-
-/* Provided static properties */
-
-#define SP(n) \
- { #n, n }
-
-static struct property_spec pg_sprops[] = {
- SP(PGRES_POLLING_OK),
- SP(PGRES_POLLING_READING),
- SP(PGRES_POLLING_WRITING),
- SP(PGRES_POLLING_FAILED),
- SP(PGRES_EMPTY_QUERY),
- SP(PGRES_COMMAND_OK),
- SP(PGRES_TUPLES_OK),
- SP(PGRES_COPY_OUT),
- SP(PGRES_COPY_IN),
- SP(PGRES_BAD_RESPONSE),
- SP(PGRES_NONFATAL_ERROR),
- SP(PGRES_FATAL_ERROR),
- SP(PG_DIAG_SEVERITY),
- SP(PG_DIAG_SQLSTATE),
- SP(PG_DIAG_MESSAGE_PRIMARY),
- SP(PG_DIAG_MESSAGE_DETAIL),
- SP(PG_DIAG_MESSAGE_HINT),
- SP(PG_DIAG_STATEMENT_POSITION),
- SP(PG_DIAG_INTERNAL_POSITION),
- SP(PG_DIAG_INTERNAL_QUERY),
- SP(PG_DIAG_CONTEXT),
- SP(PG_DIAG_SOURCE_FILE),
- SP(PG_DIAG_SOURCE_LINE),
- SP(PG_DIAG_SOURCE_FUNCTION),
- SP(CONNECTION_OK),
- SP(CONNECTION_BAD),
- SP(CONNECTION_STARTED),
- SP(CONNECTION_MADE),
- SP(CONNECTION_AWAITING_RESPONSE),
- SP(CONNECTION_AUTH_OK),
- SP(CONNECTION_SSL_STARTUP),
- SP(CONNECTION_SETENV),
- SP(PQTRANS_IDLE),
- SP(PQTRANS_ACTIVE),
- SP(PQTRANS_INTRANS),
- SP(PQTRANS_INERROR),
- SP(PQTRANS_UNKNOWN),
- SP(InvalidOid),
- SP(PQERRORS_TERSE),
- SP(PQERRORS_DEFAULT),
- SP(PQERRORS_VERBOSE),
- { NULL, 0 }
-};
-
-#undef SP
-
-
-/* PGconn class */
-static JSClass pgconn_class = {
- "PGConn", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub,
- JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,
- JS_ResolveStub, JS_ConvertStub, pgconn_finalize
-};
-
-/* Provided methods/functions */
-static JSFunctionSpec pgconn_methods[] = {
- { "finish", pgconn_m_PQfinish, 0, 0, 0 },
- { "connectPoll", pgconn_m_PQconnectPoll, 0, 0, 0 },
- { "reset", pgconn_m_PQreset, 0, 0, 0 },
- { "resetStart", pgconn_m_PQresetStart, 0, 0, 0 },
- { "resetPoll", pgconn_m_PQresetPoll, 0, 0, 0 },
- { "db", pgconn_m_PQdb, 0, 0, 0 },
- { "user", pgconn_m_PQuser, 0, 0, 0 },
- { "pass", pgconn_m_PQpass, 0, 0, 0 },
- { "host", pgconn_m_PQhost, 0, 0, 0 },
- { "port", pgconn_m_PQport, 0, 0, 0 },
- { "tty", pgconn_m_PQtty, 0, 0, 0 },
- { "options", pgconn_m_PQoptions, 0, 0, 0 },
- { "status", pgconn_m_PQstatus, 0, 0, 0 },
- { "transactionStatus", pgconn_m_PQtransactionStatus, 0, 0, 0 },
- { "parameterStatus", pgconn_m_PQparameterStatus, 1, 0, 0 },
- { "protocolVersion", pgconn_m_PQprotocolVersion, 0, 0, 0 },
- { "serverVersion", pgconn_m_PQserverVersion, 0, 0, 0 },
- { "errorMessage", pgconn_m_PQerrorMessage, 0, 0, 0 },
- { "socket", pgconn_m_PQsocket, 0, 0, 0 },
- { "backendPid", pgconn_m_PQbackendPID, 0, 0, 0 },
- { "exec", pgconn_m_PQexec, 1, 0, 0 },
- { "sendQuery", pgconn_m_PQsendQuery, 1, 0, 0 },
- { "execParams", pgconn_m_PQexecParams, 7, 0, 0 },
- { "sendQueryParams", pgconn_m_PQsendQueryParams, 7, 0, 0 },
- { "prepare", pgconn_m_PQprepare, 4, 0, 0 },
- { "sendPrepare", pgconn_m_PQsendPrepare, 4, 0, 0 },
- { "execPrepared", pgconn_m_PQexecPrepared, 6, 0, 0 },
- { "sendQueryPrepared", pgconn_m_PQsendQueryPrepared, 6, 0, 0 },
- { "makeEmptyPGResult", pgconn_m_PQmakeEmptyPGresult, 1, 0, 0 },
- { "escapeStringConn", pgconn_m_PQescapeStringConn, 1, 0, 0 },
- { "escapeByteaConn", pgconn_m_PQescapeByteaConn, 1, 0, 0 },
- { "getCancel", pgconn_m_PQgetCancel, 0, 0, 0 },
- { "notifies", pgconn_m_PQnotifies, 0, 0, 0 },
- { "getResult", pgconn_m_PQgetResult, 0, 0, 0 },
- { "consumeInput", pgconn_m_PQconsumeInput, 0, 0, 0 },
- { "isBusy", pgconn_m_PQisBusy, 0, 0, 0 },
- { "setNonBlocking", pgconn_m_PQsetnonblocking, 1, 0, 0 },
- { "isNonBlocking", pgconn_m_PQisnonblocking, 0, 0, 0 },
- { "flush", pgconn_m_PQflush, 0, 0, 0 },
- { "setErrorVerbosity", pgconn_m_PQsetErrorVerbosity, 1, 0, 0 },
- { "trace", pgconn_m_PQtrace, 0, 0, 0 },
- { "untrace", pgconn_m_PQuntrace, 0, 0, 0 },
- { "putCopyData", pgconn_m_PQputCopyData, 1, 0, 0 },
- { "putCopyEnd", pgconn_m_PQputCopyEnd, 1, 0, 0 },
- { "getCopyData", pgconn_m_PQgetCopyData, 1, 0, 0 },
- { NULL, NULL, 0, 0, 0 }
-};
-
-
-/* PGresult class */
-static JSClass pgresult_class = {
- "PGResult", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub,
- JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,
- JS_ResolveStub, JS_ConvertStub, pgresult_finalize
-};
-
-/* Provided methods/functions */
-static JSFunctionSpec pgresult_methods[] = {
- { "clear", pgresult_m_PQclear, 0, 0, 0 },
- { "resultStatus", pgresult_m_PQresultStatus, 0, 0, 0 },
- { "resultErrorMessage", pgresult_m_PQresultErrorMessage, 0, 0, 0 },
- { "resultErrorField", pgresult_m_PQresultErrorField, 1, 0, 0 },
- { "nTuples", pgresult_m_PQntuples, 0, 0, 0 },
- { "nFields", pgresult_m_PQnfields, 0, 0, 0 },
- { "fName", pgresult_m_PQfname, 1, 0, 0 },
- { "fNumber", pgresult_m_PQfnumber, 1, 0, 0 },
- { "fTable", pgresult_m_PQftable, 1, 0, 0 },
- { "fTableCol", pgresult_m_PQftablecol, 1, 0, 0 },
- { "fFormat", pgresult_m_PQfformat, 1, 0, 0 },
- { "fType", pgresult_m_PQftype, 1, 0, 0 },
- { "fMod", pgresult_m_PQfmod, 1, 0, 0 },
- { "fSize", pgresult_m_PQfsize, 1, 0, 0 },
- { "binaryTuples", pgresult_m_PQbinaryTuples, 1, 0, 0 },
- { "getValue", pgresult_m_PQgetvalue, 2, 0, 0 },
- { "getIsNull", pgresult_m_PQgetisnull, 2, 0, 0 },
- { "getLength", pgresult_m_PQgetlength, 2, 0, 0 },
- { "cmdStatus", pgresult_m_PQcmdStatus, 0, 0, 0 },
- { "cmdTuples", pgresult_m_PQcmdTuples, 0, 0, 0 },
- { "oidValue", pgresult_m_PQoidValue, 0, 0, 0 },
- { NULL, NULL, 0, 0, 0 }
-};
-
-
-/* PGcancel class */
-static JSClass pgcancel_class = {
- "PGCancel", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub,
- JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,
- JS_ResolveStub, JS_ConvertStub, pgcancel_finalize
-};
-
-/* Provided methods/functions */
-static JSFunctionSpec pgcancel_methods[] = {
- { "freeCancel", pgcancel_m_PQfreeCancel, 0, 0, 0 },
- { "cancel", pgcancel_m_PQcancel, 1, 0, 0 },
- { NULL, NULL, 0, 0, 0 }
-};
-
-
-
-static int
-buffer_grow(size_t required)
-{
- size_t new;
- void *ptr;
-
- if (required <= buffer_size)
- return 0;
-
- for (new = buffer_size; new < required; new *= 2) ;
-
- if ((ptr = realloc(buffer, new)) == NULL)
- return -1;
-
- buffer = ptr;
- buffer_size = new;
-
- return 0;
-}
-
-static int
-param_grow(int required)
-{
- int new;
- void *types, *values, *lengths, *formats;
-
- if (required <= param_entries)
- return 0;
-
- for (new = param_entries; new < required; new *= 2) ;
-
- if ((types = realloc(param_types, sizeof(Oid) * new)) == NULL ||
- (values = realloc(param_values, sizeof(char *) * new)) == NULL ||
- (lengths = realloc(param_lengths, sizeof(int) * new)) == NULL ||
- (formats = realloc(param_formats, sizeof(int) * new)) == NULL)
- return -1;
-
- param_types = types;
- param_values = values;
- param_lengths = lengths;
- param_formats = formats;
- param_entries = new;
-
- return 0;
-}
-
-
-/*
- * PG object control
- */
-
-JSObject *
-js_InitPGClass(JSContext *cx, JSObject *obj)
-{
- JSObject *proto, *ctor;
- struct property_spec *sp;
-
- if ((proto = JS_InitClass(cx, obj, NULL, &pg_class, pg_constructor, 0,
- NULL, NULL, NULL, pg_smethods)) == NULL) {
- (void) fprintf(stderr, "Error initializing PG class\n");
- goto err;
- }
-
- /* Create static properties */
- if ((ctor = JS_GetConstructor(cx, proto)) == NULL) {
- (void) fprintf(stderr, "PG: JS_GetConstructor == NULL\n");
- goto err;
- }
- for (sp = pg_sprops; sp->name != NULL; sp++) {
- if (!JS_DefineProperty(cx, ctor, sp->name,
- INT_TO_JSVAL(sp->value), NULL, NULL,
- JSPROP_READONLY | JSPROP_PERMANENT)) {
- (void) fprintf(stderr,
- "PG: Error defining property %s\n", sp->name);
- goto err;
- }
- }
-
- /* Initialize PGconn class since we'll need to instanciate it from C */
- if (js_InitPGconnClass(cx, obj) == NULL) {
- (void) fprintf(stderr, "PG: InitPGconnClass()\n");
- goto err;
- }
- /* Same for PGresult class */
- if (js_InitPGresultClass(cx, obj) == NULL) {
- (void) fprintf(stderr, "PG: InitPGresultClass()\n");
- goto err;
- }
- /* And PGcancel class */
- if (js_InitPGcancelClass(cx, obj) == NULL) {
- (void) fprintf(stderr, "PG: InitPGcancelClass()\n");
- goto err;
- }
-
- /*
- * Note that the following buffers, although allowing optimizations,
- * cause the functions using them to not be reentrant. For reentrancy
- * similar buffers could be attached to the object instances instead.
- * This would of course however mean a larger memory footprint.
- * If doing this, we would also need a custom structure for the
- * private data instead of simply wrapping around the native pointers.
- */
-
- /* Allocate an initial general purpose buffer */
- if ((buffer = malloc(16384)) == NULL) {
- (void) fprintf(stderr, "PG: malloc()\n");
- goto err;
- }
- buffer_size = 16384;
-
- /* As well as buffers for the *Params() parameter arrays */
- if ((param_types = malloc(sizeof(Oid) * 16)) == NULL ||
- (param_values = malloc(sizeof(char *) * 16)) == NULL ||
- (param_lengths = malloc(sizeof(int) * 16)) == NULL ||
- (param_formats = malloc(sizeof(int) * 16)) == NULL) {
- (void) fprintf(stderr, "PG: malloc()\n");
- goto err;
- }
- param_entries = 16;
-
- return proto;
-
-err:
- if (buffer != NULL)
- free(buffer);
- if (param_types != NULL)
- free(param_types);
- if (param_values != NULL)
- free(param_values);
- if (param_lengths != NULL)
- free(param_lengths);
- if (param_formats != NULL)
- free(param_formats);
-
- return NULL;
-}
-
-/* Non instanciable */
-static JSBool
-pg_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
-
- QUEUE_EXCEPTION("PG class uninstanciable");
-
- return JS_FALSE;
-}
-
-
-/*
- * PG object static methods
- */
-
-/*
- * Returns an array of objects which each contain the various parameters
- * returned by PQconndefaults().
- * XXX Could be more useful if it returned an object of objects using the
- * keyword as property in the first object level, perhaps.
- */
-static JSBool
-pg_sm_PQconndefaults(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PQconninfoOption *in = NULL, *p;
- JSObject *array = NULL;
- JSString *str;
- int i;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- return JS_FALSE;
- }
-
- if ((in = PQconndefaults()) == NULL) {
- QUEUE_EXCEPTION("PQconndefaults() == NULL");
- goto err;
- }
- if ((array = JS_NewArrayObject(cx, 0, NULL)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(array);
-
-#define DEFINE_STRING_PROP(n, s) do { \
- if ((str = JS_NewStringCopyZ(cx, (char *)(s))) == NULL) { \
- QUEUE_EXCEPTION("Out of memory!"); \
- goto err; \
- } \
- if (!JS_DefineProperty(cx, o, (n), STRING_TO_JSVAL(str), NULL, \
- NULL, JSPROP_ENUMERATE)) { \
- QUEUE_EXCEPTION("Internal error!"); \
- goto err; \
- } \
-} while (/* CONSTCOND */0)
-
-#define DEFINE_STRING_PROP2(n, s, l) do { \
- if ((str = JS_NewStringCopyN(cx, (char *)(s), (l))) == NULL) { \
- QUEUE_EXCEPTION("Out of memory!"); \
- goto err; \
- } \
- if (!JS_DefineProperty(cx, o, (n), STRING_TO_JSVAL(str), NULL, \
- NULL, JSPROP_ENUMERATE)) { \
- QUEUE_EXCEPTION("Internal error!"); \
- goto err; \
- } \
-} while (/* CONSTCOND */0)
-
-#define DEFINE_INT_PROP(n, i) do { \
- if (!JS_DefineProperty(cx, array, (n), INT_TO_JSVAL((int)(i)), \
- NULL, NULL, JSPROP_ENUMERATE)) { \
- QUEUE_EXCEPTION("Internal error!"); \
- goto err; \
- } \
-} while (/* CONSTCOND */0)
-
-
- /* Polulate array with objects */
- for (i = 0, p = in; p->keyword != NULL; p++, i++) {
- JSObject *o;
-
- if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Out of memory");
- goto err;
- }
- /* Root immediately by inserting object into array */
- if (!JS_DefineElement(cx, array, i, OBJECT_TO_JSVAL(o),
- NULL, NULL, JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- /* Populate object with properties */
- DEFINE_STRING_PROP("keyword", p->keyword);
- DEFINE_STRING_PROP("envvar", p->envvar);
- DEFINE_STRING_PROP("compiled", p->compiled);
- DEFINE_STRING_PROP("val", p->val);
- DEFINE_STRING_PROP("label", p->label);
- DEFINE_STRING_PROP2("dispchar", p->dispchar, 1);
- DEFINE_INT_PROP("dispsize", p->dispsize);
- }
-
-#undef DEFINE_STRING_PROP
-#undef DEFINE_STRING_PROP2
-#undef DEFINE_INT_PROP
-
- PQconninfoFree(in);
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
- if (in != NULL)
- PQconninfoFree(in);
-
- return JS_FALSE;
-}
-
-static JSBool
-pg_sm_PQconnectdb(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSObject *o;
- PGconn *pgc = NULL;
- char *str;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
- str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
-
- if ((o = JS_NewObject(cx, &pgconn_class, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if ((pgc = PQconnectdb(str)) == NULL) {
- QUEUE_EXCEPTION("PQconnectdb");
- goto err;
- }
- if (!JS_SetPrivate(cx, o, pgc)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- if (pgc != NULL)
- PQfinish(pgc);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pg_sm_PQconnectStart(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSObject *o;
- PGconn *pgc = NULL;
- char *str;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
- str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
-
- if ((o = JS_NewObject(cx, &pgconn_class, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if ((pgc = PQconnectStart(str)) == NULL) {
- QUEUE_EXCEPTION("PQconnectStart");
- goto err;
- }
- if (!JS_SetPrivate(cx, o, pgc)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- if (pgc != NULL)
- PQfinish(pgc);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pg_sm_PQresStatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- char *res;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- res = PQresStatus(JSVAL_TO_INT(argv[0]));
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/*
- * Note: Semantics are different from C PQunescapeBytea() in that it is
- * supplied a single String and that it returns a resulting String, or null on
- * error. Much easier to work with within ECMAScript this way.
- */
-static JSBool
-pg_sm_PQunescapeBytea(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- JSString *str;
- char *from;
- size_t reslen;
- unsigned char *res = NULL;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- from = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
-
- if ((res = PQunescapeBytea(from, &reslen)) == NULL) {
- QUEUE_EXCEPTION("PQescapeByteaConn()");
- goto err;
- }
-
- if ((str = JS_NewStringCopyN(cx, res, reslen)) == NULL) {
- QUEUE_EXCEPTION("Out of memory!");
- goto err;
- }
-
- PQfreemem(res);
- *rval = STRING_TO_JSVAL(str);
-
- return JS_TRUE;
-
-err:
- if (res != NULL)
- PQfreemem(res);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-
-/*
- * PGconn object control
- */
-
-static JSObject *
-js_InitPGconnClass(JSContext *cx, JSObject *obj)
-{
-
- return (JS_InitClass(cx, obj, NULL, &pgconn_class, pgconn_constructor,
- 0, NULL, pgconn_methods, NULL, NULL));
-}
-
-/* Non instanciable */
-static JSBool
-pgconn_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
-
- QUEUE_EXCEPTION("PGconn class not user-instanciable");
-
- return JS_FALSE;
-}
-
-static void
-pgconn_finalize(JSContext *cx, JSObject *obj)
-{
- PGconn *pgc;
-
- if ((pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL))
- != NULL) {
- PQfinish(pgc);
- (void) JS_SetPrivate(cx, obj, NULL);
- }
-}
-
-
-/*
- * PGconn object methods
- */
-
-static JSBool
-pgconn_m_PQfinish(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- if ((pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL))
- != NULL) {
- PQfinish(pgc);
- (void) JS_SetPrivate(cx, obj, NULL);
- }
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQconnectPoll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- PostgresPollingStatusType s;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- s = PQconnectPoll(pgc);
- *rval = INT_TO_JSVAL((int)s);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQreset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- PQreset(pgc);
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQresetStart(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQconnectPoll(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQresetPoll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- PostgresPollingStatusType s;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- s = PQresetPoll(pgc);
- *rval = INT_TO_JSVAL((int)s);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQdb(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = PQdb(pgc);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQuser(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = PQuser(pgc);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQpass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = PQpass(pgc);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQhost(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = PQhost(pgc);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = PQport(pgc);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQtty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = PQtty(pgc);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQoptions(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = PQoptions(pgc);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQstatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- ConnStatusType s = CONNECTION_BAD;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- s = PQstatus(pgc);
- *rval = INT_TO_JSVAL((int)s);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQtransactionStatus(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGconn *pgc;
- PGTransactionStatusType s;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- s = PQtransactionStatus(pgc);
- *rval = INT_TO_JSVAL((int)s);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQparameterStatus(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGconn *pgc;
- char *param;
- const char *str;
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- return JS_FALSE;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- return JS_FALSE;
- }
- param = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if ((str = PQparameterStatus(pgc, param)) != NULL)
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQprotocolVersion(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQprotocolVersion(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQserverVersion(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQserverVersion(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQerrorMessage(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = PQerrorMessage(pgc);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQsocket(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQsocket(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQbackendPID(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQbackendPID(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQexec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSObject *o;
- PGconn *pgc;
- PGresult *pgr = NULL;
- char *str;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
-
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
- str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if ((pgr = PQexec(pgc, str)) == NULL) {
- QUEUE_EXCEPTION("PQexec()");
- goto err;
- }
- if (!JS_SetPrivate(cx, o, pgr)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- if (pgr != NULL)
- PQclear(pgr);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQsendQuery(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- char *str;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
-
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
- str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQsendQuery(pgc, str));
-
- return JS_TRUE;
-
-err:
- *rval = INT_TO_JSVAL(0);
-
- return JS_FALSE;
-}
-
-/*
- * A fairly hairy function.
- */
-static JSBool
-pgconn_m_PQexecParams2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval, int async)
-{
- int nargs, i;
- JSObject *arrays[4], *o;
- JSIdArray *a = NULL;
- Oid *types = NULL;
- char **values = NULL;
- int *lengths = NULL;
- int *formats = NULL;
- char str[256];
- PGconn *pgc;
- PGresult *pgr = NULL;
-
- if (argc != 7) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not a String");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[1])) {
- QUEUE_EXCEPTION("Argument 2 not an int");
- goto err;
- }
- for (i = 2; i < 6; i++) {
- if (JSVAL_IS_NULL(argv[i])) {
- arrays[i - 2] = NULL;
- continue;
- }
- if (!JSVAL_IS_OBJECT(argv[i]) ||
- !JS_IsArrayObject(cx,
- (arrays[i - 2] = JSVAL_TO_OBJECT(argv[i])))) {
- (void) snprintf(str, 255, "Argument %d not an Array",
- i + 1);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- }
- if (!JSVAL_IS_INT(argv[6])) {
- QUEUE_EXCEPTION("Argument 7 not an int");
- goto err;
- }
-
- /* Array arguments processing */
- nargs = JSVAL_TO_INT(argv[1]);
- if (nargs < 0) {
- QUEUE_EXCEPTION("Argument 2 negative");
- goto err;
- }
-
- if (arrays[0] != NULL)
- types = param_types;
- if (arrays[1] != NULL)
- values = param_values;
- if (arrays[2] != NULL)
- lengths = param_lengths;
- if (arrays[3] != NULL)
- formats = param_formats;
-
- for (i = 0; i < 4; i++) {
- jsint len;
-
- if (arrays[i] == NULL)
- continue;
-
- if (!JS_GetArrayLength(cx, arrays[i], &len) || len != nargs) {
- (void) snprintf(str, 255,
- "Argument %d Array not holding %d elements",
- i + 2, nargs);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- }
-
- if (param_grow(nargs) == -1) {
- QUEUE_EXCEPTION("Out of memory!");
- goto err;
- }
-
- /* param_types */
- if (types != NULL) {
- jsval id, val;
- int i2;
- jsdouble v;
-
- o = arrays[0];
- if ((a = JS_Enumerate(cx, o)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- for (i2 = i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) &&
- !JSVAL_IS_VOID(val)) {
- if (!JSVAL_IS_NUMBER(val)) {
- (void) snprintf(str, 255,
- "Argument 3 Array's element %d "
- "not a number", i2);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- if (!JS_ValueToNumber(cx, val, &v)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- types[i2++] = (Oid)v;
- }
- }
- JS_DestroyIdArray(cx, a);
- a = NULL;
- }
-
- /* param_values */
- if (values != NULL) {
- jsval id, val;
- int i2;
-
- o = arrays[1];
- if ((a = JS_Enumerate(cx, o)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- for (i2 = i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) &&
- !JSVAL_IS_VOID(val)) {
- if (JSVAL_IS_NULL(val)) {
- values[i2++] = NULL;
- continue;
- }
- if (!JSVAL_IS_STRING(val)) {
- (void) snprintf(str, 255,
- "Argument 4 Array's element %d "
- "not a String", i2);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- values[i2++] = JS_GetStringBytes(
- JSVAL_TO_STRING(val));
- }
- }
- JS_DestroyIdArray(cx, a);
- a = NULL;
- }
-
- /* param_lengths */
- if (lengths != NULL) {
- jsval id, val;
- int i2;
-
- o = arrays[2];
- if ((a = JS_Enumerate(cx, o)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- for (i2 = i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) &&
- !JSVAL_IS_VOID(val)) {
- if (!JSVAL_IS_INT(val)) {
- (void) snprintf(str, 255,
- "Argument 5 Array's element %d "
- "not an int", i2);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- lengths[i2++] = JSVAL_TO_INT(val);
- }
- }
- JS_DestroyIdArray(cx, a);
- a = NULL;
- }
-
- /* param_formats */
- if (formats != NULL) {
- jsval id, val;
- int i2;
-
- o = arrays[3];
- if ((a = JS_Enumerate(cx, o)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- for (i2 = i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) &&
- !JSVAL_IS_VOID(val)) {
- if (!JSVAL_IS_INT(val)) {
- (void) snprintf(str, 255,
- "Argument 6 Array's element %d "
- "not an int", i2);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- formats[i2++] = JSVAL_TO_INT(val);
- }
- }
- JS_DestroyIdArray(cx, a);
- a = NULL;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if (!async) {
-
- if ((pgr = PQexecParams(pgc,
- JS_GetStringBytes(JSVAL_TO_STRING(argv[0])),
- nargs, types, (const char * const *)values, lengths,
- formats, JSVAL_TO_INT(argv[6]))) == NULL) {
- QUEUE_EXCEPTION("PQexecParams()");
- goto err;
- }
-
- if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL))
- == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if (!JS_SetPrivate(cx, o, pgr)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- } else {
-
- *rval = INT_TO_JSVAL(PQsendQueryParams(pgc,
- JS_GetStringBytes(JSVAL_TO_STRING(argv[0])),
- nargs, types, (const char * const *)values, lengths,
- formats, JSVAL_TO_INT(argv[6])));
-
- }
-
- return JS_TRUE;
-
-err:
- if (pgr != NULL)
- PQclear(pgr);
- if (a != NULL)
- JS_DestroyIdArray(cx, a);
-
- if (!async)
- *rval = OBJECT_TO_JSVAL(NULL);
- else
- *rval = INT_TO_JSVAL(0);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQexecParams(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
-
- return pgconn_m_PQexecParams2(cx, obj, argc, argv, rval, 0);
-}
-
-static JSBool
-pgconn_m_PQsendQueryParams(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
-
- return pgconn_m_PQexecParams2(cx, obj, argc, argv, rval, 1);
-}
-
-static JSBool
-pgconn_m_PQprepare2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval, int async)
-{
- int nargs, i;
- JSObject *array, *o;
- JSIdArray *a = NULL;
- Oid *types = NULL;
- char str[256];
- PGconn *pgc;
- PGresult *pgr = NULL;
-
- if (argc != 4) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not a String");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[1])) {
- QUEUE_EXCEPTION("Argument 2 not a String");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[2])) {
- QUEUE_EXCEPTION("Argument 3 not an int");
- goto err;
- }
- if (JSVAL_IS_NULL(argv[3]))
- array = NULL;
- else {
- if (!JSVAL_IS_OBJECT(argv[3]) ||
- !JS_IsArrayObject(cx,
- (array = JSVAL_TO_OBJECT(argv[3])))) {
- QUEUE_EXCEPTION("Argument 4 not an Array");
- goto err;
- }
- }
-
- /* Array arguments processing */
- nargs = JSVAL_TO_INT(argv[2]);
- if (nargs < 0) {
- QUEUE_EXCEPTION("Argument 3 negative");
- goto err;
- }
-
- if (array != NULL) {
- jsint len;
-
- types = param_types;
-
- if (!JS_GetArrayLength(cx, array, &len) || len != nargs) {
- (void) snprintf(str, 255,
- "Argument 4 Array not holding %d elements",
- nargs);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- }
-
- if (param_grow(nargs) == -1) {
- QUEUE_EXCEPTION("Out of memory!");
- goto err;
- }
-
- /* param_types */
- if (types != NULL) {
- jsval id, val;
- int i2;
- jsdouble v;
-
- o = array;
- if ((a = JS_Enumerate(cx, o)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- for (i2 = i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) &&
- !JSVAL_IS_VOID(val)) {
- if (!JSVAL_IS_NUMBER(val)) {
- (void) snprintf(str, 255,
- "Argument 4 Array's element %d "
- "not a number", i2);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- if (!JS_ValueToNumber(cx, val, &v)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- types[i2++] = (Oid)v;
- }
- }
- JS_DestroyIdArray(cx, a);
- a = NULL;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if (!async) {
-
- if ((pgr = PQprepare(pgc,
- JS_GetStringBytes(JSVAL_TO_STRING(argv[0])),
- JS_GetStringBytes(JSVAL_TO_STRING(argv[1])),
- nargs, types)) == NULL) {
- QUEUE_EXCEPTION("PQprepare()");
- goto err;
- }
-
- if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL))
- == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if (!JS_SetPrivate(cx, o, pgr)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- } else {
-
- *rval = INT_TO_JSVAL(PQsendPrepare(pgc,
- JS_GetStringBytes(JSVAL_TO_STRING(argv[0])),
- JS_GetStringBytes(JSVAL_TO_STRING(argv[1])),
- nargs, types));
-
- }
-
- return JS_TRUE;
-
-err:
- if (pgr != NULL)
- PQclear(pgr);
-
- if (!async)
- *rval = OBJECT_TO_JSVAL(NULL);
- else
- *rval = INT_TO_JSVAL(0);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQprepare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
-
- return pgconn_m_PQprepare2(cx, obj, argc, argv, rval, 0);
-}
-
-static JSBool
-pgconn_m_PQsendPrepare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
-
- return pgconn_m_PQprepare2(cx, obj, argc, argv, rval, 1);
-}
-
-/*
- * Also rather hairy
- */
-static JSBool
-pgconn_m_PQexecPrepared2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval, int async)
-{
- int nargs, i;
- JSObject *arrays[3], *o;
- JSIdArray *a = NULL;
- char **values = NULL;
- int *lengths = NULL;
- int *formats = NULL;
- char str[256];
- PGconn *pgc;
- PGresult *pgr = NULL;
-
- if (argc != 6) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not a String");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[1])) {
- QUEUE_EXCEPTION("Argument 2 not an int");
- goto err;
- }
- for (i = 2; i < 5; i++) {
- if (JSVAL_IS_NULL(argv[i])) {
- arrays[i - 2] = NULL;
- continue;
- }
- if (!JSVAL_IS_OBJECT(argv[i]) ||
- !JS_IsArrayObject(cx,
- (arrays[i - 2] = JSVAL_TO_OBJECT(argv[i])))) {
- (void) snprintf(str, 255, "Argument %d not an Array",
- i + 1);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- }
- if (!JSVAL_IS_INT(argv[5])) {
- QUEUE_EXCEPTION("Argument 7 not an int");
- goto err;
- }
-
- /* Array arguments processing */
- nargs = JSVAL_TO_INT(argv[1]);
- if (nargs < 0) {
- QUEUE_EXCEPTION("Argument 2 negative");
- goto err;
- }
-
- if (arrays[0] != NULL)
- values = param_values;
- if (arrays[1] != NULL)
- lengths = param_lengths;
- if (arrays[2] != NULL)
- formats = param_formats;
-
- for (i = 0; i < 3; i++) {
- jsint len;
-
- if (arrays[i] == NULL)
- continue;
-
- if (!JS_GetArrayLength(cx, arrays[i], &len) || len != nargs) {
- (void) snprintf(str, 255,
- "Argument %d Array not holding %d elements",
- i + 2, nargs);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- }
-
- if (param_grow(nargs) == -1) {
- QUEUE_EXCEPTION("Out of memory!");
- goto err;
- }
-
- /* param_values */
- if (values != NULL) {
- jsval id, val;
- int i2;
-
- o = arrays[0];
- if ((a = JS_Enumerate(cx, o)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- for (i2 = i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) &&
- !JSVAL_IS_VOID(val)) {
- if (JSVAL_IS_NULL(val)) {
- values[i2++] = NULL;
- continue;
- }
- if (!JSVAL_IS_STRING(val)) {
- (void) snprintf(str, 255,
- "Argument 3 Array's element %d "
- "not a String", i2);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- values[i2++] = JS_GetStringBytes(
- JSVAL_TO_STRING(val));
- }
- }
- JS_DestroyIdArray(cx, a);
- a = NULL;
- }
-
- /* param_lengths */
- if (lengths != NULL) {
- jsval id, val;
- int i2;
-
- o = arrays[1];
- if ((a = JS_Enumerate(cx, o)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- for (i2 = i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) &&
- !JSVAL_IS_VOID(val)) {
- if (!JSVAL_IS_INT(val)) {
- (void) snprintf(str, 255,
- "Argument 4 Array's element %d "
- "not an int", i2);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- lengths[i2++] = JSVAL_TO_INT(val);
- }
- }
- JS_DestroyIdArray(cx, a);
- a = NULL;
- }
-
- /* param_formats */
- if (formats != NULL) {
- jsval id, val;
- int i2;
-
- o = arrays[2];
- if ((a = JS_Enumerate(cx, o)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- for (i2 = i = 0; i < a->length; i++) {
- JS_IdToValue(cx, a->vector[i], &id);
- if (JS_LookupElement(cx, o, JSVAL_TO_INT(id), &val) &&
- !JSVAL_IS_VOID(val)) {
- if (!JSVAL_IS_INT(val)) {
- (void) snprintf(str, 255,
- "Argument 5 Array's element %d "
- "not an int", i2);
- QUEUE_EXCEPTION(str);
- goto err;
- }
- formats[i2++] = JSVAL_TO_INT(val);
- }
- }
- JS_DestroyIdArray(cx, a);
- a = NULL;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if (!async) {
-
- if ((pgr = PQexecPrepared(pgc,
- JS_GetStringBytes(JSVAL_TO_STRING(argv[0])),
- nargs, (const char * const *)values, lengths, formats,
- JSVAL_TO_INT(argv[5]))) == NULL) {
- QUEUE_EXCEPTION("PQexecPrepared()");
- goto err;
- }
-
- if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL))
- == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if (!JS_SetPrivate(cx, o, pgr)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- } else {
-
- *rval = INT_TO_JSVAL(PQsendQueryPrepared(pgc,
- JS_GetStringBytes(JSVAL_TO_STRING(argv[0])),
- nargs, (const char * const *)values, lengths, formats,
- JSVAL_TO_INT(argv[5])));
-
- }
-
- return JS_TRUE;
-
-err:
- if (pgr != NULL)
- PQclear(pgr);
- if (a != NULL)
- JS_DestroyIdArray(cx, a);
-
- if (!async)
- *rval = OBJECT_TO_JSVAL(NULL);
- else
- *rval = INT_TO_JSVAL(0);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQexecPrepared(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
-
- return pgconn_m_PQexecPrepared2(cx, obj, argc, argv, rval, 0);
-}
-
-static JSBool
-pgconn_m_PQsendQueryPrepared(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
-
- return pgconn_m_PQexecPrepared2(cx, obj, argc, argv, rval, 1);
-}
-
-static JSBool
-pgconn_m_PQmakeEmptyPGresult(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- JSObject *o;
- PGconn *pgc;
- PGresult *pgr = NULL;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if ((pgr = PQmakeEmptyPGresult(pgc, JSVAL_TO_INT(argv[0]))) == NULL) {
- QUEUE_EXCEPTION("PQmakeEmptyPGresult()");
- goto err;
- }
-
- if (!JS_SetPrivate(cx, o, pgr)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- if (pgr != NULL)
- PQclear(pgr);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/*
- * Note: Semantics are different from C PQescapeStringConn() in that it is
- * supplied a single String and that it returns a resulting String, or null on
- * error. Much easier to work with within ECMAScript this way.
- */
-static JSBool
-pgconn_m_PQescapeStringConn(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGconn *pgc;
- JSString *str;
- char *from;
- size_t len;
- int ret;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = JSVAL_TO_STRING(argv[0]);
- from = JS_GetStringBytes(str);
- len = JS_GetStringLength(str);
-
- if (buffer_grow((len * 2) + 2) == -1) {
- QUEUE_EXCEPTION("Out of memory!");
- goto err;
- }
-
- len = PQescapeStringConn(pgc, buffer, from, len, &ret);
- if (ret != 0) {
- QUEUE_EXCEPTION("PQescapeStringConn()");
- goto err;
- }
-
- if ((str = JS_NewStringCopyN(cx, buffer, len)) == NULL) {
- QUEUE_EXCEPTION("Out of memory!");
- goto err;
- }
- *rval = STRING_TO_JSVAL(str);
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/* PQescapeString() deprecated in favor of PQescapeStringConn() */
-
-/*
- * Note: Semantics are different from C PQescapeByteaConn() in that it is
- * supplied a single String and that it returns a resulting String, or null on
- * error. Much easier to work with within ECMAScript this way.
- */
-static JSBool
-pgconn_m_PQescapeByteaConn(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGconn *pgc;
- JSString *str;
- char *from;
- size_t fromlen, reslen;
- unsigned char *res = NULL;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = JSVAL_TO_STRING(argv[0]);
- from = JS_GetStringBytes(str);
- fromlen = JS_GetStringLength(str);
-
- if ((res = PQescapeByteaConn(pgc, from, fromlen, &reslen)) == NULL) {
- QUEUE_EXCEPTION("PQescapeByteaConn()");
- goto err;
- }
-
- if ((str = JS_NewStringCopyN(cx, res, reslen)) == NULL) {
- QUEUE_EXCEPTION("Out of memory!");
- goto err;
- }
-
- PQfreemem(res);
- *rval = STRING_TO_JSVAL(str);
-
- return JS_TRUE;
-
-err:
- if (res != NULL)
- PQfreemem(res);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQgetCancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSObject *o;
- PGconn *pgc;
- PGcancel *pgcn = NULL;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if ((o = JS_NewObject(cx, &pgcancel_class, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if ((pgcn = PQgetCancel(pgc)) == NULL) {
- QUEUE_EXCEPTION("PQgetCancel()");
- goto err;
- }
- if (!JS_SetPrivate(cx, o, pgcn)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- if (pgcn != NULL)
- PQfreeCancel(pgcn);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/*
- * Note: Unlike C native PQnotifies(), returns null or a normal object with
- * the three properties set, rather than specifically a PQnotify object.
- */
-static JSBool
-pgconn_m_PQnotifies(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSObject *o;
- PGconn *pgc;
- PGnotify *pgn = NULL;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if ((pgn = PQnotifies(pgc)) == NULL) {
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_TRUE;
- }
-
- if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if (!JS_DefineProperty(cx, o, "relname", STRING_TO_JSVAL(
- JS_NewStringCopyZ(cx, pgn->relname)), NULL, NULL,
- JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- if (!JS_DefineProperty(cx, o, "be_pid", INT_TO_JSVAL(pgn->be_pid),
- NULL, NULL, JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- if (!JS_DefineProperty(cx, o, "extra", STRING_TO_JSVAL(
- JS_NewStringCopyZ(cx, pgn->extra)), NULL, NULL,
- JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- PQfreemem(pgn);
-
- return JS_TRUE;
-
-err:
- if (pgn != NULL)
- PQfreemem(pgn);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQgetResult(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSObject *o;
- PGconn *pgc;
- PGresult *pgr = NULL;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- if ((pgr = PQgetResult(pgc)) == NULL) {
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_TRUE;
- }
-
- if ((o = JS_NewObject(cx, &pgresult_class, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if (!JS_SetPrivate(cx, o, pgr)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- if (pgr != NULL)
- PQclear(pgr);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQconsumeInput(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQconsumeInput(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQisBusy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQisBusy(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQsetnonblocking(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not an int");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQsetnonblocking(pgc, JSVAL_TO_INT(argv[0])));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQisnonblocking(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQisnonblocking(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQflush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQflush(pgc));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQsetErrorVerbosity(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not an int");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQsetErrorVerbosity(pgc, JSVAL_TO_INT(argv[0])));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQtrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = OBJECT_TO_JSVAL(NULL);
- PQtrace(pgc, stderr);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQuntrace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = OBJECT_TO_JSVAL(NULL);
- PQuntrace(pgc);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgconn_m_PQputCopyData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
- JSString *str;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- str = JSVAL_TO_STRING(argv[0]);
- *rval = INT_TO_JSVAL(PQputCopyData(pgc, JS_GetStringBytes(str),
- JS_GetStringLength(str)));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgconn_m_PQputCopyEnd(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGconn *pgc;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0]) && !JSVAL_IS_NULL(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String or null");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- *rval = INT_TO_JSVAL(PQputCopyEnd(pgc, (JSVAL_IS_NULL(argv[0]) ? NULL :
- JS_GetStringBytes(JSVAL_TO_STRING(argv[0])))));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/*
- * Note: unlike native PQgetCopyData(), which returns an int but is supplied a
- * pointer to a pointer to be set, this implementation returns an object which
- * holds two elements: result (the integer) and data (null or String), to make
- * it easier to use with ECMAScript.
- */
-static JSBool
-pgconn_m_PQgetCopyData(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSObject *o;
- PGconn *pgc;
- char *data = NULL;
- int res;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not an int");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgconn_class, NULL);
- assert(pgc != NULL);
-
- res = PQgetCopyData(pgc, &data, JSVAL_TO_INT(argv[0]));
-
- if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- /* Root immediately */
- *rval = OBJECT_TO_JSVAL(o);
-
- if (!JS_DefineProperty(cx, o, "result", INT_TO_JSVAL(res),
- NULL, NULL, JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- if (data != NULL && res > 0) {
- if (!JS_DefineProperty(cx, o, "data", STRING_TO_JSVAL(
- JS_NewStringCopyN(cx, data, res)), NULL, NULL,
- JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- PQfreemem(data);
- } else {
- if (!JS_DefineProperty(cx, o, "data", OBJECT_TO_JSVAL(NULL),
- NULL, NULL, JSPROP_ENUMERATE)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
- }
-
- return JS_TRUE;
-
-err:
- if (data != NULL)
- PQfreemem(data);
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-
-/*
- * PGresult object control
- */
-
-static JSObject *
-js_InitPGresultClass(JSContext *cx, JSObject *obj)
-{
-
- return (JS_InitClass(cx, obj, NULL, &pgresult_class,
- pgresult_constructor, 0, NULL, pgresult_methods, NULL, NULL));
-}
-
-/* Non instanciable */
-static JSBool
-pgresult_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
-
- QUEUE_EXCEPTION("PGresult class not user-instanciable");
-
- return JS_FALSE;
-}
-
-static void
-pgresult_finalize(JSContext *cx, JSObject *obj)
-{
- PGresult *pgr;
-
- if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL))
- != NULL) {
- PQclear(pgr);
- (void) JS_SetPrivate(cx, obj, NULL);
- }
-}
-
-
-/*
- * PGresult object methods
- */
-
-static JSBool
-pgresult_m_PQclear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- if ((pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL))
- != NULL) {
- PQclear(pgr);
- (void) JS_SetPrivate(cx, obj, NULL);
- }
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgresult_m_PQresultStatus(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGresult *pgr;
- ExecStatusType s;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- s = PQresultStatus(pgr);
- *rval = INT_TO_JSVAL((int)s);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgresult_m_PQresultErrorMessage(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGresult *pgr;
- char *res;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- res = PQresultErrorMessage(pgr);
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgresult_m_PQresultErrorField(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGresult *pgr;
- char *res;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- res = PQresultErrorField(pgr, JSVAL_TO_INT(argv[0]));
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgresult_m_PQntuples(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQntuples(pgr));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgresult_m_PQnfields(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQnfields(pgr));
-
- return JS_TRUE;
-}
-
-static JSBool
-pgresult_m_PQfname(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
- char *res;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- res = PQfname(pgr, JSVAL_TO_INT(argv[0]));
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, res));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgresult_m_PQfnumber(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
- char *str;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument not a String");
- goto err;
- }
- str = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQfnumber(pgr, str));
-
- return JS_TRUE;
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/*
- * Note: Oid is a typedef to unsigned int. Thus, we use a double object to
- * make sure that we do not loose any precision.
- */
-static JSBool
-pgresult_m_PQftable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
- Oid oid;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- oid = PQftable(pgr, JSVAL_TO_INT(argv[0]));
-
- if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgresult_m_PQftablecol(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQftablecol(pgr, JSVAL_TO_INT(argv[0])));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/*
- * Note: we possibly could return a boolean value instead of an integer
- * but the documentation says that other values are reserved for possible
- * future use. I am surprised that they do not return enumerated values.
- */
-static JSBool
-pgresult_m_PQfformat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQfformat(pgr, JSVAL_TO_INT(argv[0])));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/*
- * Note: Oid is a typedef to unsigned int. Thus, we use a double object to
- * make sure that we do not loose any precision.
- */
-static JSBool
-pgresult_m_PQftype(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
- Oid oid;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- oid = PQftype(pgr, JSVAL_TO_INT(argv[0]));
-
- if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgresult_m_PQfmod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQfmod(pgr, JSVAL_TO_INT(argv[0])));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgresult_m_PQfsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQfsize(pgr, JSVAL_TO_INT(argv[0])));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgresult_m_PQbinaryTuples(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQbinaryTuples(pgr));
-
- return JS_TRUE;
-}
-
-/*
- * Note: The semantics of PGresult.PQgetvalue() is different from the actual
- * libpq C function PQgetvalue() in that we return null on NULL fields, and
- * that we always return either the binary or text data into the field as a
- * String (since ECMAScript Strings objects allow NUL characters).
- */
-static JSBool
-pgresult_m_PQgetvalue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
- JSString *str;
- char *res;
- int row, col;
-
- if (argc != 2) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not an int");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[1])) {
- QUEUE_EXCEPTION("Argument 2 not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- row = JSVAL_TO_INT(argv[0]);
- col = JSVAL_TO_INT(argv[1]);
-
- if (PQgetisnull(pgr, row, col)) {
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_TRUE;
- }
-
- if ((res = PQgetvalue(pgr, row, col)) == NULL) {
- QUEUE_EXCEPTION("PQgetvalue() == NULL");
- goto err;
- }
-
- if ((str = JS_NewStringCopyN(cx, res, PQgetlength(pgr, row, col)))
- == NULL) {
- QUEUE_EXCEPTION("Out of memory!");
- goto err;
- }
- *rval = STRING_TO_JSVAL(str);
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-/*
- * Note: unlike the C PQgetisnull() function, which returns 0 or 1, we return
- * true or false, since ECMAScript supports booleans.
- * Moreover, one no longer needs to call this function in JS if it is to
- * subsequently use PQgetvalue(), since ours can return null.
- * May still be useful in some situations.
- */
-static JSBool
-pgresult_m_PQgetisnull(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 2) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not an int");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[1])) {
- QUEUE_EXCEPTION("Argument 2 not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = BOOLEAN_TO_JSVAL(PQgetisnull(pgr, JSVAL_TO_INT(argv[0]),
- JSVAL_TO_INT(argv[1])));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgresult_m_PQgetlength(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 2) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not an int");
- goto err;
- }
- if (!JSVAL_IS_INT(argv[1])) {
- QUEUE_EXCEPTION("Argument 2 not an int");
- goto err;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = INT_TO_JSVAL(PQgetlength(pgr, JSVAL_TO_INT(argv[0]),
- JSVAL_TO_INT(argv[1])));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-static JSBool
-pgresult_m_PQcmdStatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, PQcmdStatus(pgr)));
-
- return JS_TRUE;
-}
-
-/*
- * We could return a double easily, but like the C API are returning a string
- */
-static JSBool
-pgresult_m_PQcmdTuples(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, PQcmdTuples(pgr)));
-
- return JS_TRUE;
-}
-
-/*
- * Note: Oid is a typedef to unsigned int. Thus, we use a double object to
- * make sure that we do not loose any precision.
- */
-static JSBool
-pgresult_m_PQoidValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGresult *pgr;
- Oid oid;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- pgr = JS_GetInstancePrivate(cx, obj, &pgresult_class, NULL);
- assert(pgr != NULL);
-
- oid = PQoidValue(pgr);
-
- if (!JS_NewDoubleValue(cx, (jsdouble)oid, rval)) {
- QUEUE_EXCEPTION("Internal error!");
- goto err;
- }
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
-
-
-
-/*
- * PGcancel object control
- */
-
-static JSObject *
-js_InitPGcancelClass(JSContext *cx, JSObject *obj)
-{
-
- return (JS_InitClass(cx, obj, NULL, &pgcancel_class,
- pgcancel_constructor, 0, NULL, pgcancel_methods, NULL, NULL));
-}
-
-/* Non instanciable */
-static JSBool
-pgcancel_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
-
- QUEUE_EXCEPTION("PGcancel class not user-instanciable");
-
- return JS_FALSE;
-}
-
-static void
-pgcancel_finalize(JSContext *cx, JSObject *obj)
-{
- PGcancel *pgc;
-
- if ((pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL))
- != NULL) {
- PQfreeCancel(pgc);
- (void) JS_SetPrivate(cx, obj, NULL);
- }
-}
-
-
-/*
- * PGcancel object methods
- */
-
-static JSBool
-pgcancel_m_PQfreeCancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGcancel *pgc;
-
- if (argc != 0) {
- QUEUE_EXCEPTION("Function allows no arguments");
- *rval = OBJECT_TO_JSVAL(NULL);
- return JS_FALSE;
- }
-
- if ((pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL))
- != NULL) {
- PQfreeCancel(pgc);
- (void) JS_SetPrivate(cx, obj, NULL);
- }
-
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_TRUE;
-}
-
-static JSBool
-pgcancel_m_PQcancel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- PGcancel *pgc;
- char *str;
- size_t len;
- JSString *s;
-
- if (argc != 1) {
- QUEUE_EXCEPTION("Wrong number of arguments");
- goto err;
- }
- if (!JSVAL_IS_STRING(argv[0])) {
- QUEUE_EXCEPTION("Argument 1 not a String");
- goto err;
- }
-
- pgc = JS_GetInstancePrivate(cx, obj, &pgcancel_class, NULL);
- assert(pgc != NULL);
-
- s = JSVAL_TO_STRING(argv[0]);
- str = JS_GetStringBytes(s);
- len = JS_GetStringLength(s);
-
- *rval = INT_TO_JSVAL(PQcancel(pgc, str, len));
-
- return JS_TRUE;
-
-err:
- *rval = OBJECT_TO_JSVAL(NULL);
-
- return JS_FALSE;
-}
+++ /dev/null
-/* $Id: js_pgsql.h,v 1.3 2006/07/11 10:25:51 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-#ifndef JSPGSQL_H
-#define JSPGSQL_H
-
-#include <jsapi.h>
-
-extern JSObject *js_InitPGClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $Id: js_signal.c,v 1.3 2005/12/12 18:34:46 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Basic UNIX signal services for ECMAScript
- *
- * XXX
- * I have to see what interface I want to export. There are several
- * possibilities we could use:
- * - Have the shell register signal handlers fore interesting events at
- * startup and allow scripts to provide a handler function, which if
- * exists upon reception of a signal, gets executed. We might then
- * need to add extra custom checks in the shell for special signals
- * which if the script doesn't end, might still end the process
- * i.e. function would be expected to cause the script to exit upon
- * reception of a SIGTERM signal...
- * - Provide a sigaction-style interface so that the script would be
- * able to define functions to execute upon reception of certain
- * signals, or null or such for the signal to be ignored.
- * This solution might allow better application customization.
- * If assuming this interface, the following would be exported:
- * - Signal class, through which static signal properties could be
- * accessed for signal numbers I.E. Signal.SIGTERM.
- * - A static method to create/set/unset/ignore a signal/handler
- * Signal.sigaction()?
- * It could be provided with the parameters:
- * Signal.sigaction(FD.SIG*, obj);
- * Where obj would contain fields sa_handler, sa_mask, sa_flags?
- * Signal.kill(pid, sig);
- * Signal.sigaltstack(...) ?
- * Signal.sigprocmask(...)
- * Signal.sigsuspend(mask)
- * And maybe provide the sigsetops(3)? We possibly could just allow an
- * array or such instead of sigset_t though if wanted.
- * I'm not sure I want to provide sigsetjmp()/siglongjmp(). JavaScript has
- * exceptions anyways. If allowing sigaltstack(), C would need to allocate
- * the stacks, so a stack object would need to be exported or such. I
- * don't think I want to support this as it's probably not needed by any of
- * the applications I'll write, I don't want to write a threading library
- * in JS.
- * - I wonder if it would be safe to invoke a JS function in a signal handler.
- * If it wasn't, I could simply queue the signal events and then call the
- * functions for the queue in normal process context.
- * If doing this, sigaction has to be called to catch any signal the
- * application wishes, and we would be rolling our own signal handling,
- * so if wanted we could potentially provide another interface than
- * sigaction to ECMAScript...
- */
-
-
-
-#include <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 2005/07/19 19:27:28 mmondor Exp $ */
-
-/*
- * Copyright (c) 2005, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-#ifndef JSSIGNAL_H
-#define JSSIGNAL_H
-
-extern JSObject *js_InitSignalClass(JSContext *, JSObject *);
-
-#endif
+++ /dev/null
-/* $Id: js-server.c,v 1.7 2006/07/12 13:47:14 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2005, Matthew Mondor
- */
-
-/*
- * TODO:
- *
- * - Verify with Brendan Eich:
- * - If reusing the context to execute several other scripts, it is
- * important that they not be able to add global properties or methods.
- * This seems to currently work using a custom api_class_property_add().
- * This however also required standard properties to be shared
- * (JS_PROP_SHARED), otherwise api_class_property_add() would be called
- * and even setting values to existing API system properties would fail in
- * user scripts. Scealing was also too strict.
- * I assumed that JS_AddNamedRoot() was required for the API class to
- * never be freed, so that it can be reused after a call to
- * js_context_reset(). Perhaps this is not necessary.
- */
-
-
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#include <assert.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <jsapi.h>
-
-#include <js_fd.h>
-#include <js_errno.h>
-#include <js_signal.h>
-#include <js_pgsql.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_fd, *class_errno, *class_signal,
- *class_pgsql;
-} js_context_t;
-
-/*
- * To hold loaded file objects (actually mmap(2)ed)
- */
-typedef struct {
- void *data;
- size_t size;
-} file_t;
-
-/*
- * Defaults for the global class
- */
-static JSClass global_class = {
- "global", 0, JS_PropertyStub, JS_PropertyStub,
- JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,
- JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
-};
-
-
-
-/*
- * PROTOTYPES
- */
-
-int main(int, char **);
-static JSBool branch_callback(JSContext *, JSScript *);
-
-static file_t *file_load(const char *);
-static void file_free(file_t *);
-
-/* Could be an exported API later on */
-static js_context_t *js_context_init(size_t, size_t);
-static void js_context_destroy(js_context_t *);
-/* XXX static void js_context_reset(js_context_t *);*/
-
-
-
-
-
-int
-main(int argc, char **argv)
-{
- file_t *file;
- js_context_t *cctx;
-
- if (argc != 2) {
- (void) fprintf(stderr, "Usage: test <scriptfile>\n");
- exit(EXIT_FAILURE);
- }
- if ((file = file_load(argv[1])) == NULL) {
- (void) fprintf(stderr, "Error loading '%s'\n", argv[1]);
- exit(EXIT_FAILURE);
- }
-
- /*
- * We always need at least one runtime per process, at least one
- * context per thread and at least a global object per context
- * (standard classes, like Date).
- */
- if ((cctx = js_context_init(GCBYTES, STACKBYTES)) == NULL) {
- file_free(file);
- (void) fprintf(stderr, "js_context_init()\n");
- exit(EXIT_FAILURE);
- }
-
- /*
- * This is a very useful and important feature, enable our callback
- * function which will get called whenever the script branches
- * backwards, returns from a function or exits. It allows us to
- * even maintain control in cases where the script loops endlessly.
- */
- (void) JS_SetBranchCallback(cctx->ctx, branch_callback);
-
- /*
- * Now enable addProperty() protection for all classes using our
- * custom api_class_property_add() function. This will prevent user
- * code from adding properties or methods to the API class for
- * instance.
- * This however requires that properties use the JSPROP_SHARED flag
- * since addProperty() method would internally get called to create
- * shadow copies for the runtime otherwise.
- */
- /*
- api_class_protect = JS_TRUE;
- */
-
- /*
- * Now execute script loaded into our file_t.
- * We simplify this process by calling JS_EvaluateScript() which
- * will first tokenize/compile the result, and then interpret/run it.
- * Moreover, it allows the script to optionally return a value
- * directly like if it was a function.
- * Alternatively, we could use JS_CompileFile() or JS_CompileScript()
- * to pre-tokenize the script, and JS_ExecuteScript() to interpret it.
- */
- {
- jsval rval, pval;
- JSString *str;
- int i;
-
- if (JS_EvaluateScript(cctx->ctx, cctx->global, file->data,
- file->size, argv[1], 1, &rval)) {
- str = JS_ValueToString(cctx->ctx, rval);
- (void) printf("Script result: %s\n",
- JS_GetStringBytes(str));
- /*
- * Attempt to call JS function "callMe" if the script
- * created it.
- */
- for (i = 0; i < 10; i++) {
- pval = INT_TO_JSVAL(i);
- if (!JS_CallFunctionName(cctx->ctx,
- cctx->global, "callMe", 1, &pval, &rval))
- break;
- }
- } else {
- /* XXX how to obtain error and stack backtrace? */
- }
- }
-
- /* Cleanup */
- file_free(file);
- js_context_destroy(cctx);
-
- exit(EXIT_SUCCESS);
-}
-
-/*
- * This function is called during the execution of the script so that we can
- * remain in control of the application. If we only allow the scripts to
- * define functions for callbacks, we can use the first instance if this event
- * to abort the script if wanted, as well. We can then set an alternative
- * callback function and execute the script provided functions at specific
- * events. Of course, it also would be possible to use setitimer(2) to have
- * a SIGALRM signal trigger a function at regular set intervals.
- */
-/* ARGSUSED */
-static JSBool
-branch_callback(JSContext *ctx, JSScript *script)
-{
- static int count = 0;
-
- if (++count > 1000) {
- count = 0;
- JS_MaybeGC(ctx);
- }
-
- /* Returning JS_FALSE here aborts the script */
- return JS_TRUE;
-}
-
-
-
-/*
- * Could be an exported API
- */
-
-static js_context_t *
-js_context_init(size_t gc_size, size_t stack_size)
-{
- js_context_t *cctx;
-
- if ((cctx = malloc(sizeof(js_context_t))) == NULL ||
- (cctx->rt = JS_NewRuntime(gc_size)) == NULL ||
- (cctx->ctx = JS_NewContext(cctx->rt, stack_size)) == NULL ||
- (cctx->global = JS_NewObject(cctx->ctx, &global_class, NULL,
- NULL)) == NULL ||
- !JS_InitStandardClasses(cctx->ctx, cctx->global) ||
- (cctx->class_fd = js_InitFDClass(cctx->ctx, cctx->global))
- == NULL ||
- (cctx->class_errno = js_InitErrnoClass(cctx->ctx, cctx->global))
- == NULL ||
- (cctx->class_signal = js_InitSignalClass(cctx->ctx, cctx->global))
- == NULL ||
- (cctx->class_pgsql = js_InitPGClass(cctx->ctx, cctx->global))
- == NULL) {
- /* An error, free any partially allocated resources */
- if (cctx != NULL)
- js_context_destroy(cctx);
-
- return NULL;
- }
-
- return cctx;
-}
-
-static void
-js_context_destroy(js_context_t *cctx)
-{
-
- assert(cctx != NULL);
-
- if (cctx->ctx != NULL)
- JS_DestroyContext(cctx->ctx);
- if (cctx->rt != NULL)
- JS_DestroyRuntime(cctx->rt);
-
- free(cctx);
-}
-
-/*
- * This function should permit to restore the context to a consistent, known
- * state before a new script can be executed using the same context instead of
- * having to destroy and recreate contexts everytime.
- */
-/* ARGSUSED */
-/* XXX
-static void
-js_context_reset(js_context_t *cctx)
-{
-
-}
-*/
-
-
-/*
- * Loads specified file and returns a file_t pointer. Note that we only
- * internally keep the memory map for read access to the file, rather than an
- * open filedescriptor.
- */
-static file_t *
-file_load(const char *filename)
-{
- int fd;
- struct stat st;
- file_t *file;
-
- assert(filename != NULL);
-
- if ((fd = open(filename, O_RDONLY)) != -1) {
- if (fstat(fd, &st) == 0) {
- if ((file = malloc(sizeof(file_t))) != NULL) {
- file->size = (size_t)st.st_size;
-
- if ((file->data = mmap(NULL, file->size,
- PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0))
- != MAP_FAILED) {
- (void) close(fd);
- return file;
- }
-
- free(file);
- }
- }
- (void) close(fd);
- }
-
- return NULL;
-}
-
-/*
- * Frees a file_t which was returned by a previous file_load() call.
- */
-static void
-file_free(file_t *file)
-{
-
- assert(file != NULL);
-
- (void) munmap(file->data, file->size);
- free(file);
-}
+++ /dev/null
-#!/bin/sh
-#
-# Configure script to help build scripts
-# by Matthew Mondor
-
-CFLAGS='-I/usr/local/spidermonkey/include -DXP_UNIX'
-LDFLAGS='-Wl,-R/usr/local/spidermonkey/lib -L/usr/local/spidermonkey/lib -ljs'
-
-DLDFLAGS='-Wl,-R/usr/local/spidermonkey/lib -L/usr/local/spidermonkey/lib -ljs_dbg'
-
-VERSION='SpiderMonkey 1.5-rc6A'
-
-usage()
-{
- echo
- echo 'Usage: spidermonkey-config [-v] [-c] [-l] [-d]'
- echo
- echo ' -v : Shows SpiderMonkey version'
- echo ' -c : Shows flags suitable to append to $CFLAGS'
- echo ' -l : Shows flags suitable to append to $LDFLAGS'
- echo ' -d : Shows debugging versions of the flags'
- echo
-}
-
-if [ -z $@ ]; then
- usage
- exit 0
-fi
-
-while getopts dclv c; do
- case $c in
- d)
- LDFLAGS="$DLDFLAGS"
- ;;
- c)
- echo $CFLAGS
- ;;
- l)
- echo $LDFLAGS
- ;;
- v)
- echo $VERSION
- ;;
- *)
- usage
- exit 0
- ;;
- esac
-done
+++ /dev/null
-# $Id: GNUmakefile,v 1.7 2006/04/23 04:40:14 mmondor Exp $
-
-MMLIB_PATH := ../../mmlib
-
-MMLIBS := $(addprefix ${MMLIB_PATH}/,mmpool.o mmstring.o mmarch.o)
-LIBS := -lc
-OBJS := main.o net.o kqueue.o sendq.o recvq.o packets.o daemon.o client.o
-CFLAGS += -Wall
-BINS := daemon
-
-all: $(BINS)
-
-%.o: %.c
- cc -c ${CFLAGS} -I. -I${MMLIB_PATH} -o $@ $<
-
-
-daemon: $(MMLIBS) $(LIBS) $(OBJS)
- cc -o $@ $(MMLIBS) $(OBJS) $(LIBS)
-
-
-clean:
- rm -f $(BINS) $(OBJS) $(MMLIBS) $(LIBS)
+++ /dev/null
-$Id: README,v 1.3 2006/04/23 04:40:14 mmondor Exp $
-
-If this all works fine, it might be the basis of a multiplayer
-networking game. However, this is primarily a test to learn kqueue
-and observe its efficiency, as well as to experiment using TCP for
-such massive multiplayer games on today's networks and internet.
-
-Many games that used TCP for LAN use a while back had to develop
-UDP based protocols to be efficient enough for dialup users or
-other internet users. We will determine if this is still necessary
-on today's average internet latencies.
-
-We probably still want to prioritize some packets over others.
-For instance, we might drop outgoing position update packets which
-cannot be sent immediately instead of queueing them, allowing less
-frequent updates to slow connections while not needing to provide
-multiple independent game heart rates.
-
-We do not expect to have to port the server part of the game, wich
-is intended to run on BSD systems supporting the kqueue(2) system
-only. We could have used an event library, however we wanted to
-avoid including non-BSD licensed components. Although there is a
-BSD licensed libevent as part of NetBSD, using kqueue directly
-allows a few fancy optimization tricks which would require a new
-portable events library to be designed to allow to achieve the full
-advantages of kqueue using an abstracted library.
-
-The client code, however, will be portable and should be able to
-run on Win32 without needing the cygwin runtime libraries. The
-SDL dependency is reasonable, and can be provided separately, so
-mingw will be used to create those executables. The client will
-however primarily be developped on NetBSD. Like SDL, OpenGL
-libraries shouldn't be a problem either.
-
-To avoid using cygwin, and because Win32 winsock does not conform
-to BSD sockets API standards, we expect to have to write a simple
-portable networking layer for use in the client.
-
-
-TODO
-
-- At this point to advance further a client needs to be written.
-- We probably don't need to align packets, we only need to make sure
- that their size is 32-bit aligned, and that their 16-bit fields are
- 16-bit aligned, their 32-bit ones 32-bit aligned. Packet type field
- can be 8-bit. If we allowed arbitrary offsets when buffering packets,
- we would need to copy each packet to an aligned buffer as we process them.
- Evaluate which is more advantageous.
+++ /dev/null
-/* $Id: client.c,v 1.9 2006/04/23 04:40:14 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"
-
-
-
-list_t clients_list;
-static list_t clients_gc_list;
-static pool_t clients_pool;
-
-
-
-void
-client_init(void)
-{
- DLIST_INIT(&clients_list);
- DLIST_INIT(&clients_gc_list);
-
- if (!pool_init(&clients_pool, "clients_pool", malloc, free, NULL,
- NULL, sizeof(client_t), MAX_CLIENTS + 1, 1, 1)) {
- syslog(LOG_NOTICE, "pool_init() - %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
-}
-
-/*
- * Enables the write polling filter for the specified client.
- */
-void
-client_enable_write_polling(void *args)
-{
- client_t *c = (client_t *)args;
-
- if (c->writepolling)
- return;
-
- kqueue_sev_alloc(1);
- kqueue_sev_add(c->fd, EVFILT_WRITE, EV_ENABLE, 0, 0, (intptr_t)c);
-
- c->writepolling = 1;
-}
-
-inline int
-client_write(client_t *c, uint8_t *buf, size_t size, int buffer)
-{
-
- if (sendq_write(&c->sendq, buf, size, client_enable_write_polling, c,
- buffer) == -1) {
- syslog(LOG_NOTICE, "sendq_write(%u) - %s", size,
- strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-/*
- * XXX We should do connection rate/concurrency checking on address here.
- */
-client_t *
-client_create(int fd, struct sockaddr *saddr)
-{
- client_t *c;
-
- if (DLIST_NODES(&clients_list) > MAX_CLIENTS) {
- syslog(LOG_NOTICE, "client_create() - MAX_CLIENTS reached");
- return NULL;
- }
-
- if ((c = (client_t *)pool_alloc(&clients_pool, FALSE)) != NULL) {
- if (sendq_init(&c->sendq, fd, SENDQ_SIZE) != -1) {
- if (recvq_init(&c->recvq, fd, RECVQ_SIZE) != -1) {
- c->fd = fd;
- (void) memcpy(&c->saddr, saddr,
- sizeof(struct sockaddr));
-
- c->object.x = random() % WORLD_X_MAX;
- c->object.y = random() % WORLD_Y_MAX;
- c->object.direction = c->object.i_direction =
- random() % 1000;
- c->object.thrust = c->object.i_thrust =
- random() % 20;
-
- c->authenticated = c->todestroy =
- c->toclose = 0;
- c->writepolling = 1;
- c->askedping = 0;
-
- DLIST_APPEND(&clients_list, &c->node);
-
- return c;
- }
- syslog(LOG_NOTICE,
- "client_create() - recvq_init() - %s",
- strerror(errno));
- sendq_destroy(&c->sendq);
- }
- syslog(LOG_NOTICE,
- "client_create() - sendq_init() - %s", strerror(errno));
- pool_free((pnode_t *)c);
- }
- syslog(LOG_NOTICE, "client_create() - pool_alloc() - %s",
- strerror(errno));
-
- return NULL;
-}
-
-void
-client_destroy_mark(client_t *c)
-{
-
- if (c->todestroy)
- return;
-
- c->todestroy = 1;
- DLIST_SWAP(&clients_gc_list, &clients_list, &c->node, FALSE);
-}
-
-void
-client_destroy_marked(void)
-{
- node_t *i, *n;
- client_t *c;
-
- for (i = DLIST_TOP(&clients_gc_list); i != NULL; i = n) {
- n = DLIST_NEXT(i);
-
- c = (client_t *)i;
- /* Closing descriptor automatically deletes its kevents */
- (void) close(c->fd);
- recvq_destroy(&c->recvq);
- sendq_destroy(&c->sendq);
- pool_free((pnode_t *)c);
- }
- DLIST_INIT(&clients_gc_list);
-}
+++ /dev/null
-/* $Id: client.h,v 1.7 2006/04/03 08:56:45 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef _CLIENT_H_
-#define _CLIENT_H_
-
-
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <mmpool.h>
-#include <mmlist.h>
-
-#include "sendq.h"
-#include "recvq.h"
-
-
-
-typedef struct object {
- node_t region;
- int32_t direction, thrust;
- int32_t i_direction, i_thrust;
- int32_t x, y;
-} object_t;
-
-typedef struct client {
- node_t node;
- object_t object;
- sendq_t sendq;
- recvq_t recvq;
- int fd;
- struct sockaddr saddr;
- int authenticated, todestroy, toclose, writepolling,
- askedping;
-} client_t;
-
-
-
-extern list_t clients_list;
-
-
-void client_init(void);
-void client_enable_write_polling(void *);
-inline int client_write(client_t *, u_int8_t *, size_t, int);
-
-client_t *client_create(int, struct sockaddr *);
-void client_destroy_mark(client_t *);
-void client_destroy_marked(void);
-
-
-
-#endif
+++ /dev/null
-/* $Id: conf.h,v 1.9 2006/04/23 04:40:14 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef _CONF_H_
-#define _CONF_H_
-
-
-
-#define SERVER_VERSION 1
-#define SERVER_STRING "daemon/mmondor\n"
-
-#define FPS 10
-#define FPS_MS (1000 / FPS)
-
-#define WORLD_X_MAX 640
-#define WORLD_Y_MAX 480
-
-#define MAX_CLIENTS 128
-#define R_EVENTS (MAX_CLIENTS * 2)
-#define S_EVENTS (MAX_CLIENTS * 2)
-
-#define SENDQ_SIZE 16384
-#define RECVQ_SIZE 1024
-
-/* In seconds */
-#define INPUT_TIMEOUT 30
-#define PING_TIMEOUT 5
-
-#define PIDFILE "/tmp/daemon.pid"
-
-
-
-#endif
+++ /dev/null
-/* $Id: daemon.c,v 1.3 2006/04/23 04:40:14 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Procedure for becoming an detached daemon, as well as to prepare process
- * signal handling. Since we'll be catching signal events through kqueue(2),
- * We simply ignore all signals for which we don't want the default behavior.
- */
-
-#include <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(void)
-{
- pid_t pid;
- int fd;
-
- /* Create new process */
- if ((pid = fork()) == -1) {
- syslog(LOG_NOTICE, "fork() - %s", strerror(errno));
- return -1;
- }
- if (pid != 0)
- exit(EXIT_SUCCESS);
-
- pidfile_write(PIDFILE);
-
- /* Create new process group and detach */
- (void) setsid();
- (void) chdir("/");
- if ((fd = open("/dev/null", O_RDWR)) != -1) {
- (void) dup2(fd, STDIN_FILENO);
- (void) dup2(fd, STDOUT_FILENO);
- (void) dup2(fd, STDERR_FILENO);
- if (fd > STDERR_FILENO)
- (void) close(fd);
- } else
- syslog(LOG_NOTICE, "daemon_init() - open(/dev/null) - %s",
- strerror(errno));
-
- if (signal_init() != 0)
- return -1;
-
- return 0;
-}
+++ /dev/null
-/* $Id: daemon.h,v 1.2 2006/03/31 20:57:08 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef _DAEMON_H_
-#define _DAEMON_H_
-
-
-
-int daemon_init(void);
-
-
-
-#endif
+++ /dev/null
-/* $Id: kqueue.c,v 1.11 2006/04/03 21:06:08 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.h"
-#include "net.h"
-#include "conf.h"
-
-
-
-static struct kevent rev[R_EVENTS], sev[S_EVENTS];
-static int kqid, rev_cnt, sev_cnt;
-
-
-
-/* The following functions simply exit on failure, because they are critical */
-
-void
-kqueue_init(void)
-{
-
- if ((kqid = kqueue()) == -1) {
- syslog(LOG_NOTICE, "kqueue_init() - kqueue() - %s",
- strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- sev_cnt = 0;
-}
-
-void
-kqueue_addlisten(int fd)
-{
-
- EV_SET(sev, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (intptr_t)NULL);
- if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) {
- syslog(LOG_NOTICE, "kqueue_addlisten() - kevent(%d) - %s",
- fd, strerror(errno));
- exit(EXIT_FAILURE);
- }
-
-}
-
-void
-kqueue_addsignal(int sig)
-{
-
- EV_SET(sev, sig, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
- (intptr_t)NULL);
- if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) {
- syslog(LOG_NOTICE, "kqueue_addsignal() - kevent(%d) - %s",
- sig, strerror(errno));
- exit(EXIT_FAILURE);
- }
-}
-
-void
-kqueue_addtimer0(int64_t ms)
-{
-
- EV_SET(sev, 0, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, ms,
- (intptr_t)NULL);
- if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) {
- syslog(LOG_NOTICE, "kevent_addtimer0() - kevent(%lld) - %s",
- ms, strerror(errno));
- exit(EXIT_FAILURE);
- }
-}
-
-void
-kqueue_main(void)
-{
-
- for (;;) {
- int e, i;
- struct kevent *kev;
- client_t *c;
-
- /*
- * Fusion to only require one kevent(2) call if there are
- * events to send. Wait for events to occur.
- * This also allows us to catch error events.
- */
- if ((rev_cnt = kevent(kqid, sev, sev_cnt, rev, R_EVENTS, NULL))
- == -1) {
- syslog(LOG_NOTICE, "kevent(1) - %s", strerror(errno));
- continue;
- }
- sev_cnt = 0;
-
- /* Run through received events and process them */
- for (e = 0; e < rev_cnt; e++) {
- kev = &rev[e];
-
- /* Report errors if any */
- if ((kev->flags & EV_ERROR) != 0) {
- syslog(LOG_NOTICE,
- "EV_ERROR: ident=%d filter=%d flags=%d "
- "fflags=%d data=%lld udata=%p",
- kev->ident, kev->filter, kev->flags,
- kev->fflags, kev->data,
- (void *)kev->udata);
- continue;
- }
-
- /* Process signals if any */
- if (kev->filter == EVFILT_SIGNAL) {
- switch (kev->ident) {
- case SIGTERM:
- syslog(LOG_NOTICE,
- "Received SIGTERM, exiting");
- exit(EXIT_SUCCESS);
- break;
- }
- continue;
- }
-
- /* Process timeouts if any */
- if (kev->filter == EVFILT_TIMER) {
- if (kev->ident == 0) {
- /* Main heartrate timer */
- update();
- continue;
- }
- /*
- * XXX input timeout timers?
- * We'll need an input timer per socket,
- * which gets canceled whenever input arrives,
- * but once expires launches a server ping
- * request which then should trigger another
- * timer, which if pong occurs gets restarted,
- * but if it exceeds client gets dropped.
- * We'll also need to match timer events with
- * filedescriptors, as well as reason for
- * timeout... And we'll need to make sure to
- * clean out any pending timers when marking a
- * client to be destroyed (or when destroying
- * them, at least).
- */
- }
-
- if (kev->ident == listen_fd) {
- int t;
-
- /*
- * Accept new connections, create clients and
- * add new descriptors to the kqueue set,
- * buffering them to minimize syscalls.
- */
- for (i = 0, t = kev->data; i < t; i++) {
- struct sockaddr saddr;
- socklen_t saddrl;
- client_t *c;
- int fd;
-
- saddrl = sizeof(struct sockaddr);
- if ((fd = accept(listen_fd, &saddr,
- &saddrl)) == -1)
- continue;
-
- if ((c = client_create(fd, &saddr))
- == NULL) {
- (void) close(fd);
- continue;
- }
-
- kqueue_sev_alloc(2);
- kqueue_sev_add(fd, EVFILT_READ,
- EV_ADD | EV_EOF | EV_ENABLE, 0, 0,
- (intptr_t)c);
- kqueue_sev_add(fd, EVFILT_WRITE,
- EV_ADD | EV_EOF | EV_ENABLE, 0, 0,
- (intptr_t)c);
-
- /* Send auth request packet */
- if (spacket_auth_send(c) == -1) {
- client_destroy_mark(c);
- continue;
- }
- }
- continue;
- }
-
- /*
- * Don't process any more events for marked to be
- * destroyed clients, or those without an associated
- * udata. When client descriptor must be closed and
- * client structure freed, we make sure to first mark
- * it as to destroy within this loop, since it's
- * possible for more than one event to occur for a
- * single descriptor. We use client_destroy_mark()
- * for this purpose.
- */
- if ((c = (client_t *)kev->udata) == NULL) {
- /* XXX */
- syslog(LOG_NOTICE, "udata == NULL");
- continue;
- }
- if (c->todestroy)
- continue;
-
- if ((kev->flags & EV_EOF) != 0) {
- client_destroy_mark(c);
- continue;
- }
-
- if (kev->filter == EVFILT_WRITE) {
- int f;
-
- /*
- * If there's a sendq for the client,
- * attempt to flush it. If there's an error,
- * drop client.
- * If client was marked to be closed, and we
- * finished flusing data, also mark it to be
- * destroyed, since were done with it.
- */
- if ((f = sendq_flush(&c->sendq, kev->data))
- == -1 || (f == 0 && c->toclose))
- client_destroy_mark(c);
- else if (f == 0) {
- /*
- * No more data to send, we can thus
- * temporarily disable write events
- * for this descriptor. client_write()
- * will re-enable it as necessary.
- */
- kqueue_sev_alloc(1);
- kqueue_sev_add(c->fd, EVFILT_WRITE,
- EV_DISABLE, 0, 0, (intptr_t)c);
- c->writepolling = 0;
- }
- continue;
- }
-
- if (kev->filter == EVFILT_READ) {
- int16_t *buf;
- size_t size;
-
- /*
- * Data to read from client. Simply read it
- * into a queue to process it at the next
- * server heartbeat event.
- * XXX We should be able to process ping
- * requests immediately. This means that
- * we need recvq_read() to report more
- * information.
- */
- if (recvq_read(&c->recvq, &buf, &size) == -1) {
- client_destroy_mark(c);
- continue;
- }
-
- /* Convert packet_type to host endian */
- *buf = BYTEORDER_HOST16(*buf);
-
- /*
- * If ping packet, process immediately
- * and discard packet from recvq.
- * We only allow ping if c->askedping
- * is unset, and we set it. It will be unset
- * at the next server heart beat. This way,
- * we only allow clients to ping once per
- * frame at most.
- */
- if (*buf == CPACKET_PING &&
- size == sizeof(struct cpacket_ping)) {
- if (!c->authenticated ||
- c->askedping ||
- spacket_pong_send(c) == -1) {
- client_destroy_mark(c);
- continue;
- }
- recvq_rewind(&c->recvq, size);
- }
-
- continue;
- }
-
- }
- /* Destroy marked to be destroyed clients */
- client_destroy_marked();
- }
- /* NOTREACHED */
-}
-
-
-/* Utility functions */
-
-inline void
-kqueue_sev_alloc(int n)
-{
-
- if (sev_cnt > S_EVENTS - n) {
- if (kevent(kqid, sev, sev_cnt, NULL, 0, NULL) == -1)
- syslog(LOG_NOTICE,
- "kqueue_sev_alloc() - kevent() - %s",
- strerror(errno));
- sev_cnt = 0;
- }
-}
-
-inline void
-kqueue_sev_add(uintptr_t ident, uint32_t filter, uint32_t flags,
- uint32_t fflags, int64_t data, intptr_t udata)
-{
-
- /*
- * Be careful to avoid macro side effects, do not use &sev[sev_cnt++]
- */
- EV_SET(&sev[sev_cnt], ident, filter, flags, fflags, data, udata);
- sev_cnt++;
-}
+++ /dev/null
-/* $Id: kqueue.h,v 1.4 2006/04/01 00:46:24 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef _KQUEUE_H_
-#define _KQUEUE_H_
-
-
-
-#include <sys/event.h>
-#include <sys/time.h>
-
-
-
-void kqueue_init(void);
-void kqueue_addlisten(int);
-void kqueue_addsignal(int);
-void kqueue_addtimer0(int64_t);
-void kqueue_main(void);
-inline void kqueue_sev_alloc(int);
-inline void kqueue_sev_add(uintptr_t, uint32_t, uint32_t, uint32_t,
- int64_t, intptr_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: main.c,v 1.10 2006/04/01 00:46:24 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 "packets.h"
-#include "client.h"
-#include "conf.h"
-#include "kqueue.h"
-
-
-
-int main(int, char **);
-
-
-
-int
-main(int argc, char **argv)
-{
-
- (void) openlog("daemon", LOG_NDELAY | LOG_PID, LOG_USER);
-
- if (daemon_init() != 0)
- exit(EXIT_FAILURE);
- syslog(LOG_NOTICE, "Started");
-
- /*
- * The following funtions exit whenever there's a problem, because
- * they set up critical things.
- */
- client_init();
- kqueue_init();
- net_init();
- kqueue_addlisten(listen_fd);
- kqueue_addsignal(SIGTERM);
- kqueue_addtimer0(FPS_MS);
-
- kqueue_main();
- /* NOTREACHED */
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-/* $Id: net.c,v 1.2 2006/04/01 00:46:24 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.2 2006/04/01 00:46:24 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef _NET_H_
-#define _NET_H_
-
-
-
-void net_init(void);
-
-
-
-extern int listen_fd;
-
-
-
-#endif
+++ /dev/null
-/* $Id: packets.c,v 1.14 2006/04/03 20:38:43 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * To validate a received packet, we'll make sure that it's larger than
- * sizeof(int), and that the first integer consists of a valid expected packet
- * type ID within range. If so, we verify if packet length really corresponds
- * to the packet type, and then can interpret the packet information.
- * An actual network packet may contain a number of these packets.
- * We thus must be able to efficiently determine each packet's length.
- * Since we're using TCP, it should be enough. However, on the server side
- * especially, proper sanity checking on input must be done to avoid crashing
- * the server because of unexpected data processing.
- */
-
-
-
-#include <stdlib.h>
-
-#include <mmstring.h>
-#include <mmarch.h>
-
-#include "packets.h"
-#include "client.h"
-#include "sendq.h"
-#include "conf.h"
-
-
-
-static int cpacket_auth_handler(client_t *, uint16_t *);
-static int cpacket_ping_handler(client_t *, uint16_t *);
-static int cpacket_pong_handler(client_t *, uint16_t *);
-static int cpacket_direction_handler(client_t *, uint16_t *);
-static int cpacket_thrust_handler(client_t *, uint16_t *);
-static int cpacket_torp_handler(client_t *, uint16_t *);
-static int cpacket_quit_handler(client_t *, uint16_t *);
-
-
-
-static struct packet_index cpacket_index[CPACKET_MAX] = {
- {sizeof(struct cpacket_auth), cpacket_auth_handler},
- {sizeof(struct cpacket_ping), cpacket_ping_handler},
- {sizeof(struct cpacket_pong), cpacket_pong_handler},
- {sizeof(struct cpacket_direction), cpacket_direction_handler},
- {sizeof(struct cpacket_thrust), cpacket_thrust_handler},
- {sizeof(struct cpacket_torp), cpacket_torp_handler},
- {sizeof(struct cpacket_quit), cpacket_quit_handler}
-};
-
-
-
-void
-update(void)
-{
- node_t *nod, *next;
- client_t *c;
- uint8_t *data;
- size_t size, off;
-
- for (nod = DLIST_TOP(&clients_list); nod != NULL; nod = next) {
- next = DLIST_NEXT(nod);
- c = (client_t *)nod;
-
- if (c->todestroy || c->toclose)
- continue;
-
- /* Reset ping request throttling flag */
- c->askedping = 0;
-
- /* First process incomming packets */
- recvq_content(&c->recvq, &data, &size);
- for (off = 0; off < size; ) {
- int16_t *id;
-
- /*
- * Verify packet type ID. It already has been
- * converted to host endian byte order, unlike other
- * packet fields.
- */
- id = (int16_t *)&data[off];
- *id = BYTEORDER_HOST16(*id);
- if (*id < 0 || *id > CPACKET_MAX) {
- client_destroy_mark(c);
- break;
- }
-
- /* There must be enough data for packet size */
- if (size - off < cpacket_index[*id].size) {
- /*
- * Kill authentucated client if other packets
- * than CPACKET_AUTH
- */
- if (!c->authenticated && *id != CPACKET_AUTH)
- goto k;
-
- /*
- * XXX We'll need to also do special
- * processing for chat packets, since they'll
- * have arbitrary length. They'll still need
- * to be 16-bit aligned, too.
- */
-
- /* Kill client on packet processing error */
- if (cpacket_index[*id].handler(c,
- (uint16_t *)off) == -1)
- goto k;
- } else
- goto k;
-
- /* Process next packet if any */
- off += cpacket_index[*id].size;
- continue;
-
-k:
- client_destroy_mark(c);
- break;
- }
-
- /*
- * XXX I don't like having to run the clients list all over,
- * to then run through all objects (including clients) to
- * update them, and then yet again through all objects to send
- * updates to all clients.
- */
-
- /* Run game frame on all objects */
- DLIST_FOREACH(&clients_list, nod) {
- client_t *c = (client_t *)nod;
-
- /*
- if (!c->authenticated)
- continue;
- */
-
- /*
- * First update direction and thrust.
- * XXX I could do a small function or macro to deal
- * with similar situations, i.e.
- * change_update(int goal, int *current, int steps);
- */
- if (c->object.direction < c->object.i_direction)
- c->object.direction++;
- else if (c->object.direction > c->object.i_direction)
- c->object.direction--;
- if (c->object.thrust < c->object.i_thrust)
- c->object.thrust++;
- else if (c->object.thrust > c->object.i_thrust)
- c->object.thrust--;
-
- /*
- * Translate object x/y position according to
- * trust/direction. When reaching borders we simply
- * bounce for now.
- * (I need to review my trigonometry a bit again :)
- */
- /* XXX */
- c->object.x += 1 - (random() & 2);
- if (c->object.x < 0)
- c->object.x = 0;
- else if (c->object.x > WORLD_X_MAX - 1)
- c->object.x = WORLD_X_MAX - 1;
- c->object.y += 1 - (random() & 2);
- if (c->object.y < 0)
- c->object.y = 0;
- else if (c->object.y > WORLD_Y_MAX - 1)
- c->object.y = WORLD_Y_MAX - 1;
- }
-
- /* Send update frame information packets */
- DLIST_FOREACH(&clients_list, nod) {
- client_t *c = (client_t *)nod;
- node_t *nod2;
-
- /*
- if (!c->authenticated)
- continue;
- */
-
- DLIST_FOREACH(&clients_list, nod2) {
- if (spacket_position_send(c,
- &((client_t *)nod2)->object) == -1)
- break;
- }
- (void) sendq_flush(&c->sendq, -1);
- }
- }
-}
-
-
-int
-spacket_auth_send(client_t *c)
-{
- struct spacket_auth p;
-
- p.packet_type = BYTEORDER_NETWORK16(SPACKET_AUTH);
- mm_memclr(p.string, 32);
- mm_strncpy(p.string, SERVER_STRING, 31);
- p.protocol_version = BYTEORDER_NETWORK16(SERVER_VERSION);
- /* XXX */
-
- return client_write(c, (uint8_t *)&p, sizeof(p), 0);
-}
-
-int
-spacket_pong_send(client_t *c)
-{
- struct spacket_pong p;
-
- p.packet_type = BYTEORDER_NETWORK16(SPACKET_PONG);
-
- return client_write(c, (uint8_t *)&p, sizeof(p), 0);
-}
-
-int
-spacket_position_send(client_t *c, object_t *o)
-{
- struct spacket_position p;
-
- p.packet_type = BYTEORDER_NETWORK16(SPACKET_POSITION);
- p.object_id = p.object_type = BYTEORDER_NETWORK16(0); /* XXX */
- p.x = BYTEORDER_NETWORK16(o->x);
- p.y = BYTEORDER_NETWORK16(o->y);
- p.direction - BYTEORDER_NETWORK16(o->direction);
-
- return client_write(c, (uint8_t *)&p, sizeof(p), 1);
-}
-
-
-/* Note that only the packet_type field is endian converted yet. */
-
-static int
-cpacket_auth_handler(client_t *c, uint16_t *ptr)
-{
- struct cpacket_auth *p = (struct cpacket_auth *)ptr;
-
- p->protocol_version = BYTEORDER_HOST16(p->protocol_version);
-
- if (c->authenticated || p->protocol_version < SERVER_VERSION)
- return -1;
-
- /*
- * XXX For now. Eventually also check user/password
- * We could use APOP-like authentication where server sends random
- * data as part of the authentication greeting/request, and expect
- * client to append password to random data and send hashed result.
- * We also should do user concurrency checking here.
- */
- c->authenticated = 1;
-
- return 0;
-}
-
-/*
- * Note: Following function is never used, since pings are handled in the
- * kqueue main loop code.
- */
-/* ARGSUSED */
-static int
-cpacket_ping_handler(client_t *c, uint16_t *ptr)
-{
- /* NOOP */
-
- return 0;
-}
-
-static int
-cpacket_pong_handler(client_t *c, uint16_t *ptr)
-{
-/* struct cpacket_pong *p = (struct cpacket_pong *)ptr;*/
-
- /* XXX */
-
- return 0;
-}
-
-static int
-cpacket_direction_handler(client_t *c, uint16_t *ptr)
-{
- struct cpacket_direction *p = (struct cpacket_direction *)ptr;
-
- p->direction = BYTEORDER_HOST16(p->direction);
-
- /* Radians */
- if (p->direction < 0 || p->direction > 1000)
- return -1;
-
- c->object.i_direction = p->direction;
-
- return 0;
-}
-
-static int
-cpacket_thrust_handler(client_t *c, uint16_t *ptr)
-{
- struct cpacket_thrust *p = (struct cpacket_thrust *)ptr;
-
- p->thrust = BYTEORDER_HOST16(p->thrust);
-
- if (p->thrust < 0 || p->thrust > 20)
- return -1;
-
- c->object.i_thrust = p->thrust;
-
- return 0;
-}
-
-static int
-cpacket_torp_handler(client_t *c, uint16_t *ptr)
-{
-/* struct cpacket_torp *p = (struct cpacket_torp *)ptr;*/
-
- /* XXX */
-
- return 0;
-}
-
-/* ARGSUSED */
-static int
-cpacket_quit_handler(client_t *c, uint16_t *ptr)
-{
-
- return -1;
-}
+++ /dev/null
-/* $Id: packets.h,v 1.9 2006/04/03 20:37:51 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * All packets must begin with an int16_t packet_type. All fields of a packet
- * must either be [u]int8_t, [u]int16_t, [u]int32+t or [u]int64_t. Moreover,
- * all fields will be passed in network/big endian byte order when sent over
- * the network. We could have used a union, but this would have resulted in
- * larger, fixed sized packets wasting bandwidth. Since a server to client
- * update will generally involve sending more than one such packet in a row,
- * it is important to minimize their size.
- * Note: If using 32-bit fields within a packet, make sure that they are
- * 32-bit aligned.
- */
-
-
-
-#ifndef _PACKETS_H_
-#define _PACKETS_H_
-
-
-
-#include <sys/types.h>
-
-#include "client.h"
-
-
-
-/*
- * For every server packet type we support, an index array will be used
- * with this structure to call the associated handler function, which will
- * perform sanity checking and process the packet, returning 0 on success or
- * -1 on error. The size field will allow to perform sanity checking on
- * data size prior to calling the handler function, as well as to increase the
- * buffer pointer.
- */
-struct packet_index {
- size_t size;
- int (*handler)(client_t *, uint16_t *);
-};
-
-
-
-/*
- * Server to client packets
- */
-
-enum spacket_types {
- SPACKET_AUTH = 0,
- SPACKET_PING,
- SPACKET_PONG,
- SPACKET_POSITION,
- SPACKET_COLLISION,
- SPACKET_MAX
-};
-
-/* Used to request client authentication and respond success/failure */
-struct spacket_auth {
- int16_t packet_type;
- int16_t protocol_version;
- char string[32];
- /* XXX */
-} __attribute__((__packed__));
-
-/* And for server to test idle connections */
-struct spacket_ping {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-/* And clients to test latency */
-struct spacket_pong {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-/* Object position/direction update to client */
-struct spacket_position {
- int16_t packet_type;
- int16_t object_id, object_type;
- int16_t x, y, direction;
-} __attribute__((__packed__));
-
-/* Collision/detonation update to client */
-struct spacket_collision {
- int16_t packet_type;
- int16_t collision_type;
- int16_t object1_id, object2_id;
-} __attribute__((__packed__));
-
-
-
-/*
- * Client to server packets
- */
-
-enum cpacket_tyoes {
- CPACKET_AUTH = 0,
- CPACKET_PING,
- CPACKET_PONG,
- CPACKET_DIRECTION,
- CPACKET_THRUST,
- CPACKET_TORP,
- CPACKET_QUIT,
- CPACKET_MAX
-};
-
-/* Used to respond to server authentication request */
-struct cpacket_auth {
- int16_t packet_type;
- int16_t protocol_version;
- char string[32];
- /* XXX */
-} __attribute__((__packed__));
-
-/* To ping server for latency mesurement */
-struct cpacket_ping {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-/* To respond to server ping requests */
-struct cpacket_pong {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-/* Angle/direction change request */
-struct cpacket_direction {
- int16_t packet_type;
- int16_t direction;
-} __attribute__((__packed__));
-
-/* Speed change request */
-struct cpacket_thrust {
- int16_t packet_type;
- int16_t thrust;
-} __attribute__((__packed__));
-
-/* Torpedo fire request */
-struct cpacket_torp {
- int16_t packet_type;
- int16_t direction;
-} __attribute__((__packed__));
-
-/* Game quit request */
-struct cpacket_quit {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-
-
-void update(void);
-int spacket_auth_send(client_t *);
-int spacket_pong_send(client_t *);
-int spacket_position_send(client_t *, object_t *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: recvq.c,v 1.5 2006/04/05 09:19:16 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "recvq.h"
-
-
-
-int
-recvq_init(recvq_t *q, int fd, size_t size)
-{
-
- if ((q->buffer = malloc(size)) == NULL)
- return -1;
-
- q->fd = fd;
- q->size = size;
- q->tail = 0;
-
- return 0;
-}
-
-void
-recvq_destroy(recvq_t *q)
-{
-
- free(q->buffer);
-}
-
-int
-recvq_read(recvq_t *q, int16_t **buf, size_t *size)
-{
- ssize_t s;
-
- if (q->tail == q->size)
- return -1;
-
- if (((s = read(q->fd, &q->buffer[q->tail], q->size - q->tail)) == -1 &&
- errno != EAGAIN) || s == 0)
- return -1;
-
- /*
- * Verify that s is a multiple of 2 before accepting it. This will
- * allow all packets and their fields to be 16-bit aligned.
- */
- if (((uint16_t)s & 1) != 0) {
- syslog(LOG_NOTICE, "Uneven packet from %d", q->fd);
- return -1;
- }
-
- *buf = (int16_t *)&q->buffer[q->tail];
- *size = s;
- q->tail += s;
-
- return 0;
-}
-
-/*
- * Useful to discard ping packets which we process on the fly
- */
-void
-recvq_rewind(recvq_t *q, size_t size)
-{
-
- if (q->tail >= size)
- q->tail -= size;
-}
-
-void
-recvq_content(recvq_t *q, uint8_t **buf, size_t *size)
-{
-
- *buf = (uint8_t *)q->buffer;
- *size = q->tail;
- q->tail = 0;
-}
+++ /dev/null
-/* $Id: recvq.h,v 1.4 2006/04/03 20:37:51 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef _RECVQ_H_
-#define _RECVQ_H_
-
-
-
-#include <sys/types.h>
-
-
-
-typedef struct recvq {
- int fd;
- uint8_t *buffer;
- size_t size, tail;
-} recvq_t;
-
-
-
-int recvq_init(recvq_t *, int, size_t);
-void recvq_destroy(recvq_t *);
-int recvq_read(recvq_t *, int16_t **, size_t *);
-void recvq_rewind(recvq_t *, size_t);
-void recvq_content(recvq_t *, uint8_t **, size_t *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: sendq.c,v 1.5 2006/04/03 20:37:51 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 <unistd.h>
-
-#include <mmlist.h>
-#include <mmstring.h>
-
-#include "sendq.h"
-
-
-
-/*
- * Initializes a sendq object, allocating the queue buffer
- */
-int
-sendq_init(sendq_t *q, int fd, size_t size)
-{
-
- if ((q->buffer = malloc(size)) == NULL)
- return -1;
-
- q->fd = fd;
- q->size = size;
- q->head = q->tail = 0;
-
- return 0;
-}
-
-/*
- * Destroys a sendq object, freeing the queue buffer
- */
-void
-sendq_destroy(sendq_t *q)
-{
-
- free(q->buffer);
-}
-
-/*
- * Attempts to write to the non-blocking socket. In the case of a partial
- * write, queue the remaining of the buffer. In case where the buffer is
- * full, return an error (-1), in which case the sendq has been exceeded and
- * client should be dropped. We only write(2) if there is no pending buffers
- * already, of course. If bfunc is not NULL, it will be called with passed
- * argument bfuncarg whenever data that couldn't be written immediately is
- * buffered. If buffer is true, no attempt will be made at a real write; Data
- * will simply be buffered if possible.
- *
- * XXX There is a potential problem where if the client is lagged enough to
- * have filled the buffer, although not enough to exceed the queue but that
- * the client cannot recoup, we could consider the queue filled and drop the
- * client. This because we aren't really using a real FIFO buffer. This code
- * should probably use the mmfifo(3) library after the multiple bytes buffer
- * functions in it have been debugged and tested properly.
- * However, since if the user was this behind this would in practice mean that
- * his commands would take a while to be reflected, this is probably okay for
- * the time being.
- */
-int
-sendq_write(sendq_t *q, uint8_t *buf, size_t size,
- void (*bfunc)(void *), void *bfuncarg, int buffer)
-{
-
- /* Sendq empty? Attempt to write(2) immediately */
- if (!buffer && q->head == q->tail) {
- ssize_t s;
-
- /* Only allow EAGAIN */
- if ((s = write(q->fd, buf, size)) == -1 && errno != EAGAIN)
- return -1;
-
- /*
- * If we wrote everything, simply return successfuly.
- * Otherwise, fix our arguments to the unwritten buffer.
- */
- if (s == size)
- return 0;
- else {
- size -= s;
- buf += s;
- }
- }
-
- /*
- * If the sendq buffer wasn't empty, or that there remains bytes
- * after a partial write(2), queue the buffer if there's enough room.
- * If there isn't enough room, return error. If a function was
- * supplied with an argument, also call this function if we buffer
- * data. This may allow our caller to enable back write polling
- * events for this descriptor.
- */
- if (size > 0) {
- if (q->tail + size > q->size)
- return -1;
-
- (void) mm_memcpy(&q->buffer[q->tail], buf, size);
- q->tail += size;
- if (bfunc != NULL)
- bfunc(bfuncarg);
- }
-
- return 0;
-}
-
-/*
- * If any pending buffers exist, attempts to write them. In the case of an
- * error, returns -1, in which case the client should be dropped. If there
- * still remains unflushed data, 1 is returned. Otherwise, 0 is.
- */
-int
-sendq_flush(sendq_t *q, size_t room)
-{
-
- if (q->head != q->tail) {
- ssize_t ns;
- size_t os = q->tail - q->head;
-
- /* Only attempt write(2) syscall if there is room */
- if (room == 0)
- return 1;
-
- if ((ns = write(q->fd, &q->buffer[q->head], os)) == -1) {
- if (errno == EAGAIN)
- return 1;
- else
- return -1;
- }
-
- if ((q->head += ns) == q->tail)
- q->head = q->tail = 0;
- else
- return 1;
- }
-
- return 0;
-}
+++ /dev/null
-/* $Id: sendq.h,v 1.5 2006/04/03 20:37:51 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef _SENDQ_H_
-#define _SENDQ_H_
-
-
-
-#include <sys/types.h>
-
-#include <mmlist.h>
-
-
-
-typedef struct sendq {
- int fd;
- uint8_t *buffer;
- size_t size, head, tail;
-} sendq_t;
-
-
-
-int sendq_init(sendq_t *, int, size_t);
-void sendq_destroy(sendq_t *);
-int sendq_write(sendq_t *, uint8_t *, size_t, void (*)(void *), void *,
- int);
-int sendq_flush(sendq_t *, size_t);
-
-
-
-#endif
+++ /dev/null
-$Id: README,v 1.1 2005/11/14 01:55:20 mmondor Exp $
-
-The goal of this project is to create new memory allocation functions
-which can work on arbitrary pools of memory, while making sure to always
-favor low, already used pages.
-
-For instance, let's consider the case where multiple processes need to
-use a fair amount of shared memory. The master process can allocate a
-region of memory using mmap(2) with MAP_ANON, making sure to use a large
-enough block that'll ever be used by the application. This region can
-be shared.
-
-UVM lazily allocates pages which need to be accessed on-the-fly. This
-means that we must use a system similar to brk(2)/sbrk(2) and favor reusing
-already allocated memory in order to avoid the application growing too much.
-The process stack is for instance allocated this way; A single very large
-region is mapped, and the actual memory in use increases with the process'
-requirements for more stack space. In the case of stacks, since they are
-indeed stacks, the growing problem is not critical and easily determined.
-
-In the case of a general purpose memory allocator, this is more complex.
-mmpool(3) library allows to allocate fixed sized objects efficiently and
-makes sure to favor recently used pages in the allocations, which is okay.
-However, mmpool(3) must also be given allocation/freeing functions which
-it must use to allocate more pages as needed, or to free pages which haven't
-been used for some time. This is where the general purpose allocators
-are used.
-
-In the case where a whole fixed large region can be used to allocate a single
-type of objects, we can simply use mmpool(3) and let it manage the large
-memory page. However, needing to allocate a new memory segment per memory
-pool or object type appears problematic in some situations. Moreover, being
-able to use a single large region for shared memory, another one for
-process-specific or shared mlocked memory per application, makes things
-easier. It also allows server administrators to more easily modify various
-parameters relating to application scalibility.
-
-So this general purpose memory allocator should work on arbitrary regions
-of memory, and should automatically perform any necessary locking for
-synchronization. This is quite similar to the libmm library in use by the
-Apache server. This however should be released under BSD license, and
-especially favor reusing of previously allocated pages.
+++ /dev/null
-# $Id: GNUmakefile,v 1.5 2005/11/22 09:24:42 mmondor Exp $
-
-MMLIB_PATH := ../../mmlib
-MMLIBS := $(addprefix ${MMLIB_PATH}/,mmlog.o mmpool.o mmstring.o)
-OBJS := mm_pthread_msg.o mm_pthread_sleep.o mm_pthread_pool.o mm_pthread_poll.o
-BINS := tests/msg_test tests/poll_test
-
-CFLAGS += -Wall
-#CFLAGS += -DDEBUG -DPTHREAD_DEBUG -g3
-
-LDFLAGS += -lc -lpthread
-#LDFLAGS += -lpthread_dbg
-
-
-all: $(BINS)
-
-
-%.o: %.c
- cc -c ${CFLAGS} -I. -I$(MMLIB_PATH) -o $@ $<
-
-
-tests/msg_test: tests/msg_test.o $(MMLIBS) $(OBJS)
- cc ${CFLAGS} -o $@ $@.c $(OBJS) -I. -I$(MMLIB_PATH) ${LDFLAGS} \
- $(MMLIBS)
-
-tests/poll_test: tests/poll_test.o $(MMLIBS) $(OBJS)
- cc ${CFLAGS} -o $@ $@.c $(OBJS) -I. -I$(MMLIB_PATH) ${LDFLAGS} \
- $(MMLIBS)
-
-
-install: all
-
-
-clean:
- rm -f tests/msg_test.o tests/poll_test.o $(BINS) $(OBJS) $(MMLIBS)
+++ /dev/null
-This library is an attempt to provide pth library like API to NetBSD SA
-threads and kqueue.
-
-What we find are missing from the POSIX standard are added here:
-
-- Implementation of efficient messages to communicate among threads. These
- messages are queued using an efficient pointer linking mechanism. It must be
- possible for a thread to wait for messages while sleeping and to be awaken
- when a message is available. It also must be possible to observe a maximum
- timeout to wait for.
-- Implementation of filedescriptors and above mentionned thread messages
- notification multiplexing, with support for timer. An example of this is
- pth library's pth_poll_ev(). A timer event can interrupt thread-safe
- filedescriptor polling, as well as thread messages arriving on a port.
-
-We beleive that it is possible to implement this using the kqueue(2)/kevent(2)
-system. The new call would be similar to:
-
-pthread_poll(struct pollfd *fds, int nfds,
- struct pthread_port *ports, int nports,
- struct pthread_sigs *sigs, int nsigs,
- struct pthread_timers *timers, int ntimers)
-
-or similar system. This would allow multiplexing of various events into a
-single application loop.
-
-
-pthread_cond_timedwait() seems especially useful, either with a signal handler
-or perhaps using kqueue concurrently... pthread_cond_timedwait() will allow
-processes to wait for message arrival though a port, while
-pthread_cond_signal() or pthread_cond_broadcast() will be able to awaken them
-as messages are queued to the message port. However, we would ideally want to
-only signal a wanted thread waiting for a port... But, normally only one
-thread should be listening for messages on any given port. I have to see what
-I'll do for a thread listening for messages on multiple ports at a time...
-Perhaps that multiple ports could use the same conditional wait variable so
-that the process would only wait on that one, and then verify the message
-queue for each before going back in waiting mode.
-
-
-TODO:
-====
-- Replace mutex and conditonal variable initializers, as well as attributes,
- with static initializers.
-- Provide similar static intializer macros as part of our API where possible.
-- I have a working message passing implementation, with possibility of a
- waiter on as many ports as wanted. I however still have a challenge:
- Multiplex system calls such as select(2), poll(2), connect(2) and accept(2)
- with the messaging capability. One must be able to cause the other to
- return. This could be tricky to properly implement. Maybe think about the
- following ideas:
- - Dedicate a thread to serve a syscall, with which communication is solely
- done using messages. This however implies that only a single syscall at a
- time can be processed by such a thread. This probably means that a pool of
- such threads would become necessary. This also assumes that the syscall in
- question do not block the whole process, but only the intended thread.
- Alot of assumptions, but this would now work properly on all BSDs and
- on Linux. Possibly also on Solaris.
- - Use a mix of signals and syscalls, since signals can interrupt syscalls.
- However, this implies adding capability in our message system to trigger
- signals rather than only using a conditional variable to notify of message
- arrival. This also probably means that the same signal handler must be
- shared by the whole process, that is, all the threads.
- - Use kqueue in a thread-safe manner with thread-specific signals (if
- possible). kqueue can be used to track signals without the need for an
- actual signal handler. It would also track filedescriptor changes at the
- same time. This also probably means that we need to use kqueue user
- events if possible, triggered from the message passing system. It also
- means non-portable code outside of the realm of BSD systems.
-
-
-
-RECENT REVIEW AFTER SOME REFLECTION
-===================================
-
-Currently, pth_accept_ev() and pth_connect_ev() are the only two cases of
-special PTh functions which my software uses, notably mmftpd(8). These could
-easily be implemented using a random thread in the pool whenever necessary,
-with which communication would entirely use messages only. This thread could
-be told: Perform syscall in non-blocking mode using the supplied
-filedescriptor and notify me weither it succeeded, failed because of a timeout,
-if any, or was interrupted by a message event occuring on the specified ring,
-if any. The application however has to know that if it was interrupted by
-an event, the connection still occurs asynchroneously within the system.
-We should verify what could be done to cancel a not yet completed connection,
-if possible. This call could also report if the call was interrupted by a
-signal arrival (EINTR), optionally. If the socket was supplied in blocking
-mode, it would have to be switched to non-blocking mode by the system and
-then back into blocking mode. The caller could ensure to set it into
-non-blocking mode for enhanced performance if no blocking mode is required.
-The challenge would be finding a both efficient and portable solution to
-have select()/poll() awake upon reception of notification events on a ring.
-Perhaps that a global filedescriptor could be used for this, SOCK_DGRAM and
-one byte sent, or that a signal handler with a signal generation should be
-used... Both methods would probably awake the whole process, however.
-pthread_sigmask() could be used perhaps... I wouldn't want to have a special
-fd required for each ready thread of the pool, ideally.
-
-Implement:
-mm_pthread_io pthread_poll_ev(), pthread_accept_ev(),
- pthread_connect_ev()
-mm_pthread_alarm pthread_sleep(), etc.
-
-or maybe:
-
-mm_pthread_misc For all of them
-
-Perhaps reimplement the system I worked on in mmserver(3) as well. This might
-be necessary for operations which really should be dedicated to a non-threaded
-process at occasions, and the subsystem should be available. It should probably
-use a pool using mmpool(3) as well, just like we are doing with threads.
-
-It would be interesting to implement better GC for mmpool(3)'s. Currently,
-pool_free() will discard pages which are no longer in use since some time,
-but the time cannot be linear, since it only accounts a certain number of
-calls made to it. It should instead be possible to use time intervals, and
-to let the application invoke the GC at wanted fixed intervals. This would
-allow to use time based average statistics rather than function call times
-based ones, without clobbering process or thread timers which the application
-might need. It simply has to provide its own and to call the GC function
-regularily.
-
-Hmm also, would be nice to be able to store the port_t pointer of the port
-which triggered notification on a ring_t, so that callers don't need to
-run through several ports attached on a ring... Maybe that it would be
-problematic however, since we can't guarantee atomicity between messages and
-messages processing, unless we kludged the whole thing with locks and lost
-efficiency. And because we only trigger notification to wakeup a waiting
-thread when a message is queued on an empty port, it's possible that the
-applicaton sleeps forever on a port if it didn't totally empty it, unless
-there was a way for the sleep function to immediately return if called on
-non-empty ports (as it's only alled on rings, and that rings don't have
-access to a list of ports in current implementation (only the ports can
-know which ring they are tied to)... I could implement something to have
-rings see their attached ports with a list, however. But this again means
-looping among ports to see if they're non-empty, heh, so why not let the
-application do it as they do now.
-
-
-
-IMPORTANT
-=========
-
-I did a test where multiple threads were polling on a single filedescriptor
-consisting of a socketpair, which other side was used to wake them up.
-Only one random thread would wake up.
-
-Using a signal to cause all threads to wake would not work either, because
-then again only a random thread will awake.
-
-It appears that the only way to ensure to wake wanted threads is using
-conditional variables and for them to only sleep on these.
-
-SIGIO possibility... threads would be sleeping on a conditional wait variable
-corresponding to the filedescriptor. For polling, the fd would be made in
-non-blocking I/O, with SIGIO sent to process. The fd and associated cond var
-would be added to a table. The SIGIO signal handler would need to check all
-fds in the set for possible I/O and awake corresponding threads waiting on
-cond var. A problem exists: How to check a filedescriptor for pending event?
-How to know if event is read or write, or hup, etc? Maybe using more ore less
-standard FION ioctls? poll/select with 0 timeout maybe, but that is still
-troublesome in terms of performance I beleive.
-
-fd cond interesting_events occured_events?
-
-Hmm and what if a thread was allocated to start polling, and another wrapper
-thread wait for it sleeping on a cond var? Would it be sane to do this?
-When a timeout or message occurs however detected by the wrapper thread,
-how would we stop the other thread polling? We still have a problem.
-If we left pending polling threads, how would a future thread with successful
-polling on the same descriptor ever wake up, a random one would.
-Why does POSIX threads suck so much as to not provide any decent way to
-work with filedescriptors!? If at least I had pthread_signal() it would
-help. I could send a signal to interrupt the wanted thread when it was polling.
-Or if only there was a way to set the wanted signal mask for wanted processes
-as necessary, so that I would only have the wanted one process a particular
-signal I could send to interrupt it and then restore the masks, and do this
-somehow atomically. If POSIX had any of these requirements in mind while
-developing the standard, pthread_poll_condwait() or pthread_signal() would
-already exist anyways!
-
-
-HMM
-===
-
-A thread reserved for polling would seem best. We need to be able to interrupt
-that thread whenever needed using a signal, which all other processes must
-be blocking. We could use SIGIO, or SIGUSR2 for instance. That thread would
-process thread messages and go back to polling. It probably could handle
-timeouts as well, but this is probably not necessary. If it did, would
-probably free other threads from calling gettimeofday() too often. The thread
-has to remove the fd from the polling list when an event returned on it
-anyways, and so it could also send a reply message for timeout. It has to
-be interrupted anyways when a new fd is to be added, and this means that
-it could fix the poll timer before calling it each time to fit the soonest
-to expire fd... I could probably use kqueue too, or libevent in that thread
-to make it high performance as possible.
+++ /dev/null
-/* $Id: mm_pthread_debug.h,v 1.2 2006/02/05 13:00:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2004-2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef MM_PTHREAD_DEBUG_H
-#define MM_PTHREAD_DEBUG_H
-
-
-
-#include <pthread.h>
-#include <syslog.h>
-
-
-
-#ifdef PTHREAD_DEBUG
-
-#define DEBUG_PTHREAD_ENTRY() \
- syslog(LOG_NOTICE, "> TID=%p FN=%s", pthread_self(), __func__)
-
-#define DEBUG_PTHREAD_EXIT() \
- syslog(LOG_NOTICE, "< TID=%p FN=%s", pthread_self(), __func__)
-
-#else
-#define DEBUG_PTHREAD_ENTRY()
-#define DEBUG_PTHREAD_EXIT()
-#endif
-
-
-
-#endif
+++ /dev/null
-/* $Id: mm_pthread_msg.c,v 1.14 2006/02/05 13:00:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * It is almost a shame that POSIX did not define a standard API for
- * inter-thread asynchroneous and synchroneous messaging. So, here is my
- * implementation. Note that for asynchroneous operation it is recommended to
- * use a memory pool such as mmpool(3) to allocate and free messages in an
- * efficient way, in cases where messages will need to be sent to the other
- * end without expecting a response back before the current function ends
- * (in which case a message obviously can't be on the stack).
- */
-
-
-
-#include <pthread.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <unistd.h>
-
-#include <mmtypes.h>
-#include <mmlog.h>
-
-#include <mm_pthread_debug.h>
-#include <mm_pthread_msg.h>
-/*#include <mm_pthread_poll.h>*/
-
-
-
-MMCOPYRIGHT("@(#) Copyright (c) 2005\n\
-\tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: mm_pthread_msg.c,v 1.14 2006/02/05 13:00:48 mmondor Exp $");
-
-
-
-/*
- * Allows to initialize a polling notification handle. When attached to a
- * port, a message arriving on an empty port causes the associated ring to
- * wake the thread from pthread_ring_wait().
- */
-int
-pthread_ring_init(pthread_ring_t *ring)
-{
- int error;
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(ring != NULL && ring->magic != PRING_MAGIC);
-
- if ((error = pthread_cond_init(&ring->cond, NULL)) == 0) {
- if ((error = pthread_mutex_init(&ring->mutex, NULL)) == 0) {
- ring->magic = PRING_MAGIC;
- ring->event = ring->mevent = 0;
- DEBUG_PTHREAD_EXIT();
- return 0;
- }
- (void) pthread_cond_destroy(&ring->cond);
- }
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Returns TRUE if the supplied ring is a valid/usable one, or FALSE
- * otherwise. Useful to conditionally destroy it.
- */
-int
-pthread_ring_valid(pthread_ring_t *ring)
-{
-
- DEBUG_PTHREAD_ENTRY();
-
- DEBUG_PTHREAD_EXIT();
- return (ring != NULL && ring->magic == PRING_MAGIC);
-}
-
-/*
- * Destroys a ring. Note that all message ports attached to this ring should
- * first be detached or destroyed.
- */
-int
-pthread_ring_destroy(pthread_ring_t *ring)
-{
- int error;
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(ring != NULL && ring->magic == PRING_MAGIC);
-
- if ((error = pthread_mutex_destroy(&ring->mutex)) == 0)
- error = pthread_cond_destroy(&ring->cond);
- ring->magic = 0;
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Causes the current thread to sleep until a message arrives on an empty port
- * associated with this ring. In normal operation, a thread only goes in wait
- * mode after it processed all queued messages on all interesting ports.
- * However, provision is made so that a the function returns immediately if
- * messages already were received on a port attached to this ring since the
- * last call to pthread_ring_wait().
- * Although using such an absolute time timespec might be disadvantageous for
- * the API compared to a timeout in milliseconds for instance, this was chosen
- * to remain API-compatible with pthread_cond_timedwait(), and upwards
- * compatible with systems where nanosecond precision can be achieved.
- */
-int
-pthread_ring_wait(pthread_ring_t *ring, const struct timespec *abstime)
-{
- int error = 0;
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(ring != NULL && ring->magic == PRING_MAGIC);
-
- /* We must hold the condition variable's mutex */
- if (pthread_mutex_lock(&ring->mutex) != 0) {
- error = -1;
- goto err;
- }
-
- /* As long as we don't have confirmation that we must stop waiting */
- for (ring->event = 0; ring->mevent == 0 &&
- !ring->event && error == 0; ) {
- /*
- * Wait on conditional variable, which will automatically
- * and atomically release the mutex and return with the mutex
- * locked again, as soon as the conditional variable gets
- * signaled.
- */
- if (abstime != NULL) {
- error = pthread_cond_timedwait(&ring->cond,
- &ring->mutex, abstime);
- } else
- error = pthread_cond_wait(&ring->cond, &ring->mutex);
- }
- ring->mevent = 0;
-
- /*
- * And we know that conditional waiting functions returned with mutex
- * locked, so now release it back.
- */
- (void) pthread_mutex_unlock(&ring->mutex);
-
-err:
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Allows to wake up waiter(s) on the specified ring, which are sleeping
- * threads within pthread_ring_wait(). This can be used to simulate the
- * arrival of a message on an empty port. Also useful to use rings as a
- * notification system only when no message passing is needed.
- */
-int
-pthread_ring_notify(pthread_ring_t *ring)
-{
- int error;
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(ring != NULL && ring->magic == PRING_MAGIC);
-
- if ((error = pthread_mutex_lock(&ring->mutex)) == 0) {
- ring->mevent++;
- ring->event = 1;
- (void) pthread_cond_signal(&ring->cond);
- (void) pthread_mutex_unlock(&ring->mutex);
- }
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Allows to initialize/create a message port.
- */
-int
-pthread_port_init(pthread_port_t *port)
-{
- int error;
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(port != NULL && port->magic != PPORT_MAGIC);
-
- if ((error = pthread_mutex_init(&port->lock, NULL)) != 0)
- goto err;
-
- port->magic = PPORT_MAGIC;
- port->ring = NULL;
- DLIST_INIT(&port->messages);
-
-err:
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Returns TRUE if the supplied port is valid/usable, or FALSE otherwise.
- * Useful to conditionally destroy a port, for instance.
- */
-int
-pthread_port_valid(pthread_port_t *port)
-{
-
- DEBUG_PTHREAD_ENTRY();
-
- DEBUG_PTHREAD_EXIT();
- return (port != NULL && port->magic == PPORT_MAGIC);
-}
-
-/*
- * Destroys the specified port, previously created using pthread_port_init().
- */
-int
-pthread_port_destroy(pthread_port_t *port)
-{
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC);
-
- port->magic = 0;
-
- DEBUG_PTHREAD_EXIT();
- return pthread_mutex_destroy(&port->lock);
-}
-
-/*
- * Attaches a port to a ring. Multiple ports may be attached to a ring. A
- * message arriving on an empty port will cause the attached ring to be
- * notified, if any, and as such to cause a thread waiting on the ring to
- * be awakened.
- */
-int
-pthread_port_set_ring(pthread_port_t *port, pthread_ring_t *ring)
-{
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC &&
- (ring == NULL || ring->magic == PRING_MAGIC));
-
- port->ring = ring;
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-}
-
-/*
- * Allows to initialize a message before it can be sent over a port. The
- * message only needs to be initialized once in general, even if it will be
- * used for bidirectional transmission for synchronous operation. If the
- * reply port needs to be changed, however, this function should be used again
- * to set the new reply port.
- */
-int
-pthread_msg_init(pthread_msg_t *msg, pthread_port_t *rport)
-{
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(msg != NULL && msg->magic != PMESG_MAGIC &&
- (rport == NULL || rport->magic == PPORT_MAGIC));
-
- msg->magic = PMESG_MAGIC;
- msg->reply = rport;
- msg->size = 0;
- msg->message = NULL;
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-}
-
-/*
- * Returns TRUE if supplied message is valid/usable or FALSE otherwise.
- */
-int
-pthread_msg_valid(pthread_msg_t *msg)
-{
-
- DEBUG_PTHREAD_ENTRY();
-
- DEBUG_PTHREAD_EXIT();
- return (msg != NULL && msg->magic == PMESG_MAGIC);
-}
-
-/*
- * Invalidates a message, so that it can no longer be sent over ports.
- */
-int
-pthread_msg_destroy(pthread_msg_t *msg)
-{
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(msg != NULL && msg->magic == PMESG_MAGIC);
-
- msg->magic = 0;
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-}
-
-/*
- * If any message exists in the queue of the specified port, unqueues it and
- * returns it. Otherwise, NULL is returned. In normal operation, all messages
- * queued to a port are processed before putting the thread back into sleep,
- * mainly for efficiency, but also because it eases synchronization.
- */
-pthread_msg_t *
-pthread_msg_get(pthread_port_t *port)
-{
- pthread_msg_t *msg = NULL;
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC);
-
- if (pthread_mutex_lock(&port->lock) != 0)
- goto err;
-
- if ((msg = DLIST_TOP(&port->messages)) != NULL) {
- DEBUG_ASSERT(msg->magic == PMESG_MAGIC);
- DLIST_UNLINK(&port->messages, (node_t *)msg);
- }
-
- (void) pthread_mutex_unlock(&port->lock);
-
-err:
- DEBUG_PTHREAD_EXIT();
- return (pthread_msg_t *)msg;
-}
-
-/*
- * Queues the specified message to the specified port, returning 0 on success.
- * Note that the message data is not copied or moved, but that a pointer
- * system is used to queue the message. Thus, the message's shared memory
- * region is leased temporarily to the other end. One has to be careful to
- * not allocate this message space on the stack when asynchroneous operation
- * is needed. In synchroneous operation mode, it is not a problem, since the
- * sender does not have to modify the data until the other end replies back
- * with the same message after modifying the message if necessary. In
- * synchroneous mode, we simply delegate that message memory region to the
- * other end until it notifies us with a reply that it is done working with
- * it. Returns 0 on success, or an error number.
- */
-int
-pthread_msg_put(pthread_port_t *port, pthread_msg_t *msg)
-{
- int error = 0;
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC &&
- msg != NULL && msg->magic == PMESG_MAGIC);
-
- if ((error = pthread_mutex_lock(&port->lock)) != 0)
- goto err;
-
- DLIST_APPEND(&port->messages, (node_t *)msg);
- if (port->ring != NULL) {
- if (DLIST_NODES(&port->messages) == 1) {
- /*
- * We know that there previously were no messages,
- * and that the reading thread then waits for any
- * message to be available. Signal it that there at
- * least is one message ready. The other end should
- * normally process all available messages before
- * going back into waiting.
- */
- if ((error = pthread_mutex_lock(&port->ring->mutex))
- == 0) {
- port->ring->event = 1;
- (void) pthread_cond_signal(&port->ring->cond);
- (void) pthread_mutex_unlock(
- &port->ring->mutex);
- }
- }
- /*
- * If the other end, however, is already locked
- * waiting for the ring to be notified while
- * there already are messages, we still trigger mevent
- * to cause it to unlock, however. This behavior is
- * useful in the polling system code, for instance.
- */
- /* XXX We don't use a mutex for now... */
- port->ring->mevent++;
- }
-
- (void) pthread_mutex_unlock(&port->lock);
-
-err:
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Meant to be used in synchroneous message transfer mode. The initial sender
- * sends a message to the other end, which then uses this function to notify
- * back the initial sender that it is done, often with a success/failure
- * result as part of the message. Returns 0 on success, or an error number.
- */
-int
-pthread_msg_reply(pthread_msg_t *msg)
-{
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(msg != NULL && msg->magic == PMESG_MAGIC &&
- msg->reply != NULL);
-
- DEBUG_PTHREAD_EXIT();
- return pthread_msg_put(msg->reply, msg);
-}
-
-/*
- * Returns the number of pending messages tied to the port, if any, or -1
- * on error.
- */
-int
-pthread_port_pending(pthread_port_t *port)
-{
- int pending = -1;
-
- DEBUG_PTHREAD_ENTRY();
- DEBUG_ASSERT(port != NULL && port->magic == PPORT_MAGIC);
-
- if (pthread_mutex_lock(&port->lock) != 0)
- goto err;
-
- pending = (int)DLIST_NODES(&port->messages);
-
- (void) pthread_mutex_unlock(&port->lock);
-
-err:
- DEBUG_PTHREAD_EXIT();
- return pending;
-}
+++ /dev/null
-/* $Id: mm_pthread_msg.h,v 1.3 2005/09/15 11:46:58 mmondor Exp $ */
-
-/*
- * Copyright (C) 2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef MM_PTHREAD_MSG_H
-#define MM_PTHREAD_MSG_H
-
-
-
-#include <pthread.h>
-
-#include <mmtypes.h>
-#include <mmlist.h>
-
-
-
-#define PRING_MAGIC 0x50524e47
-#define PPORT_MAGIC 0x50505254
-#define PMESG_MAGIC 0x504d5347
-
-typedef struct {
- u_int32_t magic;
- pthread_cond_t cond;
- pthread_mutex_t mutex;
- int mode;
- int event;
- int mevent;
-} pthread_ring_t;
-
-enum pthread_ring_modes {
- PTHREAD_RMOD_NOWAIT,
- PTHREAD_RMOD_CONDWAIT,
- PTHREAD_RMOD_FDWAIT
-};
-
-typedef struct {
- u_int32_t magic;
- pthread_ring_t *ring;
- pthread_mutex_t lock;
- list_t messages;
-} pthread_port_t;
-
-typedef struct {
- node_t node;
- u_int32_t magic;
- pthread_port_t *reply;
- size_t size;
- void *message;
-} pthread_msg_t;
-
-
-
-extern int pthread_ring_init(pthread_ring_t *);
-extern int pthread_ring_valid(pthread_ring_t *);
-extern int pthread_ring_destroy(pthread_ring_t *);
-extern int pthread_ring_wait(pthread_ring_t *,
- const struct timespec *);
-extern int pthread_ring_notify(pthread_ring_t *);
-
-extern int pthread_port_init(pthread_port_t *);
-extern int pthread_port_valid(pthread_port_t *);
-extern int pthread_port_destroy(pthread_port_t *);
-extern int pthread_port_set_ring(pthread_port_t *,
- pthread_ring_t *);
-extern int pthread_msg_init(pthread_msg_t *,
- pthread_port_t *);
-extern int pthread_msg_valid(pthread_msg_t *);
-extern int pthread_msg_destroy(pthread_msg_t *);
-extern pthread_msg_t *pthread_msg_get(pthread_port_t *);
-extern int pthread_msg_put(pthread_port_t *,
- pthread_msg_t *);
-extern int pthread_msg_reply(pthread_msg_t *);
-extern int pthread_port_pending(pthread_port_t *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: mm_pthread_poll.c,v 1.15 2006/02/05 13:00:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * I consider this code to be a major hack around the inherent problems unix
- * systems face because of the lack of support for filedescriptor polling in
- * the POSIX threads API. Although pthread defines methods for thread
- * synchronization and polling waiting for events (using conditionnal
- * variables), and that unix provides polling on filedescriptors using
- * select(2), poll(2), kqueue(2) and other mechanisms, both are totally
- * distinct entities which can be considered to either conflict with
- * eachother or to not be related enough in a unified way. The current
- * situation makes it almost impossible for a thread to both be polling for
- * interthread efficient messages implementations built upon pthread, and
- * filedescriptor events, concurrently.
- *
- * The GNU PTH library implements non-standard functions which allow to
- * multiplex interthread messages and filedescriptor events, using for
- * instance pth_poll_ev(), pth_select_ev(), pth_accept_ev(), pth_connect_ev(),
- * etc. However, this is internally implemented using a single large select(2)
- * based loop along with a slow large loop looking for non-fd events based on
- * the principles of libevent. This threading library has other disadventages,
- * such as not providing a preemptive scheduler (being a fully userspace
- * implementation) and not allowing to scale to multiple processors on SMP
- * systems. This interface however shows how good the POSIX threads API could
- * have been, if it was better designed with unix systems in mind. This
- * library also being the most portable threads library alternative for quite
- * some time, because of the fact that Operating Systems implemented POSIX
- * threads inconsistently, or not at all, caused us to use PTH during some
- * time to develop software in cases where a pool of processes was not ideal
- * because of the frequency of shared memory synchronization needs.
- *
- * With the advent of POSIX threads implementations on more unix and unix-like
- * systems and of modern implementations behaving more consistently, which can
- * scale on SMP systems and provide preemptive scheduling, it was considered
- * worthwhile for us to adapt our software again to use the standard POSIX
- * API. Especially considering that NetBSD which had no OS provided threads
- * implementation for applications now has an awesome pthreads implementation
- * starting with version 2.0. However, we encountered difficulties with some
- * software which used the complex multiplexing of thread events and
- * filedescriptor ones. This module provides a solution to port this software.
- * It however is somewhat a hack.
- *
- * The downsides of this implementation are as follows. We originally intended
- * to develop a system which would scale among an increasing number of threads
- * in a ready pool of threads, scaling with concurrency of the polling calls.
- * This however proved difficult, or impossible to achieve, the main reasons
- * being that 1) A signal delivered to a process is only received by a random
- * thread that is not blocking it. 2) In the case where multiple threads are
- * polling on a common file descriptor, similarily only one random thread
- * is awaken. 3) pthread_cond_signal() and pthread_cond_broadcast() cannot
- * wake threads waiting in filedescriptor polling. 4) to achieve what we
- * needed, two descriptors would have been necessary per notification ring.
- * this was considered an aweful solution and was promptly rejected. 5) The
- * POSIX API does not define a way for a process to set or change the signal
- * blocking masks of other threads on the fly.
- *
- * Our solution then had to rely on a main descriptor polling manager thread
- * which would be used to poll file descriptors, and would as a device serve
- * client threads via efficient interthread messages. An issue still arises
- * when a client thread sends a message to the polling thread to add new
- * descriptors for polling or to cancel polling and remove descriptors.
- * The polling thread must be able to immediately process these events
- * awaking from filedescriptor polling. Two possible hacks could be used for
- * this. 1) Use an AF_LOCAL SOCK_DGRAM socketpair(), which one side would
- * be used to trigger an event writing some data, and the other side always
- * included by the polling thread within the set of descriptors. 2) Send a
- * signal to the process which only the polling thread is not blocking,
- * to ensure that it be the one catching it, as such to awake from polling
- * with an EINTR error code. This second solution was considered more elegant
- * and is used as the basis of this implementation. We currently are
- * clubbering the SIGUSR2 signal to achieve this.
- */
-
-
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <pthread.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <poll.h>
-#include <signal.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <mmtypes.h>
-#include <mmlog.h>
-#include <mmlist.h>
-
-#include <mm_pthread_debug.h>
-#include <mm_pthread_msg.h>
-#include <mm_pthread_pool.h>
-#include <mm_pthread_poll.h>
-
-
-
-MMCOPYRIGHT("@(#) Copyright (c) 2005\n\
-\tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: mm_pthread_poll.c,v 1.15 2006/02/05 13:00:48 mmondor Exp $");
-
-
-
-/*
- * Synchroneous communications message between arbitrary threads and the
- * polling thread. Since communication is synchroneous, we only need to
- * allocate one such message per thread. We are always expecting a reply
- * back after sending a query before reusing the buffer. In fact, the
- * message passing system only serves as a means for synchronization around
- * the message, which is a shared memory object.
- */
-struct poll_msg {
- pthread_msg_t msgnode;
- /* Passed as parameters */
- bool cancel;
- struct pollfd *fds;
- nfds_t nfds;
- int timeout;
- /* Returned as result */
- int ready, error;
- /* Internally used */
- struct timeval expires;
-};
-
-/*
- * An index is maintained of descriptor number -> poll_msg_index
- * structures. Each of wich has information on the message the descriptor
- * belongs to, and the index into the pollfd array so that it be easy to
- * efficiently do per-fd work.
- */
-struct poll_idx {
- int idx;
- struct poll_msg *msg;
-};
-
-/*
- * Thread specific needed resources to use our special polling
- */
-struct poll_data {
- pthread_port_t port;
- struct poll_msg msg;
-};
-
-
-
-#define POLLWAKE() do { \
- pollingevents++; \
- if (polling != 0) \
- (void) kill(process_id, SIGUSR2); \
-} while (/* CONSTCOND */0)
-
-
-
-/*
- * Static functions prototypes
- */
-static int pthread_poll_proc_init(void);
-static void pthread_poll_proc_init2(void);
-static int pthread_poll_thread_init(struct poll_data **);
-static void pthread_poll_thread_exit(void *);
-static void *poll_thread(void *);
-static int poll_thread_attach_fds(struct poll_msg *);
-static void poll_thread_detach_fds(struct poll_msg *);
-static void poll_thread_sighandler(int);
-
-/*
- * Static process specific storage
- */
-static bool pthread_poll_initialized = FALSE;
-static pthread_once_t pthread_poll_proc_initialized = PTHREAD_ONCE_INIT;
-static pthread_key_t pthread_poll_proc_key;
-static pthread_ring_t pthread_poll_thread_started_ring;
-static pthread_port_t pthread_poll_thread_port;
-static pid_t process_id;
-static int polling = 0;
-static int pollingevents = 0;
-
-/*
- * Static global poll_thread storage. No synhronization is necessary when
- * using these, since only the polling thread does.
- */
-static struct poll_idx *poll_idx;
-static nfds_t poll_idx_size;
-static struct pollfd *poll_fds;
-static nfds_t poll_fds_size;
-static nfds_t poll_nfds;
-
-
-
-/*
- * Static internal functions
- */
-
-static int
-pthread_poll_proc_init(void)
-{
- int error;
- struct sigaction act;
-
- DEBUG_PTHREAD_ENTRY();
-
- if ((error = pthread_key_create(&pthread_poll_proc_key,
- pthread_poll_thread_exit)) != 0)
- goto err;
-
- act.sa_handler = poll_thread_sighandler;
- act.sa_flags = 0;
- (void) sigemptyset(&act.sa_mask);
- (void) sigaddset(&act.sa_mask, SIGUSR2);
- if (sigaction(SIGUSR2, &act, NULL) != 0) {
- error = errno;
- goto err;
- }
-
- process_id = getpid();
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-
-err:
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-static void
-pthread_poll_proc_init2(void)
-{
- int error;
-
- DEBUG_PTHREAD_ENTRY();
-
- if ((error = pthread_poll_proc_init()) != 0) {
- (void) fprintf(stderr, "pthread_poll_proc_init() - %s\n",
- strerror(error));
- DEBUG_PTHREAD_EXIT();
- exit(EXIT_FAILURE);
- }
-
- DEBUG_PTHREAD_EXIT();
-}
-
-static int
-pthread_poll_thread_init(struct poll_data **res)
-{
- int error;
- struct poll_data *data;
- sigset_t set;
-
- DEBUG_PTHREAD_ENTRY();
-
- (void) sigemptyset(&set);
- (void) sigaddset(&set, SIGUSR2);
- (void) pthread_sigmask(SIG_BLOCK, &set, NULL);
-
- if ((data = malloc(sizeof(struct poll_data))) == NULL) {
- error = ENOMEM;
- goto err;
- }
-
- if ((error = pthread_port_init(&data->port)) != 0)
- goto err;
- if ((error = pthread_msg_init(&data->msg.msgnode, &data->port)) != 0)
- goto err;
-
- if ((error = pthread_setspecific(pthread_poll_proc_key, data)) != 0)
- goto err;
-
- *res = data;
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-
-err:
- if (data != NULL) {
- (void) pthread_port_destroy(&data->port);
- free(data);
- }
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-static void
-pthread_poll_thread_exit(void *specific)
-{
- struct poll_data *data = (struct poll_data *)specific;
-
- DEBUG_PTHREAD_ENTRY();
-
- (void) pthread_port_destroy(&data->port);
- (void) pthread_msg_destroy(&data->msg.msgnode);
- free(data);
-
- /*
- * Some implementations need this
- */
- (void) pthread_setspecific(pthread_poll_proc_key, NULL);
-
- DEBUG_PTHREAD_EXIT();
-}
-
-
-/*
- * Actual polling thread, with which we communicate using messages polling on
- * pthread_port_t and pthread_ring_t. This is the only thread that should be
- * catching SIGUSR2 signals (used to wake us up and reiterate our main loop.
- * Note: Although less efficient than using kqueue(2) or libevent(3), after
- * discussion with 3s4i we settled to using poll(2) for now, which minimizes
- * OS dependencies as well as third party software dependencies. Because
- * pthread_poll_ring(2) is only sparsely used by our software (migrating from
- * using PTH library which provided pth_poll_ev()), and that we only provide
- * it small pollfd arrays, this implementation was considered to meet our
- * needs using poll(2). This also met the requirements for Tact group.
- */
-/* ARGSUSED */
-static void *
-poll_thread(void *args)
-{
- sigset_t set;
- pthread_ring_t ring;
- list_t msg_list;
- register int i;
-
- DEBUG_PTHREAD_ENTRY();
-
- /*
- * This initialization shouldn't fail. If it did, it would be nice to
- * be able to simply panic eventually. XXX
- */
-
- /*
- * Create set for SIGUSR2 which we'll unblock/block
- */
- (void) sigemptyset(&set);
- (void) sigaddset(&set, SIGUSR2);
-
- /*
- * Allocate an initial buffer size for our pollfd array as well as for
- * our descriptor based index. We'll double these buffers as
- * necessary at runtime.
- */
- poll_fds_size = 64;
- poll_fds = malloc(sizeof(struct pollfd) * poll_fds_size);
- poll_nfds = 0;
- poll_idx_size = 64;
- poll_idx = malloc(sizeof(struct poll_msg) * poll_idx_size);
- for (i = 0; i < poll_idx_size; i++)
- poll_idx[i].msg = NULL;
- DLIST_INIT(&msg_list);
-
- /*
- * Initialize message port and associated ring. The message port is
- * module global, so that it be public to pthread_poll_ring().
- */
- (void) pthread_port_init(&pthread_poll_thread_port);
- (void) pthread_ring_init(&ring);
- (void) pthread_port_set_ring(&pthread_poll_thread_port, &ring);
-
- /*
- * Notify parent that we're ready.
- */
- (void) pthread_ring_notify(&pthread_poll_thread_started_ring);
-
- /*
- * Main loop from which we never exit
- */
- for (;;) {
- register int n;
- int timeout;
- struct timeval tv, ttv;
- struct poll_msg *msg, *nextmsg;
-
- /*
- * Get time of day in a rather high resolution. We need to
- * do this to be able to evaluate timeouts later on. We
- * attempt to only require one time syscall per loop.
- */
- (void) gettimeofday(&tv, NULL);
-
- pollingevents = 0;
-
- /*
- * Process any messages. We need to add the descriptors if
- * they aren't already added. Also store yet unsatisfied
- * request messages into a list.
- */
- while ((msg = (struct poll_msg *)pthread_msg_get(
- &pthread_poll_thread_port)) != NULL) {
- if (msg->cancel) {
- /*
- * Immediately satisfy request on demand
- */
- msg->error = ECANCELED;
- DLIST_UNLINK(&msg_list, (node_t *)msg);
- poll_thread_detach_fds(msg);
- (void) pthread_msg_reply(&msg->msgnode);
- continue;
- }
- if (poll_thread_attach_fds(msg) == 0) {
- msg->ready = msg->error = 0;
- if (msg->timeout != -1) {
- /*
- * Convert millisecond timeout to an
- * absolute time timeval
- */
- msg->expires.tv_sec = tv.tv_sec;
- msg->expires.tv_usec = tv.tv_usec;
- ttv.tv_sec = msg->timeout / 1000;
- ttv.tv_usec = (msg->timeout % 1000)
- * 1000;
- timeradd(&msg->expires, &ttv,
- &msg->expires);
- }
- DLIST_APPEND(&msg_list, (node_t *)msg);
- } else {
- msg->ready = 0;
- msg->error = EINVAL;
- (void) pthread_msg_reply(&msg->msgnode);
- }
- }
-
- /*
- * Process timeouts. For request messages which timed out,
- * satisfy them immediately using ETIMEDOUT error.
- * This also allows to evaluate which is the soonest to expire
- * entry, which poll(2) will have to use as timeout.
- */
- ttv.tv_sec = ttv.tv_usec = 99999;
- for (msg = DLIST_TOP(&msg_list); msg != NULL; msg = nextmsg) {
- nextmsg = DLIST_NEXT(msg);
-
- if (msg->timeout == -1)
- continue;
- if (timercmp(&msg->expires, &tv, <)) {
- msg->error = ETIMEDOUT;
- DLIST_UNLINK(&msg_list, (node_t *)msg);
- poll_thread_detach_fds(msg);
- (void) pthread_msg_reply(&msg->msgnode);
- } else if (timercmp(&msg->expires, &ttv, <)) {
- ttv.tv_sec = msg->expires.tv_sec;
- ttv.tv_usec = msg->expires.tv_usec;
- }
- }
-
- /*
- * If there are no registered descriptors to poll for, wait
- * using the thread friendly ring until messages occur, and
- * reiterate.
- */
- if (poll_nfds == 0) {
- (void) pthread_ring_wait(&ring, NULL);
- continue;
- }
-
- /*
- * Perform polling. poll(2) for as much time as possible,
- * although making sure to allow the soonest to expire query
- * to stop polling. Next to expire entry time is in ttv and
- * current time in tv. Calculate difference and convert to
- * milliseconds.
- */
- if (ttv.tv_sec == 99999 && ttv.tv_usec == 99999)
- timeout = -1;
- else {
- timersub(&ttv, &tv, &ttv);
- timeout = (ttv.tv_sec * 1000) + (ttv.tv_usec / 1000);
- }
-
- /*
- * Unblock the SIGUSR2 signal, which we should be the only
- * thread to receive, all other threads blocking it.
- * Only leave it unblocked for the duration of the poll(2)
- * syscall. We cause our loop to reiterate in any case of
- * error, EINTR or no file descriptor with pending event.
- */
- (void) pthread_sigmask(SIG_UNBLOCK, &set, NULL);
- polling++;
-
- n = 0;
- if (pollingevents != 0)
- goto unblock;
-
- n = poll(poll_fds, poll_nfds, timeout);
-
-unblock:
- polling--;
- (void) pthread_sigmask(SIG_BLOCK, &set, NULL);
- if (pollingevents != 0 || n < 1)
- continue;
-
- /*
- * Verify which descriptors have interesting events set,
- * increasing events counter of corresponding requests.
- */
- for (i = 0; n != 0 && i < poll_nfds; i++) {
- if (poll_fds[i].revents != 0) {
- (poll_idx[poll_fds[i].fd].msg->ready)++;
- n--;
- }
- }
- /*
- * Now verify pending request messages for events, and satisfy
- * the requests of those who do.
- */
- for (msg = DLIST_TOP(&msg_list); msg != NULL; msg = nextmsg) {
- nextmsg = DLIST_NEXT(msg);
-
- if (msg->ready != 0) {
- /*
- * ready and error fields are already set
- */
- DLIST_UNLINK(&msg_list, (node_t *)msg);
- poll_thread_detach_fds(msg);
- (void) pthread_msg_reply(&msg->msgnode);
- }
- }
- }
-
- /* NOTREACHED */
- DEBUG_PTHREAD_EXIT();
- pthread_exit(NULL);
- return NULL;
-}
-
-/*
- * Permits to merge supplied pollfd set with the main set
- */
-static int
-poll_thread_attach_fds(struct poll_msg *msg)
-{
- register int i, fd, idx;
-
- DEBUG_PTHREAD_ENTRY();
-
- for (i = 0; i < msg->nfds; i++) {
- fd = msg->fds[i].fd;
-
- /*
- * Ignore unset descriptors
- */
- if (fd == -1)
- continue;
-
- /*
- * Grow index buffer if necessary. Either grow by doubling
- * size, or even more if necessary to hold index to fd.
- * If we only grew to hold fd, we might need to realloc(3) too
- * often. Take care to also NULL msg field of new entries.
- */
- if (poll_idx_size <= fd) {
- struct poll_idx *idx;
- int size, i2;
-
- size = poll_idx_size * 2;
- if (fd > size)
- size = fd;
- if ((idx = realloc(poll_idx,
- sizeof(struct poll_idx) * size)) == NULL)
- goto err;
- poll_idx = idx;
- for (i2 = poll_idx_size; i2 < size; i2++)
- poll_idx[i2].msg = NULL;
- poll_idx_size = size;
- }
-
- /*
- * Error if descriptor not unique before adding to set.
- * We do not allow multiple threads polling on the same
- * descriptor at the same time in our system. We would
- * otherwise need to gracefully handle duplicates,
- * multiplexing them, which isn't required at all by our
- * applications. So let's keep things simple.
- */
- if (poll_idx[fd].msg != NULL)
- goto err;
-
- /*
- * Resize pollfd array if needed. Grow by doubling.
- * This should happen very rarely.
- * XXX We could check this condition only once at the
- * top of this fonction and take in consideration the
- * number of descriptors to add, if wanted for optimization.
- */
- if (poll_fds_size <= poll_nfds) {
- struct pollfd *ptr;
-
- if ((ptr = realloc(poll_fds,
- sizeof(struct pollfd) * (poll_fds_size * 2)))
- == NULL)
- goto err;
- poll_fds = ptr;
- poll_fds_size *= 2;
- }
-
- /*
- * Finally add descriptor to set and register it for indexing.
- * We simply need to append it to the existing entries in our
- * global polling set array.
- */
- idx = poll_nfds;
- poll_fds[idx].fd = fd;
- poll_fds[idx].events = msg->fds[i].events;
- poll_fds[idx].revents = 0;
- poll_idx[fd].msg = msg;
- poll_idx[fd].idx = idx;
- poll_nfds = ++idx;
- }
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-
-err:
- (void) poll_thread_detach_fds(msg);
-
- DEBUG_PTHREAD_EXIT();
- return -1;
-}
-
-/*
- * Permits to disunite supplied pollfd set from the main set. Also sets the
- * revents fields of the supplied set to the ones of the main set.
- */
-static void
-poll_thread_detach_fds(struct poll_msg *msg)
-{
- register int i, fd, idx;
-
- DEBUG_PTHREAD_ENTRY();
-
- for (i = 0; i < msg->nfds; i++) {
- fd = msg->fds[i].fd;
-
- /*
- * Make sure fd was properly registered
- */
- if (poll_idx[fd].msg != msg)
- continue;
-
- /*
- * Find index in global pollfd set for this fd
- */
- idx = poll_idx[fd].idx;
-
- /*
- * Update pollfd entry according to global one
- */
- msg->fds[i].revents = poll_fds[idx].revents;
-
- /*
- * Unlink fd from the global set. The removal method is
- * simple; Take the last entry of the global set and move it
- * over the current entry, updating index links, and lower
- * the gobal nfds by one. If we're the last entry, simply
- * remove it invalidating its index entry lowering the global
- * nfds.
- */
-
- if (--poll_nfds != idx) {
- /*
- * Not last entry, move last entry over entry to
- * delete.
- */
- register struct pollfd *deleted, *last;
- int deleted_fd, deleted_idx;
-
- last = &poll_fds[poll_nfds];
- deleted = &poll_fds[idx];
- deleted_fd = deleted->fd;
- deleted_idx = poll_idx[deleted_fd].idx;
-
- /* Copy last entry over deleted one */
- deleted->fd = last->fd;
- deleted->events = last->events;
- deleted->revents = last->revents;
-
- /*
- * Reindex last entry which was moved, don't touch
- * the msg pointer though.
- */
- poll_idx[last->fd].idx = deleted_idx;
-
- /* And finally invalidate last entry */
- poll_idx[deleted_fd].msg = NULL;
- } else {
- /* Invalidate last entry */
- poll_idx[poll_fds[poll_nfds].fd].msg = NULL;
- }
- }
-
- DEBUG_PTHREAD_EXIT();
-}
-
-/*
- * Called upon reception of SIGUSR2
- */
-/* ARGSUSED */
-static void
-poll_thread_sighandler(int sig)
-{
-
- DEBUG_PTHREAD_ENTRY();
-
- pollingevents++;
-
- DEBUG_PTHREAD_EXIT();
-}
-
-
-
-/*
- * Public API exported functions
- */
-
-/*
- * Must be called before launching any thread. Sets up the signal mask and
- * launches the dedicated poll slave thread. Important note: this system
- * clobbers the SIGUSR2 signal, which the application can no longer use for
- * other purposes. The only solution to wake the thread manager thread from
- * poll(2) is either to trigger an event through a dedicated filedescriptor,
- * or to send a signal to the process which only the polling thread allows.
- */
-int
-pthread_poll_init(void)
-{
- int error;
- sigset_t set;
- pthread_attr_t attr;
- pthread_t thread;
-
- DEBUG_PTHREAD_ENTRY();
-
- if (pthread_poll_initialized) {
- error = 0;
- goto err;
- }
-
- /*
- * First block SIGUSR2 signal in the parent. The reason why this must
- * be called before the application launches any thread is that
- * threads inherit the sigmask of their parent, and that all threads,
- * but the polling thread, must block the signal. This ensures that
- * only the wanted thread wakes up when a SIGUSR2 signal is received.
- * This way, we can interrupt the polling thread in poll(2), for
- * instance, and cause it to reiterate its main loop.
- */
- (void) sigemptyset(&set);
- (void) sigaddset(&set, SIGUSR2);
- if ((error = pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
- goto err;
-
- /*
- * We'll use this pthread_ring_t to get notification from child that
- * it is ready to process requests before proceeding.
- */
- if ((error = pthread_ring_init(&pthread_poll_thread_started_ring))
- != 0)
- goto err;
-
- /*
- * We may now launch the poll thread and wait for notification from it
- * that it is ready to serve requests. We won't need to exit this
- * thread, so it can be launched in detached state.
- */
- if ((error = pthread_attr_init(&attr)) != 0)
- goto err;
- if ((error = pthread_attr_setdetachstate(&attr, TRUE)) != 0)
- goto err;
- if ((error = pthread_create(&thread, &attr, poll_thread, NULL)) != 0)
- goto err;
-
- /*
- * Wait until thread is ready to serve requests
- */
- (void) pthread_ring_wait(&pthread_poll_thread_started_ring, NULL);
-
- pthread_poll_initialized = TRUE;
-
- return 0;
-
-err:
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * poll(2) replacement which can also be awakened by a notification happening
- * on the specified ring. This for instance allows to process thread messages
- * as well as descriptor events. Like poll(2), returns the number of
- * descriptors with events on success (can be 0), or returns -1 with the
- * specified error set in errno. Unlike poll, the error ETIMEDOUT will occur
- * if the timeout expires before an event existed, or ECANCELLED if a ring
- * notification event occurred instead of a filedescriptor one. Can also
- * return errors such as EINVAL.
- * XXX Check for ETIMEDOUT! We probably don't do this yet. Also, we could
- * return 0 in this case like poll(2).
- */
-int
-pthread_poll_ring(struct pollfd *fds, nfds_t nfds, int timeout,
- pthread_ring_t *ring)
-{
- int error;
- struct poll_data *data;
- pthread_ring_t *oring;
-
- DEBUG_PTHREAD_ENTRY();
-
- if (!pthread_poll_initialized) {
- error = EINVAL;
- goto err;
- }
-
- /*
- * Implicit process and thread specific initializations
- */
- if ((error = pthread_once(&pthread_poll_proc_initialized,
- pthread_poll_proc_init2)) != 0)
- goto err;
- /*
- * XXX Use a mutex or pthread_once() equivalent here too?
- */
- if ((data = pthread_getspecific(pthread_poll_proc_key)) == NULL) {
- if ((error = pthread_poll_thread_init(&data)) != 0)
- goto err;
- }
-
- /*
- * Perform some sanity checking on supplied arguments
- */
- if (fds == NULL || nfds < 1 || ring == NULL || ring->magic !=
- PRING_MAGIC) {
- error = EINVAL;
- goto err;
- }
-
- /*
- * Ensure that our message port's ring uses the same ring which
- * the user supplies us. If we didn't do this we would need to
- * be able to wait for events on more than one ring simultaneously.
- * Because we don't have a ring multiplexer object yet (which would
- * be needed since a ring maps to a conditional variable among other
- * things), we need to do process this way.
- * XXX Could there be a race condition here? It needs to be stressed.
- */
- {
- int mevent;
-
- mevent = (data->port.ring != NULL ?
- data->port.ring->mevent : 0);
- oring = data->port.ring;
- (void) pthread_port_set_ring(&data->port, ring);
- data->port.ring->mevent = mevent;
- }
-
- /*
- * Send query to polling thread. It is safe to simply reuse our
- * message since we then expect a reply back and synchronize it.
- */
- data->msg.cancel = FALSE;
- data->msg.fds = fds;
- data->msg.nfds = nfds;
- data->msg.timeout = timeout;
- if ((error = pthread_msg_put(&pthread_poll_thread_port,
- &data->msg.msgnode)) != 0)
- goto err;
-
- /*
- * Interrupt polling thread which may still be waiting in poll(2).
- * We do this by sending SIGUSR2 to the process, which only the
- * polling thread is not blocking. This causes the thread to reiterate
- * its main loop, thus processing this message and going back to
- * sleep in poll(2).
- */
- POLLWAKE();
-
- /*
- * Wait until en event occurs and notifies our ring. An event could
- * either be triggered by the poll request ending or by another
- * interrupting event on the supplied ring. If a message is queued
- * on the port between pthread_port_set_ring() and
- * pthread_ring_wait(), the latter immediately returns.
- */
- if ((error = pthread_ring_wait(ring, NULL)) != 0)
- goto err;
- if (pthread_msg_get(&data->port) == NULL) {
- /*
- * No message replied back from poll thread yet, this means
- * that our ring was notified by another event. Cancel request
- * by sending event back with the cancel flag, and wait for
- * reply message to occur (which will be the original request
- * results we were waiting for). error field will be set to
- * ECANCELED by the poll thread.
- */
- data->msg.cancel = TRUE;
- (void) pthread_msg_put(&pthread_poll_thread_port,
- &data->msg.msgnode);
- POLLWAKE();
- while (pthread_msg_get(&data->port) == NULL)
- (void) pthread_ring_wait(ring, NULL);
- }
- /* Unclobber user supplied ring from our port events */
- (void) pthread_port_set_ring(&data->port, oring);
-
- /*
- * Error, return error number.
- */
- if (data->msg.error != 0) {
- error = data->msg.error;
- goto err;
- }
-
- /*
- * Success, return number of descriptors with detected events.
- */
- DEBUG_PTHREAD_EXIT();
- return data->msg.ready;
-
-err:
- errno = error;
-
- DEBUG_PTHREAD_EXIT();
- return -1;
-}
-
-/*
- * accept(2) replacement which can both observe a timeout and be interrupted
- * via pthread_ring_t events. Internally implemented using
- * pthread_poll_ring(). Will internally set the descriptor in non-blocking
- * mode if necessary, then reverting it to the mode it was supplied in.
- * Returns a new descriptor on success, or -1 on error, in which case errno
- * is set. errno can then be EINVAL, ETIMEDOUT, ECANCELED, or others.
- * Timeout is in milliseconds, like for poll(2) and can be -1.
- */
-int
-pthread_accept_ring(int s, struct sockaddr *addr, socklen_t *addrlen,
- int timeout, pthread_ring_t *ring)
-{
- int oflags, nflags, d, error = 0;
- struct pollfd fd;
-
- DEBUG_PTHREAD_ENTRY();
-
- if (!pthread_poll_initialized) {
- errno = EINVAL;
- goto err;
- }
-
- /*
- * First get current fcntl status flags, and set descriptor to
- * non-blocking mode if necessary.
- */
- if ((oflags = nflags = fcntl(s, F_GETFL)) == -1)
- goto err;
- if ((oflags & O_NONBLOCK) == 0) {
- nflags |= O_NONBLOCK;
- if (fcntl(s, F_SETFL, nflags) == -1)
- goto err;
- }
-
- if ((d = accept(s, addr, addrlen)) == -1) {
- if (errno != EAGAIN) /* XXX Add others? */
- goto end;
- } else
- goto end;
-
- /*
- * EAGAIN, poll until completion, timeout or ring event.
- */
- fd.fd = d;
- fd.events = POLLIN;
- if ((error = pthread_poll_ring(&fd, 1, timeout, ring)) == 1 &&
- (fd.revents & POLLIN) != 0)
- error = 0;
- else
- error = errno;
-
-end:
- /*
- * Restore supplied descriptor fcntl status flags if necessary
- */
- if (nflags != oflags)
- (void) fcntl(s, F_SETFL, oflags);
-
- if (error != 0) {
- if (d != -1) {
- (void) close(d);
- d = -1;
- }
- errno = error;
- goto err;
- }
-
- DEBUG_PTHREAD_EXIT();
- return d;
-
-err:
- DEBUG_PTHREAD_EXIT();
- return -1;
-}
-
-/*
- * connect(2) replacement which can both observe a timeout and be interrupted
- * via pthread_ring_t events. Internally implemented using
- * pthread_poll_ring(). Will internally set the descriptor in non-blocking
- * mode if necessary, then reverting it back to the mode it was supplied in.
- * Returns 0 on success, or -1, in which case errno is set. errno can be
- * EINVAL, ETIMEDOUT, ECANCELED or others.
- * Timeout is in milliseconds, like for poll(2) and can be -1.
- * For the application to know the actual connection status result, it should
- * poll until completion and verify the status using getsockopt(2) with
- * SOL_SOCKET level and SO_ERROR option. It can alternatively continue to call
- * this function in a loop until completion. Calling the function on an
- * already connected socket will result in EISCONN.
- */
-int
-pthread_connect_ring(int s, const struct sockaddr *name, socklen_t namelen,
- int timeout, pthread_ring_t *ring)
-{
- int oflags, nflags, error = 0;
- struct pollfd fd;
-
- DEBUG_PTHREAD_ENTRY();
-
- if (!pthread_poll_initialized) {
- errno = EINVAL;
- goto err;
- }
-
- /*
- * First get current fcntl status flags, and set descriptor to
- * non-blocking mode if necessary.
- */
- if ((oflags = nflags = fcntl(s, F_GETFL)) == -1)
- goto err;
- if ((oflags & O_NONBLOCK) == 0) {
- nflags |= O_NONBLOCK;
- if (fcntl(s, F_SETFL, nflags) == -1)
- goto err;
- }
-
- if ((error = connect(s, name, namelen)) == -1) {
- if (errno != EINPROGRESS && errno != EALREADY) {
- error = errno;
- goto end;
- }
- } else
- goto end;
-
- /*
- * EINPROGRESS or EALREADY, poll until completion, timeout or ring
- * event.
- */
- fd.fd = s;
- fd.events = POLLOUT;
- if (pthread_poll_ring(&fd, 1, timeout, ring) == 1 &&
- (fd.revents & POLLOUT) != 0) {
- socklen_t l;
-
- /*
- * connect(2) completed, return result
- */
- if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &l) == -1)
- error = errno;
- }
-
-end:
- /*
- * Restore supplied descriptor fcntl status flags if necessary
- */
- if (nflags != oflags)
- (void) fcntl(s, F_SETFL, oflags);
-
- if (error != 0) {
- errno = error;
- goto err;
- }
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-
-err:
- DEBUG_PTHREAD_EXIT();
- return -1;
-}
+++ /dev/null
-/* $Id: mm_pthread_poll.h,v 1.6 2005/11/22 18:03:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef MM_PTHREAD_POLL_H
-#define MM_PTHREAD_POLL_H
-
-
-
-#include <pthread.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <fcntl.h>
-#include <poll.h>
-
-#include <mm_pthread_msg.h>
-
-
-
-extern int pthread_poll_init(void);
-extern int pthread_poll_ring(struct pollfd *, nfds_t, int,
- pthread_ring_t *);
-extern int pthread_accept_ring(int, struct sockaddr *, socklen_t *, int,
- pthread_ring_t *);
-extern int pthread_connect_ring(int, const struct sockaddr *, socklen_t,
- int, pthread_ring_t *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: mm_pthread_pool.c,v 1.7 2006/02/05 13:00:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2004-2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Implementation of a pool of ready threads which adapts with concurrency
- * needs. These ready threads can serve requests passed through efficient
- * inter-thread messaging. mmpool(3) is used for the pool functionality.
- */
-
-
-
-#include <pthread.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <mm_pthread_debug.h>
-#include <mm_pthread_pool.h>
-
-
-
-MMCOPYRIGHT("@(#) Copyright (c) 2004-2005\n\
-\tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: mm_pthread_pool.c,v 1.7 2006/02/05 13:00:48 mmondor Exp $");
-
-
-
-/*
- * STATIC FUNCTIONS PROTOTYPES
- */
-
-inline static pthread_object_t *thread_object_alloc(void);
-inline static void thread_object_free(pthread_object_t *);
-static bool thread_object_constructor(pnode_t *);
-static void thread_object_destructor(pnode_t *);
-static void *thread_object_main(void *);
-
-
-
-/*
- * GLOBALS
- */
-
-static bool thread_object_initialized = FALSE;
-static pthread_attr_t thread_object_attr;
-static pool_t thread_object_pool;
-static pool_t thread_object_msg_pool;
-static pthread_mutex_t thread_object_pool_mutex =
- PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t thread_object_msg_pool_mutex =
- PTHREAD_MUTEX_INITIALIZER;
-static pthread_ring_t thread_started_ring;
-
-
-
-/*
- * EXPORTED PUBLIC FUNCTIONS
- */
-
-/*
- * Must be called to initialize the pthreads pool subsystem, before calling
- * any other function of this API. Returns 0 on success, or an error number.
- * <initial> threads are launched, and more will be launched in increments
- * of <initial> whenever necessary. These will also only be destroyed in
- * decrements of <initial> whenever that many threads have not been in use for
- * some time, and a minimum of <initial> threads will always be kept.
- * Setting <initial> to high values may actually degrade performance with some
- * unefficient threading implementations. It is not recommended to use more
- * than 8 using the pth(3) library. Using NetBSD 2.0+ SA threads, a high
- * number does not reduce performance. We current do not observe any limit
- * whatsoever according to the number of threads launched over time. It is the
- * application's responsibility to ensure to observe decent concurrency limits
- * before calling pthread_object_call().
- */
-int
-pthread_object_init(int initial)
-{
- int error = 0;
-
- DEBUG_PTHREAD_ENTRY();
-
- if (thread_object_initialized) {
- error = EINVAL;
- goto err;
- }
-
- /*
- * Create attributes which will be used for threads of the pool.
- * We want them to be joinable.
- */
- if ((error = pthread_attr_init(&thread_object_attr)) != 0)
- goto err;
- if ((error = pthread_attr_setdetachstate(&thread_object_attr, 0))
- != 0)
- goto err;
-
- /*
- * We use this ring to obtain notification of ready children when
- * launching them. This is required for proper synchronization to
- * avoid aweful race conditions.
- */
- if ((error = pthread_ring_init(&thread_started_ring)) != 0)
- goto err;
-
- /*
- * First initialize the message subsystem pool
- */
- if (!pool_init(&thread_object_msg_pool, "thread_object_msg_pool",
- malloc, free, NULL, NULL, sizeof(pthread_object_msg_t),
- 32768 / sizeof(pthread_object_msg_t), 1, 0)) {
- error = ENOMEM;
- goto err;
- }
-
- /*
- * Now initialize the threads pool. This creates threads, uses
- * synchronization with thread_started_ring, and uses the message
- * subsystem, which all must be initialized and ready.
- */
- if (!pool_init(&thread_object_pool, "thread_object_pool",
- malloc, free, thread_object_constructor, thread_object_destructor,
- sizeof(pthread_object_t), initial, 1, 0)) {
- error = ENOMEM;
- goto err;
- }
-
- thread_object_initialized = TRUE;
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-
-err:
- if (POOL_VALID(&thread_object_msg_pool))
- pool_destroy(&thread_object_msg_pool);
- if (POOL_VALID(&thread_object_pool))
- pool_destroy(&thread_object_pool);
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Allows allocation/creation of a message suitable for asynchronous requests
- * with the threads via their main message port provided by this system.
- * Returns new message, or NULL on error.
- */
-inline pthread_object_msg_t *
-pthread_object_msg_alloc(void)
-{
- pthread_object_msg_t *msg = NULL;
-
- DEBUG_PTHREAD_ENTRY();
-
- if (pthread_mutex_lock(&thread_object_msg_pool_mutex) != 0)
- goto err;
- msg = (pthread_object_msg_t *)pool_alloc(&thread_object_msg_pool,
- FALSE);
- (void) pthread_mutex_unlock(&thread_object_msg_pool_mutex);
-
- (void) pthread_msg_init(&msg->message, NULL);
-
-err:
- DEBUG_PTHREAD_EXIT();
- return msg;
-}
-
-/*
- * Permits to free/destroy a message which was allocated using
- * pthread_object_msg_alloc() and sent asynchroneously.
- */
-inline int
-pthread_object_msg_free(pthread_object_msg_t *msg)
-{
- int error = 0;
-
- DEBUG_PTHREAD_ENTRY();
-
- (void) pthread_msg_destroy(&msg->message);
-
- if ((error = pthread_mutex_lock(&thread_object_msg_pool_mutex)) != 0)
- goto err;
- (void) pool_free((pnode_t *)msg);
- (void) pthread_mutex_unlock(&thread_object_msg_pool_mutex);
-
-err:
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Allows to invoke a thread of the pool to perform execution of the wanted
- * function. This is very efficient since the threads are already created and
- * are waiting for requests. There is no maximum concurrency limit enforced by
- * this system; It is the responsibility of the application to restrict
- * concurrency as necessary by keeping internal information on the current
- * number of requests. 0 is returned on success, or an error number.
- * XXX Add support for synchroneous and asynchroneous operation. Current
- * operation is only asynchroneous, but we would like to add a boolean here to
- * decide. We also could add back the result value of the thread function
- * which would only be useful in synchroneous operation, when we are waiting
- * until the task ends... Of course, it's still easy for applications to use
- * these in a synchroneous manner, by using a message and/or ring,
- * conditionnal variable, etc.
- * Also evaluate if a callback function to be called to notify end of
- * asynchroneous operation would be useful.
- */
-int
-pthread_object_call(pthread_port_t **port,
- void (*function)(pthread_object_t *, void *), void *args)
-{
- pthread_object_t *obj = NULL;
- pthread_object_msg_t *msg = NULL;
- int error;
-
- DEBUG_PTHREAD_ENTRY();
-
- if (function == NULL) {
- error = EINVAL;
- goto err;
- }
-
- /*
- * Allocate a thread from the pool to reserve it, and tell it to call
- * a function via a message. The message cannot be on the stack in
- * this case, since it holds arguments to be passed to a thread, and
- * also consists of an asynchroneous message for wich we do not expect
- * a response back, waiting for it. We just dispatch it and go on.
- */
- if ((obj = thread_object_alloc()) == NULL) {
- error = ENOMEM;
- goto err;
- }
- if ((msg = pthread_object_msg_alloc()) == NULL) {
- error = ENOMEM;
- goto err;
- }
-
- msg->command = PTHREAD_OBJ_CALL;
- msg->u.call.function = function;
- msg->u.call.arguments = args;
- if ((error = pthread_msg_put(obj->port, &msg->message)) != 0)
- goto err;
-
- /*
- * Everything successful;
- * If caller wants the message port of the thread, supply it
- */
- if (port != NULL)
- *port = obj->port;
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-
-err:
- if (msg != NULL)
- pthread_object_msg_free(msg);
- if (obj != NULL)
- thread_object_free(obj);
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-
-
-/*
- * INTERNAL STATIC FUNCTIONS
- */
-
-/*
- * Internally used to allocate a ready thread from the pool.
- */
-inline static pthread_object_t *
-thread_object_alloc(void)
-{
- pthread_object_t *obj = NULL;
-
- DEBUG_PTHREAD_ENTRY();
-
- if (pthread_mutex_lock(&thread_object_pool_mutex) != 0)
- goto err;
- obj = (pthread_object_t *)pool_alloc(&thread_object_pool, FALSE);
- (void) pthread_mutex_unlock(&thread_object_pool_mutex);
-
-err:
- return obj;
-}
-
-/*
- * Internally used to free a no longer needed thread back to the pool of ready
- * threads.
- */
-inline static void
-thread_object_free(pthread_object_t *obj)
-{
-
- DEBUG_PTHREAD_ENTRY();
-
- if (pthread_mutex_lock(&thread_object_pool_mutex) == 0) {
- (void) pool_free((pnode_t *)obj);
- (void) pthread_mutex_unlock(&thread_object_pool_mutex);
- }
-
- DEBUG_PTHREAD_EXIT();
-}
-
-/*
- * Internally called by mmpool(3) to create a thread object.
- */
-static bool
-thread_object_constructor(pnode_t *pnode)
-{
- pthread_object_t *obj = (pthread_object_t *)pnode;
- int success = TRUE;
-
- DEBUG_PTHREAD_ENTRY();
-
- /*
- * Note that we leave thread_object_main() initialize the port field
- * when it creates its port and ring.
- */
- if (pthread_create(&obj->thread, &thread_object_attr,
- thread_object_main, obj) != 0) {
- success = FALSE;
- goto err;
- }
-
- /*
- * Wait until new thread ready notification. Without this, at least
- * with NetBSD 2.0 SA threads, hell would break loose. Thread creation
- * isn't really a bottleneck in our case anyways, since we only need
- * to do it when all threads of the pool are already busy.
- */
- (void) pthread_ring_wait(&thread_started_ring, NULL);
-
-err:
- DEBUG_PTHREAD_EXIT();
- return success;
-}
-
-/*
- * Internally called by mmpool(3) to destroy a thread object.
- */
-static void
-thread_object_destructor(pnode_t *pnode)
-{
- pthread_object_t *obj = (pthread_object_t *)pnode;
- pthread_object_msg_t *msg;
-
- DEBUG_PTHREAD_ENTRY();
-
- /*
- * To be freed, the thread has to be terminated. We thus send it a
- * quit message and then wait for it to exit using pthread_join().
- * Note that we let the thread destroy the port field. Although we
- * theoretically could use a message on the stack here, let's be safe.
- * Thread destruction is only performed rarely anyways, so this isn't
- * a performance problem.
- */
- if ((msg = pthread_object_msg_alloc()) != NULL) {
- msg->command = PTHREAD_OBJ_QUIT;
- (void) pthread_msg_put(obj->port, &msg->message);
- }
- (void) pthread_join(obj->thread, NULL);
-
- DEBUG_PTHREAD_EXIT();
-}
-
-/*
- * Actual thread's main loop. We create a message port and listen for command
- * messages (quit and call). When we obtain a quit request, we destroy the
- * port and exit cleanly. The quit event can never occur during the execution
- * of a call command, since it is only called on already freed thread nodes
- * (by mmpool(3) pool_free()). It is advized to applications which need to
- * obtain and use the port of the thread after thread_object_call() to only
- * send proper user messages, not system reserved ones.
- */
-static void *
-thread_object_main(void *args)
-{
- pthread_object_t *obj = (pthread_object_t *)args;
- pthread_port_t port;
- pthread_ring_t ring;
- pthread_msg_t *imsg;
- pthread_object_msg_t *msg;
-
- DEBUG_PTHREAD_ENTRY();
-
- /*
- * Create our incomming message port as well as its corresponding
- * notification ring we can sleep on. Then advertize our port address.
- * Ideally, we should somehow panic if any of this initialization
- * fails. XXX
- */
- (void) pthread_port_init(&port);
- (void) pthread_ring_init(&ring);
- (void) pthread_port_set_ring(&port, &ring);
- obj->port = &port;
-
- /*
- * Notify parent that we are ready, so that it may proceed
- */
- (void) pthread_ring_notify(&thread_started_ring);
-
- /*
- * Main loop, which keeps executing until we obtain a PTHREAD_OBJ_QUIT
- * message, at which event we cleanly exit.
- */
- for (;;) {
- /*
- * Wait for any message(s) to be available, without taking any
- * CPU time.
- */
- (void) pthread_ring_wait(&ring, NULL);
-
- /*
- * We were awaken because at least one message is available.
- * Process all messages in the queue.
- */
- while ((imsg = pthread_msg_get(&port)) != NULL) {
- msg = (pthread_object_msg_t *)(&((pnode_t *)imsg)[-1]);
- if (msg->command == PTHREAD_OBJ_QUIT) {
- /*
- * We are ordered to exit by the object
- * destructor.
- */
- pthread_object_msg_free(msg);
- goto end;
- }
- if (msg->command == PTHREAD_OBJ_CALL) {
- /*
- * Request to execute a function. This means
- * that we were allocated/reserved first.
- */
- msg->u.call.function(obj,
- msg->u.call.arguments);
- pthread_object_msg_free(msg);
- /*
- * Free/release us back, so that we be
- * available again to process further
- * requests. It is possible that freeing
- * ourselves cause a PTHREAD_OBJ_QUIT message
- * to be queued soon on our port by the
- * destructor function. This is safe, since
- * the destructor does not cause us to be
- * destroyed until it waits for us to have
- * ended cleanly using pthread_join().
- */
- thread_object_free(obj);
- }
- }
- }
-
-end:
- /*
- * Discard messages that are still queued on our port (if any)
- */
- while ((imsg = pthread_msg_get(&port)) != NULL) {
- msg = (pthread_object_msg_t *)(&((pnode_t *)imsg)[-1]);
- pthread_object_msg_free(msg);
- }
- /*
- * Free our resources and exit.
- */
- (void) pthread_port_destroy(&port);
- (void) pthread_ring_destroy(&ring);
-
- DEBUG_PTHREAD_EXIT();
- pthread_exit(NULL);
-
- /* NOTREACHED */
- return NULL;
-}
+++ /dev/null
-/* $Id: mm_pthread_pool.h,v 1.1 2004/12/27 11:16:16 mmondor Exp $ */
-
-/*
- * Copyright (C) 2004-2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef MM_PTHREAD_POOL_H
-#define MM_PTHREAD_POOL_H
-
-
-
-#include <pthread.h>
-
-#include <mmtypes.h>
-#include <mmpool.h>
-
-#include <mm_pthread_msg.h>
-
-
-
-typedef struct {
- pnode_t node;
- pthread_t thread;
- pthread_port_t *port;
-} pthread_object_t;
-
-typedef struct {
- pnode_t node;
- pthread_msg_t message;
- int command;
- union {
- /* PTHREAD_OBJ_CALL, sent to thread_object_main() */
- struct {
- void (*function)(pthread_object_t *, void *);
- void *arguments;
- } call;
- /* PTHREAD_OBJ_QUIT, sent to thread_oject_reaper() */
- pthread_object_t *quit;
- /* PTHREAD_OBJ_USER, custom user messages */
- struct {
- int user_command;
- void *user_data;
- } user;
- } u;
-} pthread_object_msg_t;
-
-enum pthread_object_commands {
- PTHREAD_OBJ_CALL,
- PTHREAD_OBJ_QUIT,
- PTHREAD_OBJ_USER,
- PTHREAD_OBJ_MAX
-};
-
-
-
-extern int pthread_object_init(int);
-extern inline pthread_object_msg_t *pthread_object_msg_alloc(void);
-extern inline int pthread_object_msg_free(
- pthread_object_msg_t *);
-extern int pthread_object_call(pthread_port_t **,
- void (*)(pthread_object_t *,
- void *), void *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: mm_pthread_sleep.c,v 1.5 2006/02/05 13:00:48 mmondor Exp $ */
-
-/*
- * Copyright (C) 2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <pthread.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include <mm_pthread_debug.h>
-#include <mm_pthread_msg.h>
-#include <mm_pthread_sleep.h>
-
-
-
-MMCOPYRIGHT("@(#) Copyright (c) 2005\n\
-\tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: mm_pthread_sleep.c,v 1.5 2006/02/05 13:00:48 mmondor Exp $");
-
-
-
-static int pthread_sleep_proc_init(void);
-static void pthread_sleep_proc_init2(void);
-static int pthread_sleep_thread_init(pthread_ring_t **);
-static void pthread_sleep_thread_exit(void *);
-
-static pthread_key_t pthread_sleep_proc_key;
-static pthread_once_t pthread_sleep_proc_initialized = PTHREAD_ONCE_INIT;
-
-
-
-static int
-pthread_sleep_proc_init(void)
-{
- int error;
-
- DEBUG_PTHREAD_ENTRY();
-
- error = pthread_key_create(&pthread_sleep_proc_key,
- pthread_sleep_thread_exit);
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-static void
-pthread_sleep_proc_init2(void)
-{
- int error;
-
- DEBUG_PTHREAD_ENTRY();
-
- if ((error = pthread_sleep_proc_init()) != 0) {
- (void) fprintf(stderr, "pthread_sleep_proc_init() - %s\n",
- strerror(error));
- DEBUG_PTHREAD_EXIT();
- exit(EXIT_FAILURE);
- }
-
- DEBUG_PTHREAD_EXIT();
-}
-
-static int
-pthread_sleep_thread_init(pthread_ring_t **res)
-{
- int error;
- pthread_ring_t *ring;
-
- DEBUG_PTHREAD_ENTRY();
-
- if ((ring = malloc(sizeof(pthread_ring_t))) == NULL) {
- error = ENOMEM;
- goto err;
- }
-
- if ((error = pthread_ring_init(ring)) != 0)
- goto err;
-
- if ((error = pthread_setspecific(pthread_sleep_proc_key, ring)) != 0)
- goto err;
-
- *res = ring;
-
- DEBUG_PTHREAD_EXIT();
- return 0;
-
-err:
- if (ring != NULL)
- free(ring);
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-static void
-pthread_sleep_thread_exit(void *specific)
-{
- pthread_ring_t *ring = (pthread_ring_t *)specific;
-
- DEBUG_PTHREAD_ENTRY();
-
- (void) pthread_ring_destroy(ring);
- free(ring);
-
- /*
- * Although NetBSD threads don't need this, some pthread
- * implementations do. Some will crash for attempting to reference the
- * already freed memory twice calling us again until we NULL the
- * pointer for the data. Lame, but the POSIX standard was unclear
- * about this.
- */
- (void) pthread_setspecific(pthread_sleep_proc_key, NULL);
-
- DEBUG_PTHREAD_EXIT();
-}
-
-
-
-/*
- * Suspends the calling thread for duration specified in supplied timespec.
- * Returns 0 on success, or an error number.
- */
-int
-pthread_nanosleep(struct timespec *ts)
-{
- int error;
- struct timeval tv;
- struct timespec its;
- pthread_ring_t *ring;
-
- DEBUG_PTHREAD_ENTRY();
-
- /*
- * Process specific initialization if needed
- */
- if ((error = pthread_once(&pthread_sleep_proc_initialized,
- pthread_sleep_proc_init2)) != 0)
- goto err;
- /*
- * Thread specific initialization if needed
- * XXX Use pthread_once() here too, or mutex around ring?
- */
- if ((ring = pthread_getspecific(pthread_sleep_proc_key)) == NULL) {
- if ((error = pthread_sleep_thread_init(&ring)) != 0)
- goto err;
- }
-
- /*
- * Generate absolute time timespec using current time and supplied
- * timespec delay.
- */
- if (gettimeofday(&tv, NULL) == -1) {
- error = errno;
- goto err;
- }
- TIMEVAL_TO_TIMESPEC(&tv, &its);
- timespecadd(&its, ts, &its);
-
- /*
- * We can finally sleep. We expect ETIMEDOUT to be the normal return
- * value in this case, which we convert to a no-error. Other errors
- * will be returned un changed.
- */
- if ((error = pthread_ring_wait(ring, &its)) == ETIMEDOUT)
- error = 0;
-
-err:
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Suspends the current thread for the duration specified into supplied
- * timeval. Returns 0 on success or an error number.
- */
-int
-pthread_microsleep(struct timeval *tv)
-{
- struct timespec ts;
- int error;
-
- DEBUG_PTHREAD_ENTRY();
-
- TIMEVAL_TO_TIMESPEC(tv, &ts);
- error = pthread_nanosleep(&ts);
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Suspends execution of current thread for duration of specified
- * milliseconds. Returns 0 on success or an error number.
- */
-int
-pthread_millisleep(unsigned int ms)
-{
- struct timeval tv;
- int error;
-
- DEBUG_PTHREAD_ENTRY();
-
- tv.tv_sec = ms / 1000;
- tv.tv_usec = (ms % 1000) * 1000;
- error = pthread_microsleep(&tv);
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Suspends execution of thread for duration of specified number of seconds.
- * Returns 0 on success or an error number.
- */
-unsigned int
-pthread_sleep(unsigned int seconds)
-{
- struct timespec ts;
- int error;
-
- DEBUG_PTHREAD_ENTRY();
-
- ts.tv_sec = seconds;
- ts.tv_nsec = 0;
- error = pthread_nanosleep(&ts);
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
-
-/*
- * Suspends execution of thread for durection of specified number of
- * microseconds. Like usleep(3).
- */
-int
-pthread_usleep(useconds_t ms)
-{
- struct timeval tv;
- int error;
-
- DEBUG_PTHREAD_ENTRY();
-
- tv.tv_sec = 0;
- tv.tv_usec = ms;
- error = pthread_microsleep(&tv);
-
- DEBUG_PTHREAD_EXIT();
- return error;
-}
+++ /dev/null
-/* $Id: mm_pthread_sleep.h,v 1.2 2005/09/16 08:49:06 mmondor Exp $ */
-
-/*
- * Copyright (C) 2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef MM_PTHREAD_SLEEP_H
-#define MM_PTHREAD_SLEEP_H
-
-
-
-#include <sys/time.h>
-#include <pthread.h>
-#include <unistd.h>
-
-
-
-extern int pthread_nanosleep(struct timespec *);
-extern int pthread_microsleep(struct timeval *);
-extern int pthread_millisleep(unsigned int);
-extern unsigned int pthread_sleep(unsigned int);
-extern int pthread_usleep(useconds_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: msg_test.c,v 1.3 2005/11/18 10:54:58 mmondor Exp $ */
-
-/*
- * Copyright (C) 2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdarg.h>
-
-#include <mm_pthread_msg.h>
-#include <mm_pthread_pool.h>
-#include <mm_pthread_poll.h>
-
-
-
-MMCOPYRIGHT("@(#) Copyright (c) 2005\n\
-\tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: msg_test.c,v 1.3 2005/11/18 10:54:58 mmondor Exp $");
-
-
-
-#define THREADS 32
-#define ROUNDS 8
-#define TIMEOUT 1
-/*#define PRINTLOCK*/
-/*#define NOPRINT*/
-
-
-
-struct message {
- pthread_msg_t node;
- int id, i;
-};
-
-
-
-int main(void);
-static void threadfunc(pthread_object_t *, void *);
-static void printfunc(const char *, ...);
-
-
-
-static pthread_port_t main_port;
-static pthread_mutex_t print_lock;
-
-
-
-int
-main(void)
-{
- pthread_ring_t ring;
- struct message *msg;
- int i, err;
- int threads_args[THREADS];
- struct timeval tv;
- struct timespec ts, ts1;
-
- if ((err = pthread_mutex_init(&print_lock, NULL)) != 0) {
- (void) printf("main() - stdout lock - %s\n", strerror(err));
- exit(EXIT_FAILURE);
- }
-
- if ((err = pthread_port_init(&main_port)) != 0 ||
- (err = pthread_ring_init(&ring)) != 0 ||
- (err = pthread_port_set_ring(&main_port, &ring)) != 0) {
- printfunc("main() - initialization - %s\n", strerror(err));
- exit(EXIT_FAILURE);
- }
-
- printfunc("Main: launching threads\n");
-
- if ((err = pthread_poll_init()) != 0) {
- printfunc("main() - pthread_poll_init() - %s\n",
- strerror(err));
- exit(EXIT_FAILURE);
- }
-
- /*
- * Initializes a poll of ready threads which can be dispatched
- * functions to execute.
- */
- if ((err = pthread_object_init(THREADS + 1)) != 0) {
- printfunc("main() - pthread_object_init() - %s\n",
- strerror(err));
- exit(EXIT_FAILURE);
- }
-
- /*
- * Now dispatch a main reentrant function to many threads, without
- * waiting for them to complete, in an asynchroneous manner.
- * XXX Because of the way this works, the parent main thread should
- * actually already be listening to messages... We did create a port
- * however, which should queue messages until we reach the main loop.
- */
- for (i = 0; i < THREADS; i++) {
- threads_args[i] = i;
- if ((err = pthread_object_call(NULL, threadfunc,
- &threads_args[i])) != 0)
- printfunc("main() - pthread_object_call() - %s\n",
- strerror(errno));
- }
-
- ts1.tv_sec = TIMEOUT;
- ts1.tv_nsec = 0;
- for (;;) {
- /*
- * Read messages as long as there are any, and reply to each
- * of them in a synchroneous manner.
- */
- while ((msg = (struct message *)pthread_msg_get(&main_port))
- != NULL) {
-
- printfunc(
- "Main: Received message %d from thread #%d\n",
- msg->i, msg->id);
-
- if ((err = pthread_msg_reply((pthread_msg_t *)msg))
- != 0)
- printfunc(
- "Main: pthread_message_reply() - %s\n",
- strerror(err));
- }
-
- /*
- * No more messages to process; Wait for any message(s) to be
- * available.
- * Note that there is special provision in the event where
- * this loop first polling for new messages before processing
- * them, which causes waiting for the ring to immediately
- * return instead of actually waiting if any messages already
- * have been sent.
- */
- printfunc("Main: Waiting for messages\n");
-
- (void) gettimeofday(&tv, NULL);
- TIMEVAL_TO_TIMESPEC(&tv, &ts);
- timespecadd(&ts, &ts1, &ts);
- if ((err = pthread_ring_wait(&ring, &ts)) != 0) {
- printfunc("Main: pthread_ring_wait() - %s\n",
- strerror(err));
- break;
- }
- }
-
- (void) pthread_mutex_destroy(&print_lock);
- (void) pthread_port_destroy(&main_port);
- (void) pthread_ring_destroy(&ring);
-
- return 0;
-}
-
-static void
-threadfunc(pthread_object_t *obj, void *args)
-{
- int id = *(int *)args;
- int i, err;
- struct message msg;
- pthread_port_t rport;
- pthread_ring_t rring;
-
- if ((err = pthread_port_init(&rport)) != 0 ||
- (err = pthread_ring_init(&rring)) != 0 ||
- (err = pthread_port_set_ring(&rport, &rring)) != 0 ||
- (err = pthread_msg_init((pthread_msg_t *)&msg, &rport)) != 0) {
- printfunc("threadfunc() - initialization - %s\n",
- strerror(err));
- return;
- }
-
- msg.id = id;
-
- (void) printfunc("Thread #%d started\n", id);
-
- for (i = 0; i < ROUNDS; i++) {
- /*
- * Prepare and send synchronous message. For asynchronous
- * operation, we would need to allocate a message and to send
- * it, and not expect a reply back immediately, even letting
- * the other end free the message as necessary. In synchronous
- * mode we can use the same message over and over and share
- * its memory area using proper send/reply methods for
- * synchronization.
- */
- msg.i = i;
- if ((err = pthread_msg_put(&main_port, (pthread_msg_t *)&msg))
- != 0)
- printfunc("Thread: pthread_message_put() - %s\n",
- strerror(err));
-
- /* Now wait for synchronous reply and discard it */
- if ((err = pthread_ring_wait(&rring, NULL)) != 0) {
- printfunc("Thread: pthread_ring_wait() - %s\n",
- strerror(err));
- break;
- }
- if (pthread_msg_get(&rport) == NULL)
- printfunc("Thread: pthread_msg_get() == NULL!?\n");
- printfunc("Thread #%d received reply message for %d\n",
- id, i);
- }
-
- printfunc("Thread #%d ending\n", id);
-
- (void) pthread_port_destroy(&rport);
- (void) pthread_ring_destroy(&rring);
- (void) pthread_msg_destroy((pthread_msg_t *)&msg);
-}
-
-static void
-printfunc(const char *fmt, ...)
-{
- char buf[1024];
- va_list arg_ptr;
- int len;
-
-#ifdef NOPRINT
- return;
-#endif
-
- *buf = '\0';
- va_start(arg_ptr, fmt);
- if ((len = vsnprintf(buf, 1023, fmt, arg_ptr)) < 1)
- return;
- va_end(arg_ptr);
-
-#ifdef PRINTLOCK
- (void) pthread_mutex_lock(&print_lock);
-#endif
- (void) fwrite(buf, len, 1, stdout);
-#ifdef PRINTLOCK
- (void) fflush(stdout);
- (void) pthread_mutex_unlock(&print_lock);
-#endif
-}
+++ /dev/null
-/* $Id: poll_test.c,v 1.1 2005/09/14 23:48:10 mmondor Exp $ */
-
-/*
- * Copyright (C) 2005, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdarg.h>
-
-#include <mm_pthread_msg.h>
-#include <mm_pthread_pool.h>
-#include <mm_pthread_poll.h>
-
-
-
-MMCOPYRIGHT("@(#) Copyright (c) 2005\n\
-\tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: poll_test.c,v 1.1 2005/09/14 23:48:10 mmondor Exp $");
-
-
-
-int main(void);
-
-
-
-int
-main(void)
-{
- int err;
-
- if ((err = pthread_poll_init()) != 0) {
- (void) fprintf(stderr, "main() - pthread_poll_init() - %s\n",
- strerror(err));
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * The goal of this program is to verify if it is valid for multiple threads
- * to poll(2) on the same filedescriptor, and if so, what happens whenever
- * an event is triggered on that descriptor.
- *
- * XXX Problems:
- * - Only one of the polling threads seems to be awaken when an event occurs
- * on the descriptor. This probably means that using a signal would be
- * better... I sure don't want to need a filedescriptor per ring...
- * If I did however, would this really hurt? Are there that many rings?
- * But oops, this actually means two filedescriptors for each!
- * using a signal is probably better. However, we then need to clobber some
- * signal... We could use SIGUSR2.
- */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <pthread.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-
-
-#define THREADS 8
-
-
-
-int main(void);
-static void *thread_poll(void *);
-static void *thread_notify(void *);
-static void thread_print(int, const char *);
-
-
-
-static int sockets[2];
-static int threadargs[THREADS];
-static pthread_mutex_t print_mutex;
-static pthread_mutex_t sockets_mutex;
-
-
-
-int
-main(void)
-{
- pthread_t threadid;
- int i;
-
- /*
- * Create socketpair which will be used to trigger events to awaken
- * polling threads.
- */
- if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, sockets) != 0) {
- perror("socketpair()");
- exit(EXIT_FAILURE);
- }
- if (fcntl(sockets[0], F_SETFL, O_NONBLOCK) != 0 ||
- fcntl(sockets[1], F_SETFL, O_NONBLOCK) != 0) {
- perror("fcntl()");
- exit(EXIT_FAILURE);
- }
-
- pthread_mutex_init(&print_mutex, NULL);
- pthread_mutex_init(&sockets_mutex, NULL);
-
- /*
- * First launch THREADS polling threads
- */
- for (i = 0; i < THREADS; i++) {
- threadargs[i] = i;
- pthread_create(&threadid, NULL, thread_poll, &threadargs[i]);
- }
- sleep(1);
-
- /*
- * And finally launch notifyer thread
- */
- pthread_create(&threadid, NULL, thread_notify, NULL);
-
- /*
- * Now just wait
- */
- for (;;)
- (void) pause();
-}
-
-static void *
-thread_poll(void *args)
-{
- struct pollfd fds[1];
- int n;
- int id = *(int *)args;
- char c;
-
- fds[0].fd = sockets[1];
- fds[0].events = POLLIN;
- for (;;) {
- thread_print(id, "Polling");
- if ((n = poll(fds, 1, -1)) == -1) {
- perror("poll()");
- return NULL;
- }
- thread_print(id, "Poll returned");
- if (n == 0) {
- thread_print(id, "Woke up! (no data)");
- continue;
- }
- if ((fds[0].revents & POLLIN) != 0) {
- /* Attempt to read event/byte */
- thread_print(id, "Woke up! (with data)");
- pthread_mutex_lock(&sockets_mutex);
- while ((n = read(sockets[1], &c, 1)) == 1)
- thread_print(id, "Read data!");
- if (n == -1)
- thread_print(id, strerror(errno));
- pthread_mutex_unlock(&sockets_mutex);
- }
- }
-}
-
-/* ARGSUSED */
-static void *
-thread_notify(void *args)
-{
- char c = '\0';
- struct pollfd fds[1];
-
- fds[0].fd = sockets[0];
- fds[0].events = POLLOUT;
- for (;;) {
- sleep(1);
- thread_print(-1, "Notifying");
- pthread_mutex_lock(&sockets_mutex);
- if (write(sockets[0], &c, 1) != 1) {
- /* Poll until we can send data */
- (void) poll(fds, 1, -1);
- }
- pthread_mutex_unlock(&sockets_mutex);
- }
-}
-
-static void
-thread_print(int id, const char *str)
-{
-
- pthread_mutex_lock(&print_mutex);
- printf("%d: %s\n", id, str);
- pthread_mutex_unlock(&print_mutex);
-}
+++ /dev/null
-/*
- * The goal of this program is to verify if it is valid for multiple threads
- * to be awaken from a poll(2) call by a single process-wide signal. This
- * would allow the notifyer of a thread message event to generate this signal
- * if needed to cause interested treads to wake up. Threads which do not want
- * to receive the signal can simply ignore it using pthread_sigmask().
- */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <pthread.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-
-
-#define THREADS 8
-
-
-
-int main(void);
-static void *thread_poll(void *);
-static void *thread_notify(void *);
-static void thread_print(int, const char *);
-
-
-
-static int sockets[2];
-static int threadargs[THREADS];
-static pthread_mutex_t print_mutex;
-static pthread_mutex_t sockets_mutex;
-
-
-
-int
-main(void)
-{
- pthread_t threadid;
- int i;
-
- /*
- * Create socketpair which will be used to trigger events to awaken
- * polling threads.
- */
- if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, sockets) != 0) {
- perror("socketpair()");
- exit(EXIT_FAILURE);
- }
- if (fcntl(sockets[0], F_SETFL, O_NONBLOCK) != 0 ||
- fcntl(sockets[1], F_SETFL, O_NONBLOCK) != 0) {
- perror("fcntl()");
- exit(EXIT_FAILURE);
- }
-
- pthread_mutex_init(&print_mutex, NULL);
- pthread_mutex_init(&sockets_mutex, NULL);
-
- /*
- * First launch THREADS polling threads
- */
- for (i = 0; i < THREADS; i++) {
- threadargs[i] = i;
- pthread_create(&threadid, NULL, thread_poll, &threadargs[i]);
- }
- sleep(1);
-
- /*
- * And finally launch notifyer thread
- */
- pthread_create(&threadid, NULL, thread_notify, NULL);
-
- /*
- * Now just wait
- */
- for (;;)
- (void) pause();
-}
-
-static void *
-thread_poll(void *args)
-{
- struct pollfd fds[1];
- int n;
- int id = *(int *)args;
- char c;
-
- fds[0].fd = sockets[1];
- fds[0].events = POLLIN;
- for (;;) {
- thread_print(id, "Polling");
- if ((n = poll(fds, 1, -1)) == -1) {
- perror("poll()");
- return NULL;
- }
- thread_print(id, "Poll returned");
- if (n == 0) {
- thread_print(id, "Woke up! (no data)");
- continue;
- }
- if ((fds[0].revents & POLLIN) != 0) {
- /* Attempt to read event/byte */
- thread_print(id, "Woke up! (with data)");
- pthread_mutex_lock(&sockets_mutex);
- while ((n = read(sockets[1], &c, 1)) == 1)
- thread_print(id, "Read data!");
- if (n == -1)
- thread_print(id, strerror(errno));
- pthread_mutex_unlock(&sockets_mutex);
- }
- }
-}
-
-/* ARGSUSED */
-static void *
-thread_notify(void *args)
-{
- char c = '\0';
- struct pollfd fds[1];
-
- fds[0].fd = sockets[0];
- fds[0].events = POLLOUT;
- for (;;) {
- sleep(1);
- thread_print(-1, "Notifying");
- pthread_mutex_lock(&sockets_mutex);
- if (write(sockets[0], &c, 1) != 1) {
- /* Poll until we can send data */
- (void) poll(fds, 1, -1);
- }
- pthread_mutex_unlock(&sockets_mutex);
- }
-}
-
-static void
-thread_print(int id, const char *str)
-{
-
- pthread_mutex_lock(&print_mutex);
- printf("%d: %s\n", id, str);
- pthread_mutex_unlock(&print_mutex);
-}
+++ /dev/null
-# $Id: GNUmakefile,v 1.2 2006/05/06 12:25:02 mmondor Exp $
-
-CC := cc
-RM := rm
-UNAME := uname
-
-CFLAGS += -Wall
-
-# Enable for verbosity/debugging
-#CFLAGS += -v -H -g
-#LDFLAGS += -v -g
-
-# And to disable assertions
-CFLAGS += -DNDEBUG
-
-OBJS := main.o
-BINS := rotate
-CBINS := $(addsuffix .exe,$(BINS))
-
-SDL_CFLAGS := $(shell sdl-config --cflags)
-SDL_LDFLAGS := $(shell sdl-config --libs)
-SDL_LDFLAGS += -lSDL_gfx
-
-# OS dependent settings follow
-OS := $(shell $(UNAME) -s)
-ifneq (,$(findstring CYGWIN,$(OS)))
- # cygwin-mingw
- CFLAGS += -mno-cygwin -I/usr/include/mingw
- LDFLAGS += -mwindows -mno-cygwin -L/usr/lib/mingw -L/usr/local/lib
-# GL_CFLAGS :=
-# GL_LDFLAGS := -lopengl32 -lglu32
-else
- # unix
- CFLAGS += -I/usr/include -I/usr/pkg/include -I/usr/X11R6/include
- LDFLAGS += -L/usr/lib -L/usr/pkg/lib -L/usr/X11R6/lib
-# GL_CFLAGS :=
-# GL_LDFLAGS := -lGL -lGLU
-endif
-
-#CFLAGS += $(SDL_CFLAGS) $(GL_CFLAGS)
-#LDFLAGS += $(SDL_LDFLAGS) $(GL_LDFLAGS)
-CFLAGS += $(SDL_CFLAGS)
-LDFLAGS += $(SDL_LDFLAGS)
-
-all: $(BINS)
-
-%.o: %.c
- $(CC) -c $(CFLAGS) -I. -o $@ $<
-
-#$(BINS): $(OBJS)
-# $(CC) -o $@ $(CFLAGS) $(OBJS) $(LDFLAGS)
-
-$(BINS): $(OBJS)
- $(CC) -o $@ $(OBJS) $(LDFLAGS)
-
-clean:
- $(RM) -f $(BINS) $(CBINS) $(OBJS) stdout.txt stderr.txt
+++ /dev/null
-/* $Id: main.c,v 1.1 2006/05/06 12:25:02 mmondor Exp $ */
-
-/*
- * Copyright (C) 2006, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <assert.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <SDL.h>
-#include <SDL_rotozoom.h>
-#include <SDL_gfxPrimitives.h>
-
-
-
-int main(int, char **);
-
-
-
-static SDL_Surface *screen_surface;
-
-
-
-int
-main(int argc, char **argv)
-{
- SDL_Surface *s, *save;
- int a, ad, at;
- size_t fnlen;
- char *fnbuf, *file;
-
- if (argc != 3) {
- (void) fprintf(stderr, "Usage: rotate \"<file>\" <steps>\n");
- exit(EXIT_FAILURE);
- }
-
- if ((file = strdup(argv[1])) != NULL) {
- char *cptr;
-
- if ((cptr = strrchr(file, '.')) != NULL)
- *cptr = '\0';
- } else {
- (void) fprintf(stderr, "strdup(%s)\n", argv[1]);
- exit(EXIT_FAILURE);
- }
-
- fnlen = strlen(file) + 16;
- if ((fnbuf = malloc(fnlen)) == NULL) {
- (void) fprintf(stderr, "fnbuf = malloc(%d)\n", (int)fnlen + 8);
- exit(EXIT_FAILURE);
- }
-
- at = (double)strtol(argv[2], NULL, 10);
- ad = (360 / at);
- if (at * ad != 360) {
- (void) fprintf(stderr, "<steps> must be a divisor of 360\n");
- exit(EXIT_FAILURE);
- }
-
- if (SDL_Init(SDL_INIT_VIDEO) == -1) {
- (void) fprintf(stderr, "SDL_Init() - %s\n", SDL_GetError());
- exit(EXIT_FAILURE);
- }
- if ((screen_surface = SDL_SetVideoMode(640, 480, 32, SDL_DOUBLEBUF))
- == NULL) {
- (void) fprintf(stderr, "SDL_SetVideoMode() - %s\n",
- SDL_GetError());
- goto err;
- }
-
- if ((s = SDL_LoadBMP(argv[1])) == NULL) {
- (void) fprintf(stderr, "SDL_LoadBMP(%s) - %s",
- argv[1], SDL_GetError());
- goto err;
- }
- if ((save = SDL_CreateRGBSurface(SDL_SWSURFACE, s->w, s->h, 32,
- s->format->Rmask, s->format->Gmask, s->format->Bmask,
- s->format->Amask)) == NULL) {
- (void) fprintf(stderr, "SDL_CreateRGBSurface() - %s\n",
- SDL_GetError());
- goto err;
- }
-
- /*
- * XXX Assumes 0x000000 is the trasparent color, should probably be
- * part of the command line parameters.
- */
- {
- Uint32 col;
-
- col = SDL_MapRGB(s->format, 0x00, 0x00, 0x00);
- if (SDL_SetColorKey(s, SDL_SRCCOLORKEY, col) == -1) {
- (void) fprintf(stderr, "SDL_SetColorKey() - %s\n",
- SDL_GetError());
- goto err;
- }
- }
-
- for (a = 0; a < 360; a += ad) {
- SDL_Surface *t;
- SDL_Rect d;
- int cx, cy, c;
-
- cx = s->w / 2;
- cy = s->h / 2;
- c = (cx >= cy ? cx : cy);
-
- /*
- * XXX Again assumes that 0x000000 color is transparent.
- * Moreover, in a future version, we also could allow to scale
- * the bitmap image as wanted...
- */
- if (boxRGBA(save, 0, 0, s->w - 1, s->h - 1,
- 0x00, 0x00, 0x00, 0xff) != 0 ||
- boxRGBA(screen_surface, 320 - c, 240 - c, 320 + c, 240 + c,
- 0x00, 0x00, 0x00, 0xff) != 0) {
- (void) fprintf(stderr, "boxRGBA() - %s\n",
- SDL_GetError());
- goto err;
- }
-
- if ((t = rotozoomSurface(s, (double)-a, 1.0, 1)) == NULL) {
- (void) fprintf(stderr, "rotozoomSurface() - %s\n",
- SDL_GetError());
- goto err;
- }
- d = (SDL_Rect){320 - (t->w / 2), 240 - (t->h / 2), 0, 0};
- if (SDL_BlitSurface(t, NULL, screen_surface, &d) != 0) {
- (void) fprintf(stderr, "SDL_BlitSurface() - %s\n",
- SDL_GetError());
- goto err;
- }
- SDL_FreeSurface(t);
-
- d = (SDL_Rect){320 - c, 240 - c, 320 + c, 240 + c};
- if (SDL_BlitSurface(screen_surface, &d, save, NULL) != 0) {
- (void) fprintf(stderr, "SDL_BlitSurface() - %s\n",
- SDL_GetError());
- goto err;
- }
- (void) snprintf(fnbuf, fnlen - 1, "%s-%03d.bmp", file, (int)a);
- if (SDL_SaveBMP(save, fnbuf) != 0) {
- (void) fprintf(stderr, "SDL_SaveBMP(%s) - %s\n",
- fnbuf, SDL_GetError());
- goto err;
- }
-
- if (SDL_Flip(screen_surface) != 0) {
- (void) fprintf(stderr, "SDL_Flip() - %s",
- SDL_GetError());
- goto err;
- }
- }
-
- SDL_Quit();
- exit(EXIT_SUCCESS);
-
-err:
- SDL_Quit();
- exit(EXIT_FAILURE);
-}
+++ /dev/null
-# $Id: GNUmakefile,v 1.31 2006/05/23 01:32:50 mmondor Exp $
-
-CC := cc
-RM := rm
-UNAME := uname
-TOUCH := touch
-OBJDUMP := objdump
-OBJCOPY := objcopy
-GREP := grep
-AWK := awk
-DATE := date
-STRIP := strip
-
-TMPDIR := /tmp
-
-CFLAGS += -Wall
-
-# Enable for verbosity/debugging
-#CFLAGS += -v -H -g
-#LDFLAGS += -v -g
-
-# And to disable assertions
-CFLAGS += -DNDEBUG
-CFLAGS += -g
-
-OBJS := main.o debug.o pool.o thread_msg.o thread_net_recv.o \
- thread_net_send.o screen.o decode.o
-BINS := client
-CBINS := $(addsuffix .exe,$(BINS))
-RAWOBJS := bmp/RomDD.bmp.enc.o bmp/FedCA.bmp.enc.o wav/nt_cloaked.wav.enc.o \
- wav/nt_uncloak.wav.enc.o wav/nt_shield_up.wav.enc.o \
- wav/nt_shield_down.wav.enc.o wav/nt_fire_torp_other.wav.enc.o \
- wav/nt_plasma_hit.wav.enc.o wav/nt_explosion_other.wav.enc.o \
- fnt/7x13.fnt.enc.o
-
-SDL_CFLAGS := $(shell sdl-config --cflags)
-SDL_LDFLAGS := $(shell sdl-config --libs)
-SDL_LDFLAGS += -lSDL_image -lSDL_mixer -lSDL_net -lSDL_gfx
-
-# OS dependent settings follow
-OS := $(shell $(UNAME) -s)
-ifneq (,$(findstring CYGWIN,$(OS)))
- # cygwin-mingw
- CFLAGS += -mno-cygwin -I/usr/include/mingw -DWIN32
- LDFLAGS += -mwindows -mno-cygwin -L/usr/lib/mingw -L/usr/local/lib
-# GL_CFLAGS :=
-# GL_LDFLAGS := -lopengl32 -lglu32
-else
- # unix
- CFLAGS += -I/usr/include -I/usr/pkg/include -I/usr/X11R6/include
- LDFLAGS += -L/usr/lib -L/usr/pkg/lib -L/usr/X11R6/lib
-# GL_CFLAGS :=
-# GL_LDFLAGS := -lGL -lGLU
-endif
-
-# Determine target of compiled objects so that we may convert binaries
-# to compatible objects using objcopy and then link them like other modules.
-OBJTARGET := $(shell $(TOUCH) $(TMPDIR)/obj.c && \
- $(CC) $(CFLAGS) -c -o $(TMPDIR)/obj.o $(TMPDIR)/obj.c && \
- $(OBJDUMP) -t $(TMPDIR)/obj.o | \
- $(GREP) 'file format' | $(AWK) '{print $$4}' \
- && $(RM) $(TMPDIR)/obj.o $(TMPDIR)/obj.c)
-OBJARCH := $(shell echo $(OBJTARGET) | $(AWK) -F '-' '{print $$2}')
-SEED := $(shell date +%s)
-
-#CFLAGS += $(SDL_CFLAGS) $(GL_CFLAGS)
-#LDFLAGS += $(SDL_LDFLAGS) $(GL_LDFLAGS)
-CFLAGS += $(SDL_CFLAGS)
-LDFLAGS += $(SDL_LDFLAGS)
-
-all: $(BINS)
-
-%.o: %.c
- $(CC) -c $(CFLAGS) -I. -o $@ $<
-
-encode:
- $(CC) -o encode encode.c
-
-$(RAWOBJS): encode
- ./encode $(basename $(basename $@)) $(basename $@) $(SEED)
- $(OBJCOPY) -I binary -B $(OBJARCH) -O $(OBJTARGET) $(basename $@) $@
- $(RM) -f $(basename $@)
-
-$(BINS): $(OBJS) $(RAWOBJS)
- $(CC) -o $@ $(OBJS) $(RAWOBJS) $(LDFLAGS)
- $(STRIP) -s -w -R .comment -R .ident -R .debug* $@*
-
-clean:
- $(RM) -f $(BINS) $(CBINS) $(OBJS) $(RAWOBJS) encode encode.exe \
- stdout.txt stderr.txt
+++ /dev/null
-$Id: README,v 1.16 2006/05/23 01:32:50 mmondor Exp $
-
-An attempt to develop a portable game client using SDL among unix
-and windows operating systems. The only officially supported
-compiler should be GCC (and mingw under windows, avoiding the need
-for cygwin libraries). The compiling/development environment when
-under windows will use cygwin to provide a shell, vim and cvs,
-while compile options will be specified as needed for it to use
-the mingw compiler (which also comes as part of cygwin).
-
-Initial tests will be using SDL, SDL_mixer and SDL_net.
-
-If all works well, it should also be easy to use OpenGL portably.
-I was already able to get portable SDL/OpenGL code working, a while
-ago, but this generated no sound and had no network requirements.
-Hence this test.
-
-Moreover, the test server under kqueue/ needed a client for further
-testing to be possible at its development stage.
-
-Since SDL_net does not provide non-blocking I/O (and it remains
-unclear if windows supports this properly), a decision was made to
-port to SDL an inter-thread messaging library I had done for use
-with POSIX threads, and to use multiple threads.
-
-One thread will be used to send data to the server, another thread
-to receive data form the server, yet another thread to deal with
-all user input events, and a main thread to receive all those events
-in an asynchroneous manner form the utility threads and run the
-main loop.
-
-04:21 <@lucca> one thread to send, one thread to receive, one thread to poll
- for io events, and one thread to bring them all and in the darkness
- bind them.
-
-:)
-
-Ogg-vorbis will be used for music, using SDL_mixer. Ship rotations
-will be performed using SDL_gfx.
-
-It is very important to avoid having to link this client statically
-against GPL or LGPL libraries, because of the viral nature of those
-licenses. I do not intend to release my code under those licenses.
-If it ever publically is released, it shall be done under a MIT/BSD
-derived license.
-
-I am also thinking about dedicating a thread for the connect state
-to the server, or possibly to have the writer or reader thread also
-perform that task.
-
-It is possible that a thread be ideal for rendering as well. It
-could be notified when a screen refresh is wanted, when it could
-set a flag. When it's time for it to draw a frame (honoring FPS),
-it would if the flag is set, or perhaps it simply could when it
-wants. I wonder if it would be appropriate for the display thread
-to not need a mutex, since it would always be read-only accessing
-the data.
-
-Anticipated design so far:
- Main thread
- User events thread
- Network receive thread
- Network Send/Connect thread
- Display thread (the Receive thread will draw for now).
-
-It is possible that states may be desired. For instance, there
-would be the connection state, the one where the user and client
-have to provide authentication information (this could be part of
-connect phase perhaps), and in-game state.
-
-At first, as a test, the world will all be seen by everyone and
-will fit into their screen. A world of 1024x768 could be used for
-this :) Then there will probably be addition of a chat system with
-messages window, to continue enhancing the protocol. Things will
-go on from there...
-
-Hmm for now I want to simply use a joypad.
-- Button 0 fires in direction of the paddle direction.
-- Buttons 1 and 3 could act like button 0, to provide secondary weapon (1)
- and special weapon (3).
-- Button 2 attempts to correct navigation direction in the direction
- of the paddle.
-- Button 6 would accelerate.
-- Button 7 would decelerate.
-- Button 4 could toggle shields
-- Button 5 could toggle cloak
-
-Equivalent keyboard layout:
-- uiojlm,. would change angle just like the gamepad directions.
-- w would toggle cloak
-- s would toggle shield
-- z would thrust up
-- a would thrust down
-- d would cause direction change
-- space would torp
-- f would fire second weapon
-- g would fire special weapon
-
-And we're already out of buttons, we can't beam up/down armies or
-bomb. Unless button 3 was special instead of a special weapon,
-and allowed to perform various commands depending on the paddle
-direction (i.e. up/down to beam up/down, left to bomb). This also
-means that orbiting/launching would need to be automatic. Of course
-all this is if we're thinking about a game like netrek. But we'll
-simply only allow dogfighting at first.
-
-Now it becomes tricky how I'll minimize bandwidth sent from the
-client to the server. Probably that buttons events will be monitored,
-and then current paddle direction when required. In the case of
-direction change button, the last paddle direction applied would
-be remembered, and if the same, the event could be dropped. If
-not, send a direction change packet. What happens if a button
-remains pressed while a direction change occurs? We probably should
-ignore it.
-
-
-Threading limitations under win32
-=================================
-
-There seem to be bugs when using SDL with multiple threads under windows
-which I did not observe on unix systems. The docs specify that the main
-thread should perform the drawing, but it wasn't specified that another
-thread than the initial one would not be able to obtain all user input
-events on windows. Typed keys would not be received, for instance.
-
-It then appears that most of the processing must be done in the main
-initial thread, while only networking related blocking functions will
-be done in slave threads.
-
-There also seem to be other windows-specific problems using SDL threads,
-such as instability. I have noticed that when using another thread
-for user events reception, part of the application would often lockup,
-despite my code properly using mutexes as required, and all sound and
-greaphics being performed by the main initial thread nevertheless.
-These problems were also not found to occur on unix systems.
-
-The design was thus changed for now, and threads will be used for SDL_net
-functions only, since they are blocking. Let's hope that this will work
-stably, however. It remains to be tested.
-
-
-Storing images and sound samples as part of the executable binary
-=================================================================
-
-I was able to include read-only (.rodata) and read-write (.data) into
-binaries directly from files using objcopy and linking them on NetBSD,
-Linux and cygwin-mingw. The SDL_image library, which I now successfully
-built for mingw, includes functions that can use RWops, and the SDL
-library allows to easily create RWops from memory buffers. Moreover, the
-SDL_mixer library also allows to do this to load sound samples. I should
-thus modify the makefiles and code to very easily use these features.
-
-The SDL_mixer library however does not allow by itself to do this easily
-with music files. However, thise generally being considerably larger,
-it should not be a problem and they can remain external.
-
-If this works fine, it would be easy to generate a cryptographic block
-cipher key at build time and to include that key within the executable
-as well. Copies of the binary files to be included could then be
-encrypted using that cipher to a temporary copy which will be linked
-in, and an initialization function could be provided to unencrypt the
-files prior to use. Of course, since the key is also in the executable,
-there is no real security. However, it could prevent computer-illiterate
-people from too easily ripping our original content. RC4 could be used
-for this for instance, or even a much more simple custom encoding
-algorithm :)
-
-
-Using a map
-===========
-
-We probably want to randomize the ship's positions on the map.
-Randomization in this respect could be done on a position in a
-circle of varied radius distance to the location of the actual
-ship. This would prevent clients from being able to exactly fire
-at the actual ship position using the map (which also would make
-cloaking useful against an unofficial client).
-
-
-PROGRAMMING STYLE
-=================
-
-The style chosen for this project consists of the (Net)BSD KNF style
-(Kernel Normal Form) style borrowed from. All code should conform to it.
-Additionally, lint(1)-style comments are used to make the code clearer
-for future code auditors.
-
-
-
-Matt
+++ /dev/null
-For these tests, the winxp ships library were borrowed temporarily.
+++ /dev/null
-/* $Id: conf.h,v 1.6 2006/05/19 11:16:42 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Various hardcoded configuration parameters.
- */
-
-#define SERVER_HOST "hal.xisop"
-#define SERVER_PORT 7777
+++ /dev/null
-/* $Id: debug.c,v 1.2 2006/05/19 09:16:14 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.2 2006/05/19 09:36:57 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef DEBUG_H
-#define DEBUG_H
-
-
-
-#ifndef NDEBUG
-
-/*
- * Macro similar to assert(3) but which does not exit the application. Will
- * instead log the condition unless DEBUG_ASSERT_ABORT is set.
- * Moreover, the aborting one actually simply calls exit(2) after logging the
- * error instead of generating a SIGABRT signal.
- */
-#ifdef ASSERT_ABORT
-#define ASSERT(c) if (!(c)) \
- debug2(__FILE__, __func__, __LINE__, "ASSERT(" #c ") == %d", c);
-#else
-#define ASSERT(c) if (!(c)) \
- debug(__FILE__, __func__, __LINE__, "ASSERT(" #c ") == %d", c);
-#endif
-
-#else
-
-#define ASSERT(c) ;
-
-#endif
-
-
-
-void debug(const char *, const char *, int, const char *, ...);
-void debug2(const char *, const char *, int, const char *, ...);
-
-
-
-#endif
+++ /dev/null
-/* $Id: decode.c,v 1.2 2006/05/10 01:28:01 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Function to easily decode data processed by the encode command.
- */
-
-
-
-#include <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/05/10 00:48:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Function to easily decode data processed by the encode command.
- */
-
-
-
-#ifndef DECODE_H
-#define DECODE_H
-
-
-
-void decode(void **, size_t *, void *, size_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: dlist.h,v 1.5 2006/04/27 10:59:19 mmondor Exp $ */
-
-/*
- * Copyright (C) 2001-2006, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#ifndef DLIST_H
-#define DLIST_H
-
-
-
-typedef struct list list_t;
-typedef struct node node_t;
-
-
-
-struct node {
- node_t *prev, *next;
-};
-
-struct list {
- node_t *top, *bottom;
- int nodes;
-};
-
-
-
-/* Some macros to optimize operations on doubly linked lists */
-#define DLIST_INITIALIZER {NULL, NULL, 0}
-
-#define DLIST_INIT(lst) do { \
- (lst)->top = (lst)->bottom = NULL; \
- (lst)->nodes = 0; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_UNLINK(lst, nod) do { \
- register node_t *prev = (nod)->prev, *next = (nod)->next; \
- \
- if (prev != NULL) \
- prev->next = next; \
- else \
- (lst)->top = next; \
- if (next != NULL) \
- next->prev = prev; \
- else \
- (lst)->bottom = prev; \
- (lst)->nodes--; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_APPEND(lst, nod) do { \
- register node_t *tmp = (lst)->bottom; \
- \
- if (tmp != NULL) { \
- tmp->next = (nod); \
- (nod)->prev = tmp; \
- (nod)->next = NULL; \
- (lst)->bottom = (nod); \
- } else { \
- (lst)->bottom = (lst)->top = (nod); \
- (nod)->next = (nod)->prev = NULL; \
- } \
- (lst)->nodes++; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_INSERT(lst, nod) do { \
- register node_t *tmp = (lst)->top; \
- \
- if (tmp != NULL) { \
- tmp->prev = (nod); \
- (nod)->prev = NULL; \
- (nod)->next = tmp; \
- (lst)->top = (nod); \
- } else { \
- (lst)->top = (lst)->bottom = (nod); \
- (nod)->next = (nod)->prev = NULL; \
- } \
- (lst)->nodes++; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_INSERTAT(lst, atnode, nod) do { \
- register node_t *prev = (atnode)->prev, *next = (atnode); \
- \
- (nod)->next = next; \
- next->prev = (nod); \
- if (prev != NULL) { \
- prev->next = (nod); \
- (nod)->prev = prev; \
- } else { \
- (lst)->top = (nod); \
- (nod)->prev = NULL; \
- } \
- (lst)->nodes++; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_SWAP(dst, src, nod, ins) do { \
- register node_t *prev = (nod)->prev, *next = (nod)->next; \
- \
- if (prev != NULL) \
- prev->next = next; \
- else \
- (src)->top = next; \
- if (next != NULL) \
- next->prev = prev; \
- else \
- (src)->bottom = prev; \
- (src)->nodes--; \
- if ((ins)) { \
- if ((prev = (dst)->top) != NULL) { \
- prev->prev = (nod); \
- (nod)->prev = NULL; \
- (nod)->next = prev; \
- (dst)->top = (nod); \
- } else { \
- (dst)->top = (dst)->bottom = (nod); \
- (nod)->next = (nod)->prev = NULL; \
- } \
- } else { \
- if ((prev = (dst)->bottom) != NULL) { \
- prev->next = (nod); \
- (nod)->prev = prev; \
- (nod)->next = NULL; \
- (dst)->bottom = (nod); \
- } else { \
- (dst)->bottom = (dst)->top = (nod); \
- (nod)->next = (nod)->prev = NULL; \
- } \
- } \
- (dst)->nodes++; \
-} while (/* CONSTCOND */0)
-
-#define DLIST_TOP(lst) ((void *)((list_t *)(lst))->top)
-#define DLIST_BOTTOM(lst) ((void *)((list_t *)(lst))->bottom)
-#define DLIST_NEXT(var) ((void *)((node_t *)(var))->next)
-#define DLIST_PREV(var) ((void *)((node_t *)(var))->prev)
-
-#define DLIST_FOREACH(lst, var) \
- for ((var) = DLIST_TOP((lst)); (var) != NULL; (var) = DLIST_NEXT((var)))
-
-#define DLIST_NODES(lst) (((list_t *)(lst))->nodes)
-
-
-
-#endif
+++ /dev/null
-/* $Id: encode.c,v 1.4 2006/05/19 03:35:43 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHT RESERVED.
- */
-
-/*
- * Very simple encoding algorithm. Given a 32-bit key,
- * it will encode the supplied file using XOR encoding and changing
- * the integer values.
- * For even more simplicity, the key is randomly generated and stored as
- * part of the output file as the first four bytes :)
- * This really shouldn't be considered encryption, it merely is simple
- * encoding for computer-illiterates to not too easily rip our images
- * and sound samples.
- */
-
-
-
-#include <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: main.c,v 1.73 2006/05/23 01:32:50 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>
-
-/* 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.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_FPS
-};
-
-struct font {
- int w, h;
- void *data;
- size_t size;
-};
-
-#define VECTOR_X(x, a, r) ((int)(x) + (cos_table[(a)] * (r)))
-#define VECTOR_Y(y, a, r) ((int)(y) + (sin_table[(a)] * (r)))
-
-
-
-/* PRIVATE PROTOTYPES */
-
-int main(int, char **);
-
-static void axis_angle_update(void);
-static void handle_uevent(SDL_Event *);
-static void handle_recvmsg(thread_amsg_t *);
-static void objects_update(void);
-static void frame_clear(void);
-static void frame_draw(void);
-
-static int surface_blit_angle(SDL_Surface *, int, int, double,
- int);
-static SDL_Surface *bmp_load_key(void *, size_t);
-
-static Mix_Chunk *sample_load(void *, size_t);
-
-static struct font *font_load(void *, size_t, int, int);
-static void font_blit_string(struct font *, int, int,
- const char *, uint8_t, uint8_t, uint8_t, uint8_t);
-
-static Uint32 fpscnt_callback(Uint32, void *);
-
-static void trig_init(void);
-
-
-
-/* PUBLIC GLOBALS */
-
-thread_port_t main_port;
-
-
-
-/* PRIVATE GLOBALS */
-
-static int joy_angle = 0;
-
-static int shields = 0, cloaked = 0, nav_thrust = 0,
- cur_nav_thrust = 0, nav_angle = 0,
- cur_nav_angle = 0, nav_pos_x = 512, nav_pos_y = 384,
- max_thrust = 12;
-static SDL_Surface *rship, *fship;
-
-static Mix_Chunk *snd_cloak, *snd_uncloak, *snd_shield, *snd_unshield,
- *snd_torp, *snd_hit, *snd_explode;
-
-static FPSmanager fpsh;
-static SDL_TimerID fpst;
-static char fpsstr[8];
-static int fpscnt;
-
-static struct font *font;
-
-static double cos_table[360], sin_table[360];
-static int fired = 0;
-
-
-
-/* PRIVATE FUNCTIONS */
-
-/* ARGSUSED */
-int
-main(int argc, char **argv)
-{
- thread_ring_t main_ring;
- SDL_Thread *recv_threadid, *send_threadid;
- Mix_Music *mus;
-
- /* Initialization */
- screen_init();
- trig_init();
- (void) SDL_ShowCursor(0);
- (void) SDL_EnableKeyRepeat(0, 0);
-
- /*
- * Ignore a few events with potentially high frequency but which
- * we don't need
- */
- (void) SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
- (void) SDL_EventState(SDL_JOYAXISMOTION, SDL_IGNORE);
- (void) SDL_EventState(SDL_JOYBALLMOTION, SDL_IGNORE);
- (void) SDL_EventState(SDL_JOYHATMOTION, SDL_IGNORE);
- (void) SDL_EventState(SDL_KEYUP, SDL_IGNORE);
-
- /*
- * Network
- */
- if (SDLNet_Init() != 0) {
- (void) fprintf(stderr, "main() - SDLNet_Init() - %s\n",
- SDLNet_GetError());
- exit(EXIT_FAILURE);
- }
-
- /*
- * Audio
- */
- if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) != 0) {
- (void) fprintf(stderr, "main() - Mix_OpenAudio() - %s\n",
- Mix_GetError());
- exit(EXIT_FAILURE);
- }
- snd_cloak = sample_load((void *)&_binary_wav_nt_cloaked_wav_enc_start,
- (size_t)&_binary_wav_nt_cloaked_wav_enc_size);
- snd_uncloak = sample_load(
- (void *)&_binary_wav_nt_uncloak_wav_enc_start,
- (size_t)&_binary_wav_nt_uncloak_wav_enc_size);
- snd_shield = sample_load(
- (void *)&_binary_wav_nt_shield_up_wav_enc_start,
- (size_t)&_binary_wav_nt_shield_up_wav_enc_size);
- snd_unshield = sample_load(
- (void *)&_binary_wav_nt_shield_down_wav_enc_start,
- (size_t)&_binary_wav_nt_shield_down_wav_enc_size);
- snd_torp = sample_load(
- (void *)&_binary_wav_nt_fire_torp_other_wav_enc_start,
- (size_t)&_binary_wav_nt_fire_torp_other_wav_enc_size);
- snd_hit = sample_load((void *)&_binary_wav_nt_plasma_hit_wav_enc_start,
- (size_t)&_binary_wav_nt_plasma_hit_wav_enc_size);
- snd_explode = sample_load(
- (void *)&_binary_wav_nt_explosion_other_wav_enc_start,
- (size_t)&_binary_wav_nt_explosion_other_wav_enc_size);
- (void) Mix_AllocateChannels(16);
- (void) Mix_ReserveChannels(2);
- if ((mus = Mix_LoadMUS("ogg/1.ogg")) == NULL) {
- (void) fprintf(stderr, "main() - Mix_LoadMUS() - %s\n",
- Mix_GetError());
- exit(EXIT_FAILURE);
- }
- if (Mix_PlayMusic(mus, -1) != 0) {
- (void) fprintf(stderr, "main() - Mix_PlayMusic() - %s\n",
- Mix_GetError());
- exit(EXIT_FAILURE);
- }
-
- /*
- * Bitmap graphics
- */
- rship = bmp_load_key((void *)&_binary_bmp_RomDD_bmp_enc_start,
- (size_t)&_binary_bmp_RomDD_bmp_enc_size);
- fship = bmp_load_key((void *)&_binary_bmp_FedCA_bmp_enc_start,
- (size_t)&_binary_bmp_FedCA_bmp_enc_size);
-
- /*
- * Bitmap fonts
- */
- font = font_load((void *)&_binary_fnt_7x13_fnt_enc_start,
- (size_t)&_binary_fnt_7x13_fnt_enc_size, 7, 13);
-
- /*
- * We're already the main thread.
- * Initialize our message port and notification ring.
- */
- if (thread_ring_init(&main_ring) == -1) {
- (void) fprintf(stderr,
- "main() - thread_ring_init(main_port) - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
- if (thread_port_init(&main_port) == -1) {
- (void) fprintf(stderr,
- "main() - thread_port_init(main_port) - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
- thread_port_set_ring(&main_port, &main_ring);
-
- /* XXX We should obtain user login information */
-
- if (thread_amsg_pool_init() != 0) {
- (void) fprintf(stderr, "main() - thread_amsg_init()\n");
- exit(EXIT_FAILURE);
- }
-
- /* Launch network utility threads */
- if ((recv_threadid = SDL_CreateThread(thread_net_recv, NULL))
- == NULL) {
- (void) fprintf(stderr,
- "main() - SDL_CreateThread(thread_net_recv) - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
- if ((send_threadid = SDL_CreateThread(thread_net_send, NULL))
- == NULL) {
- (void) fprintf(stderr,
- "main() - SDL_CreateThread(thread_net_send) - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
-
- /*
- * XXX Wait until we receive the server's connection status.
- * We should display a "connecting to server" or such message to the
- * user during this time.
- */
- {
- struct msg_connect *cmsg;
-
- while ((cmsg = (struct msg_connect *)thread_msg_get(
- &main_port)) == NULL)
- (void) thread_ring_wait(&main_ring, -1);
-
- if (cmsg->status == -1) {
- (void) fprintf(stderr, "%s\n", cmsg->error);
- exit(EXIT_FAILURE);
- }
-
- (void) thread_msg_reply(&cmsg->msg);
- }
-
- /* FPS counter initialization */
- (void) strcpy(fpsstr, "000 fps");
- fpscnt = 0;
- fpst = SDL_AddTimer(5000, fpscnt_callback, NULL);
-
- /* FPS management initialization */
- SDL_initFramerate(&fpsh);
- SDL_setFramerate(&fpsh, 10);
-
- /*
- * Main loop.
- * Listen for messages and events and process them, while drawing
- * frames.
- *
- * XXX There remain design choices to make here.
- * We could only draw a frame once we received all data for a frame
- * from the server, thus waiting for the EndOfFrame message to draw,
- * in which case we only need to bother drawing the current new
- * received data. If we did this, we potentially could reduce the
- * incomming asynchroneous messages rate so that a single one is sent
- * once every messages for a frame were obtained. This method would
- * probably be the most efficient, while allowing the client to update
- * its display as fast as it is able to obtain the server information
- * for a frame. However, this also means that for a very large world,
- * if a map exists or such, there could be more data needing to be
- * sent per frame, unless there also were in a frame the information
- * to update the existing world information, which would be similar to
- * the second method. Hmm since we need to send a difference packet
- * for every object on the map, or to possibly resend their position,
- * what could be done is having the server sending updates less
- * frequently for that data. Although, we would need to make sure to
- * avoid causing lag to the normal 10fps display when
- * sending/processing large map packets. We possibly could
- * intermingle some map information data per normal frame, having the
- * server round-robin the map information among the clients in a
- * distributed way? Say we have 50 connected clients, and that we
- * want a map update rate of approximately 1 second interval, we could
- * send the data among them at 50 / 10, meaning that we send each
- * client update of 5 clients. With round robin this means that every
- * client, 10 times per second are receiving enough update information
- * so that within a second the whole map be updated. Of course, we
- * would need not to break this when clients connect/disconnect.
- *
- * Or, we could instead maintain our own known world and display
- * states and only update them via the messages received from the
- * server, allowing us to maintain a steady frame rate (although this
- * would also mean that no change could be made between certain
- * frames).
- */
- for (;;) {
- thread_amsg_t *amsg;
- SDL_Event ev;
-
- /* Process incomming server messages. */
- while ((amsg = (thread_amsg_t *)thread_msg_get(&main_port))
- != NULL) {
- handle_recvmsg(amsg);
- thread_amsg_destroy(amsg);
- }
-
- /* Update gamepad current angle. */
- if (gamepad != NULL)
- axis_angle_update();
-
- /*
- * Process incomming user events, sending corresponding
- * messages to the server when appropriate.
- */
- while (SDL_PollEvent(&ev))
- handle_uevent(&ev);
-
- /*
- * XXX Probably to be done on the server, but such a step
- * might still be necessary perhaps.
- * Update objects position in preparation to draw the current
- * frame.
- */
- objects_update();
-
- /*
- * Draw current frame and wait until it's time to draw another
- * frame.
- */
- frame_draw();
- SDL_framerateDelay(&fpsh);
- }
- /* NOTREACHED */
-
- thread_port_set_ring(&main_port, NULL);
- thread_port_destroy(&main_port);
- thread_ring_destroy(&main_ring);
-
- exit(EXIT_SUCCESS);
-}
-
-static void
-axis_angle_update(void)
-{
- int angle, x, y;
-
- angle = joy_angle;
-
- /* On win32 -1 is reported for dead state instead of 0! */
- if ((x = SDL_JoystickGetAxis(gamepad, 0)) > 127)
- x = 1;
- else if (x < -128)
- x = -1;
- else
- x = 0;
-
- if ((y = SDL_JoystickGetAxis(gamepad, 1)) > 127)
- y = 1;
- else if (y < -128)
- y = -1;
- else
- y = 0;
-
- if (x == 0 && y == 0)
- return;
- if (x == 0 && y == -1)
- angle = 0;
- else if (x == 1 && y == -1)
- angle = 45;
- else if (x == 1 && y == 0)
- angle = 90;
- else if (x == 1 && y == 1)
- angle = 135;
- else if (x == 0 && y == 1)
- angle = 180;
- else if (x == -1 && y == 1)
- angle = 225;
- else if (x == -1 && y == 0)
- angle = 270;
- else if (x == -1 && y == -1)
- angle = 315;
-
- if (joy_angle != angle)
- joy_angle = angle;
-
- return;
-}
-
-static void
-handle_uevent(SDL_Event *ev)
-{
-
- if (ev->type == SDL_MOUSEMOTION)
- return;
-
- /*
- * Quit commands events.
- */
- if ((ev->type == SDL_KEYDOWN && ev->key.keysym.sym == SDLK_ESCAPE) ||
- ev->type == SDL_QUIT)
- exit(EXIT_FAILURE);
-
- /*
- * Button events. joy_angle affects several of them.
- * We currently ignore release events.
- */
- if (ev->type == SDL_JOYBUTTONDOWN) {
- int ch;
-
- if (ev->jbutton.state != 1)
- return;
-
- switch (ev->jbutton.button) {
- case 0:
- /* Torp */
- if ((ch = Mix_PlayChannel(-1, snd_torp, 0)) != -1)
- Mix_SetPosition(ch, joy_angle, 50);
- fired = 1;
- break;
- case 1:
- /* Secondary weapon */
- if ((ch = Mix_PlayChannel(-1, snd_hit, 0)) != -1)
- Mix_SetPosition(ch, joy_angle, 50);
- break;
- case 2:
- /* Navigation direction change */
- if (nav_angle != joy_angle)
- nav_angle = joy_angle;
- break;
- case 3:
- /* Special weapon */
- if ((ch = Mix_PlayChannel(-1, snd_explode, 0)) != -1)
- Mix_SetPosition(ch, joy_angle, 50);
- break;
- case 4:
- /* Toggle shields */
- shields = (shields == 0 ? 1 : 0);
- Mix_PlayChannel(1,
- (shields == 1 ? snd_shield : snd_unshield), 0);
- break;
- case 5:
- /* Toggle cloak */
- cloaked = (cloaked == 0 ? 1 : 0);
- Mix_PlayChannel(2,
- (cloaked == 1 ? snd_cloak : snd_uncloak), 0);
- break;
- case 6:
- /* Thrust accelerate */
- if (nav_thrust < max_thrust)
- nav_thrust++;
- break;
- case 7:
- /* Thrust decelerate */
- if (nav_thrust > 0)
- nav_thrust--;
- break;
- }
-
- return;
- }
-
- /*
- * Keyboard events
- */
- if (ev->type == SDL_KEYDOWN) {
- int angle = joy_angle, ch;
-
- switch (ev->key.keysym.sym) {
- /* Angle changes */
- case SDLK_i:
- angle = 0;
- break;
- case SDLK_o:
- angle = 45;
- break;
- case SDLK_l:
- angle = 90;
- break;
- case SDLK_PERIOD:
- angle = 135;
- break;
- case SDLK_COMMA:
- angle = 180;
- break;
- case SDLK_m:
- angle = 225;
- break;
- case SDLK_j:
- angle = 270;
- break;
- case SDLK_u:
- angle = 315;
- break;
- /* Navigation change */
- case SDLK_d:
- /* Direction */
- if (nav_angle != joy_angle)
- nav_angle = joy_angle;
- break;
- case SDLK_z:
- /* Thrust up */
- if (nav_thrust < max_thrust)
- nav_thrust++;
- break;
- case SDLK_a:
- /* Thrust down */
- if (nav_thrust > 0)
- nav_thrust--;
- break;
- /* Weapons */
- case SDLK_SPACE:
- /* Torp */
- if ((ch = Mix_PlayChannel(-1, snd_torp, 0)) != -1)
- Mix_SetPosition(ch, joy_angle, 50);
- fired = 1;
- break;
- case SDLK_f:
- /* Secondary weapon */
- if ((ch = Mix_PlayChannel(-1, snd_hit, 0)) != -1)
- Mix_SetPosition(ch, joy_angle, 50);
- break;
- case SDLK_g:
- /* Special weapon */
- if ((ch = Mix_PlayChannel(-1, snd_explode, 0)) != -1)
- Mix_SetPosition(ch, joy_angle, 50);
- break;
- /* Toggles */
- case SDLK_s:
- /* Shield */
- shields = (shields == 0 ? 1 : 0);
- Mix_PlayChannel(1,
- (shields == 1 ? snd_shield : snd_unshield), 0);
- break;
- case SDLK_w:
- /* Cloak */
- cloaked = (cloaked == 0 ? 1 : 0);
- Mix_PlayChannel(2,
- (cloaked == 1 ? snd_cloak : snd_uncloak), 0);
- break;
- default:
- break;
- }
-
- if (joy_angle != angle)
- joy_angle = angle;
- return;
- }
-
-}
-
-static void
-handle_recvmsg(thread_amsg_t *amsg)
-{
-
- /* XXX */
- if (amsg->size != -1)
- (void) fwrite(amsg->data, amsg->size, 1, stdout);
- else {
- (void) fprintf(stderr, "Error reading from server socket\n");
- exit(EXIT_FAILURE);
- }
-}
-
-/*
- * These will normally occur on the server.
- * However, it's nice for temporary testing.
- */
-static void
-objects_update(void)
-{
-
- /*
- * XXX
- * Adjust current thrust according to ship's acceleration/deceleration
- * speeds in order to eventually reach nav_thrust. It seems that
- * we need floating point variables to perform this.
- */
- if (cur_nav_thrust != nav_thrust) {
- if (cur_nav_thrust < nav_thrust)
- cur_nav_thrust++;
- else
- cur_nav_thrust--;
- }
-
- /*
- * XXX
- * For now we also should use cos/sin and allow the ship to move
- * around in its current direction at its current thrust.
- * We should also bounce when reaching the borders for now,
- * by reversing its angle.
- */
- if (cur_nav_thrust != 0) {
- nav_pos_x = VECTOR_X(nav_pos_x, cur_nav_angle,
- cur_nav_thrust + 1);
- nav_pos_y = VECTOR_Y(nav_pos_y, cur_nav_angle,
- cur_nav_thrust + 1);
- }
-
- /*
- * XXX
- * We need to rotate cur_nav_angle in the shortest direction to
- * nav_angle, while making sure to always have angles in the range
- * 0 - 359 only. Moreover, if the degrees stepped are too large
- * to exactly reach nav_angle, we want to just reach it at the last
- * step. To properly test the later condition, we step relatively
- * to the current thrust.
- */
- if (cur_nav_angle != nav_angle) {
- if (cur_nav_angle < nav_angle) {
- if ((cur_nav_angle += (max_thrust + 10) -
- cur_nav_thrust) > nav_angle)
- cur_nav_angle = nav_angle;
- } else {
- if ((cur_nav_angle -= (max_thrust + 10) -
- cur_nav_thrust) < nav_angle)
- cur_nav_angle = nav_angle;
- }
- }
-}
-
-static void
-frame_clear(void)
-{
- uint32_t *ptr, *tptr;
-
- /*
- for (ptr = screen_surface->pixels,
- tptr = &ptr[screen_width * screen_height];
- ptr < tptr; ) {
- *ptr++ = 0x00000000;
- *ptr++ = 0x00000000;
- *ptr++ = 0x00000000;
- *ptr++ = 0x00000000;
- *ptr++ = 0x00000000;
- *ptr++ = 0x00000000;
- *ptr++ = 0x00000000;
- *ptr++ = 0x00000000;
- }
- */
-
- for (ptr = screen_surface->pixels,
- tptr = &ptr[screen_width * screen_height];
- ptr < tptr; ptr = &ptr[8]) {
- ptr[0] = 0x00000000;
- ptr[1] = 0x00000000;
- ptr[2] = 0x00000000;
- ptr[3] = 0x00000000;
- ptr[4] = 0x00000000;
- ptr[5] = 0x00000000;
- ptr[6] = 0x00000000;
- ptr[7] = 0x00000000;
- }
-}
-
-static void
-frame_draw(void)
-{
-
- frame_clear();
- /* SDL_FillRect(screen_surface, NULL, 0); */
-
- surface_blit_angle(rship, nav_pos_x, nav_pos_y, cur_nav_angle, 0);
- if (cloaked)
- filledCircleRGBA(screen_surface, nav_pos_x, nav_pos_y, 25,
- 0x00, 0x00, 0x00, 0x80);
- if (shields) {
- aacircleRGBA(screen_surface, nav_pos_x, nav_pos_y, 25,
- 0xe0, 0xe0, 0x20, 0x60);
- filledCircleRGBA(screen_surface, nav_pos_x, nav_pos_y, 25,
- 0xf0, 0xf0, 0x30, 0x40);
- }
- aalineRGBA(screen_surface,
- VECTOR_X(nav_pos_x, cur_nav_angle, 40),
- VECTOR_Y(nav_pos_y, cur_nav_angle, 40),
- VECTOR_X(nav_pos_x, cur_nav_angle, 60),
- VECTOR_Y(nav_pos_y, cur_nav_angle, 60),
- 0xff, 0xff, 0xff, 0x80);
-
- if (fired) {
- int i;
-
- fired = 0;
- for (i = 0; i < 255; i++) {
- pixelRGBA(screen_surface,
- VECTOR_X(nav_pos_x, joy_angle, i),
- VECTOR_Y(nav_pos_y, joy_angle, i),
- 0xff, 0xff, 0x00, 0xff - i);
- }
- }
-
- {
- char str[256];
-
- (void) snprintf(str, 255,
- "Thrust: %02d/%02d (%02d), "
- "Angle: %03d (%03d), "
- "Position: (%03d,%03d), "
- "Shields: %3s, "
- "Cloak: %3s",
- cur_nav_thrust, max_thrust, nav_thrust,
- cur_nav_angle, nav_angle,
- nav_pos_x, nav_pos_y,
- (shields == 1 ? "On" : "Off"),
- (cloaked == 1 ? "On" : "Off"));
- font_blit_string(font, 17, 17, str,
- 0x00, 0x00, 0x00, 0xff);
- font_blit_string(font, 16, 16, str,
- 0xff, 0xff, 0xff, 0xff);
- }
-
- font_blit_string(font, 17, 741, fpsstr, 0x00, 0x00, 0x00, 0xff);
- font_blit_string(font, 16, 740, fpsstr, 0xff, 0xff, 0xff, 0xff);
-
- (void) SDL_Flip(screen_surface);
- fpscnt++;
-}
-
-static SDL_Surface *
-bmp_load_key(void *data, size_t size)
-{
- SDL_RWops *rwo;
- SDL_Surface *s;
- Uint32 c;
- void *ndata;
- size_t nsize;
-
- decode(&ndata, &nsize, data, size);
-
- if ((rwo = SDL_RWFromMem(ndata, nsize)) == NULL)
- goto err;
- if ((s = IMG_LoadBMP_RW(rwo)) == NULL)
- goto err;
- SDL_FreeRW(rwo);
-
- c = SDL_MapRGB(s->format, 0, 0, 0);
- if (SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, c) == -1)
- goto err;
-
- return s;
-
-err:
- (void) fprintf(stderr, "bmp_load_key() - %s\n", SDL_GetError());
- exit(EXIT_FAILURE);
-}
-
-static int
-surface_blit_angle(SDL_Surface *s, int x, int y, double a, int clean)
-{
- SDL_Surface *t;
- SDL_Rect d;
- int r;
-
- ASSERT(a > -1 && a < 360);
-
- if (clean) {
- int cx, cy, c;
-
- cx = s->w / 2 + 2;
- cy = s->h / 2 + 2;
- c = (cx >= cy ? cx : cy);
- (void) boxRGBA(screen_surface, x - c, y - c,
- x + c, y + c, 0x00, 0x00, 0x00, 0xff);
- }
-
- if ((t = rotozoomSurface(s, -a, 1.0, 1)) != NULL) {
- d = (SDL_Rect){x - (t->w / 2), y - (t->h / 2), 0, 0};
- r = SDL_BlitSurface(t, NULL, screen_surface, &d);
- SDL_FreeSurface(t);
- } else
- r = -1;
-
- return r;
-}
-
-static Mix_Chunk *
-sample_load(void *data, size_t size)
-{
- SDL_RWops *rwo;
- Mix_Chunk *c;
- void *ndata;
- size_t nsize;
-
- decode(&ndata, &nsize, data, size);
-
- if ((rwo = SDL_RWFromMem(ndata, nsize)) == NULL)
- goto err;
-
- if ((c = Mix_LoadWAV_RW(rwo, 0)) == NULL)
- goto err;
-
- SDL_FreeRW(rwo);
- return c;
-
-err:
- (void) fprintf(stderr, "sample_load() - %s\n", SDL_GetError());
- exit(EXIT_FAILURE);
-}
-
-static struct font *
-font_load(void *data, size_t size, int width, int height)
-{
- struct font *font;
- void *ndata;
- size_t nsize;
-
- decode(&ndata, &nsize, data, size);
-
- if ((font = malloc(sizeof(struct font))) == NULL)
- goto err;
-
- font->data = ndata;
- font->w = width;
- font->h = height;
- font->size = nsize;
- return font;
-
-err:
- (void) fprintf(stderr, "font_load()\n");
- exit(EXIT_FAILURE);
-}
-
-static void
-font_blit_string(struct font *font, int x, int y, const char *str,
- uint8_t r, uint8_t g, uint8_t b, uint8_t a)
-{
-
- (void) gfxPrimitivesSetFont(font->data, font->w, font->h);
- (void) stringRGBA(screen_surface, x, y, str, r, g, b, a);
-}
-
-/* ARGSUSED */
-static Uint32
-fpscnt_callback(Uint32 interval, void *args)
-{
-
- (void) sprintf(fpsstr, "%03d fps", fpscnt / 5);
- fpscnt = 0;
-
- return interval;
-}
-
-/* Initialize trigonometric tables */
-static void
-trig_init(void)
-{
- int i;
- double f;
-
- for (i = 0; i < 360; i++) {
- f = ((2 * M_PI) / 360) * (i - 90);
- sin_table[i] = sin(f);
- cos_table[i] = cos(f);
- }
-}
+++ /dev/null
-/* $Id: main.h,v 1.3 2006/05/19 09:13:42 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Exported resources by the main program, mainly for thread modules.
- */
-
-
-
-#ifndef MAIN_H
-#define MAIN_H
-
-
-
-#include <SDL.h>
-
-#include <thread_msg.h>
-
-
-
-extern thread_port_t main_port;
-
-
-
-#endif
+++ /dev/null
-For how, place any ogg track that you like as 1.ogg under this directory.
-Eventually there shall be some of my own composed music, when I have time
-and care enough to add it.
-
-Matt
+++ /dev/null
-/* $Id: packets.c,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * To validate a received packet, we'll make sure that it's larger than
- * sizeof(int), and that the first integer consists of a valid expected packet
- * type ID within range. If so, we verify if packet length really corresponds
- * to the packet type, and then can interpret the packet information.
- * An actual network packet may contain a number of these packets.
- * We thus must be able to efficiently determine each packet's length.
- * Since we're using TCP, it should be enough. However, on the server side
- * especially, proper sanity checking on input must be done to avoid crashing
- * the server because of unexpected data processing.
- */
-
-
-
-#include <stdlib.h>
-
-#include <mmstring.h>
-#include <mmarch.h>
-
-#include "packets.h"
-#include "client.h"
-#include "sendq.h"
-#include "conf.h"
-
-
-
-static int cpacket_auth_handler(client_t *, uint16_t *);
-static int cpacket_ping_handler(client_t *, uint16_t *);
-static int cpacket_pong_handler(client_t *, uint16_t *);
-static int cpacket_direction_handler(client_t *, uint16_t *);
-static int cpacket_thrust_handler(client_t *, uint16_t *);
-static int cpacket_torp_handler(client_t *, uint16_t *);
-static int cpacket_quit_handler(client_t *, uint16_t *);
-
-
-
-static struct packet_index cpacket_index[CPACKET_MAX] = {
- {sizeof(struct cpacket_auth), cpacket_auth_handler},
- {sizeof(struct cpacket_ping), cpacket_ping_handler},
- {sizeof(struct cpacket_pong), cpacket_pong_handler},
- {sizeof(struct cpacket_direction), cpacket_direction_handler},
- {sizeof(struct cpacket_thrust), cpacket_thrust_handler},
- {sizeof(struct cpacket_torp), cpacket_torp_handler},
- {sizeof(struct cpacket_quit), cpacket_quit_handler}
-};
-
-
-
-void
-update(void)
-{
- node_t *nod, *next;
- client_t *c;
- uint8_t *data;
- size_t size, off;
-
- for (nod = DLIST_TOP(&clients_list); nod != NULL; nod = next) {
- next = DLIST_NEXT(nod);
- c = (client_t *)nod;
-
- if (c->todestroy || c->toclose)
- continue;
-
- /* Reset ping request throttling flag */
- c->askedping = 0;
-
- /* First process incomming packets */
- recvq_content(&c->recvq, &data, &size);
- for (off = 0; off < size; ) {
- int16_t *id;
-
- /*
- * Verify packet type ID. It already has been
- * converted to host endian byte order, unlike other
- * packet fields.
- */
- id = (int16_t *)&data[off];
- *id = BYTEORDER_HOST16(*id);
- if (*id < 0 || *id > CPACKET_MAX) {
- client_destroy_mark(c);
- break;
- }
-
- /* There must be enough data for packet size */
- if (size - off < cpacket_index[*id].size) {
- /*
- * Kill authentucated client if other packets
- * than CPACKET_AUTH
- */
- if (!c->authenticated && *id != CPACKET_AUTH)
- goto k;
-
- /*
- * XXX We'll need to also do special
- * processing for chat packets, since they'll
- * have arbitrary length. They'll still need
- * to be 16-bit aligned, too.
- */
-
- /* Kill client on packet processing error */
- if (cpacket_index[*id].handler(c,
- (uint16_t *)off) == -1)
- goto k;
- } else
- goto k;
-
- /* Process next packet if any */
- off += cpacket_index[*id].size;
- continue;
-
-k:
- client_destroy_mark(c);
- break;
- }
-
- /*
- * XXX I don't like having to run the clients list all over,
- * to then run through all objects (including clients) to
- * update them, and then yet again through all objects to send
- * updates to all clients.
- */
-
- /* Run game frame on all objects */
- DLIST_FOREACH(&clients_list, nod) {
- client_t *c = (client_t *)nod;
-
- /*
- if (!c->authenticated)
- continue;
- */
-
- /*
- * First update direction and thrust.
- * XXX I could do a small function or macro to deal
- * with similar situations, i.e.
- * change_update(int goal, int *current, int steps);
- */
- if (c->object.direction < c->object.i_direction)
- c->object.direction++;
- else if (c->object.direction > c->object.i_direction)
- c->object.direction--;
- if (c->object.thrust < c->object.i_thrust)
- c->object.thrust++;
- else if (c->object.thrust > c->object.i_thrust)
- c->object.thrust--;
-
- /*
- * Translate object x/y position according to
- * trust/direction. When reaching borders we simply
- * bounce for now.
- * (I need to review my trigonometry a bit again :)
- */
- /* XXX */
- c->object.x += 1 - (random() & 2);
- if (c->object.x < 0)
- c->object.x = 0;
- else if (c->object.x > WORLD_X_MAX - 1)
- c->object.x = WORLD_X_MAX - 1;
- c->object.y += 1 - (random() & 2);
- if (c->object.y < 0)
- c->object.y = 0;
- else if (c->object.y > WORLD_Y_MAX - 1)
- c->object.y = WORLD_Y_MAX - 1;
- }
-
- /* Send update frame information packets */
- DLIST_FOREACH(&clients_list, nod) {
- client_t *c = (client_t *)nod;
- node_t *nod2;
-
- /*
- if (!c->authenticated)
- continue;
- */
-
- DLIST_FOREACH(&clients_list, nod2) {
- if (spacket_position_send(c,
- &((client_t *)nod2)->object) == -1)
- break;
- }
- (void) sendq_flush(&c->sendq, -1);
- }
- }
-}
-
-
-int
-spacket_auth_send(client_t *c)
-{
- struct spacket_auth p;
-
- p.packet_type = BYTEORDER_NETWORK16(SPACKET_AUTH);
- mm_memclr(p.string, 32);
- mm_strncpy(p.string, SERVER_STRING, 31);
- p.protocol_version = BYTEORDER_NETWORK16(SERVER_VERSION);
- /* XXX */
-
- return client_write(c, (uint8_t *)&p, sizeof(p), 0);
-}
-
-int
-spacket_pong_send(client_t *c)
-{
- struct spacket_pong p;
-
- p.packet_type = BYTEORDER_NETWORK16(SPACKET_PONG);
-
- return client_write(c, (uint8_t *)&p, sizeof(p), 0);
-}
-
-int
-spacket_position_send(client_t *c, object_t *o)
-{
- struct spacket_position p;
-
- p.packet_type = BYTEORDER_NETWORK16(SPACKET_POSITION);
- p.object_id = p.object_type = BYTEORDER_NETWORK16(0); /* XXX */
- p.x = BYTEORDER_NETWORK16(o->x);
- p.y = BYTEORDER_NETWORK16(o->y);
- p.direction - BYTEORDER_NETWORK16(o->direction);
-
- return client_write(c, (uint8_t *)&p, sizeof(p), 1);
-}
-
-
-/* Note that only the packet_type field is endian converted yet. */
-
-static int
-cpacket_auth_handler(client_t *c, uint16_t *ptr)
-{
- struct cpacket_auth *p = (struct cpacket_auth *)ptr;
-
- p->protocol_version = BYTEORDER_HOST16(p->protocol_version);
-
- if (c->authenticated || p->protocol_version < SERVER_VERSION)
- return -1;
-
- /*
- * XXX For now. Eventually also check user/password
- * We could use APOP-like authentication where server sends random
- * data as part of the authentication greeting/request, and expect
- * client to append password to random data and send hashed result.
- * We also should do user concurrency checking here.
- */
- c->authenticated = 1;
-
- return 0;
-}
-
-/*
- * Note: Following function is never used, since pings are handled in the
- * kqueue main loop code.
- */
-/* ARGSUSED */
-static int
-cpacket_ping_handler(client_t *c, uint16_t *ptr)
-{
- /* NOOP */
-
- return 0;
-}
-
-static int
-cpacket_pong_handler(client_t *c, uint16_t *ptr)
-{
-/* struct cpacket_pong *p = (struct cpacket_pong *)ptr;*/
-
- /* XXX */
-
- return 0;
-}
-
-static int
-cpacket_direction_handler(client_t *c, uint16_t *ptr)
-{
- struct cpacket_direction *p = (struct cpacket_direction *)ptr;
-
- p->direction = BYTEORDER_HOST16(p->direction);
-
- /* Radians */
- if (p->direction < 0 || p->direction > 1000)
- return -1;
-
- c->object.i_direction = p->direction;
-
- return 0;
-}
-
-static int
-cpacket_thrust_handler(client_t *c, uint16_t *ptr)
-{
- struct cpacket_thrust *p = (struct cpacket_thrust *)ptr;
-
- p->thrust = BYTEORDER_HOST16(p->thrust);
-
- if (p->thrust < 0 || p->thrust > 20)
- return -1;
-
- c->object.i_thrust = p->thrust;
-
- return 0;
-}
-
-static int
-cpacket_torp_handler(client_t *c, uint16_t *ptr)
-{
-/* struct cpacket_torp *p = (struct cpacket_torp *)ptr;*/
-
- /* XXX */
-
- return 0;
-}
-
-/* ARGSUSED */
-static int
-cpacket_quit_handler(client_t *c, uint16_t *ptr)
-{
-
- return -1;
-}
+++ /dev/null
-/* $Id: packets.h,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * All packets must begin with an int16_t packet_type. All fields of a packet
- * must either be [u]int8_t, [u]int16_t, [u]int32+t or [u]int64_t. Moreover,
- * all fields will be passed in network/big endian byte order when sent over
- * the network. We could have used a union, but this would have resulted in
- * larger, fixed sized packets wasting bandwidth. Since a server to client
- * update will generally involve sending more than one such packet in a row,
- * it is important to minimize their size.
- * Note: If using 32-bit fields within a packet, make sure that they are
- * 32-bit aligned.
- */
-
-
-
-#ifndef _PACKETS_H_
-#define _PACKETS_H_
-
-
-
-#include <stdint.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 *);
-};
-
-
-
-/*
- * Server to client packets
- */
-
-enum spacket_types {
- SPACKET_AUTH = 0,
- SPACKET_PING,
- SPACKET_PONG,
- SPACKET_POSITION,
- SPACKET_COLLISION,
- SPACKET_MAX
-};
-
-/* Used to request client authentication and respond success/failure */
-struct spacket_auth {
- int16_t packet_type;
- int16_t protocol_version;
- char string[32];
- /* XXX */
-} __attribute__((__packed__));
-
-/* And for server to test idle connections */
-struct spacket_ping {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-/* And clients to test latency */
-struct spacket_pong {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-/* Object position/direction update to client */
-struct spacket_position {
- int16_t packet_type;
- int16_t object_id, object_type;
- int16_t x, y, direction;
-} __attribute__((__packed__));
-
-/* Collision/detonation update to client */
-struct spacket_collision {
- int16_t packet_type;
- int16_t collision_type;
- int16_t object1_id, object2_id;
-} __attribute__((__packed__));
-
-
-
-/*
- * Client to server packets
- */
-
-enum cpacket_tyoes {
- CPACKET_AUTH = 0,
- CPACKET_PING,
- CPACKET_PONG,
- CPACKET_DIRECTION,
- CPACKET_THRUST,
- CPACKET_TORP,
- CPACKET_QUIT,
- CPACKET_MAX
-};
-
-/* Used to respond to server authentication request */
-struct cpacket_auth {
- int16_t packet_type;
- int16_t protocol_version;
- char string[32];
- /* XXX */
-} __attribute__((__packed__));
-
-/* To ping server for latency mesurement */
-struct cpacket_ping {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-/* To respond to server ping requests */
-struct cpacket_pong {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-/* Angle/direction change request */
-struct cpacket_direction {
- int16_t packet_type;
- int16_t direction;
-} __attribute__((__packed__));
-
-/* Speed change request */
-struct cpacket_thrust {
- int16_t packet_type;
- int16_t thrust;
-} __attribute__((__packed__));
-
-/* Torpedo fire request */
-struct cpacket_torp {
- int16_t packet_type;
- int16_t direction;
-} __attribute__((__packed__));
-
-/* Game quit request */
-struct cpacket_quit {
- int16_t packet_type;
-} __attribute__((__packed__));
-
-
-
-#endif
+++ /dev/null
-/* $Id: pool.c,v 1.3 2006/05/19 09:36:57 mmondor Exp $ */
-
-/*
- * Copyright (C) 2001-2006, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <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/04/27 10:59:19 mmondor Exp $ */
-
-/*
- * Copyright (C) 2001-2006, Matthew Mondor
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Matthew Mondor.
- * 4. The name of Matthew Mondor may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- * 5. Redistribution of source code may not be released under the terms of
- * any GNU Public License derivate.
- *
- * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-
-#ifndef MPOOL_H
-#define MPOOL_H
-
-
-
-#include <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.4 2006/05/10 00:48:40 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/* XXX This file should probably be auto-generated */
-
-
-
-#ifndef RAWOBJS_H
-#define RAWOBJS_H
-
-
-
-/*
- * On win32, binary objects must be referenced without the underscore prefix.
- * Let's then define a few aliases.
- */
-#ifdef WIN32
-
-#define _binary_bmp_FedCA_bmp_enc_size binary_bmp_FedCA_bmp_enc_size
-#define _binary_bmp_FedCA_bmp_enc_start binary_bmp_FedCA_bmp_enc_start
-#define _binary_bmp_RomDD_bmp_enc_size binary_bmp_RomDD_bmp_enc_size
-#define _binary_bmp_RomDD_bmp_enc_start binary_bmp_RomDD_bmp_enc_start
-
-#define _binary_wav_nt_cloaked_wav_enc_size binary_wav_nt_cloaked_wav_enc_size
-#define _binary_wav_nt_cloaked_wav_enc_start binary_wav_nt_cloaked_wav_enc_start
-#define _binary_wav_nt_explosion_other_wav_enc_size binary_wav_nt_explosion_other_wav_enc_size
-#define _binary_wav_nt_explosion_other_wav_enc_start binary_wav_nt_explosion_other_wav_enc_start
-#define _binary_wav_nt_fire_torp_other_wav_enc_size binary_wav_nt_fire_torp_other_wav_enc_size
-#define _binary_wav_nt_fire_torp_other_wav_enc_start binary_wav_nt_fire_torp_other_wav_enc_start
-#define _binary_wav_nt_plasma_hit_wav_enc_size binary_wav_nt_plasma_hit_wav_enc_size
-#define _binary_wav_nt_plasma_hit_wav_enc_start binary_wav_nt_plasma_hit_wav_enc_start
-#define _binary_wav_nt_shield_down_wav_enc_size binary_wav_nt_shield_down_wav_enc_size
-#define _binary_wav_nt_shield_down_wav_enc_start binary_wav_nt_shield_down_wav_enc_start
-#define _binary_wav_nt_shield_up_wav_enc_size binary_wav_nt_shield_up_wav_enc_size
-#define _binary_wav_nt_shield_up_wav_enc_start binary_wav_nt_shield_up_wav_enc_start
-#define _binary_wav_nt_uncloak_wav_enc_size binary_wav_nt_uncloak_wav_enc_size
-#define _binary_wav_nt_uncloak_wav_enc_start binary_wav_nt_uncloak_wav_enc_start
-
-#define _binary_fnt_7x13_fnt_enc_start binary_fnt_7x13_fnt_enc_start
-#define _binary_fnt_7x13_fnt_enc_size binary_fnt_7x13_fnt_enc_size
-
-#endif
-
-
-
-/*
- * And export symbols to other modules
- */
-
-extern void *_binary_bmp_FedCA_bmp_enc_size;
-extern void *_binary_bmp_FedCA_bmp_enc_start;
-extern void *_binary_bmp_RomDD_bmp_enc_size;
-extern void *_binary_bmp_RomDD_bmp_enc_start;
-
-extern void *_binary_wav_nt_cloaked_wav_enc_size;
-extern void *_binary_wav_nt_cloaked_wav_enc_start;
-extern void *_binary_wav_nt_explosion_other_wav_enc_size;
-extern void *_binary_wav_nt_explosion_other_wav_enc_start;
-extern void *_binary_wav_nt_fire_torp_other_wav_enc_size;
-extern void *_binary_wav_nt_fire_torp_other_wav_enc_start;
-extern void *_binary_wav_nt_plasma_hit_wav_enc_size;
-extern void *_binary_wav_nt_plasma_hit_wav_enc_start;
-extern void *_binary_wav_nt_shield_down_wav_enc_size;
-extern void *_binary_wav_nt_shield_down_wav_enc_start;
-extern void *_binary_wav_nt_shield_up_wav_enc_size;
-extern void *_binary_wav_nt_shield_up_wav_enc_start;
-extern void *_binary_wav_nt_uncloak_wav_enc_size;
-extern void *_binary_wav_nt_uncloak_wav_enc_start;
-
-extern void *_binary_fnt_7x13_fnt_enc_start;
-extern void *_binary_fnt_7x13_fnt_enc_size;
-
-
-
-#endif
+++ /dev/null
-/* $Id: screen.c,v 1.11 2006/05/08 21:05:17 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Display related initialization.
- */
-
-
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <SDL.h>
-
-#include <conf.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_destroy(void)
-{
-
- SDL_Quit();
-}
+++ /dev/null
-/* $Id: screen.h,v 1.3 2006/05/08 08:17:00 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Screen related initialization.
- */
-
-
-
-#ifndef SCREEN_H
-#define SCREEN_H
-
-
-
-#include <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: draw.c,v 1.1 2006/05/03 14:20:47 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * XXX TODO XXX
- * - Fix most all code using multiplication to locate x,y pixel positions into
- * the display to use the screen_lines array for optimization.
- * This however could cause a problem for SDL surfaces other than the screen
- * which are involved. A possibility to fix this would be to wrap the
- * SDL_Surface into a custom structure, which could also hold the optimized
- * line position array for each...
- * - Implement a diff function, to fill a surface with the difference between
- * two other surfaces... This might be good for some type of effects and
- * animations. Hmm there possibly would be a problem with the 0 transparent
- * color though... Consider following situation:
- * - Diff detects new 0 color pixels in second image, how would we effect
- * transformation of first image to second one, since we can't blit zero
- * color images? If we did blit zero color, how would we detect what
- * pixels consist in the difference? Perhaps that we would need a special
- * diff format rather than just blitting differences into a zero color
- * surface. Diff format could be very similar to runlength-style encoding
- * format, if not the same, perhaps. I would only need to add section
- * support, other than just compression... I.E. section starting at x,y,
- * of total length l, followed by runlength compressed section...
- * Such diffs might also be nice to send over the network when images change
- * or such...
- * - Implement simple runlength compression and decompression support, with
- * possibly load/save image support.
- * - Maybe implement simple animation format based on above diff and runlength
- * support. It would be enough to implement small animated sets with limited
- * motion.
- * - Implement ellipse and ellipse arc support
- * - Splines support would be great but I would need to read about it.
- * If really required, SDL_gfx might be worth using.
- * - Once all required primitives have been implemented, allow support for
- * parallel postscript commands output, so that it would be possible to
- * print results. This would allow to have consistent on-screen and printer
- * results (this is not game related, it could be used for applications).
- * * Postscript has all of the above. It actually would be nice if I could
- * have my application draw postscript in real time while internally keeping
- * the necessary information in memory of the screen state to be able to
- * also print to a postscript printer. It seems that ghostscript has a
- * graphics library. Can it be used by a third party application easily?
- * Also, if I used postscript, how would I allow bitmap images to be
- * supported? Especially as the relative scaling of everything would have
- * to be respected?
- * - It also would be possible to simply use X11R6. However, this would
- * limit client applications to run on unix systems in most cases.
- * Moreover, I probably would still require some kind of hack to
- * internally keep track of postscript equivalents for printing, or the
- * contrary, to process using postscript and draw using X11, etc...
- * I of course still would need to provide my own toolkit.
- * - The main problem I would have with both above techniques is dealing
- * with fonts. Obviously, fonts would need to be vectorial, and in a way
- * which is useful for postscript output. I would need to have an onscreen
- * WYSIWYG renderer for the same type of fonts. X11 can supposedly output
- * GS fonts to screen, using the X Font Server (XFS)... But this requires
- * clients to have a special configuration, once more.
- * NOTE: This is fixed, SDL_ttf can be used.
- * - It would be possible to use bitmap fonts, as long as they are of the
- * right size to be equivalent to the postscript output, and that the
- * screen has proper visual to represent the page... If we support
- * proportional fonts, some new code would need to be added. Moreover, we
- * probably would not want the proportional fonts to be used where the
- * user types text, to ease the editor's tasks... We would need to at
- * least support fixed/courrier, times and helvetica. We would need a font
- * format which can specify the size of each character of proportional
- * fonts. Maybe an aditionnal data file along with the bitmap file...
- */
-
-
-
-/* HEADERFILES */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <math.h>
-
-#include <SDL.h>
-
-#include <draw.h>
-#include <conf.h>
-#include <screen.h>
-
-
-
-/* DEFINITIONS */
-
-#ifdef ANTIALIASING /* conf.h */
-#define DRAW_PIXEL draw_pixel_antialias
-#else
-#define DRAW_PIXEL draw_pixel
-#endif
-
-
-
-/* STATIC FUNCTIONS PROTOTYPES */
-
-
-
-/* GLOBALS */
-
-static draw_point_t *stack_start, *stack_pos, pen;
-static double sin_table[1000], cos_table[1000];
-static uint32_t **screen_lines;
-
-
-
-/* PRIVATE FUNCTIONS */
-
-
-
-/* PUBLIC FUNCTIONS */
-
-int
-draw_init(void)
-{
-
- /* Allocate memory for draw_fill_*() stack */
- if ((stack_start = malloc(sizeof(draw_point_t) *
- (screen_width * screen_height) * 4)) == NULL)
- return -1;
-
- /*
- * Allocate and initialize fast screen display line index lookup table,
- * which can be used by pixel drawing routines without the need for
- * multiplications.
- */
- {
- uint32_t *lptr;
- int i;
-
- if ((screen_lines = malloc(sizeof(uint32_t *) *
- screen_height)) == NULL)
- return -1;
-
- for (lptr = (uint32_t *)(screen_surface->pixels), i = 0;
- i < screen_height; i++, lptr += screen_width)
- screen_lines[i] = lptr;
- }
-
- /*
- * Initialize our sin/cos trigonometric functions arrays for
- * performance when mesuring rotations in radians.
- * XXX We should add pixel ratio modifyer too here.
- */
- {
- int i;
- double f;
-
- for (i = 0; i < 1000; i++) {
- f = ((2 * M_PI) / 1000) * (i - 250);
- sin_table[i] = sin(f);
- cos_table[i] = cos(f);
- }
- }
-
- return 0;
-}
-
-/* The following functions require that screen_surface be locked. */
-
-/* Draws a vertical line at specified position and of specified height */
-inline void
-draw_vline(draw_point_t *p, int height, uint32_t col)
-{
- register uint32_t *ptr;
- register int i;
-
- assert(p != NULL);
- assert(!(p->x < 0 || p->x >= screen_width ||
- p->y < 0 || p->y + height > screen_height));
-
- /* XXX Should use lines array */
- for (ptr = &((uint32_t *)screen_surface->pixels)[
- (p->y * screen_width) + p->x], i = height; i > 0;
- i--, ptr = &ptr[screen_width])
- *ptr = col;
-}
-
-/* Draws an horizontal line at specified position and of specified width */
-inline void
-draw_hline(draw_point_t *p, int width, uint32_t col)
-{
- register uint32_t *ptr, *tptr;
-
- assert(p != NULL);
- assert(!(p->x < 0 || p->x + width > screen_width ||
- p->y < 0 || p->y >= screen_height));
-
- /* XXX Should use lines array */
- for (ptr = &((uint32_t *)screen_surface->pixels)[
- (p->y * screen_width + p->x)], tptr = &ptr[width]; ptr < tptr; )
- *ptr++ = col;
-}
-
-/*
- * Draws a rectangle of specified colors. Two colors can be specified for a
- * special 3d-like effect (although both can be the same if wanted).
- */
-inline void
-draw_rect(draw_rect_t *r, uint32_t hicol, uint32_t locol)
-{
- draw_point_t p;
-
- assert(r != NULL);
- assert(!(r->x < 0 || r->x + r->w > screen_width ||
- r->y < 0 || r->y + r->h > screen_height));
-
- p.x = r->x;
- p.y = r->y;
- draw_hline(&p, r->w, hicol);
- p.y++;
- draw_vline(&p, r->h - 1, hicol);
- p.x = r->x + r->w - 1;
- p.y--;
- draw_vline(&p, r->h, locol);
- p.x = r->x + 1;
- p.y = r->y + r->h - 1;
- draw_hline(&p, r->w - 1, locol);
-}
-
-/*
- * Draws rectangular box at (x,y) to (x+w-1,y+h-1), using lcol for the 3d
- * border light color, dcol for 3d border dark color, filled with fcol (or 0
- * for transparent interior) and of specified 3d depth.
- */
-void
-draw_rect_3d(draw_rect_t *r, uint32_t hicol, uint32_t locol, uint32_t fillcol,
- int depth)
-{
- draw_rect_t rect;
-
- assert(r != NULL);
- assert(!(r->x < 0 || r->x + r->w > screen_width ||
- r->y < 0 || r->y + r->h > screen_height));
-
- if (fillcol != 0) {
- rect.x = r->x + 1;
- rect.y = r->y + 1;
- rect.w = r->w - 2;
- rect.h = r->h - 2;
- draw_rect_fill(&rect, fillcol);
- }
- rect = *r;
- for (; depth > 0; depth--, rect.x += 1, rect.y += 1,
- rect.w -= 2, rect.h -= 2)
- draw_rect(&rect, hicol, locol);
-}
-
-/* Allows to fill specified rectangle with specified color */
-inline void
-draw_rect_fill(draw_rect_t *r, uint32_t col)
-{
- register uint32_t *ptr, *tptr = NULL, *lptr;
-
- assert(r != NULL);
- assert(!(r->x < 0 || r->x + r->w > screen_width ||
- r->y < 0 || r->y + r->h > screen_height));
-
- if (r->w == screen_width) {
- /*
- * Even better optimization, setting an entire single block of
- * multiple lines together.
- * XXX Should use lines array
- */
- for (ptr = &((uint32_t *)screen_surface->pixels)[
- (r->y * screen_width) + r->x],
- tptr = &ptr[screen_width * r->h];
- ptr < tptr; )
- *ptr++ = col;
- } else {
- /* Use line-based optimizations */
- /* XXX Should use lines array */
- for (ptr = &((uint32_t *)screen_surface->pixels)[
- (r->y * screen_width) + r->x],
- tptr = &((uint32_t *)screen_surface->pixels)[
- ((r->y + r->h - 1) * screen_width) + (r->x + r->w)];
- ptr < tptr; ptr = &ptr[screen_width - r->w]) {
- for (lptr = &ptr[r->w]; ptr < lptr; )
- *ptr++ = col;
- }
- }
-}
-
-/*
- * Similar to draw_rect_fill(), but causes pixels to be XORed against m,
- * to toggle their bits. This is reversable by another call to this function
- * on the same area again. This can be useful for the implementation of a
- * block cursor. However, because of the disposition of the current 256 color
- * palette, the effect might not be what is expected (some colors will render
- * too bright or too dark to be considered an inversing effect).
- */
-void
-draw_rect_fill_xor(draw_rect_t *r, uint32_t mask)
-{
- register uint32_t *ptr, *toptr, *lptr;
-
- assert(r != NULL);
- assert(!(r->x < 0 || r->x + r->w > screen_width ||
- r->y < 0 || r->y + r->h > screen_height));
-
- /* XXX Should use lines array */
- for (ptr = &((uint32_t *)screen_surface->pixels)[
- (r->y * screen_width) + r->x],
- toptr = &((uint32_t *)screen_surface->pixels)[
- ((r->y + r->h - 1) * screen_width) + (r->x + r->w)];
- ptr < toptr; ptr = &ptr[screen_width - r->w]) {
- for (lptr = &ptr[r->w]; ptr < lptr; )
- *ptr++ ^= mask;
- }
-}
-
-/*
- * Similar to draw_rect_fill(), but causes pixels which are not of the bgcol
- * to be reversed to the bgcol. Other pixels, which already were of bgcol are
- * set to col. This function is useful for the implementation of a block
- * cursor.
- */
-void
-draw_rect_fill_inverse(draw_rect_t *r, uint32_t col, uint32_t bgcol)
-{
- register uint32_t *ptr, *toptr, *lptr;
-
- assert(r != NULL);
- assert(!(r->x < 0 || r->x + r->w > screen_width ||
- r->y < 0 || r->y + r->h > screen_height));
-
- /* XXX Should use lines array */
- for (ptr = &((uint32_t *)screen_surface->pixels)[
- (r->y * screen_width) + r->x],
- toptr = &((uint32_t *)screen_surface->pixels)[
- ((r->y + r->h - 1) * screen_width) + (r->x + r->w)];
- ptr < toptr; ptr = &ptr[screen_width - r->w]) {
- for (lptr = &ptr[r->w]; ptr < lptr; ptr++)
- *ptr = (*ptr == bgcol ? col : bgcol);
- }
-}
-
-/*
- * Very similar to draw_rect_fill(), but causes empty/transparent pixels to be
- * left at 50%. Can be used to simulate transparency without the need for
- * alpha values. Especially useful with restricted palettes, or simply to
- * create an effect.
- */
-void
-draw_rect_fill_transparent(draw_rect_t *r, uint32_t col)
-{
- register uint32_t *ptr, *toptr, *lptr;
- int even;
-
- assert(r != NULL);
- assert(!(r->x < 0 || r->x + r->w > screen_width ||
- r->y < 0 || r->y + r->h > screen_height));
-
- /* XXX Should use lines array */
- for (ptr = &((uint32_t *)screen_surface->pixels)[
- (r->y * screen_width) + r->x],
- toptr = &((uint32_t *)screen_surface->pixels)[
- ((r->y + r->h - 1) * screen_width) + (r->x + r->w)], even = 0;
- ptr < toptr;
- ptr = &ptr[screen_width - r->w], even = (even == 0 ? 1 : 0)) {
- ptr += even;
- for (lptr = &ptr[r->w]; ptr < lptr; ptr += 2)
- *ptr = col;
- ptr -= even;
- }
-}
-
-/*
- * Similar to SDL_BlitSurface(), but less efficient, it allows to blit the
- * specified image in one color. All non-zero pixels are copied, while others
- * are output as fg. In the case where bg is non-zero, zero pixels output bg.
- * The screen surface should be locked when calling this function.
- */
-void
-draw_surface_color(SDL_Surface *surface, draw_rect_t *r, draw_point_t *p,
- uint32_t fg, uint32_t bg)
-{
- register uint32_t *dstptr, *tdstptr, *srcptr, *tptr;
-
- assert(surface != NULL);
- assert(r != NULL);
- assert(p != NULL);
- assert(!(r->x < 0 || r->x + r->w > surface->w ||
- r->y < 0 || r->y + r->h > surface->h));
- assert(!(p->x < 0 || p->x + r->w > surface->w));
-
- /*
- * For performance we want to avoid having to include the bg color
- * check every loop iteration. We prefer to have two loops instead.
- * Moreover, we optimize the loop as such to avoid multiplications
- * and divisions, using a line-based algorithm with additions only.
- */
- if (bg == 0) {
- /* XXX Should use lines array */
- for (dstptr = &((uint32_t *)screen_surface->pixels)[
- (p->y * screen_width) + p->x],
- tdstptr = &((uint32_t *)screen_surface->pixels)[
- ((p->y + r->h - 1) * screen_width) + (p->x + r->w)],
- srcptr = &((uint32_t *)surface->pixels)[
- (r->y * surface->w) + r->x];
- dstptr < tdstptr;
- dstptr = &dstptr[screen_width - r->w],
- srcptr = &srcptr[surface->w - r->w]) {
- for (tptr = &dstptr[r->w]; dstptr < tptr;
- dstptr++, srcptr++) {
- if ((*srcptr & 0xff000000) != 0)
- *dstptr = fg;
- }
- }
- } else {
- /* XXX Should use lines array */
- for (dstptr = &((uint32_t *)screen_surface->pixels)[
- (p->y * screen_width) + p->x],
- tdstptr = &((uint32_t *)screen_surface->pixels)[
- ((p->y + r->h - 1) * screen_width) + (p->x + r->w)],
- srcptr = &((uint32_t *)surface->pixels)[
- (r->y * surface->w) + r->x];
- dstptr < tdstptr;
- dstptr = &dstptr[screen_width - r->w],
- srcptr = &srcptr[surface->w - r->w]) {
- for (tptr = &dstptr[r->w]; dstptr < tptr; )
- *dstptr++ = ((*srcptr++ & 0xff000000) == 0 ?
- bg : fg);
- }
- }
-}
-
-void
-draw_surface_color_antialias(SDL_Surface *surface, draw_rect_t *r,
- draw_point_t *p, uint32_t fg, uint32_t bg)
-{
- register uint32_t *srcptr, *tsrcptr, *tptr;
- register int x, y;
-
- assert(surface != NULL);
- assert(r != NULL);
- assert(p != NULL);
- assert(!(r->x < 0 || r->x + r->w > surface->w ||
- r->y < 0 || r->y + r->h > surface->h));
- assert(!(p->x < 0 || p->x + r->w > screen_width ||
- p->y < 0 || p->y + r->h > screen_height));
-
- /* XXX Should use lines array */
- for (tsrcptr = &((uint32_t *)surface->pixels)[
- ((r->y + r->h - 1) * surface->w) + (r->x + r->w)],
- srcptr = &((uint32_t *)surface->pixels)[
- (r->y * surface->w) + r->x],
- y = p->y;
- srcptr < tsrcptr;
- srcptr = &srcptr[surface->w - r->w], y++) {
- for (x = p->x, tptr = &srcptr[r->w]; srcptr < tptr;
- srcptr++, x++) {
- if (bg == 0) {
- if ((*srcptr & 0xff000000) != 0)
- draw_pixel_antialias(x, y, fg);
- } else
- draw_pixel_antialias(x, y,
- ((*srcptr & 0xff000000) == 0 ? bg : fg));
- }
- }
-}
-
-/*
- * Similar to SDL_BlitSurface(), but less efficient, it allows to blit a
- * perfect copy of a surface area to another surface area. This is most useful
- * when colors have been setup as transparent and that an opaque identical
- * copy is wanted.
- */
-void
-draw_surface_copy(SDL_Surface *dstsurface, draw_point_t *p,
- SDL_Surface *srcsurface, draw_rect_t *r)
-{
- uint32_t *dstptr, *tdstptr, *srcptr, *tptr;
-
- assert(dstsurface != NULL);
- assert(p != NULL);
- assert(srcsurface != NULL);
- assert(r != NULL);
- assert(!(r->x < 0 || r->x + r->w > srcsurface->w ||
- r->y < 0 || r->y + r->h > srcsurface->h));
- assert(!(p->x < 0 || p->x + r->w > dstsurface->w ||
- p->y < 0 || p->y + r->h > dstsurface->h));
-
- /* XXX Should use lines array */
- for (dstptr = &((uint32_t *)dstsurface->pixels)[
- (p->y * dstsurface->w) + p->x],
- tdstptr = &((uint32_t *)dstsurface->pixels)[
- ((p->y + r->h - 1) * dstsurface->w) + (p->x + r->w)],
- srcptr = &((uint32_t *)srcsurface->pixels)[
- (r->y * srcsurface->w) + r->x];
- dstptr < tdstptr;
- dstptr = &dstptr[dstsurface->w - r->w],
- srcptr = &srcptr[srcsurface->w - r->w]) {
- for (tptr = &dstptr[r->w]; dstptr < tptr; )
- *dstptr++ = *srcptr++;
- }
-}
-
-/*
- * Blits a single pixel of specified color to screen at specified position.
- * Note that this function does not consider color 0 to be transparent.
- * This function, by exception, does not require the use of a draw_point_t.
- * This allows to use register based optimizations in functions calling us
- * (They do not need to keep counters in a memory structure when looping).
- */
-inline void
-draw_pixel(int x, int y, uint32_t col)
-{
-
- /*
- * Instead of using this alternative, which requires multiplication:
- * ((uint32_t *)screen_surface->pixels)[(y * screen_width) + x] = col;
- * Use the advantage of our screen_lines array for indexing
- * optimization.
- */
- screen_lines[y][x] = col;
-}
-
-inline void
-draw_pixel_transparent(int x, int y, uint32_t col, int alpha)
-{
- uint32_t ccol;
-
- /* This condition can happen because we are called for antialiasing */
- if (x >= screen_width || x < 0 || y >= screen_height || y < 0)
- return;
-
- alpha &= 0xff;
-
- /* First optimize possible cases */
- if (alpha == 0x00)
- return;
- if (alpha == 0xff) {
- draw_pixel(x, y, col);
- return;
- }
-
- /* Transparently blit pixel if necessary */
- if ((ccol = draw_getpixel2(x, y)) != col) {
- int r, g, b, nr, ng, nb;
-
- /*
- * Decompose into r, g, b levels, first current screen pixel
- * color
- */
- r = ((ccol & 0x00ff0000) >> 16);
- g = ((ccol & 0x0000ff00) >> 8);
- b = (ccol & 0x000000ff);
- /* Then supplied color */
- nr = ((col & 0x00ff0000) >> 16);
- ng = ((col & 0x0000ff00) >> 8);
- nb = (col & 0x000000ff);
-
- /*
- * Apply opacity modification on supplied color according to
- * alpha
- */
- nr = nr * alpha / 0xff;
- ng = ng * alpha / 0xff;
- nb = nb * alpha / 0xff;
-
- /*
- * XXX bug here! Works fine when blitting lighter colors on
- * darker ones, but not for dark colors over brighter
- * background. I probably need something as simple as reverse
- * proportional function or such...
- */
- /* Mix supplied color with pixel color */
- if ((r = (r + nr) / 2) > 0xff)
- r = 0xff;
- if ((g = (g + ng) / 2) > 0xff)
- g = 0xff;
- if ((b = (b + nb) / 2) > 0xff)
- b = 0xff;
-
- /* Recompose into a 32-bit color */
- ccol = (((r << 16) & 0x00ff0000) | ((g << 8) & 0x0000ff00) |
- (b & 0x000000ff));
-
- /* Finally blit resulting pixel back to screen */
- draw_pixel(x, y, ccol);
- }
-}
-
-/*
- * XXX Test.
- */
-inline void
-draw_pixel_antialias(int x, int y, uint32_t col)
-{
-#define AA_T 0xd0 /*0x60*/
-
- assert(!(x >= screen_width || x < 0 || y >= screen_height || y < 0));
-
- draw_pixel(x, y, col);
- draw_pixel_transparent(x, y - 1, col, AA_T);
- draw_pixel_transparent(x + 1, y, col, AA_T);
- draw_pixel_transparent(x, y + 1, col, AA_T);
- draw_pixel_transparent(x - 1, y, col, AA_T);
-
-#undef AA_T
-}
-
-/* Returns the color of pixel at specified location on screen. */
-inline uint32_t
-draw_getpixel(draw_point_t *p)
-{
-
- assert(p != NULL);
-
- return screen_lines[p->y][p->x];
-}
-
-inline uint32_t
-draw_getpixel2(int x, int y)
-{
-
- return screen_lines[y][x];
-}
-
-/*
- * Blits a line on screen of specified color (x,y)->(x2,y2). Note that color 0
- * is not considered transparent. Probably does not use the best algorithm,
- * but SDL doesn't provide hardware line blitting support, and I at least
- * needed such a function... This seems to be fast enough, using this
- * home-rolled algorithm for now.
- */
-/* XXX Modify so that it performs a gradient using draw_pixel_transparent()
- * with various alpha values when lines are nor vertical, horizontal or
- * diagonal. I.E.
- * --++--
- * --++--
- * This is apparently a known antialiasing algorithm for lines and would
- * probably be faster than using my current method.
- */
-void
-draw_line(draw_point_t *p1, draw_point_t *p2, uint32_t col)
-{
- int ix, iy;
- register int x, y, tx, ty;
- double f, fa;
-
- assert(p1 != NULL);
- assert(p2 != NULL);
- assert(!(p1->x < 0 || p1->x >= screen_width ||
- p1->y < 0 || p1->y >= screen_height));
- assert(!(p2->x < 0 || p2->x >= screen_width ||
- p2->y < 0 || p2->y >= screen_height));
-
- /*
- * Convert quadrant 1 to 3, and quadrant 2 to 4. This ensures that we
- * only need to blit the line from top to bottom.
- */
- if (p1->y > p2->y) {
- draw_point_t *p;
-
- p = p1;
- p1 = p2;
- p2 = p;
- }
-
- /* Test for cases which can easily be optimized */
- if (p1->x == p2->x) {
- if (p1->y == p2->y) {
- /* Only a single pixel to blit */
- draw_pixel(p1->x, p1->y, col);
- return;
- }
- /* We can use draw_vline() */
- draw_vline(p1, (p2->y - p1->y), col);
- return;
- }
- if (p1->y == p2->y) {
- /* We can use draw_hline() */
- if (p1->x < p2->x)
- draw_hline(p1, (p2->x - p1->x), col);
- else
- draw_hline(p2, (p1->x - p2->x), col);
- return;
- }
-
- if (p1->x < p2->x) {
- /* Left to right blitting, quadrant 4 */
-
- /* Obtain width/height ratio */
- ix = p2->x - p1->x;
- iy = p2->y - p1->y;
- if (ix == iy) {
- /*
- * Equal ratio, no need to use floating point
- * arithmetic
- */
- for (x = p1->x, y = p1->y, tx = p2->x, ty = p2->y;
- x <= tx && y <= ty; x++, y++)
- DRAW_PIXEL(x, y, col);
- } else if (ix > iy) {
- /*
- * XXX Use tf double instead of converting f to int
- * in comparision and test if speed is gained
- */
- /* X ratio larger, use floating point for Y count */
- for (fa = (double)iy / (double)ix,
- f = (double)p1->y, x = p1->x, tx = p2->x;
- (int)f <= p2->y && x <= tx; x++, f += fa)
- DRAW_PIXEL(x, (int)f, col);
- } else {
- /* Y ratio larger, use floating point for X count */
- for (fa = (double)ix / (double)iy,
- f = (double)p1->x, y = p1->y, ty = p2->y;
- (int)f <= p2->x && y <= ty; y++, f += fa)
- DRAW_PIXEL((int)f, y, col);
- }
- } else {
- /* Right to left blitting, quadrant 3 */
-
- /* Obtain width/height ratio */
- ix = p1->x - p2->x;
- iy = p2->y - p1->y;
- if (ix == iy) {
- /*
- * Equal ratio, no need to use floating point
- * arithmetic
- */
- for (x = p1->x, y = p1->y, tx = p2->x, ty = p2->y;
- x >= tx && y <= ty; x--, y++)
- DRAW_PIXEL(x, y, col);
- } else if (ix > iy) {
- /* X ratio larger, use floating point for Y count */
- for (fa = (double)iy / (double)ix,
- f = (double)p1->y, x = p1->x, tx = p2->x;
- (int)f <= p2->y && x >= tx; x--, f += fa)
- DRAW_PIXEL(x, (int)f, col);
- } else {
- /* Y ratio larger, use floating point for X count */
- for (fa = (double)ix / (double)iy,
- f = (double)p1->x, y = p1->y, ty = p2->y;
- (int)f >= p2->x && y <= ty; y++, f -= fa)
- DRAW_PIXEL((int)f, y, col);
- }
- }
-}
-
-inline void
-draw_moveto(draw_point_t *p)
-{
-
- assert(p != NULL);
- assert(!(p->x < 0 || p->x >= screen_width ||
- p->y < 0 || p->y >= screen_height));
-
- pen = *p;
-}
-
-inline void
-draw_lineto(draw_point_t *p, uint32_t col)
-{
-
- assert(p != NULL);
- assert(!(p->x < 0 || p->x >= screen_width ||
- p->y < 0 || p->y >= screen_height));
-
- draw_line(&pen, p, col);
- pen = *p;
-}
-
-/*
- * Fills the specified region, as long as pixels are bgcol, in all directions.
- * This implementation uses a custom stack with iteration to avoid overflowing
- * the stack. SDL can internally be compiled against various threading
- * libraries, several of which have a fixed stack size (I.E. Pth). Thus,
- * filling rather large areas crashes using the previous implementation.
- * With this one, we don't need to worry about stack room, since we are using
- * the process heap instead. It however may be slower on some architectures
- * than implementations using the actual stack.
- */
-void
-draw_fill_bg(draw_point_t *p, uint32_t col, uint32_t bgcol)
-{
- draw_point_t node;
-
- assert(p != NULL);
- assert(!(p->x < 0 || p->x >= screen_width ||
- p->y < 0 || p->y >= screen_height));
-
- stack_pos = stack_start;
- *stack_pos++ = *p;
-
- while (stack_pos > stack_start) {
- node = *--stack_pos;
- if (!(node.x < 0 || node.y > screen_width - 1 ||
- node.y < 0 || node.y > screen_height - 1)) {
- if (draw_getpixel(&node) == bgcol) {
- draw_pixel(node.x, node.y, col);
- *stack_pos++ =
- (draw_point_t){node.x + 1, node.y};
- *stack_pos++ =
- (draw_point_t){node.x, node.y + 1};
- *stack_pos++ =
- (draw_point_t){node.x - 1, node.y};
- *stack_pos++ =
- (draw_point_t){node.x, node.y - 1};
- }
- }
- }
-}
-
-/*
- * This implementation varies in that all pixels which are not of specified
- * colors are filled. This is only useful if you know the borders of your
- * surface to fill has the same color. It then allows to perform opaque
- * filling over a variety of colors, contrary to the last function.
- */
-void
-draw_fill_fg(draw_point_t *p, uint32_t col)
-{
- draw_point_t node;
-
- assert(p != NULL);
- assert(!(p->x < 0 || p->x >= screen_width ||
- p->y < 0 || p->y >= screen_height));
-
- stack_pos = stack_start;
- *stack_pos++ = *p;
-
- while (stack_pos > stack_start) {
- node = *--stack_pos;
- if (!(node.x < 0 || node.y > screen_width - 1 ||
- node.y < 0 || node.y > screen_height - 1)) {
- if (draw_getpixel(&node) != col) {
- draw_pixel(node.x, node.y, col);
- *stack_pos++ =
- (draw_point_t){node.x + 1, node.y};
- *stack_pos++ =
- (draw_point_t){node.x, node.y + 1};
- *stack_pos++ =
- (draw_point_t){node.x - 1, node.y};
- *stack_pos++ =
- (draw_point_t){node.x, node.y - 1};
- }
- }
- }
-}
-
-/*
- * Draws a perfect circle pixel-wise, radius <radius>, expressed in pixels.
- * What it does is draw in four sections, adding pixels at both sides of each
- * section until the sections close the circle.
- */
-void
-draw_circle(draw_point_t *p, int radius, uint32_t col)
-{
- int x = 0, y = radius, u = 1, v = 2 * radius - 1, e = 0;
-
- assert(p != NULL);
- assert(!(p->x < 0 || p->x + radius > screen_width ||
- p->y < 0 || p->y + radius > screen_height ||
- p->x - radius < 0 || p->y - radius < 0));
-
- while (x < y) {
- DRAW_PIXEL(p->x + x, p->y + y, col);
- DRAW_PIXEL(p->x + y, p->y - x, col);
- DRAW_PIXEL(p->x - x, p->y - y, col);
- DRAW_PIXEL(p->x - y, p->y + x, col);
- x++;
- e += u;
- u += 2;
- if (v < (2 * e)) {
- y--;
- e -= v;
- v -= 2;
- }
- if (x > y)
- break;
- DRAW_PIXEL(p->x + y, p->y + x, col);
- DRAW_PIXEL(p->x + x, p->y - y, col);
- DRAW_PIXEL(p->x - y, p->y - x, col);
- DRAW_PIXEL(p->x - x, p->y + y, col);
- }
-}
-
-/*
- * Returns x/y position in (xp,yp), for point at (x,y) of radius <radius> at
- * angle <angle> (in radians). Uses an array suitable for onscreen resolution
- * for efficiency instead of having to always call sin()/cos() functions.
- */
-inline void
-draw_get_vector(draw_point_t *tp, draw_point_t *p, draw_vect_t *v)
-{
- int angle;
-
- assert(tp != NULL);
- assert(p != NULL);
- assert(v != NULL);
- assert(!(p->x < 0 || p->x + v->r > screen_width ||
- p->y < 0 || p->y + v->r > screen_height ||
- p->x - v->r < 0 || p->y - v->r < 0));
-
- if ((angle = v->a) < 0)
- angle = 1000 - (-angle);
- else if (angle > 999)
- angle %= 1000;
-
- tp->x = (int)(p->x + (cos_table[angle] * v->r));
- tp->y = (int)(p->y + (sin_table[angle] * v->r));
-}
-
-/*
- * Slower implementation of draw_circle() using floating point, but which
- * can draw arcs.
- */
-void draw_circle_arc(draw_point_t *p, int radius, int a1, int a2, int step,
- uint32_t col)
-{
- register int i;
- draw_point_t tp;
- draw_vect_t v;
-
- assert(p != NULL);
- assert(!(p->x < 0 || p->x + radius > screen_width ||
- p->y < 0 || p->y + radius > screen_height ||
- p->x - radius < 0 || p->y - radius < 0));
-
- if (a2 < a1) {
- i = a1;
- a1 = a2;
- a2 = i;
- }
- v.r = radius;
- v.a = a1;
- draw_get_vector(&tp, p, &v);
- draw_moveto(&tp);
- for (i = a1; i <= a2; i += step) {
- v.a = i;
- draw_get_vector(&tp, p, &v);
- draw_lineto(&tp, col);
- }
- if (i > a2) {
- v.a = a2;
- draw_get_vector(&tp, p, &v);
- draw_lineto(&tp, col);
- }
-}
+++ /dev/null
-/* $Id: draw.h,v 1.1 2006/05/03 14:22:06 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-#ifndef DRAW_H
-#define DRAW_H
-
-
-
-#include <stdint.h>
-
-
-
-#define SCREEN_LOCK() if (SDL_LockSurface(screen_surface) != 0) \
- abort();
-#define SCREEN_UNLOCK() SDL_UnlockSurface(screen_surface)
-
-
-
-typedef struct draw_point {
- int x, y;
-} draw_point_t;
-
-typedef struct draw_vect {
- int a, r;
-} draw_vect_t;
-
-typedef struct draw_rect {
- int x, y;
- int w, h;
-} draw_rect_t;
-
-
-
-extern int draw_init(void);
-extern inline void draw_vline(draw_point_t *, int, uint32_t);
-extern inline void draw_hline(draw_point_t *, int, uint32_t);
-extern inline void draw_rect(draw_rect_t *, uint32_t, uint32_t);
-extern void draw_rect_3d(draw_rect_t *, uint32_t, uint32_t,
- uint32_t, int);
-extern inline void draw_rect_fill(draw_rect_t *, uint32_t);
-extern void draw_rect_fill_xor(draw_rect_t *, uint32_t);
-extern void draw_rect_fill_inverse(draw_rect_t *, uint32_t,
- uint32_t);
-extern void draw_rect_fill_transparent(draw_rect_t *, uint32_t);
-extern void draw_surface_color(SDL_Surface *, draw_rect_t *,
- draw_point_t *, uint32_t, uint32_t);
-extern void draw_surface_color_antialias(SDL_Surface *,
- draw_rect_t *, draw_point_t *, uint32_t,
- uint32_t);
-extern void draw_surface_copy(SDL_Surface *, draw_point_t *,
- SDL_Surface *, draw_rect_t *);
-extern inline void draw_pixel(int, int, uint32_t);
-extern inline void draw_pixel_transparent(int, int, uint32_t, int);
-extern inline void draw_pixel_antialias(int, int, uint32_t);
-extern inline uint32_t draw_getpixel(draw_point_t *);
-extern inline uint32_t draw_getpixel2(int, int);
-extern void draw_line(draw_point_t *, draw_point_t *, uint32_t);
-extern void draw_moveto(draw_point_t *);
-extern void draw_lineto(draw_point_t *, uint32_t);
-extern void draw_fill_bg(draw_point_t *, uint32_t, uint32_t);
-extern void draw_fill_fg(draw_point_t *, uint32_t);
-extern void draw_circle(draw_point_t *, int, uint32_t);
-extern inline void draw_get_vector(draw_point_t *, draw_point_t *,
- draw_vect_t *);
-extern void draw_circle_arc(draw_point_t *, int, int, int, int,
- uint32_t);
-
-
-
-#endif
+++ /dev/null
-/* $Id: fonts.c,v 1.1 2006/05/03 14:20:47 mmondor Exp $ */
-
-/*
- * Copyright (c) 2004-2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-/* HEADERFILES */
-
-#include <assert.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <SDL.h>
-
-#include <fonts.h>
-#include <screen.h>
-#include <draw.h>
-#include <conf.h>
-
-
-
-/* DEFINITIONS */
-
-#ifdef ANTIALIASING /* conf,h */
-#define DRAW_SURFACE_COLOR draw_surface_color_antialias
-#else
-#define DRAW_SURFACE_COLOR draw_surface_color
-#endif
-
-
-
-/* STATIC FUNCTIONS PROTOTYPES */
-
-inline static void font_rectangle(font_t *, draw_rect_t *, int);
-
-
-
-/* GLOBALS */
-
-font_t *font_small, *font_small_bold,
- *font_large, *font_large_bold;
-
-
-
-/* PRIVATE FUNCTIONS */
-
-/*
- * Fills the rectangle coordinates for the specified character of specified
- * font within the font's surface
- */
-inline static void
-font_rectangle(font_t *font, draw_rect_t *rect, int c)
-{
- assert(font != NULL);
- assert(rect != NULL);
-
- /* Make sure that c is within 0-255 */
- c &= 0xff;
-
- /* Map c (0-255) to 0-15 x/y coordinates */
- rect->x = font->x * (c & 0x0f);
- rect->y = font->y * (c / 16);
- rect->w = font->x;
- rect->h = font->y;
-}
-
-
-
-/* PUBLIC FUNCTIONS */
-
-/* Load fonts */
-int
-font_init(void)
-{
-
- font_small = font_load(
- "bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp");
- font_small_bold = font_load(
- "bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp");
- font_large = font_load(
- "bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp");
- font_large_bold = font_load(
- "bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp");
-
- /*
- * On error, we don't free any successfully loaded fonts, and expect
- * the application to exit.
- */
- if (font_small == NULL || font_small_bold == NULL ||
- font_large == NULL || font_large_bold == NULL)
- return -1;
-
- return 0;
-}
-
-/*
- * Allows to load a 256 characters (16x16 tiled) font map from an image.
- * This image usually is in 1 bit depth, but it internally gets converted to
- * 32-bit depth, and we then create a per-pixel alpha blending enabled surface
- * for it.
- */
-font_t *
-font_load(const char *file)
-{
- font_t *font;
- SDL_Surface *surface1 = NULL, *surface2 = NULL, *surface3 = NULL;
- int ok;
-
- assert(file != NULL);
-
- /* Load 1 bit depth bitmap */
- if ((surface1 = SDL_LoadBMP(file)) == NULL)
- goto err;
-
- /*
- * Convert loaded surface to screen_surface's depth, into new surface.
- * bits 0 become 0x00000000 and 1 0x00ffffff. Remember that surface2
- * is the one we'll copy to the final destination one.
- */
- if ((surface2 = SDL_ConvertSurface(surface1, screen_surface->format,
- SDL_SWSURFACE)) == NULL)
- goto err;
- SDL_FreeSurface(surface1);
- surface1 = NULL;
-
- /*
- * Create new surface suitable for source alpha blending blitting.
- * I previously attempted to use the converted surface and create an
- * alpha blending enabled copy, but this seemed to fail. I thus must
- * explicitely create a new surface myself (which proved to work in a
- * test program).
- */
- if ((surface1 = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
- surface2->w, surface2->h, 32, 0, 0, 0, 0)) == NULL)
- goto err;
-
- /*
- * Convert newly created surface to support per-pixel alpha blending
- * with hardware support. Resulting surface3 is the one we'll copy
- * surface2 to.
- */
- (void) SDL_SetAlpha(surface1, SDL_SRCALPHA | SDL_RLEACCEL, 128);
- if ((surface3 = SDL_DisplayFormatAlpha(surface1)) == NULL)
- goto err;
- SDL_FreeSurface(surface1);
- surface1 = NULL;
-
- /*
- * We now have both our 32-bit depth font surface (surface2) and our
- * destination alpha surface (surface3). Copy surface2 data to
- * surface3, making sure to set the alpha color to 0x00 for pixels
- * which originally were 0 bits, that is, now 0x00000000, and to 0xff
- * for pixels which were 1 bits, now being 0x00ffffff.
- */
- ok = -1;
- if (SDL_LockSurface(surface2) == 0) {
- if (SDL_LockSurface(surface3) == 0) {
- uint32_t *sptr, *dptr, *dtptr;
-
- for (sptr = (uint32_t *)surface2->pixels,
- dptr = (uint32_t *)surface3->pixels,
- dtptr = &dptr[surface3->w * surface3->h];
- dptr < dtptr;
- sptr++, dptr++)
- *dptr = (*sptr == 0 ? 0x00000000 : 0xffffffff);
- ok = 0;
- SDL_UnlockSurface(surface3);
- }
- SDL_UnlockSurface(surface2);
- }
- SDL_FreeSurface(surface2);
- surface2 = NULL;
- if (ok == -1)
- goto err;
-
- /* Allocate font structure */
- if ((font = malloc(sizeof(font_t))) == NULL)
- goto err;
-
- /* Everything successful, fill font structure and return it. */
- font->x = surface3->w / 16;
- font->y = surface3->h / 16;
- font->surface = surface3;
-
- return font;
-
-err:
- if (surface1 != NULL)
- SDL_FreeSurface(surface1);
- if (surface2 != NULL)
- SDL_FreeSurface(surface2);
- if (surface3 != NULL)
- SDL_FreeSurface(surface3);
-
- return NULL;
-}
-
-/* Frees previously loaded font using font_load() */
-void
-font_free(font_t *font)
-{
-
- assert(font != NULL);
- assert(font->surface != NULL);
-
- SDL_FreeSurface(font->surface);
- free(font);
-}
-
-/*
- * Efficiently draws a character to screen at specified position. The output
- * color however can only be white using this function.
- */
-void
-font_draw_char(font_t *font, draw_point_t *p, int c)
-{
- SDL_Rect screen_rect, font_rect;
- draw_rect_t r;
-
- assert(font != NULL);
- assert(p != NULL);
-
- screen_rect.x = p->x;
- screen_rect.y = p->y;
- font_rectangle(font, &r, c);
- font_rect = (SDL_Rect){r.x, r.y, r.w, r.h};
- (void) SDL_BlitSurface(font->surface, &font_rect,
- screen_surface, &screen_rect);
-}
-
-/*
- * Efficiently draws a character string to screen at specified position.
- * The output color however can only be white using this function.
- */
-void
-font_draw_string(font_t *font, draw_point_t *p, const char *string)
-{
- register const char *ptr;
- SDL_Rect screen_rect, font_rect;
- draw_rect_t r;
-
- assert(font != NULL);
- assert(p != NULL);
- assert(string != NULL);
-
- screen_rect.x = p->x;
- screen_rect.y = p->y;
- for (ptr = string; *ptr != '\0'; ptr++, screen_rect.x += font->x) {
- font_rectangle(font, &r, *ptr);
- font_rect = (SDL_Rect){r.x, r.y, r.w, r.h};
- (void) SDL_BlitSurface(font->surface, &font_rect,
- screen_surface, &screen_rect);
- }
-}
-
-/*
- * Returns the necessary number of horizontal pixels required to blit the
- * specified string.
- */
-int
-font_string_width(font_t *font, const char *string)
-{
-
- assert(font != NULL);
- assert(string != NULL);
-
- return (strlen(string) * font->x);
-}
-
-
-/* The following functions require that the screen surface be locked. */
-
-/*
- * font_draw_char() variant which is a slightly less efficient but allows to
- * blit a character on screen at specified position with specified color.
- * If bg color is 0, font is drawn in overlay mode. Otherwise, it is drawn
- * overstrike with specified color. Note that if calling this function, the
- * screen_surface SDL_Surface should be locked using SDL_LockSurface().
- */
-void
-font_draw_char_color(font_t *font, uint32_t fg, uint32_t bg, draw_point_t *p,
- int c)
-{
- draw_rect_t rect;
-
- assert(font != NULL);
- assert(p != NULL);
-
- font_rectangle(font, &rect, c);
- DRAW_SURFACE_COLOR(font->surface, &rect, p, fg, bg);
-}
-
-/*
- * font_draw_string() variant which is a little less efficient but allows to
- * blit a character string on screen at specified position with specified
- * color. If bg is 0, string is drawn in overlay mode. Otherwise, it is drawn
- * in overstrike mode with specified background color.
- */
-void
-font_draw_string_color(font_t *font, uint32_t fg, uint32_t bg, draw_point_t *p,
- const char *string)
-{
- register const char *ptr;
- draw_rect_t rect;
-
- assert(font != NULL);
- assert(p != NULL);
- assert(string != NULL);
-
- for (ptr = string; *ptr != '\0'; ptr++, p->x += font->x) {
- font_rectangle(font, &rect, *ptr);
- DRAW_SURFACE_COLOR(font->surface, &rect, p, fg, bg);
- }
-}
+++ /dev/null
-/* $id$ */
-
-/*
- * Copyright (c) 2004-2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#ifndef FONTS_H
-#define FONTS_H
-
-
-
-#include <SDL.h>
-
-#include <draw.h>
-
-
-
-typedef struct font {
- int x, y;
- SDL_Surface *surface;
-} font_t;
-
-
-
-extern int font_init(void);
-extern font_t *font_load(const char *);
-extern void font_free(font_t *);
-extern void font_draw_char(font_t *, draw_point_t *, int);
-extern void font_draw_string(font_t *, draw_point_t *,
- const char *);
-extern int font_string_width(font_t *, const char *);
-extern void font_draw_char_color(font_t *, uint32_t, uint32_t,
- draw_point_t *, int);
-extern void font_draw_string_color(font_t *, uint32_t, uint32_t,
- draw_point_t *, const char *);
-
-
-extern font_t *font_small, *font_small_bold,
- *font_large, *font_large_bold;
-
-
-
-#endif
+++ /dev/null
-/* $Id: msg.c,v 1.1 2006/04/27 13:26:51 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <SDL.h>
-#include <SDL_thread.h>
-
-#include <thread_msg.h>
-
-
-
-#define CLIENTS 16
-#define ROUNDS 32
-
-
-
-struct client {
- SDL_Thread *thread;
- int id;
-};
-
-struct client_msg {
- thread_msg_t msg;
- struct client *c;
- int rounds;
-};
-
-
-
-int main(int, char **);
-
-static void done(int);
-static int thread_client(void *);
-
-
-
-thread_port_t server_port;
-
-
-
-/* ARGSUSED */
-int
-main(int argc, char **argv)
-{
- struct client clients[CLIENTS];
- int i;
- thread_ring_t server_ring;
-
- if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
- (void) fprintf(stderr, "main() - SDL_Init() - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
-
- /*
- * We're already the server thread.
- * Initialize our message port and notification ring.
- */
- if (thread_ring_init(&server_ring) == -1) {
- (void) fprintf(stderr,
- "main() - thread_ring_init(server_port) - %s\n",
- SDL_GetError());
- done(EXIT_FAILURE);
- }
- if (thread_port_init(&server_port) == -1) {
- (void) fprintf(stderr,
- "main() - thread_port_init(server_port) - %s\n",
- SDL_GetError());
- done(EXIT_FAILURE);
- }
- thread_port_set_ring(&server_port, &server_ring);
-
- /* Launch client threads */
- for (i = 0; i < CLIENTS; i++) {
- clients[i].id = i;
- if ((clients[i].thread = SDL_CreateThread(thread_client,
- &clients[i])) == NULL) {
- (void) fprintf(stderr,
- "main() - SDL_CreateThread(%d) - %s\n",
- i, SDL_GetError());
- done(EXIT_FAILURE);
- }
- }
-
- /*
- * Listen for messages from our client threads with a one second
- * timeout delay and display them.
- */
- for (;;) {
- struct client_msg *msg;
-
- while ((msg = (struct client_msg *)thread_msg_get(
- &server_port)) != NULL) {
- (void) printf("Message from client %d: %d\n",
- msg->c->id, msg->rounds);
- if (thread_msg_reply(&msg->msg) != 0) {
- (void) fprintf(stderr,
- "main() - thread_msg_reply() - %s\n",
- SDL_GetError());
- break;
- }
- }
- (void) printf("Server polling\n");
- if (thread_ring_wait(&server_ring, 1000) != 0) {
- (void) fprintf(stderr,
- "main() - thread_ring_wait() - %s\n",
- SDL_GetError());
- break;
- }
- }
-
- thread_port_set_ring(&server_port, NULL);
- thread_port_destroy(&server_port);
- thread_ring_destroy(&server_ring);
-
- exit(EXIT_SUCCESS);
-}
-
-static void
-done(code)
-{
-
- SDL_Quit();
- exit(code);
-}
-
-static int
-thread_client(void *data)
-{
- struct client *c = (struct client *)data;
- struct client_msg msg, *rmsg;
- thread_ring_t client_ring;
- thread_port_t client_port;
- int i;
-
- /* Initialize reply port and ring */
- if (thread_ring_init(&client_ring) == -1) {
- (void) fprintf(stderr,
- "thread_client(%d) - thread_ring_init(server_port) - %s\n",
- c->id, SDL_GetError());
- done(EXIT_FAILURE);
- }
- if (thread_port_init(&client_port) == -1) {
- (void) fprintf(stderr,
- "thread_client(%d) - thread_port_init(server_port) - %s\n",
- c->id, SDL_GetError());
- done(EXIT_FAILURE);
- }
- thread_port_set_ring(&client_port, &client_ring);
-
- /* Initialize message with our reply port */
- thread_msg_init(&msg.msg, &client_port);
- msg.c = c;
-
- for (i = 0; i < ROUNDS; i++) {
- msg.rounds = i;
- if (thread_msg_put(&server_port, &msg.msg) == -1) {
- (void) fprintf(stderr,
- "thread_client(%d) - thread_msg_put(%d) - %s\n",
- c->id, i, SDL_GetError());
- break;
- }
- while ((rmsg = (struct client_msg *)thread_msg_get(
- &client_port)) == NULL) {
- (void) printf("Client polling\n");
- if (thread_ring_wait(&client_ring, 1000) != 0) {
- (void) fprintf(stderr,
- "thread_client(%d) - thread_ring_wait(%d)"
- "- %s\n", c->id, i, SDL_GetError());
- break;
- }
- }
- if (rmsg != NULL)
- (void) printf("Received reply for %d: %d\n",
- rmsg->c->id, rmsg->rounds);
- else
- break;
- }
-
- return 0;
-}
+++ /dev/null
-/* $Id: netrek-like.c,v 1.1 2006/05/03 08:58:01 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <SDL.h>
-#include <SDL_rotozoom.h>
-#include <SDL_gfxPrimitives.h>
-
-#include <screen.h>
-#include <fonts.h>
-
-
-
-int main(int, char **);
-
-static int surface_blit_angle(SDL_Surface *, int, int,
- double, int);
-static SDL_Surface *bmp_load_key(const char *);
-
-
-
-/* ARGSUSED */
-int
-main(int argc, char **argv)
-{
- int i;
- SDL_Surface *rship, *fship;
-
- screen_init();
- draw_init();
- if (font_init() != 0) {
- (void) fprintf(stderr, "Could not load font bitmaps\n");
- screen_destroy();
- }
-
- rship = bmp_load_key("bmp/RomDD.bmp");
- fship = bmp_load_key("bmp/FedCA.bmp");
-
- (void) rectangleRGBA(screen_surface, 0, 0, 750, 750,
- 0xff, 0xff, 0xff, 0xff);
- (void) rectangleRGBA(screen_surface, 750, 0, 1023, 274,
- 0xff, 0xff, 0xff, 0xff);
- (void) rectangleRGBA(screen_surface, 750, 274, 1023, 506,
- 0xff, 0xff, 0xff, 0xff);
- (void) rectangleRGBA(screen_surface, 750, 274, 1023, 767,
- 0xff, 0xff, 0xff, 0xff);
- (void) rectangleRGBA(screen_surface, 0, 750, 1023, 767,
- 0xff, 0xff, 0xff, 0xff);
-
- for (i = 0; i < 1440; i += 2) {
- if (surface_blit_angle(rship, 100, 100, (double)i, 1) == -1 ||
- surface_blit_angle(fship, 200, 100, (double)i, 1) == -1)
- (void) fprintf(stderr, "surface_blit_angle()\n");
- SDL_Flip(screen_surface);
- SDL_Delay(10);
- }
-
- SDL_Delay(1000);
-
- /* Attempt to connect to server */
-
- screen_destroy();
- /* NOTREACHED */
-
- exit(EXIT_SUCCESS);
-}
-
-static SDL_Surface *
-bmp_load_key(const char *file)
-{
- SDL_Surface *s;
- Uint32 c;
-
- if ((s = SDL_LoadBMP(file)) == NULL)
- goto err;
-
- c = SDL_MapRGB(s->format, 0, 0, 0);
- if (SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, c) == -1)
- goto err;
-
- return s;
-
-err:
- (void) fprintf(stderr, "bmp_load_key(%s) - %s\n", file, SDL_GetError());
- screen_destroy();
- /* NOTREACHED */
- return NULL;
-}
-
-static int
-surface_blit_angle(SDL_Surface *s, int x, int y, double a, int clean)
-{
- SDL_Surface *t;
- SDL_Rect d;
- int r;
-
- if (clean) {
- int cx, cy, c;
-
- cx = s->w / 2 + 2;
- cy = s->h / 2 + 2;
- c = (cx >= cy ? cx : cy);
- (void) boxRGBA(screen_surface, x - c, y - c,
- x + c, y + c, 0x00, 0x00, 0x00, 0xff);
- }
-
- if ((t = rotozoomSurface(s, a, 0.75, 1)) == NULL)
- return -1;
-
- d = (SDL_Rect){x - (t->w / 2), y - (t->h / 2), 0, 0};
- r = SDL_BlitSurface(t, NULL, screen_surface, &d);
- SDL_FreeSurface(t);
-
- return r;
-}
+++ /dev/null
-/* $Id: rot.c,v 1.3 2006/05/03 08:09:37 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <SDL.h>
-#include <SDL_rotozoom.h>
-#include <SDL_gfxPrimitives.h>
-
-#include <screen.h>
-#include <fonts.h>
-
-
-
-typedef struct angle_cache {
- SDL_Surface *source;
- SDL_Surface *angles[360];
-} angle_cache_t;
-
-
-
-int main(int, char **);
-
-static angle_cache_t *angle_cache_init(SDL_Surface *);
-static void angle_cache_destroy(angle_cache_t *);
-static int surface_blit_angle(struct angle_cache *, int, int,
- double, int);
-static SDL_Surface *bmp_load_key(const char *);
-
-
-
-/* ARGSUSED */
-int
-main(int argc, char **argv)
-{
- int i, i2;
- SDL_Surface *rship, *fship;
- angle_cache_t *rshipc, *fshipc;
-
- screen_init();
- draw_init();
- if (font_init() != 0) {
- (void) fprintf(stderr, "Could not load font bitmaps\n");
- screen_destroy();
- }
-
- rship = bmp_load_key("bmp/RomDD.bmp");
- fship = bmp_load_key("bmp/FedCA.bmp");
- if ((rshipc = angle_cache_init(rship)) == NULL ||
- (fshipc = angle_cache_init(fship)) == NULL) {
- (void) fprintf(stderr, "Could not initialize angle caches\n");
- screen_destroy();
- }
-
- for (i2 = 0; i2 < 2; i2++) {
- for (i = 0; i < 360; i += 5) {
- if (surface_blit_angle(rshipc, 100, 100, (double)i, 1)
- == -1 || surface_blit_angle(fshipc, 200, 100,
- (double)i, 1) == -1)
- (void) fprintf(stderr,
- "surface_blit_angle()\n");
- SDL_Flip(screen_surface);
- SDL_Delay(33);
- }
- }
-
- SDL_Delay(1000);
-
- /* Attempt to connect to server */
-
- screen_destroy();
- /* NOTREACHED */
-
- exit(EXIT_SUCCESS);
-}
-
-static SDL_Surface *
-bmp_load_key(const char *file)
-{
- SDL_Surface *s;
- Uint32 c;
-
- if ((s = SDL_LoadBMP(file)) == NULL)
- goto err;
-
- c = SDL_MapRGB(s->format, 0, 0, 0);
- if (SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, c) == -1)
- goto err;
-
- return s;
-
-err:
- (void) fprintf(stderr, "bmp_load_key(%s) - %s\n", file, SDL_GetError());
- screen_destroy();
- /* NOTREACHED */
- return NULL;
-}
-
-static angle_cache_t *
-angle_cache_init(SDL_Surface *s)
-{
- angle_cache_t *ac;
- int i;
-
- assert(s != NULL);
-
- if ((ac = malloc(sizeof(angle_cache_t))) != NULL) {
- ac->source = s;
- for (i = 0; i < 360; i++)
- ac->angles[i] = NULL;
-
- return ac;
- }
-
- return NULL;
-}
-
-static void
-angle_cache_destroy(angle_cache_t *ac)
-{
- int i;
-
- assert(ac != NULL);
-
- for (i = 0; i < 360; i++) {
- if (ac->angles[i] != NULL)
- SDL_FreeSurface(ac->angles[i]);
- }
-
- free(ac);
-}
-
-static int
-surface_blit_angle(angle_cache_t *ac, int x, int y, double a, int clean)
-{
- SDL_Surface *t, *s = ac->source;
- SDL_Rect d;
- int r, i;
-
- assert(a > -1 && a < 360);
- assert(((int)a) == a);
-
- if (clean) {
- int cx, cy, c;
-
- cx = s->w / 2 + 2;
- cy = s->h / 2 + 2;
- c = (cx >= cy ? cx : cy);
- (void) boxRGBA(screen_surface, x - c, y - c,
- x + c, y + c, 0x00, 0x00, 0x00, 0xff);
- }
-
- if ((t = ac->angles[(int)a]) == NULL) {
- for (r = 0; r < 2; r++) {
- if ((t = rotozoomSurface(s, a, 1.0, 1)) != NULL) {
- ac->angles[(int)a] = t;
- break;
- }
- /* Free some room and retry */
- for (i = 0; i < 360; i++) {
- if (ac->angles[i] != NULL) {
- SDL_FreeSurface(ac->angles[i]);
- ac->angles[i] = NULL;
- }
- }
- }
- if (t == NULL)
- return -1;
- }
-
- d = (SDL_Rect){x - (t->w / 2), y - (t->h / 2), 0, 0};
- return SDL_BlitSurface(t, NULL, screen_surface, &d);
-}
+++ /dev/null
-/* $Id: snd.c,v 1.1 2006/05/01 03:52:45 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <SDL.h>
-#include <SDL_mixer.h>
-
-
-
-int main(int, char **);
-
-static void done(int);
-static Mix_Chunk *sample_load(const char *);
-
-
-
-static SDL_Surface *surface_screen = NULL;
-static Mix_Chunk *cloak, *uncloak, *shield, *unshield, *torp, *hit,
- *explode;
-
-static int iscloaked, isshielded;
-
-
-
-/* ARGSUSED */
-int
-main(int argc, char **argv)
-{
- Mix_Music *mus;
- SDL_Event ev;
-
- if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
- (void) fprintf(stderr, "main() - SDL_Init() - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
- if ((surface_screen = SDL_SetVideoMode(640, 480, 32,
- SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF/* | SDL_FULLSCREEN*/))
- == NULL) {
- (void) fprintf(stderr, "main() - SDL_SetVideoMode() - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
-
- if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) != 0) {
- (void) fprintf(stderr, "main() - Mix_OpenAudio() - %s\n",
- Mix_GetError());
- done(EXIT_FAILURE);
- }
-
- cloak = sample_load("wav/nt_cloaked.wav");
- uncloak = sample_load("wav/nt_uncloak.wav");
- shield = sample_load("wav/nt_shield_up.wav");
- unshield = sample_load("wav/nt_shield_down.wav");
- torp = sample_load("wav/nt_fire_torp_other.wav");
- hit = sample_load("wav/nt_plasma_hit.wav");
- explode = sample_load("wav/nt_explosion_other.wav");
-
- (void) Mix_AllocateChannels(16);
- (void) Mix_ReserveChannels(2);
-
- if ((mus = Mix_LoadMUS("ogg/1.ogg")) == NULL) {
- (void) fprintf(stderr, "main() - Mix_LoadMUS() - %s\n",
- Mix_GetError());
- done(EXIT_FAILURE);
- }
-
- if (Mix_PlayMusic(mus, -1) != 0) {
- (void) fprintf(stderr, "main() - Mix_PlayMusic() - %s\n",
- Mix_GetError());
- done(EXIT_FAILURE);
- }
-
- for (iscloaked = isshielded = 0; SDL_WaitEvent(&ev) != 0; ) {
- int ch;
-
- if (ev.type == SDL_KEYUP && ev.key.keysym.sym == SDLK_ESCAPE)
- break;
- if (ev.type == SDL_KEYDOWN) {
- switch (ev.key.keysym.sym) {
- case SDLK_SPACE:
- /* Torp */
- if ((ch = Mix_PlayChannel(-1, torp, 0)) == -1)
- (void) fprintf(stderr, "main() - "
- "Mix_PlayChannel(torp) - %s\n",
- SDL_GetError());
- else
- Mix_SetPosition(ch, rand() % 359,
- 255 - (200 + (rand() % 54)));
- break;
- case SDLK_w:
- /* Cloak toggle */
- iscloaked = (iscloaked == 0 ? 1 : 0);
- if (Mix_PlayChannel(0,
- (iscloaked == 1 ? cloak : uncloak), 0)
- == -1)
- (void) fprintf(stderr, "main() - "
- "Mix_PlayChannel(cloack) - %s\n",
- SDL_GetError());
- break;
- case SDLK_s:
- /* Shield toggle */
- isshielded = (isshielded == 0 ? 1 : 0);
- if (Mix_PlayChannel(1,
- (isshielded == 1 ? shield : unshield), 0)
- == -1)
- (void) fprintf(stderr, "main() - "
- "Mix_PlayChannel(shield) - %s\n",
- SDL_GetError());
- break;
- default:
- break;
- }
- }
- }
-
- if (Mix_FadeOutMusic(2000))
- SDL_Delay(2000);
-
- Mix_HaltMusic();
- Mix_CloseAudio();
-
- done(EXIT_SUCCESS);
- /* NOTREACHED */
- exit(EXIT_SUCCESS);
-}
-
-static void
-done(code)
-{
-
- SDL_Quit();
- exit(code);
-}
-
-static Mix_Chunk *
-sample_load(const char *file)
-{
- Mix_Chunk *c;
-
- if ((c = Mix_LoadWAV(file)) == NULL) {
- (void) fprintf(stderr,
- "sample_load() - Mix_LoadWAV(%s) - %s\n",
- file, SDL_GetError());
- done(EXIT_FAILURE);
- }
-
- return c;
-}
+++ /dev/null
-/* $Id: thread_msg.c,v 1.12 2006/05/19 10:14:35 mmondor Exp $ */
-
-/*
- * Copyright (C) 2006, Matthew Mondor
- * All rights reserved.
- *
- * Adapted from older code intended for use with POSIX threads,
- * for use within SDL.
- */
-
-
-
-#include <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);
- }
-
- return amsg;
-}
-
-/*
- * Destroys an amsg previously created with thread_amsg_create().
- */
-void
-thread_amsg_destroy(thread_amsg_t *amsg)
-{
-
- ASSERT(amsg_initialized && amsg != NULL);
-
- if (SDL_LockMutex(amsg_mutex) == 0) {
- (void) pool_free((pnode_t *)amsg);
- SDL_UnlockMutex(amsg_mutex);
- }
-}
+++ /dev/null
-/* $Id: thread_msg.h,v 1.9 2006/05/19 10:14:35 mmondor Exp $ */
-
-/*
- * Copyright (C) 2006, Matthew Mondor
- * All rights reserved.
- *
- * Adapted from older code intended for use with POSIX threads,
- * for use within SDL.
- */
-
-
-
-#ifndef THREAD_MSG_H
-#define THREAD_MSG_H
-
-
-
-#include <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 256
-
-typedef struct {
- uint32_t magic;
- SDL_cond *cond;
- SDL_mutex *mutex;
- int event;
-} thread_ring_t;
-
-typedef struct {
- uint32_t magic;
- thread_ring_t *ring;
- SDL_mutex *lock;
- list_t messages;
-} thread_port_t;
-
-typedef struct {
- pnode_t node;
- uint32_t magic;
- thread_port_t *reply;
-} thread_msg_t;
-
-typedef struct {
- thread_msg_t msg;
- size_t size;
- uint32_t data[AMSG_MAXSIZE / 4];
-} thread_amsg_t;
-
-
-
-extern int thread_ring_init(thread_ring_t *);
-extern int thread_ring_valid(thread_ring_t *);
-extern void thread_ring_destroy(thread_ring_t *);
-extern int thread_ring_wait(thread_ring_t *, Uint32);
-extern int thread_ring_notify(thread_ring_t *);
-
-extern int thread_port_init(thread_port_t *);
-extern int thread_port_valid(thread_port_t *);
-extern void thread_port_destroy(thread_port_t *);
-extern void thread_port_set_ring(thread_port_t *, thread_ring_t *);
-extern void thread_msg_init(thread_msg_t *, thread_port_t *);
-extern int thread_msg_valid(thread_msg_t *);
-extern void thread_msg_destroy(thread_msg_t *);
-extern thread_msg_t *thread_msg_get(thread_port_t *);
-extern int thread_msg_put(thread_port_t *, thread_msg_t *);
-extern int thread_msg_reply(thread_msg_t *);
-extern int thread_port_pending(thread_port_t *);
-
-extern int thread_amsg_pool_init(void);
-extern thread_amsg_t *thread_amsg_create(void);
-extern void thread_amsg_destroy(thread_amsg_t *);
-
-
-
-#endif
+++ /dev/null
-/* $Id: thread_net_recv.c,v 1.4 2006/05/19 14:54:38 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * User events polling thread. Reports all events to the master thread
- * via efficient inter-thread messages.
- */
-
-
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <main.h>
-#include <thread_net_recv.h>
-#include <conf.h>
-
-
-
-static void ssend(thread_msg_t *);
-static int state_connect(void);
-static void state_recv(void);
-static ssize_t server_recv(void *, size_t);
-
-
-
-static thread_ring_t recv_ring;
-static thread_port_t recv_port;
-
-
-
-TCPsocket server_socket = NULL;
-
-
-
-int
-thread_net_recv(void *data)
-{
-
- /* Initialize reply port and ring */
- if (thread_ring_init(&recv_ring) == -1) {
- (void) fprintf(stderr,
- "thread_net_recv() - thread_ring_init(recv_port) - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
- if (thread_port_init(&recv_port) == -1) {
- (void) fprintf(stderr,
- "thread_net_recv() - thread_port_init(recv_port) - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
- thread_port_set_ring(&recv_port, &recv_ring);
-
- if (state_connect() == 0)
- state_recv();
-
- /* Wait peacefully until our process exits */
- for (;;)
- (void) thread_ring_wait(&recv_ring, -1);
-
- /* NOTREACHED */
-
- return 0;
-}
-
-/*
- * Sends a message synchroneously, that is, sends and waits for a reply.
- */
-static void
-ssend(thread_msg_t *msg)
-{
- thread_msg_t *rmsg;
-
- if (thread_msg_put(&main_port, msg) == -1)
- (void) fprintf(stderr,
- "ssend() - thread_msg_put()\n");
- while ((rmsg = thread_msg_get(&recv_port)) == NULL) {
- if (thread_ring_wait(&recv_ring, -1) != 0)
- (void) fprintf(stderr,
- "ssend() - thread_ring_wait()\n");
- }
-}
-
-/*
- * Attempts to connect to server. On success, 0 is returned and a success
- * reply packet is sent synchroneously to the main thread. On error, a
- * failure packet is sent instead and we return -1.
- */
-static int
-state_connect(void)
-{
- struct msg_connect msg;
- IPaddress addr;
-
- thread_msg_init(&msg.msg, &recv_port);
- msg.status = 0;
- msg.error = NULL;
-
- if (SDLNet_ResolveHost(&addr, SERVER_HOST, SERVER_PORT) != 0) {
- msg.status = -1;
- msg.error = "Could not resolve server hostname";
- goto end;
- }
-
- if ((server_socket = SDLNet_TCP_Open(&addr)) == NULL) {
- msg.status = -1;
- msg.error = "Could not connect to server";
- }
-
-end:
- ssend(&msg.msg);
-
- return 0;
-}
-
-/*
- * We endlessly read packets from the server and queue them asynchroneously to
- * the main thread's port, unless a read error occurs, in which case we queue
- * an empty/error special packet and return.
- */
-static void
-state_recv(void)
-{
- thread_amsg_t *amsg = NULL;
-
- for (;;) {
- uint8_t type, length;
-
- /*
- * We must already have an allocated message since we'll read
- * data directly into its buffer. If this operation fails,
- * it's fatal since the receiving main thread will never be
- * notified.
- */
- if ((amsg = thread_amsg_create()) == NULL) {
- (void) fprintf(stderr,
- "state_recv() - thread_amsg_create()\n");
- exit(EXIT_FAILURE);
- }
-
- /*
- * Start reading a single byte, the message type, and evaluate
- * the message size. For variable packets, we also must read
- * another byte for the legth. Then read the remaining of the
- * message. On any invalid message, we error and stop as if
- * the connection had problems, but we log it. Upon
- * successful reception of a complete message, directly
- * transmit it to the main thread asynchroneously and
- * continue for another packet. These read operations are
- * blocking automatically when no data is available to read.
- */
- if (server_recv(amsg->data, 16) == -1)
- break;
- type = *(amsg->data);
-
- /* XXX */
- amsg->size = 16;
-
- /* Valid fixed length packet type? If so, set length */
- /* Valid variable length packet type? Obtain length */
-
- /* Read remaining of packet */
-
- /* Asynchroneously send packet to the main thread */
- (void) thread_msg_put(&main_port, &amsg->msg);
- }
-
- /* Send the end of stream message to the main thread */
- amsg->size = -1;
- (void) thread_msg_put(&main_port, &amsg->msg);
-}
-
-static ssize_t
-server_recv(void *data, size_t size)
-{
- size_t len;
- int res;
-
- for (len = 0; len < size; len += res) {
- if ((res = SDLNet_TCP_Recv(server_socket, data + len,
- size - len)) < 1)
- return -1;
- }
-
- return len;
-}
+++ /dev/null
-/* $Id: thread_net_recv.h,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * User events polling thread. Reports all events to the master thread
- * via efficient inter-thread messages.
- */
-
-
-
-#ifndef THREAD_NET_RECV_H
-#define THREAD_NET_RECV_H
-
-
-
-#include <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/05/19 09:13:42 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Server socket writing thread. We post all asynchroneous messages
- * (thread_amsg_t) we receive from the main thread to the server socket.
- */
-
-
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <thread_net_send.h>
-#include <thread_net_recv.h>
-
-
-
-static thread_ring_t send_ring;
-
-
-
-thread_port_t send_port;
-
-
-
-int
-thread_net_send(void *data)
-{
- thread_amsg_t *amsg;
-
- /* Initialize reply port and ring */
- if (thread_ring_init(&send_ring) == -1) {
- (void) fprintf(stderr,
- "thread_net_recv() - thread_ring_init(send_port) - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
- if (thread_port_init(&send_port) == -1) {
- (void) fprintf(stderr,
- "thread_net_send() - thread_port_init(send_port) - %s\n",
- SDL_GetError());
- exit(EXIT_FAILURE);
- }
- thread_port_set_ring(&send_port, &send_ring);
-
- /*
- * Simply process all incomming messages, sending their data to
- * the server port, and discarding the messages. When the port is
- * empty, wait polling until messages become available.
- */
- for (;;) {
- while ((amsg = (thread_amsg_t *)thread_msg_get(&send_port))
- != NULL) {
- if (SDLNet_TCP_Send(server_socket,
- amsg->data, amsg->size) != amsg->size)
- (void) fprintf(stderr,
- "Error writing to server socket\n");
- thread_amsg_destroy(amsg);
- }
- (void) thread_ring_wait(&send_ring, -1);
- }
-
- /* NOTREACHED */
-
- return 0;
-}
+++ /dev/null
-/* $Id: thread_net_send.h,v 1.1 2006/05/19 09:13:42 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-/*
- * Server socket writing thread. We post all asynchroneous messages
- * (thread_amsg_t) we receive from the main thread to the server socket.
- */
-
-
-
-#ifndef THREAD_NET_SEND_H
-#define THREAD_NET_SEND_H
-
-
-
-#include <SDL.h>
-#include <SDL_net.h>
-
-#include <thread_msg.h>
-
-
-
-int thread_net_send(void *);
-
-
-
-extern thread_port_t send_port;
-
-
-
-#endif
+++ /dev/null
-For now these have been borrowed from the netrekxp client.
+++ /dev/null
-$Id: README,v 1.1 2006/06/15 14:23:24 mmondor Exp $
-
-Simple zlib compression/decompression tests.
+++ /dev/null
-/* $Id: deflate.c,v 1.1 2006/06/15 14:23:24 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <zlib.h>
-
-
-
-#define RBUF_SIZE 4096
-#define DBUF_SIZE (RBUF_SIZE * 2)
-
-
-
-int main(void);
-
-
-
-int
-main(void)
-{
- z_stream s;
- char rbuf[RBUF_SIZE], dbuf[DBUF_SIZE];
- size_t size;
-
- if (setvbuf(stdin, NULL, _IONBF, 0) == EOF)
- perror("setvbuf(stdin)");
- if (setvbuf(stdout, NULL, _IONBF, 0) == EOF)
- perror("setvbuf(stdout)");
-
- s.next_in = s.next_out = NULL;
- s.avail_in = s.avail_out = 0;
- s.zalloc = NULL;
- s.zfree = NULL;
- s.opaque = NULL;
-
- if (deflateInit(&s, Z_DEFAULT_COMPRESSION) != Z_OK) {
- perror("deflateInit()");
- exit(EXIT_FAILURE);
- }
-
- while (!feof(stdin) && !ferror(stdin)) {
- if ((size = fread(rbuf, 1, RBUF_SIZE, stdin)) < 1)
- continue;
- s.next_in = rbuf;
- s.avail_in = size;
- s.next_out = dbuf;
- s.avail_out = DBUF_SIZE;
- if (deflate(&s, Z_NO_FLUSH/*Z_SYNC_FLUSH*/) != Z_OK) {
- perror("deflate()");
- exit(EXIT_FAILURE);
- }
- if (fwrite(dbuf, 1, DBUF_SIZE - s.avail_out, stdout)
- != DBUF_SIZE - s.avail_out) {
- perror("fwrite()");
- exit(EXIT_FAILURE);
- }
- }
-
- s.next_in = rbuf;
- s.avail_in = 0;
- s.next_out = dbuf;
- s.avail_out = DBUF_SIZE;
- if (deflate(&s, Z_FINISH) != Z_STREAM_END) {
- perror("deflate()");
- exit(EXIT_FAILURE);
- }
- if (fwrite(dbuf, 1, DBUF_SIZE - s.avail_out, stdout)
- != DBUF_SIZE - s.avail_out) {
- perror("fwrite()");
- exit(EXIT_FAILURE);
- }
-
- if (deflateEnd(&s) != Z_OK) {
- perror("deflateEnd()");
- exit(EXIT_FAILURE);
- }
-
- exit(feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE);
-}
+++ /dev/null
-/* $Id: inflate.c,v 1.1 2006/06/15 14:23:24 mmondor Exp $ */
-
-/*
- * Copyright (c) 2006, Matthew Mondor
- * ALL RIGHTS RESERVED.
- */
-
-
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <zlib.h>
-
-
-
-#define RBUF_SIZE 4096
-#define IBUF_SIZE (RBUF_SIZE * 64)
-
-
-
-int main(void);
-
-
-
-int
-main(void)
-{
- z_stream s;
- char rbuf[RBUF_SIZE], ibuf[IBUF_SIZE];
- size_t size;
- int ret;
-
- if (setvbuf(stdin, NULL, _IONBF, 0) == EOF)
- perror("setvbuf(stdin)");
- if (setvbuf(stdout, NULL, _IONBF, 0) == EOF)
- perror("setvbuf(stdout)");
-
- s.next_in = s.next_out = NULL;
- s.avail_in = s.avail_out = 0;
- s.zalloc = NULL;
- s.zfree = NULL;
- s.opaque = NULL;
-
- if (inflateInit(&s) != Z_OK) {
- perror("inflateInit()");
- exit(EXIT_FAILURE);
- }
-
- while (!feof(stdin) && !ferror(stdin)) {
- if ((size = fread(rbuf, 1, RBUF_SIZE, stdin)) < 1)
- continue;
- s.next_in = rbuf;
- s.avail_in = size;
- s.next_out = ibuf;
- s.avail_out = IBUF_SIZE;
- if ((ret = inflate(&s, Z_SYNC_FLUSH)) != Z_OK &&
- ret != Z_STREAM_END) {
- perror("inflate()");
- exit(EXIT_FAILURE);
- }
- if (fwrite(ibuf, 1, IBUF_SIZE - s.avail_out, stdout)
- != IBUF_SIZE - s.avail_out) {
- perror("fwrite()");
- exit(EXIT_FAILURE);
- }
- if (ret == Z_STREAM_END)
- break;
- }
-
- s.next_in = rbuf;
- s.avail_in = 0;
- s.next_out = ibuf;
- s.avail_out = IBUF_SIZE;
- if (inflate(&s, Z_FINISH) != Z_STREAM_END) {
- perror("inflate()");
- exit(EXIT_FAILURE);
- }
- if (fwrite(ibuf, 1, IBUF_SIZE - s.avail_out, stdout)
- != IBUF_SIZE - s.avail_out) {
- perror("fwrite()");
- exit(EXIT_FAILURE);
- }
-
- if (inflateEnd(&s) != Z_OK) {
- perror("inflateEnd()");
- exit(EXIT_FAILURE);
- }
-
- exit(feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE);
-}