[Zodb-checkins] CVS: Zope/lib/python/zdaemon - zdctl.py:1.9.10.1 zdctl.sh:1.2.18.1 Daemon.py:1.11.4.6 zdaemon.py:1.22.2.2

Chris McDonough chrism@zope.com
Fri, 3 Jan 2003 01:39:51 -0500


Update of /cvs-repository/Zope/lib/python/zdaemon
In directory cvs.zope.org:/tmp/cvs-serv28978

Modified Files:
      Tag: chrism-install-branch
	Daemon.py zdaemon.py 
Added Files:
      Tag: chrism-install-branch
	zdctl.py zdctl.sh 
Log Message:
Merging chrism-install-branch with HEAD (hopefully for one of the last
times).



=== Added File Zope/lib/python/zdaemon/zdctl.py ===
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""zdctl -- control an application run by zdaemon.

Usage: python zdctl.py -C config-file [action [arguments]]

Options:
-C/--configuration URL -- configuration file or URL
action [arguments] -- see below

If no action is specified on the command line, a "shell" interpreting
actions typed interactively is started.

Use the action "help" to find out about available actions.
"""

from __future__ import nested_scopes

# XXX Related code lives in lib/python/Controller/ZctlLib.py on the
# 'chrism-install-branch' branch.
# The code there knows more about Zope and about Windows, but doesn't
# use zdaemon.py or ZConfig.

import os
import re
import cmd
import sys
import time
import signal
import socket

if __name__ == "__main__":
    # Add the parent of the script directory to the module search path
    from os.path import dirname, abspath, normpath
    sys.path.append(dirname(dirname(normpath(abspath(sys.argv[0])))))

from ZEO.runsvr import Options


class ZDOptions(Options):

    # Where's python?
    python = sys.executable

    # Where's zdaemon?
    if __name__ == "__main__":
        _file = sys.argv[0]
    else:
        _file = __file__
    _file = os.path.normpath(os.path.abspath(_file))
    _dir = os.path.dirname(_file)
    zdaemon = os.path.join(_dir, "zdaemon.py")

    # Options for zdaemon
    backofflimit = 10                   # -b SECONDS
    forever = 0                         # -f
    sockname = os.path.abspath("zdsock") # -s SOCKET
    exitcodes = [0, 2]                  # -x LIST
    user = None                         # -u USER
    zdirectory = "/"                    # -z DIRECTORY

    # Program (and arguments) for zdaemon
    program = None

    def load_configuration(self):
        Options.load_configuration(self) # Sets self.rootconf
        if not self.rootconf:
            self.usage("a configuration file is required; use -C")
        # XXX Should allow overriding more zdaemon options here
        if self.program is None:
            self.program = self.rootconf.getlist("program")
        if self.program is None:
            self.usage("no program specified in configuration")


class ZDCmd(cmd.Cmd):

    prompt = "(zdctl) "

    def __init__(self, options):
        self.options = options
        cmd.Cmd.__init__(self)
        self.get_status()
        if self.zd_status:
            m = re.search("(?m)^args=(.*)$", self.zd_status)
            if m:
                s = m.group(1)
                args = eval(s, {"__builtins__": {}})
                if args != self.options.program:
                    print "WARNING! zdaemon is managing a different program!"
                    print "our program   =", self.options.program
                    print "daemon's args =", args

    def send_action(self, action):
        """Send an action to the zdaemon server and return the response.

        Return None if the server is not up or any other error happened.
        """
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            sock.connect(self.options.sockname)
            sock.send(action + "\n")
            sock.shutdown(1) # We're not writing any more
            response = ""
            while 1:
                data = sock.recv(1000)
                if not data:
                    break
                response += data
            sock.close()
            return response
        except socket.error, msg:
            return None

    def get_status(self):
        self.zd_up = 0
        self.zd_pid = 0
        self.zd_status = None
        resp = self.send_action("status")
        if not resp:
            return
        m = re.search("(?m)^application=(\d+)$", resp)
        if not m:
            return
        self.zd_up = 1
        self.zd_pid = int(m.group(1))
        self.zd_status = resp

    def awhile(self, cond, msg):
        try:
            self.get_status()
            while not cond():
                sys.stdout.write(". ")
                sys.stdout.flush()
                time.sleep(1)
                self.get_status()
        except KeyboardInterrupt:
            print "^C"
        else:
            print msg % self.__dict__

    def help_help(self):
        print "help          -- Print a list of available actions."
        print "help <action> -- Print help for <action>."

    def do_start(self, arg):
        self.get_status()
        if not self.zd_up:
            args = [
                self.options.python,
                self.options.zdaemon,
                "-b", str(self.options.backofflimit),
                "-d",
                "-s", self.options.sockname,
                "-x", ",".join(map(str, self.options.exitcodes)),
                "-z", self.options.zdirectory,
                ]
            if self.options.forever:
                args.append("-f")
            if self.options.user:
                argss.extend(["-u", str(self.options.user)])
            args.extend(self.options.program)
            os.spawnvp(os.P_WAIT, args[0], args)
        elif not self.zd_pid:
            self.send_action("start")
        else:
            print "daemon process already running; pid=%d" % self.zd_pid
            return
        self.awhile(lambda: self.zd_pid,
                    "daemon process started, pid=%(zd_pid)d")

    def help_start(self):
        print "start -- Start the daemon process."
        print "         If it is already running, do nothing."

    def do_stop(self, arg):
        self.get_status()
        if not self.zd_up:
            print "daemon manager not running"
        elif not self.zd_pid:
            print "daemon process not running"
        else:
            self.send_action("stop")
            self.awhile(lambda: not self.zd_pid, "daemon process stopped")

    def help_stop(self):
        print "stop -- Stop the daemon process."
        print "        If it is not running, do nothing."

    def do_restart(self, arg):
        self.get_status()
        pid = self.zd_pid
        if not pid:
            self.do_start(arg)
        else:
            self.send_action("restart")
            self.awhile(lambda: self.zd_pid not in (0, pid),
                        "daemon process restarted, pid=%(zd_pid)d")

    def help_restart(self):
        print "restart -- Stop and then start the daemon process."

    def do_kill(self, arg):
        if not arg:
            sig = signal.SIGTERM
        else:
            try:
                sig = int(arg)
            except: # int() can raise any number of exceptions
                print "invalid signal number", `arg`
                return
        self.get_status()
        if not self.zd_pid:
            print "daemon process not running"
            return
        print "kill(%d, %d)" % (self.zd_pid, sig)
        try:
            os.kill(self.zd_pid, sig)
        except os.error, msg:
            print "Error:", msg
        else:
            print "signal %d sent to process %d" % (sig, self.zd_pid)

    def help_kill(self):
        print "kill [sig] -- Send signal sig to the daemon process."
        print "              The default signal is SIGTERM."

    def do_wait(self, arg):
        self.awhile(lambda: not self.zd_pid, "daemon process stopped")
        self.do_status()

    def help_wait(self):
        print "wait -- Wait for the daemon process to exit."

    def do_status(self, arg=""):
        self.get_status()
        if not self.zd_up:
            print "daemon manager not running"
        elif not self.zd_pid:
            print "daemon manager running; daemon process not running"
        else:
            print "program running; pid=%d" % self.zd_pid
        if arg == "-l" and self.zd_status:
            print self.zd_status

    def help_status(self):
        print "status [-l] -- Print status for the daemon process."
        print "               With -l, show raw status output as well."

    def do_logreopen(self, arg):
        self.do_kill(str(signal.SIGUSR2))

    def help_logreopen(self):
        print "logreopen -- Send a SIGUSR2 signal to the daemon process."
        print "             This is designed to reopen the log file."

    def do_quit(self, arg):
        self.get_status()
        if not self.zd_up:
            print "daemon manager not running"
        elif not self.zd_pid:
            print "daemon process not running; stopping daemon manager"
            self.send_action("exit")
            self.awhile(lambda: not self.zd_up, "daemon manager stopped")
        else:
            print "daemon process and daemon manager still running"
        return 1

    def help_quit(self):
        print "quit -- Exit the zdctl shell."
        print ("        If the daemon process is not running, "
               "stop the daemon manager.")

def main(args=None):
    options = ZDOptions(args)
    c = ZDCmd(options)
    if options.args:
        c.onecmd(" ".join(options.args))
    else:
        print "program:", " ".join(options.program)
        c.do_status()
        c.cmdloop()

if __name__ == "__main__":
    main()


=== Added File Zope/lib/python/zdaemon/zdctl.sh ===
#! /bin/sh
#
# Copy this script into the /etc/rc.d/init.d directory and edit the
# description line below and the pathnames; then run chkconfig(8).
#
# chkconfig: 345 90 10
# description: start a Zope-related server (Zope, ZEO or ZRS)

# Edit to indicate which Python to use
PYTHON=/usr/local/bin/python2.2

# Edit to indicate where the core Zope software lives
ZOPE_HOME=$HOME/projects/Zope

# Edit to indicate where your Zope instance lives
INSTANCE_HOME=$HOME/projects/Zope

# Edit to indicate where your config file is
CONFIG_LOCATION=$INSTANCE_HOME/sample.conf

# You shouldn't need to edit these
SOFTWARE_HOME=$ZOPE_HOME/lib/python
ZDCTL=$SOFTWARE_HOME/zdaemon/zdctl.py
CMD="$PYTHON $ZDCTL -C $CONFIG_LOCATION"

# Parse the command line
case $1 in
[a-z]*[a-z]) $CMD "$@";;
-i) $CMD;;
*)  echo "Usage: $0 start|stop|restart|status|help|etc."
    echo "       $0 -i starts an interactive zdctl shell."
   ;;
esac


=== Zope/lib/python/zdaemon/Daemon.py 1.11.4.5 => 1.11.4.6 ===
--- Zope/lib/python/zdaemon/Daemon.py:1.11.4.5	Sun Nov 24 18:53:58 2002
+++ Zope/lib/python/zdaemon/Daemon.py	Fri Jan  3 01:39:15 2003
@@ -12,7 +12,7 @@
 #
 ##############################################################################
 
-import os, sys, time, signal
+import os, sys, signal
 import zLOG
 
 pyth = sys.executable


=== Zope/lib/python/zdaemon/zdaemon.py 1.22.2.1 => 1.22.2.2 ===
--- Zope/lib/python/zdaemon/zdaemon.py:1.22.2.1	Sun Nov 24 18:53:58 2002
+++ Zope/lib/python/zdaemon/zdaemon.py	Fri Jan  3 01:39:15 2003
@@ -1,7 +1,18 @@
 #! /usr/bin/env python
-
-"""
-zdaemon -- run an application as a daemon.
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""zdaemon -- run an application as a daemon.
 
 Usage: python zdaemon.py [zdaemon-options] program [program-arguments]
 Or:    python zdaemon.py -c [command]
@@ -154,7 +165,7 @@
                 print __doc__,
                 sys.exit(0)
             if o == "-s":
-                self.sockname = a
+                self.sockname = os.path.abspath(a)
             if o == "-u":
                 self.user = a
             if o == "-x":
@@ -797,6 +808,7 @@
 
 def main(args=None):
     assert os.name == "posix", "This code makes many Unix-specific assumptions"
+    zLOG.initialize()
     d = Daemonizer()
     d.main(args)