$Id: readme.txt,v 1.7 2003/01/23 00:07:57 dancy Exp $

Turn your Common Lisp application into a Windows NT/2000 service with
the ntservice package.

If you have a Common Lisp application that you'd like to start up
automatically when the system starts... and shut down cleanly when the
system is shutting down, then this package is for you.

Follow these steps and you'll be on the road to servicedom. 

1) First write/test/debug your application without ntservice.  Your
   application should be working properly as a standalone application
   (generated by generate-executable [Allegro CL v6.1] or
   generate-application) before attempting to involve ntservice.

2) Compile ntservice.cl and update your application so that it
   loads ntservice.fasl.

3) The main function in your application should call
   ntservice:start-service as soon as possible.
   ntservice:start-service will be responsible for executing any
   initialization functions which your program may need.  It is also
   responsible for starting the main loop of your program.  Please
   note: ntservice:start-service calls (exit 0 :no-unwind t :quiet t)
   when the service is stopped.  If you need things to happen before
   'exit' is called, use the 'stop' keyword argument to start-service
   to pass in a function that can do cleanup.
   
   The definition for ntservice:start-service is as follows:

(defun start-service (main &key init stop)
  ...)

'main' should be a function (or a symbol naming a function) which
constitutes the main loop of your program.  This function will be
called when the service starts running.  No arguments are passed to
this function.  This function should never return [if it does, Windows
will complain that the service terminated prematurely].

The keyword arguments 'init' and 'stop' are optional.  

'init' specifies a function (or a symbol naming a function) that
should be executed before the main loop is executed.  Such a function
might load in configuration settings or verify the system environment.
The 'init' function should be prepared to accept a single argument.
This argument is the list of "Start parameters" that have been
specified for the service.  This list is usually empty but can be
modified using the Services control panel applet.  If the 'init'
function returns 'nil', the service will not be started and an error
will be logged and/or reported.  Make sure 'init' returns non-nil
under normal circumstances.

'stop' specifies a function (or a symbol naming a function) that
should be executed when the service is to be stopped.  Such a function
might do work that your application needs done before Lisp exits.
This function should do its job fairly swiftly, otherwise Windows
might complain that the service isn't stopping properly.  No arguments
are passed to this function.

Please remember that ntservice:start-service never returns to its
caller.  It calls (exit 0 :no-unwind t :quiet t) to exit Lisp.

4) Regenerate your application w/ the updated code.

5) Call ntservice:create-service to add your program to the list of
Windows services.  Usually this would be done by the program that
installs your application.  See testapp.cl for an example of how to
add command-line switches to your program to allow a user to
add/remove the service easily.

The definition for ntservice:create-service is as follows:

(defun create-service (name displaystring cmdline &key (start :manual))
  ...)

'name' should be a string that identifies your service.  The maximum
string length is 256 characters. The service control manager database
preserves the case of the characters, but service name comparisons are
always case insensitive.  Forward-slash (/) and back-slash (\) are
invalid service name characters.

'displaystring' should be a string that contains the display name to
be used by user interface programs to identify the service. This
string has a maximum length of 256 characters. The name is
case-preserved in the service control manager. display name
comparisons are always case-insensitive.

'cmdline' should be a string that contains the command line for
executing your service program.  The first word in the string should
be the fully-qualified pathname to the executable.

'start' can either be :manual or :auto.  If :manual, the service must
be started and stopped manually.  If :auto, the service will start
automatically at boot time.

Return values:
If create-service is successful, it returns 't'.  If it is not
successful, it returns two values:  nil and the Windows error code.
You can use ntserver:winstrerror to convert the code into a string.

Example:

(multiple-value-bind (success errcode)
      (ntservice:create-service 
	"MyService" 
	"My Common Lisp program service" 
	"c:\\devel\\program\\program.exe /runfast /dontcrash")
   (if success
       (format t "all is well~%")
     (error "create-service failed: ~A"
            (ntservice:winstrerror errcode))))

Your service will be created w/ the following properties:
Manual start
Run as LocalSystem account
Allow program to interact with desktop

6) Try it out!  Your service should now be listed in the Services
control panel applet.  Try starting and stopping your service.  If it
works as planned, you can use the Services control panel applet to
make the service start automatically instead of manually.

Other notes:
-
If you want to remove your program from the list of services, load
ntservice.fasl and evaluate:

(ntservice:delete-service name)

where 'name' is the name you gave the service in your call to
ntservice:create-service.  It seems to be possible to request deletion
of a running service.  This disables the service from further starts
and marks it for deletion once it stops.  delete-service turns 't' if
the removal was successful, otherwise it returns three values: nil,
the Windows error code, and a string with the name of the function
that actually failed.

-
The LocalSystem account is very powerful!  Be careful of what you
allow your program to do.  

Also note that the LocalSystem account usually does not have access to
network filesystems.  This may lead to confusion if your service tries
to access drive letters that are mapped to network drives, or if it
tries to access remote filesystems via UNC names (\\host\\share\file).

You can use the Services control panel applet to change who the
service runs as.  Note that no account but LocalSystem will be able to
interact w/ the desktop (i.e., your program's window will be invisible
if you don't run as LocalSystem).

See testapp.cl for an example skeleton for a service application.  
