Fork me on GitHub
tcllauncher
by flightaware
Launcher program for Tcl applications.

Overview

tcllauncher is a way to have Tcl programs run out of /usr/local/bin under their own name, be installed in one place with their support files, and provides commands to facilitate server-oriented application execution.

Now you might think, why bother? I'll just put my script in there and do a #! thing to invoke Tcl.

Well, OK, but this has certain problems:

You'd like to be able to have stuff show up as its script name.

You could just copy or even link tclsh to the name of your program. Say, for instance, trackserver.

But then you have to invoke trackserver with arguments and do stuff to prep it, like:

cd ...somewhere...
/usr/local/bin/trackserver main.tcl

That's the original purpose for tcllauncher, just to make that reasonable.

cp /usr/local/bin/tcllauncher /usr/local/bin/trackserver

trackserver

How does it find its files? It cd's to the corresponding lib directory and a directory underneath that of the same name as the application, and sources the file main.tcl with the variable tcl_interactive set to 0.

SO when tcllauncher is installed as trackserver and you run trackserver, what happens, /usr/local/bin/trackserver starts up like the Tcl shell, except that it sources in the file /usr/local/bin/trackserver/main.tcl.

Also a global variable called launchdir is set, containing the "launch directory," i.e. the directory where main.tcl was loaded from. (In the above example, /usr/local/lib/trackserver.)

What Directory

Tcllauncher doesn't change your directory behind your back, so wherever you are when you run it, you're still in that directory.

But a lot of times you want to go to your application directory, so you can just
cd $::launchdir

Then you can source in all of your various files and stuff like that. A common tcllauncher coding pattern is to store the current directory in a variable, cd to launchdir, source in files, then cd back to the original directory.

PROCESS GROUP

If you are going to fork off children, exec them, or whatever, you should probably become your own process group so hopefully your children might inherit your signals and Do The Right Thing.

id process group set

The id command is from the TclX extension.

PID FILE

Lots of apps write a file with the server's process ID in it. Upon relaunch, the program can come along and look in its own pid file to see if it's already alive or not, and also to potentially kill it.

Our pidfile support is a studied Tcl-based copy of BSD's pidfile C library.

::tcllauncher::pidfile_open ?path? ?mode?

Given an optional path to a directory and optional permissions, pidfile_open opens (or creates) a file specified by the path and locks it with TclX's interface to the flock system call.

If the file cannot be locked, the PID of an already running daemon is returned.

Otherwise zero is returned and you've got the lock. You can now call pidfile_write to get your pid into the lock file.

This function does not write your process' PID into the file, so it can be used before forking if needed.

If path is not specified, /var/run is used, and if mode is not specified, 0600 is used.

::tcllauncher::pidfile_write Writes your pid into the pid file previously opened by [cmd pidfile_open].

::tcllauncher::pidfile_close closes a pidfile. It should be used after your daemon forks to start a child process.

::tcllauncher::pidfile_remove closes and removes a pidfile.

Example

    set pid [lb]::tcllauncher::pidfile_open "/var/run/daemon.pid 0600[rb]
    if {$pid > 0} {
	puts stderr "pid $pid already has the lock"
	exit 1
    }

    ::tcllauncher::daemonize

    ::tcllauncher::pidfile_write

    ...do work...

    ::tcllauncher::pidfile_remove
    exit

DAEMONIZE

Sometimes you want your program to spawn itself off into the background in a way that when you logout it doesn't kill the process, etc.

To daemonize a tcllauncher app,

::tcllauncher::daemonize] ?-noclose? ?-nochdir?

By default this forks off a child and exits the parent. In the child, it changes the current directory to /, and redirects stdin, stdout and stderr to/from /dev/null.

Specifying -noclose prevents the closing and redirecting of stdin, stdout and stderr, while -nochdir prevents the changing of the working dir to /.

This is a rough copy of BSD 4.4's daemon library routine.

USER AND GROUP ID MANAGEMENT

If a program needs to be run as a certain user, it can invoke

::tcllauncher::require_user userName

This requires the program to either be run as fred or as root or something like that, by a user that has permissions to become fred.

If the program is running as user fred or can change the user id (suid) to fred, it continues, else it aborts.

This means if the superuser invokes the program, it will change user to the correct user. If the correct user invokes the program, it will correctly do nothing and proceed. Handy.

::tcllauncher::require_group groupName does for groups what require_user does for users.

::tcllauncher::require_user_and_group] userName groupName

combines changing the group and user into a single procedure.

Note that if you require user first then require group, the process may have lost the privileges necessary to change groups after changing users. Either require the group ID first or use ::tcllauncher::require_user_and_group to do both.

Code

Get it on github: http://github.com/flightaware/tcllauncher

Support

You can sometimes find tcllauncher on IRC in the #tcl channel on the Freenode network or you can file issues with github here.

Download

You can also clone the project with Git by running:

$ git clone git://github.com/flightaware/tcllauncher

Using tcllauncher

License

Copyright (c) 2010-2011, FlightAware LLC
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    * 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.

    * Neither the name of the FlightAware LLC nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER 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.