Forum OpenACS Q&A: Re: restart-aolserver not working for group member restarts

I wrote a tcl script to replace the perl script. When used with sudo, it has the following features:

  • Permission can be as restrictive as 0500 when owned by the user which owns the AOLserver process.
  • Any combination of users and groups can be allowed access to the restart/kill script.
  • The script is run as the user which owns the AOLserver process
  • Tcl doesn't support setuid, so just running the script without sudo, and with the setuid bit set, doesn't work.
  • root cannot execute the script except through sudo.
  • The script doesn't take command line parameters, each server requires a separate script file, but command line parsing is eliminated.
  • The pid file has to be a regular file.
  • Pid file format is limited. It must start with integer characters and ends with \n or "". The integer value must be less than 32768.
  • The pid file is read one char at a time to avoid running out of memory, or any other type of buffer overflow condition, it also might avoid any kind of subst type problem as each char must be an integer.
  • The pidfile is removed using tcl's file delete command, an uses 'exec /usr/bin/kill -9 $pid' to kill the process.

Seems like a waste of time to investigate a script I never use, but I ran into an installation that needed it. I noticed the documentation on that part of the install isn't really maintained, and has fallen behind. I'm also working on a semi-auto installation script for OpenACS related software, and putting nonworking scripts in it wasn't an option.

Here is the script:

#!/usr/local/aolserver/bin/tclsh8.4

# to allow a user other than the file owner to execute this file,
# edit the /etc/sudoers file (using visudo), adding a line similar to:
# otheruser localhost = (scriptowner) /path/to/this/file/scriptfile
# for an entire group:
# %groupname localhost = (scriptowner) /path/to/this/file/scriptfile

# Avoid command line arguments.
set SERVER "server1"
set PIDFILE "/tmp/aolserver/log/nspid.$SERVER"

########## Nothing to edit below this line ################

#Tests:
# 1: Pidfile exists, and path to file is readable
if {![file exists $PIDFILE]} {
    puts stdout "Pidfile does not exist, or path not readable by this user."
    exit
}
# 2. Pidfile is regular file, not directory, symlink, or other
# type of file.
if {![file isfile $PIDFILE]} {
    puts stdout "Pidfile isn't regular file"
    exit
}
# 3. Pidfile is owned by user running this script:
if {![file owned $PIDFILE]} {
    puts stdout "Pidfile isn't owned by current user"
    exit
}
# Open Pidfile and read it.
set FD [open $PIDFILE r]

# Make sure we catch any expected or unexpected errors
# while the pidfile is open.
# The Pidfile must start with numbers and end with EOF
# or end with \nEOF. AOLserver writes the latter format.
# Reading one char at a time to avoid memory exhaustion,
# and any unexpected variable substitutions.

if {[catch {
    set PIDVALUE 0
    while {![eof $FD] && ($PIDVALUE < 32768)} {
        set CHAR [read $FD 1]
        if {[string is integer -strict "${CHAR}"]} {
            set PIDVALUE [expr (10 * ${PIDVALUE} ) + "${CHAR}"]
        } elseif {[string eq "\n" "${CHAR}"] || [string eq "" "${CHAR}"] } {
            break
        } else {
            # pid format is 123\n or just 123.
            error "Unexpected format in Pid file"
        }
    }
} err ]} {
    close $FD
    puts stdout $err
    exit
} else {
    close $FD
}

# Last Test: Pidvalue is in range:
if {!($PIDVALUE < 32768) || ($PIDVALUE < 2) } {
    puts stdout "Pidvalue out of range."
}

# a race condition exists where two users could be running this script,
# or otherwise deleting the pidfile and restarting the server. In this
# case, the new pidfile, containing the pid of the running server may
# be deleted. The following kill command will not have any effect, but
# will return the error to the terminal. The result will be a running
# server without the pidfile. This condition is will likely never
# occur.

file delete "$PIDFILE"
exec /usr/bin/kill -9 "$PIDVALUE"

Any suggestions or comments are welcome.