[Zodb-checkins] SVN: zdaemon/trunk/ Updated release info:
Jim Fulton
jim at zope.com
Wed Jan 10 06:36:03 EST 2007
Log message for revision 71854:
Updated release info:
New Features:
- Added support for setting environment variables in the configuration
file. This is useful when zdaemon is used to run programs that need
environment variables set (e.g. LD_LIBRARY_PATH).
- Added a command to rotate the transcript log.
Changed:
U zdaemon/trunk/CHANGES.txt
U zdaemon/trunk/README.txt
U zdaemon/trunk/setup.py
U zdaemon/trunk/src/zdaemon/README.txt
U zdaemon/trunk/src/zdaemon/component.xml
U zdaemon/trunk/src/zdaemon/schema.xml
U zdaemon/trunk/src/zdaemon/tests/tests.py
U zdaemon/trunk/src/zdaemon/zdctl.py
U zdaemon/trunk/src/zdaemon/zdrun.py
-=-
Modified: zdaemon/trunk/CHANGES.txt
===================================================================
--- zdaemon/trunk/CHANGES.txt 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/CHANGES.txt 2007-01-10 11:36:01 UTC (rev 71854)
@@ -1,28 +1,18 @@
zdaemon Changelog
*****************
-To-Dos
-======
+zdaemon 2.0a2 (2007/01/08)
+==========================
-More docs:
+New Features
+------------
-- Document/demonstrate some important features, such as:
+- Added support for setting environment variables in the configuration
+ file. This is useful when zdaemon is used to run programs that need
+ environment variables set (e.g. LD_LIBRARY_PATH).
- - transcript log
+- Added a command to rotate the transcript log.
- - working directory
-
-Features
-
-- environment variables
-
-- transcript log rotation
-
-Bugs
-
-- help command
-
-
zdaemon 2.0a1 (2006/12/21)
==========================
@@ -105,3 +95,16 @@
- SVN tag: svn://svn.zope.org/repos/main/zdaemon/tags/zdaemon-1.1
- Tagged to make better 'svn:externals' linkage possible.
+
+To-Dos
+======
+
+More docs:
+
+- Document/demonstrate some important features, such as:
+
+ - working directory
+
+Bugs
+
+- help command
Modified: zdaemon/trunk/README.txt
===================================================================
--- zdaemon/trunk/README.txt 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/README.txt 2007-01-10 11:36:01 UTC (rev 71854)
@@ -1,11 +1,10 @@
-****************************************************
-``zdaemon`` process controller for Unix-ased systems
-****************************************************
+*****************************************************
+``zdaemon`` process controller for Unix-based systems
+*****************************************************
-Overview
-********
+.. contents::
-'zdaemon' is a Python package which provides APIs for managing spplications
+'zdaemon' is a Python package which provides APIs for managing applications
run as daemons. Its principal use to date has been to manage the application
server and storage server daemons for Zope / ZEO, although it is not limited
to running Python-based applications (for instance, it has been used to
Modified: zdaemon/trunk/setup.py
===================================================================
--- zdaemon/trunk/setup.py 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/setup.py 2007-01-10 11:36:01 UTC (rev 71854)
@@ -29,7 +29,6 @@
entry_points=entry_points,
include_package_data = True,
install_requires=["ZConfig"],
- tests_require=["zope.testing"],
)
except ImportError:
from distutils.core import setup
@@ -38,7 +37,7 @@
name = "zdaemon"
setup(
name=name,
- version="2.0a1",
+ version="2.0a2",
url="http://www.python.org/pypi/zdaemon",
license="ZPL 2.1",
description=
@@ -53,7 +52,7 @@
'Detailed Documentation\n'
'**********************\n'
+ '\n' +
- read('src', 'zdaemon', 'README.txt')
+ read('src/zdaemon/README.txt')
+ '\n' +
'Download\n'
'**********************\n'
@@ -61,5 +60,13 @@
packages=["zdaemon", "zdaemon.tests"],
package_dir={"": "src"},
+ classifiers = [
+ 'Development Status :: 3 - Alpha',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Topic :: Utilities',
+ 'Operating System :: POSIX',
+ ],
**setuptools_options)
Modified: zdaemon/trunk/src/zdaemon/README.txt
===================================================================
--- zdaemon/trunk/src/zdaemon/README.txt 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/src/zdaemon/README.txt 2007-01-10 11:36:01 UTC (rev 71854)
@@ -7,7 +7,7 @@
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:
+accepts commands teling it what do do. The commands are:
start
Start a process as a daemon
@@ -27,6 +27,10 @@
kill signal
Send a signal to the daemon process
+reopen_transcript
+ Reopen the transcript log. See the discussion of the transcript
+ log below.
+
help command
Get help on a command
@@ -137,6 +141,117 @@
>>> system("./zdaemon -Cconf stop")
daemon process stopped
+Environment Variables
+---------------------
+
+Sometimes, it is necessary to set environment variables before running
+a program. Perhaps the most common case for this is setting
+LD_LIBRARY_PATH so that dynamically loaded libraries can be found.
+
+ >>> open('conf', 'w').write(
+ ... '''
+ ... <runner>
+ ... program env
+ ... socket-name /tmp/demo.zdsock
+ ... </runner>
+ ... <environment>
+ ... LD_LIBRARY_PATH /home/foo/lib
+ ... HOME /home/foo
+ ... </environment>
+ ... ''')
+
+ >>> system("./zdaemon -Cconf fg")
+ env
+ USER=jim
+ HOME=/home/foo
+ LOGNAME=jim
+ USERNAME=jim
+ TERM=dumb
+ PATH=/home/jim/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin
+ EMACS=t
+ LANG=en_US.UTF-8
+ SHELL=/bin/bash
+ EDITOR=emacs
+ LD_LIBRARY_PATH=/home/foo/lib
+
+Transcript log
+---------------
+
+When zdaemon run a program in daemon mode, it disconnects the
+program's standard input, standard output, and standard error from the
+controlling terminal. It can optionally redirect the output to
+standard error and standard output to a file. This is done with the
+transcript option. This is, of course, useful for logging output from
+long-running applications.
+
+Let's look at an example. We'll have a long-running process that
+simple tails a data file:
+
+ >>> f = open('data', 'w', 0)
+ >>> import os
+ >>> f.write('rec 1\n'); os.fsync(f.fileno())
+
+ >>> open('conf', 'w').write(
+ ... '''
+ ... <runner>
+ ... program tail -f data
+ ... transcript log
+ ... </runner>
+ ... ''')
+
+ >>> system("./zdaemon -Cconf start")
+ . daemon process started, pid=7963
+
+.. Wait a little bit to make sure tail has a chance to work
+
+ >>> import time
+ >>> time.sleep(0.1)
+
+Now, if we look at the log file, it contains the tail output:
+
+ >>> open('log').read()
+ 'rec 1\n'
+
+We can rotate the transcript log by renaming it and telling zdaemon to
+reopen it:
+
+ >>> import os
+ >>> os.rename('log', 'log.1')
+
+If we generate more output:
+
+ >>> f.write('rec 2\n'); os.fsync(f.fileno())
+
+.. Wait a little bit to make sure tail has a chance to work
+
+ >>> time.sleep(1)
+
+The output will appear in the old file, because zdaemon still has it
+open:
+
+ >>> open('log.1').read()
+ 'rec 1\nrec 2\n'
+
+Now, if we tell zdaemon to reopen the file:
+
+ >>> system("./zdaemon -Cconf reopen_transcript")
+
+and generate some output:
+
+ >>> f.write('rec 3\n'); os.fsync(f.fileno())
+
+.. Wait a little bit to make sure tail has a chance to work
+
+ >>> time.sleep(1)
+
+the output will show up in the new file, not the old:
+
+ >>> open('log').read()
+ 'rec 3\n'
+
+ >>> open('log.1').read()
+ 'rec 1\nrec 2\n'
+
Reference Documentation
-----------------------
@@ -321,8 +436,3 @@
In this example, log output is sent to a file and to standard out.
Log output from zdaemon usually isn't very interesting but can be
handy for debugging.
-
-
-
-
-
Modified: zdaemon/trunk/src/zdaemon/component.xml
===================================================================
--- zdaemon/trunk/src/zdaemon/component.xml 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/src/zdaemon/component.xml 2007-01-10 11:36:01 UTC (rev 71854)
@@ -272,4 +272,11 @@
</sectiontype>
+ <sectiontype name="environment" keytype="string">
+ <key name="+"
+ attribute="mapping"
+ required="no"
+ />
+ </sectiontype>
+
</component>
Modified: zdaemon/trunk/src/zdaemon/schema.xml
===================================================================
--- zdaemon/trunk/src/zdaemon/schema.xml 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/src/zdaemon/schema.xml 2007-01-10 11:36:01 UTC (rev 71854)
@@ -21,6 +21,8 @@
<section name="*" type="runner" attribute="runner" required="yes" />
+ <section name="*" type="environment" attribute="environment" required="no" />
+
<section name="*" type="eventlog" attribute="eventlog" required="no" />
</schema>
Modified: zdaemon/trunk/src/zdaemon/tests/tests.py
===================================================================
--- zdaemon/trunk/src/zdaemon/tests/tests.py 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/src/zdaemon/tests/tests.py 2007-01-10 11:36:01 UTC (rev 71854)
@@ -11,11 +11,7 @@
# 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
@@ -98,6 +94,11 @@
print o.read(),
+def checkenv(match):
+ match = [a for a in match.group(1).split('\n')[:-1]
+ if a.split('=')[0] in ('HOME', 'LD_LIBRARY_PATH')]
+ match.sort()
+ return '\n'.join(match) + '\n'
def test_suite():
return unittest.TestSuite((
@@ -112,6 +113,7 @@
setUp=setUp, tearDown=tearDown,
checker=renormalizing.RENormalizing([
(re.compile('pid=\d+'), 'pid=NNN'),
+ (re.compile('^env\n((\w+=[^\n]*\n)+)$'), checkenv),
])
),
))
Modified: zdaemon/trunk/src/zdaemon/zdctl.py
===================================================================
--- zdaemon/trunk/src/zdaemon/zdctl.py 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/src/zdaemon/zdctl.py 2007-01-10 11:36:01 UTC (rev 71854)
@@ -134,6 +134,13 @@
print "our program =", program
print "daemon's args =", args
+ if (options.configroot is not None
+ and
+ options.configroot.environment is not None
+ ):
+ for k, v in options.configroot.environment.mapping.items():
+ os.environ[k] = v
+
def emptyline(self):
# We don't want a blank line to repeat the last command.
# Showing status is a nice alternative.
@@ -286,6 +293,14 @@
self.send_action("stop")
self.awhile(lambda: not self.zd_pid, "daemon process stopped")
+ def do_reopen_transcript(self, arg):
+ if not self.zd_up:
+ print "daemon manager not running"
+ elif not self.zd_pid:
+ print "daemon process not running"
+ else:
+ self.send_action("reopen_transcript")
+
def help_stop(self):
print "stop -- Stop the daemon process."
print " If it is not running, do nothing."
Modified: zdaemon/trunk/src/zdaemon/zdrun.py
===================================================================
--- zdaemon/trunk/src/zdaemon/zdrun.py 2007-01-10 10:16:55 UTC (rev 71853)
+++ zdaemon/trunk/src/zdaemon/zdrun.py 2007-01-10 11:36:01 UTC (rev 71854)
@@ -74,6 +74,7 @@
import socket
import select
import signal
+import threading
from stat import ST_MODE
if __name__ == "__main__":
@@ -349,6 +350,8 @@
if pid:
self.waitstatus = pid, sts
+ transcript = None
+
def daemonize(self):
# To daemonize, we need to become the leader of our own session
@@ -390,10 +393,7 @@
% self.options.directory)
os.close(0)
sys.stdin = sys.__stdin__ = open("/dev/null")
- os.close(1)
- sys.stdout = sys.__stdout__ = open(self.options.transcript, "a", 0)
- os.close(2)
- sys.stderr = sys.__stderr__ = open(self.options.transcript, "a", 0)
+ self.transcript = Transcript(self.options.transcript)
os.setsid()
os.umask(self.options.umask)
# XXX Stevens, in his Advanced Unix book, section 13.3 (page
@@ -626,6 +626,10 @@
"filename=%r\n" % self.proc.filename +
"args=%r\n" % self.proc.args)
+ def cmd_reopen_transcript(self, args):
+ if self.transcript is not None:
+ self.transcript.reopen()
+
def cmd_help(self, args):
self.sendreply(
"Available commands:\n"
@@ -655,6 +659,40 @@
self.logger.warn("Error sending reply: %s" % str(msg))
+class Transcript:
+
+ def __init__(self, filename):
+ self.read_from, w = os.pipe()
+ os.dup2(w, 1)
+ sys.stdout = sys.__stdout__ = os.fdopen(1, "a", 0)
+ os.dup2(w, 2)
+ sys.stderr = sys.__stderr__ = os.fdopen(2, "a", 0)
+ self.filename = filename
+ self.file = open(filename, 'a', 0)
+ self.write = self.file.write
+ self.lock = threading.Lock()
+ thread = threading.Thread(target=self.copy)
+ thread.setDaemon(True)
+ thread.start()
+
+ def copy(self):
+ lock = self.lock
+ i = [self.read_from]
+ o = e = []
+ while 1:
+ ii, oo, ee = select.select(i, o, e)
+ lock.acquire()
+ for fd in ii:
+ self.write(os.read(fd, 8192))
+ lock.release()
+
+ def reopen(self):
+ self.lock.acquire()
+ self.file.close()
+ self.file = open(self.filename, 'a', 0)
+ self.write = self.file.write
+ self.lock.release()
+
# Helpers for dealing with signals and exit status
def decode_wait_status(sts):
More information about the Zodb-checkins
mailing list