
/*
 * Copyright (C) 2003  Sam Horrocks
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

Frontend
========
- Save original argv
- Do "Frontend Connect"
- Do the speedy protocol

Frontend Connect
================
- While no connection:
    - Stat our script file
    - Call "Get a Backend"
    - If we got a backend:
	- Try to connect
	- If connection successful, return
	- If connection failed:
	    - Unless there's a bug in the code somewhere, this BE must've
	      been killed and not yet cleaned up.  Since we've already
	      unlocked the file, if we try to go do a cleanup, we may get
	      there after another process does.  So, we may be overwriting
	      some other unrelated slot data instead.  This could be
	      solved with some data in the BE slot where we allocate
	      this BE to our pid before unlocking, making us the only pid
	      allowed to clean it up.  Or we could go back and track down our
	      group/script again and find this BE on the group's list,
	      if it's still there.  But, both of these options are a lot
	      of trouble for what should be a rare occurance anyways -
	      if the process is dead, the normal cleanup mechanism should
	      eventually get rid of it anyways (assuming no PID wrap).
	      For now just ignore a connection failed and move on.
    - Endif
- End Loop

Get a Backend
=============
- Lock file
- Do "Find Script" to find our group/script slots
- Allocate an FE slot and add it to the end of the FE list for this group
- While no timeout:
    - Call "Send Sigs"
    - If our sent_sig flag is set and there are BE's available:
	- Remove a BE from the idle list
	- Break out of loop
    - Endif
    - Do a "Backend Check"
    - Block alarm signal, saving previous state
    - Unlock file
    - Set alarm for 1-second
    - Wait for an alarm
    - Lock file
    - Revert alarm signal to previous state
    - If our group slot is invalid, break out of loop
- End Loop
- Remove our FE slot
- Unlock file
- Return the BE slotnum, if one was found


Send Sigs
=========
- Foreach BE in the be_wait list
    - Loop Forever
	- Get the next FE from fe_wait
	- If no more FE's, end loop
	- If pid is not ours:
	    - Send alarm signal using kill
	    - If kill failed:
		- Dispose of FE
		- Continue loop
	    - Endif
	- Endif
	- Set the sent_sig flag for this FE
	- Break out of loop
    - End Loop
- End Loop


Backend Check
=============
- Loop Forever
    - Check the first BE on the groups be_list with a signal-0
    - If running, end-loop
    - If not running do a "BE Dispose"
- End Loop
- If number of BE's running is too low (below one, or below the run limit),
  then do a "Backend Spawn"


Backend Spawn
=============
- Fork/exec the speedy_backend prog with original argv[]


Backend
=======
- Stat our script
- Lock temp file
- Find our group/script in the temp file
- Do "Allocate Slot"
- Write pid into slot
- Put BE onto group's be list
- Unlock temp file
- Bring up perl interpreter and compile cgi code
- Listen on socket
- Loop Forever
    - Lock temp file
    - If our group is invalid, cleanup and exit
    - Add our slot to the beginning of the be_wait list
    - Do "Send Sigs"
    - Unlock temp file
    - Do "Backend Accept"
    - Read environment and argv on stdin
    - Run perl code
    - Shutdown stdio
- End Loop

Backend Accept
==============
- Loop Forever
    - Wait for accept or timeout
    - If got accept, return
    - Do "Try Exit"
- End Loop


Try Exit
========
Lock file
Set timeout to zero
Wait for accept or timeout
If no accept is pending:
    - Do a Backend Dispose on our slot
    - Exit
Else:
    - Unlock file
    - Return

Exit Unconditionally
====================
- "Try Exit"
- Exit


Backend Dispose
===============
- Remove this BE from the group's be list
- Remove this BE from the be_wait list
- Unlink the socket associated with this slot
- Do "Free Slot"
- If group is now empty free the group


Frontend Dispose
================
- ???


File Operations
---------------

File Lock
=========
- If file is already locked, return.
- If file-descriptor is marked suspect:
    - If file is open:
	- Fstat the open file-descriptor
	- If fstat fails or different dev/inode:
	    - "Unmap the file"
	    - Forget this file-descriptor
	- Endif
    - Endif
    - Mark file-descriptor OK
- Endif
- Loop forever
    - If file is not open, open it.
    - Lock the file
    - Fstat the file-descriptor
    - "Map the file"
    - If file is too small:
	- truncate to a new length
	- Fstat the file-descriptor
	- remap the file
    - Endif
    - If file is marked as valid, break out of loop
- End Loop


Unmap the file
==============
- If file is mapped, call munmap()


Map the file
============
- If file is not mmaped with the correct size:
    - "Unmap the file"
    - Call mmap()
- Endif


File Unlock
===========
- Release lock on file-descriptor


FD Is Suspect
=============
- Mark fd as suspect


Close File
==========
- Unlock file
- "Unmap the file"
- Close file


Remove File
===========
- Lock the file
- Set the "invalid flag" in the file
- Unlink the file
- "Close File"


Find Script
===========
- Find our script by searching for dev/ino
- If not found:
    - Unlock file
    - Read command-line options from script
    - Lock file
    - Create a new script entry
    - Find our group and point our script entry to it
- If entry has older mtime:
    - Invalidate this group
    - Send shutdown requests to all be's on the be_wait queue
    - Update the mtime
- Endif
- If no valid group, create a new group and point our script entry to it.
