Permalink
Browse files

Move the support routines such as daemonize, pidfile_open, etc, into …

…a separate file (tcllauncher-support.tcl) from the tcllauncher program and make it so they can be picked up by a non-tcllauncher program by using package require Tcllauncher.

Make the tcllauncher-support.tcl file be generated by expanding tcllauncher-support.tcl.in so the package version will always be generated as configured in the configure.in file.

Add a new pidfile support routine, pidfile_mtime, that returns the mtime of the PID file... useful for a guy who can't obtain the pid to see how long the guy who's holding it has been holding it.

Update the documentation to reflect the above.

Version bump to 1.3.
BUGZID:
  • Loading branch information...
lehenbauer committed Nov 26, 2012
1 parent 9760f2e commit 0e06b79981255aad1d534c07f0e5f82790e34d0b
Showing with 275 additions and 232 deletions.
  1. +1 −0 .gitignore
  2. +3 −3 configure.in
  3. +10 −0 doc/tcllauncher.man
  4. +6 −1 doc/tcllauncher.n
  5. +6 −0 doc/tcllauncher.txt
  6. +1 −1 pkgIndex.tcl.in
  7. +246 −0 tcllauncher-support.tcl.in
  8. +2 −227 tcllauncher.tcl
View
@@ -9,3 +9,4 @@ pkgIndex.tcl
*.dylib
tcllauncher
libtcllauncher.so*
+tcllauncher-support.tcl
View
@@ -19,7 +19,7 @@ dnl to configure the system for the local environment.
# so you can encode the package version directly into the source files.
#-----------------------------------------------------------------------
-AC_INIT([tcllauncher], [1.2])
+AC_INIT([tcllauncher], [1.3])
#--------------------------------------------------------------------
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
@@ -77,7 +77,7 @@ TEA_ADD_INCLUDES([])
TEA_ADD_LIBS([])
TEA_ADD_CFLAGS([])
TEA_ADD_STUB_SOURCES([])
-TEA_ADD_TCL_SOURCES([tcllauncher.tcl])
+TEA_ADD_TCL_SOURCES([tcllauncher.tcl tcllauncher-support.tcl])
#--------------------------------------------------------------------
# __CHANGE__
@@ -201,4 +201,4 @@ AC_SUBST(CC_SEARCH_FLAGS)
# which require substituting th AC variables in. Include these here.
#--------------------------------------------------------------------
-AC_OUTPUT([Makefile pkgIndex.tcl])
+AC_OUTPUT([Makefile pkgIndex.tcl tcllauncher-support.tcl])
View
@@ -135,6 +135,16 @@ specified, 0600 is used.
Writes your pid into the pid file previously opened by [cmd pidfile_open].
+[call [cmd ::tcllauncher::pidfile_mtime]]
+
+Return the mtime of the pidfile.
+
+[para]
+
+Can be used after a successful or unsuccessful
+call to pidfile_open. Considered useful after pidfile_open fails due to another
+process holding the lock to examine when the owner process got the lock.
+
[call [cmd ::tcllauncher::pidfile_close ]]
Close a pidfile. It should be used after your daemon forks to start
View
@@ -123,6 +123,11 @@ specified, 0600 is used.
\fB::tcllauncher::pidfile_write\fR
Writes your pid into the pid file previously opened by \fBpidfile_open\fR.
.TP
+\fB::tcllauncher::pidfile_mtime\fR
+Return the mtime of the pidfile. Can be used after a successful or unsuccessful
+call to pidfile_open. Considered useful after pidfile_open fails due to another
+process holding the lock to examine when the owner process got the lock.
+.TP
\fB::tcllauncher::pidfile_close\fR
Close a pidfile. It should be used after your daemon forks to start
a child process.
@@ -195,4 +200,4 @@ background, daemon, daemonize, tcllauncher
.nf
Copyright (c) 2007-2009 FlightAware LLC (BSD Liscense)
-.fi
+.fi
View
@@ -102,6 +102,12 @@ Our pidfile support is a studied Tcl-based copy of BSD's pidfile C library.
Writes your pid into the pid file previously opened by pidfile_open.
+::tcllauncher::pidfile_mtime
+
+Return the mtime of the pidfile. Can be used after a successful or unsuccessful
+call to pidfile_open. Considered useful after pidfile_open fails due to another
+process holding the lock to examine when the owner process got the lock.
+
::tcllauncher::pidfile_close
Close a pidfile. It should be used after your daemon forks to start
View
@@ -2,4 +2,4 @@
# Tcl package index file
#
package ifneeded Tcllauncher @PACKAGE_VERSION@ \
- [list load [file join $dir @PKG_LIB_FILE@] @PACKAGE_NAME@]
+ [list load [file join $dir @PKG_LIB_FILE@] @PACKAGE_NAME@]\n[list source [file join $dir tcllauncher-support.tcl]]
View
@@ -0,0 +1,246 @@
+#
+# tcllauncher-support.tcl - support and standalone routines for tcllauncher
+#
+
+package require Tclx
+
+namespace eval ::tcllauncher {
+
+#
+# require_group - require a certain group ID, exit with message to stderr if not
+#
+proc require_group {group} {
+ if {[id group] == $group} {
+ return
+ }
+
+ # see if we can set to that group, maybe we're root?
+ if {[catch {id group $group} result] == 1} {
+ puts stderr "requires and can't set to group '$group': $result"
+ exit 254
+ }
+
+ return
+}
+
+#
+# require_user - require a certain user ID, exit with message to stderr if not
+#
+proc require_user {user} {
+ if {[id user] == $user} {
+ return
+ }
+
+ # see if we can set to that group, maybe we're root?
+ if {[catch {id user $user} result] == 1} {
+ puts stderr "requires and can't set to user '$user': $result"
+ exit 253
+ }
+
+ return
+}
+
+#
+# require_user_and_group - require the invoker to either be of a certain
+# user and group or if they're superuser or some kind of equivalent,
+# force this process to have the specified user (uid) and group (gid)
+#
+proc require_user_and_group {user group} {
+
+ # try group first because if we're root we might not be after setting
+ # user
+
+ require_group $group
+
+ require_user $user
+}
+
+#
+# daemonize - rough tclx-based copy of BSD 4.4's daemon library routine
+#
+
+proc daemonize {args} {
+ set doClose 1
+ set doChdir 1
+
+ foreach arg $args {
+ switch $arg {
+ "-noclose" {
+ set doClose 0
+ }
+
+ "-nochdir" {
+ set doChdir 0
+ }
+
+ default {
+ error "unrecognized option: $arg"
+ }
+ }
+ }
+
+ set pid [fork]
+
+ if {$pid != 0} {
+ exit 0
+ }
+
+ id process group set
+
+ if {$doChdir} {
+ cd "/"
+ }
+
+ if {$doClose} {
+ set fp [open /dev/null RDWR]
+ dup $fp stdin
+ dup $fp stdout
+ dup $fp stderr
+ close $fp
+ }
+
+ return
+}
+
+#
+# pidfile_verify - insane checks of pid file
+#
+proc pidfile_verify {} {
+ variable pfh
+
+ if {[catch {fstat $pfh(fp)} stat] == 1} {
+ error "programming error"
+ }
+
+ set dev [keylget stat dev]
+ set ino [keylget stat ino]
+
+ if {$dev != $pfh(dev) || $ino != $pfh(ino)} {
+ error "programming error"
+ }
+
+ return 0
+}
+
+#
+# pidfile_read - given a path and the name of a pid variable, set the
+# PID into the variable
+#
+proc pidfile_read {path _pid} {
+ variable pfh
+
+ upvar $_pid pid
+
+ set fp [open $path "RDONLY"]
+ set pid [read -nonewline $fp]
+ close $fp
+
+ set pfh(path) $path
+}
+
+#
+# pidfile_open - given an optional path to a directory and optional permissions,
+# open the file, try to lock it, get its contents. Return the pid contained
+# therein if there is one and the lock failed. (Somebody's already got the
+# pid.)
+#
+# else you've got the lock and call pidfile_write to get your pid in there
+#
+proc pidfile_open {{path "/var/run"} {mode 0600}} {
+ variable pfh
+
+ set pidfile $path/$::argv0.pid
+ set pfh(path) $pidfile
+
+ # Open the PID file and obtain exclusive lock.
+ # We truncate PID file here only to remove old PID immediately,
+ # PID file will be truncated again in pidfile_write(), so
+ # pidfile_write() can be called multiple times.
+
+ set fp [open $pidfile "RDWR CREAT"]
+
+ # try to lock the file
+
+ if {![flock -write -nowait $fp]} {
+ # failed to lock the file, read it for the pid of the owner
+ set pid [read -nonewline $fp]
+
+ # if we can get an integer out of it, return that
+ if {[scan $pid %d pid] > 0} {
+ close $fp
+ return $pid
+ }
+ }
+
+ # i got the lock
+
+ # can fstat really fail on a file i have open?
+ set stat [fstat $fp]
+
+ set pfh(fp) $fp
+ set pfh(dev) [keylget stat dev]
+ set pfh(ino) [keylget stat ino]
+
+ return 0
+}
+
+#
+# pidfile_mtime - return the mtime of the pidfile, returns -1 if
+# "file mtime" failed.
+#
+proc pidfile_mtime {} {
+ variable pfh
+
+ if {[catch {file mtime $pfh(path)} catchResult] == 1} {
+ # some kind of error statting the file
+ return -1
+ }
+
+ # catchResult is the mtime of the file
+ return $catchResult
+}
+
+#
+# pidfile_write - write my pid into the pid file
+#
+proc pidfile_write {} {
+ variable pfh
+
+ pidfile_verify
+
+ set fp $pfh(fp)
+
+ ftruncate -fileid $fp 0
+
+ puts $fp [pid]
+ flush $fp
+}
+
+#
+# pidfile_close - close the pid file
+#
+proc pidfile_close {} {
+ variable pfh
+
+ pidfile_verify
+
+ close $pfh(fp)
+}
+
+#
+# pidfile_remove - remove the pidfile, unlock the lock, and close it
+#
+proc pidfile_remove {} {
+ variable pfh
+
+ pidfile_verify
+
+ file delete $pfh(path)
+ funlock $pfh(fp)
+
+ close $pfh(fp)
+}
+
+} ;# namespace tcllauncher
+
+package provide Tcllauncher @PACKAGE_VERSION@
Oops, something went wrong.

0 comments on commit 0e06b79

Please sign in to comment.