[Zope-Checkins] SVN: zdaemon/trunk/ Bugs Fixed

Jim Fulton jim at zope.com
Thu Dec 21 18:46:15 EST 2006


Log message for revision 71643:
  Bugs Fixed
  ----------
  
  - In non-daemon mode, start hung, producing annoying dots
    when the program exited.
  
  - The start command hung producing annoying dots if the deamon failed
    to start.
  
  - foreground and start had different semantics because one used
    os.system and another used os.spawn
  
  New Features
  ------------
  
  - Documentation
  
  - Command-line arguments can now be supplied to the start and
    foreground (fg) commands
  
  - zdctl now invokes itself to run zdrun.  This means that it's
    no-longer necessary to generate a separate zdrun script.  This
    especially when the magic techniques to find and run zdrun using
    directory sniffing fail to set the path corrrectly.
  
  - The daemon mode is now enabled by default.  To get non-deamon mode,
    you have to use a configuration file and set deamon to off
    there. The old -d option is kept for backward compatibility, but is
    a no-op.
  

Changed:
  U   zdaemon/trunk/CHANGES.txt
  A   zdaemon/trunk/src/zdaemon/README.txt
  U   zdaemon/trunk/src/zdaemon/component.xml
  A   zdaemon/trunk/src/zdaemon/tests/tests.py
  U   zdaemon/trunk/src/zdaemon/zdctl.py
  U   zdaemon/trunk/src/zdaemon/zdoptions.py

-=-
Modified: zdaemon/trunk/CHANGES.txt
===================================================================
--- zdaemon/trunk/CHANGES.txt	2006-12-21 17:27:38 UTC (rev 71642)
+++ zdaemon/trunk/CHANGES.txt	2006-12-21 23:46:14 UTC (rev 71643)
@@ -1,19 +1,88 @@
 zdaemon Changelog
-=================
+*****************
 
+To-Dos
+======
+
+Tests:
+
+- non-daemon mode
+
+- no infinite loop when program fails on start
+
+More docs:
+
+- Document/demonstrate some important features, such as:
+
+  - transcript log
+
+  - working directory
+
+- Reference docs
+
+
+Features
+
+- environment variables
+
+- transcript log rotation
+
+Bugs 
+
+- help command
+
+
+zdaemon 2.0a1 (2006/12/21)
+==========================
+
+Bugs Fixed
+----------
+
+- In non-daemon mode, start hung, producing annoying dots
+  when the program exited.
+
+- The start command hung producing annoying dots if the deamon failed
+  to start.
+
+- foreground and start had different semantics because one used
+  os.system and another used os.spawn
+
+New Features
+------------
+
+- Documentation
+
+- Command-line arguments can now be supplied to the start and
+  foreground (fg) commands
+
+- zdctl now invokes itself to run zdrun.  This means that it's
+  no-longer necessary to generate a separate zdrun script.  This
+  especially when the magic techniques to find and run zdrun using
+  directory sniffing fail to set the path corrrectly.
+
+- The daemon mode is now enabled by default.  To get non-deamon mode,
+  you have to use a configuration file and set deamon to off
+  there. The old -d option is kept for backward compatibility, but is
+  a no-op.
+
+zdaemon 1.4a1 (2005/11/21)
+==========================
+
+Fixed a bug in the distribution setup file.
+
 zdaemon 1.4a1 (2005/11/05)
---------------------------
+==========================
 
 First semi-formal release.
 
 After some unknown release(???)
--------------------------------
+===============================
 
  - Made 'zdaemon.zdoptions' not fail for --help when __main__.__doc__
    is None.
 
 After zdaemon 1.1
------------------
+=================
 
  - Updated test 'testRunIgnoresParentSignals':
   
@@ -40,7 +109,7 @@
    forceful warning.
 
 zdaemon 1.1 (2005/06/09)
-------------------------
+========================
 
  - SVN tag:  svn://svn.zope.org/repos/main/zdaemon/tags/zdaemon-1.1
 

Added: zdaemon/trunk/src/zdaemon/README.txt
===================================================================
--- zdaemon/trunk/src/zdaemon/README.txt	2006-12-21 17:27:38 UTC (rev 71642)
+++ zdaemon/trunk/src/zdaemon/README.txt	2006-12-21 23:46:14 UTC (rev 71643)
@@ -0,0 +1,139 @@
+Using zdaemon
+=============
+
+zdaemon provides a script, zdaemon, that can be used to running other
+programs as POSIX (Unix) daemons. (Of course, it is only usable on
+POSIX-complient systems.
+
+Using zdaemon requires specifying a number of options, which can be
+given in a configuration file, or as command-line options.  It also
+accepts commands teling it what do do.  The commants are:
+
+start
+    Start a process as a daemon
+
+stop
+    Stop a running daemon process
+
+restart
+    Stop and then restart a program
+
+status
+    Find out if the program is running
+
+foreground or fg
+    Run a program
+
+kill signal
+    Send a signal to the daemon process
+
+help command
+    Get help on a command
+
+
+Commands can be given on a command line, or can be given using an
+interactive interpreter.
+
+Let's start with a simple example.  We'll use command-line options to
+run the echo command:
+
+    >>> system("./zdaemon -p 'echo hello world' fg")
+    echo hello world
+    hello world
+
+Here we used the -p option to specify a program to run.  We can
+specify a program name and command-line options in the program
+command. Note, however, that the command-line parsing is pretty
+primitive.  Quotes and spaces aren't handled correctly.  Let's look at
+a slightly more complex example.  We'll run the sleep command as a
+daemon :)
+
+    >>> system("./zdaemon -p 'sleep 100' start")
+    . daemon process started, pid=819
+
+This ran the sleep deamon.  We can check whether it ran with the
+status command:  
+
+    >>> system("./zdaemon -p 'sleep 100' status")
+    program running; pid=819
+
+We can stop it with the stop command:
+
+    >>> system("./zdaemon -p 'sleep 100' stop")
+    daemon process stopped
+
+    >>> system("./zdaemon -p 'sleep 100' status")
+    daemon manager not running
+
+Normally, we control zdaemon using a configuration file.  Let's create
+a typical configuration file:
+
+    >>> open('conf', 'w').write(
+    ... '''
+    ... <runner>
+    ...   program sleep 100
+    ... </runner>
+    ... ''')
+
+Now, we can run with the -C option to read the configuration file:
+
+    >>> system("./zdaemon -Cconf start")
+    . daemon process started, pid=1136
+
+If we list the directory:
+
+    >>> system("ls")
+    conf
+    zdaemon
+    zdsock
+
+We'll see that a file, zdsock, was created.  This is a unix-domain
+socket used internally by ZDaemon.  We'll normally want to control
+where this goes.
+
+    >>> system("./zdaemon -Cconf stop")
+    daemon process stopped
+
+    >>> open('conf', 'w').write(
+    ... '''
+    ... <runner>
+    ...   program sleep 100
+    ...   socket-name /tmp/demo.zdsock
+    ... </runner>
+    ... ''')
+
+
+    >>> system("./zdaemon -Cconf start")
+    . daemon process started, pid=1139
+
+    >>> system("ls")
+    conf
+    zdaemon
+
+    >>> import os
+    >>> os.path.exists("/tmp/demo.zdsock")
+    True
+
+    >>> system("./zdaemon -Cconf stop")
+    daemon process stopped
+
+In the example, we included a command-line argument in the program
+option. We can also provide options on the command line:
+
+    >>> open('conf', 'w').write(
+    ... '''
+    ... <runner>
+    ...   program sleep
+    ...   socket-name /tmp/demo.zdsock
+    ... </runner>
+    ... ''')
+
+    >>> system("./zdaemon -Cconf start 100")
+    . daemon process started, pid=1149
+
+    >>> system("./zdaemon -Cconf status")
+    program running; pid=1149
+
+    >>> system("./zdaemon -Cconf stop")
+    daemon process stopped
+


Property changes on: zdaemon/trunk/src/zdaemon/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: zdaemon/trunk/src/zdaemon/component.xml
===================================================================
--- zdaemon/trunk/src/zdaemon/component.xml	2006-12-21 17:27:38 UTC (rev 71642)
+++ zdaemon/trunk/src/zdaemon/component.xml	2006-12-21 23:46:14 UTC (rev 71643)
@@ -93,7 +93,7 @@
 
     <key name="daemon" datatype="boolean"
          required="no"
-         default="false">
+         default="on">
       <description>
         Command-line option: -d or --daemon.
 

Added: zdaemon/trunk/src/zdaemon/tests/tests.py
===================================================================
--- zdaemon/trunk/src/zdaemon/tests/tests.py	2006-12-21 17:27:38 UTC (rev 71642)
+++ zdaemon/trunk/src/zdaemon/tests/tests.py	2006-12-21 23:46:14 UTC (rev 71643)
@@ -0,0 +1,87 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""XXX short summary goes here.
+
+$Id$
+"""
+
+import os, re, shutil, sys, tempfile, unittest
+import ZConfig, zdaemon
+from zope.testing import doctest, renormalizing
+
+try:
+    import pkg_resources
+except ImportError:
+    zdaemon_loc = os.path.dirname(os.path.dirname(zdaemon.__file__))
+    zconfig_loc = os.path.dirname(os.path.dirname(ZConfig.__file__))
+else:
+    zdaemon_loc = pkg_resources.working_set.find(
+        pkg_resources.Requirement.parse('zdaemon')).location
+    zconfig_loc = pkg_resources.working_set.find(
+        pkg_resources.Requirement.parse('ZConfig')).location
+
+def setUp(test):
+    test.globs['_td'] = td = []
+    here = os.getcwd()
+    td.append(lambda : os.chdir(here))
+    workspace = tempfile.mkdtemp()
+    td.append(lambda : shutil.rmtree(workspace))
+    os.chdir(workspace)
+    open('zdaemon', 'w').write(zdaemon_template % dict(
+        python = sys.executable,
+        zdaemon = zdaemon_loc,
+        ZConfig = zconfig_loc,
+        ))
+    os.chmod('zdaemon', 0755)
+    test.globs.update(dict(
+        system = system
+        ))
+
+def tearDown(test):
+    for f in test.globs['_td']:
+        f()
+
+def system(command, input=''):
+    i, o = os.popen4(command)
+    if input:
+        i.write(input)
+    i.close()
+    print o.read(),
+
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite(
+            '../README.txt',
+            setUp=setUp, tearDown=tearDown,
+            checker=renormalizing.RENormalizing([
+                (re.compile('pid=\d+'), 'pid=NNN'),
+                ])
+        ),
+        ))
+
+
+zdaemon_template = """#!%(python)s
+
+import sys
+sys.path[0:0] = [
+  %(zdaemon)r,
+  %(ZConfig)r,
+  ]
+
+import zdaemon.zdctl
+
+if __name__ == '__main__':
+    zdaemon.zdctl.main()
+"""


Property changes on: zdaemon/trunk/src/zdaemon/tests/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: zdaemon/trunk/src/zdaemon/zdctl.py
===================================================================
--- zdaemon/trunk/src/zdaemon/zdctl.py	2006-12-21 17:27:38 UTC (rev 71642)
+++ zdaemon/trunk/src/zdaemon/zdctl.py	2006-12-21 23:46:14 UTC (rev 71643)
@@ -76,7 +76,7 @@
 
 class ZDCtlOptions(RunnerOptions):
 
-    positional_args_allowed = 1
+    positional_args_allowed = True
 
     def __init__(self):
         RunnerOptions.__init__(self)
@@ -111,16 +111,6 @@
         if not self.python:
             self.python = sys.executable
 
-        # Where's zdrun?
-        if not self.zdrun:
-            if __name__ == "__main__":
-                file = sys.argv[0]
-            else:
-                file = __file__
-            file = os.path.normpath(os.path.abspath(file))
-            dir = os.path.dirname(file)
-            self.zdrun = os.path.join(dir, "zdrun.py")
-
     def set_schemafile(self, file):
         self.schemafile = file
 
@@ -138,9 +128,10 @@
             if m:
                 s = m.group(1)
                 args = eval(s, {"__builtins__": {}})
-                if args != self.options.program:
+                program = self.options.program 
+                if args[:len(program)] != program:
                     print "WARNING! zdrun is managing a different program!"
-                    print "our program   =", self.options.program
+                    print "our program   =", program
                     print "daemon's args =", args
 
     def emptyline(self):
@@ -175,22 +166,27 @@
         self.zd_status = None
         resp = self.send_action("status")
         if not resp:
-            return
+            return resp
         m = re.search("(?m)^application=(\d+)$", resp)
         if not m:
-            return
+            return resp
         self.zd_up = 1
         self.zd_pid = int(m.group(1))
         self.zd_status = resp
+        return resp
 
     def awhile(self, cond, msg):
+        n = 0
         try:
             self.get_status()
             while not cond():
                 sys.stdout.write(". ")
                 sys.stdout.flush()
                 time.sleep(1)
-                self.get_status()
+                n += 1
+                if not self.get_status() and n > 10:
+                    print "\nDaemon manager not running."
+                    return
         except KeyboardInterrupt:
             print "^C"
         else:
@@ -210,14 +206,14 @@
     def do_start(self, arg):
         self.get_status()
         if not self.zd_up:
-            args = [
-                self.options.python,
-                self.options.zdrun,
-                ]
+            if self.options.zdrun:
+                args = [self.options.python, self.options.zdrun]
+            else:
+                args = [self.options.python, sys.argv[0], '--zdrun']
+                
             args += self._get_override("-S", "schemafile")
             args += self._get_override("-C", "configfile")
             args += self._get_override("-b", "backofflimit")
-            args += self._get_override("-d", "daemon", flag=1)
             args += self._get_override("-f", "forever", flag=1)
             args += self._get_override("-s", "sockname")
             args += self._get_override("-u", "user")
@@ -228,6 +224,7 @@
                 "-x", "exitcodes", ",".join(map(str, self.options.exitcodes)))
             args += self._get_override("-z", "directory")
             args.extend(self.options.program)
+            args.extend(self.options.args[1:])
             if self.options.daemon:
                 flag = os.P_NOWAIT
             else:
@@ -238,8 +235,9 @@
         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")
+        if self.options.daemon:
+            self.awhile(lambda: self.zd_pid,
+                        "daemon process started, pid=%(zd_pid)d")
 
     def _get_override(self, opt, name, svalue=None, flag=0):
         value = getattr(self.options, name)
@@ -486,10 +484,12 @@
         if pid:
             print "To run the program in the foreground, please stop it first."
             return
-        program = " ".join(self.options.program)
-        print program
+
+        program = self.options.program + self.options.args[1:]
+        print " ".join(program)
+        sys.stdout.flush()
         try:
-            os.system(program)
+            os.spawnlp(os.P_WAIT, program[0], *program)
         except KeyboardInterrupt:
             print
 
@@ -580,6 +580,12 @@
         return os.fstat(self.f.fileno())[stat.ST_SIZE]
 
 def main(args=None, options=None, cmdclass=ZDCmd):
+    if args is None:
+        args = sys.argv[1:]
+    if args[0] == '--zdrun':
+        import zdaemon.zdrun
+        return zdaemon.zdrun.main(args[1:])
+        
     if options is None:
         options = ZDCtlOptions()
     options.realize(args)

Modified: zdaemon/trunk/src/zdaemon/zdoptions.py
===================================================================
--- zdaemon/trunk/src/zdaemon/zdoptions.py	2006-12-21 17:27:38 UTC (rev 71642)
+++ zdaemon/trunk/src/zdaemon/zdoptions.py	2006-12-21 23:46:14 UTC (rev 71643)
@@ -347,7 +347,7 @@
         ZDOptions.__init__(self)
         self.add("backofflimit", "runner.backoff_limit",
                  "b:", "backoff-limit=", int, default=10)
-        self.add("daemon", "runner.daemon", "d", "daemon", flag=1, default=0)
+        self.add("daemon", "runner.daemon", "d", "daemon", flag=1, default=1)
         self.add("forever", "runner.forever", "f", "forever",
                  flag=1, default=0)
         self.add("sockname", "runner.socket_name", "s:", "socket-name=",



More information about the Zope-Checkins mailing list