__FILENAME__ = conf
# -*- coding: utf-8 -*-
#
# Supervisor documentation build configuration file
#
# This file is execfile()d with the current directory set to its containing
# dir.
#
# The contents of this file are pickled, so don't put values in the
# namespace that aren't pickleable (module imports are okay, they're
# removed automatically).
#
# All configuration values have a default value; values that are commented
# out serve to show the default value.

import sys, os
from datetime import date

# If your extensions are in another directory, add it here. If the
# directory is relative to the documentation root, use os.path.abspath to
# make it absolute, like shown here.
#sys.path.append(os.path.abspath('some/directory'))

parent = os.path.dirname(os.path.dirname(__file__))
sys.path.append(os.path.abspath(parent))
wd = os.getcwd()
os.chdir(parent)
os.system('%s setup.py test -q' % sys.executable)
os.chdir(wd)

for item in os.listdir(parent):
    if item.endswith('.egg'):
        sys.path.append(os.path.join(parent, item))

version_txt = os.path.join(parent, 'supervisor/version.txt')
supervisor_version = open(version_txt).read().strip()

# General configuration
# ---------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General substitutions.
project = 'Supervisor'
year = date.today().year
copyright = '2004-%d, Agendaless Consulting and Contributors' % year

# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
#
# The short X.Y version.
version = supervisor_version
# The full version, including alpha/beta/rc tags.
release = version

# There are two options for replacing |today|: either, you set today to
# some non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# List of directories, relative to source directories, that shouldn't be
# searched for source files.
#exclude_dirs = []

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'


# Options for HTML output
# -----------------------

# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
html_style = 'repoze.css'

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as
# html_title.
#html_short_title = None

# The name of an image file (within the static path) to place at the top of
# the sidebar.
html_logo = '.static/logo_hi.gif'

# The name of an image file (within the static path) to use as favicon of
# the docs.  This file should be a Windows icon file (.ico) being 16x16 or
# 32x32 pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets)
# here, relative to this directory. They are copied after the builtin
# static files, so a file named "default.css" will overwrite the builtin
# "default.css".
html_static_path = ['.static']

# If not '', a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_use_modindex = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, the reST sources are included in the HTML build as
# _sources/<name>.
#html_copy_source = True

# If true, an OpenSearch description file will be output, and all pages
# will contain a <link> tag referring to it.  The value of this option must
# be the base URL from which the finished HTML is served.
#html_use_opensearch = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'supervisor'


# Options for LaTeX output
# ------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, document class [howto/manual]).
latex_documents = [
  ('index', 'supervisor.tex', 'supervisor Documentation',
   'Supervisor Developers', 'manual'),
]

# The name of an image file (relative to this directory) to place at the
# top of the title page.
latex_logo = '.static/logo_hi.gif'

# For "manual" documents, if this is true, then toplevel headings are
# parts, not chapters.
#latex_use_parts = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_use_modindex = True

########NEW FILE########
__FILENAME__ = childutils
import sys
import time

from supervisor.compat import xmlrpclib
from supervisor.compat import long

from supervisor.xmlrpc import SupervisorTransport
from supervisor.events import ProcessCommunicationEvent
from supervisor.dispatchers import PEventListenerDispatcher

def getRPCTransport(env):
    u = env.get('SUPERVISOR_USERNAME', '')
    p = env.get('SUPERVISOR_PASSWORD', '')
    return SupervisorTransport(u, p, env['SUPERVISOR_SERVER_URL'])

def getRPCInterface(env):
    # dumbass ServerProxy won't allow us to pass in a non-HTTP url,
    # so we fake the url we pass into it and always use the transport's
    # 'serverurl' to figure out what to attach to
    return xmlrpclib.ServerProxy('http://127.0.0.1', getRPCTransport(env))

def get_headers(line):
    return dict([ x.split(':') for x in line.split() ])

def eventdata(payload):
    headerinfo, data = payload.split('\n', 1)
    headers = get_headers(headerinfo)
    return headers, data

def get_asctime(now=None):
    if now is None: # for testing
        now = time.time() # pragma: no cover
    msecs = (now - long(now)) * 1000
    part1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now))
    asctime = '%s,%03d' % (part1, msecs)
    return asctime

class ProcessCommunicationsProtocol:
    def send(self, msg, fp=sys.stdout):
        fp.write(ProcessCommunicationEvent.BEGIN_TOKEN)
        fp.write(msg)
        fp.write(ProcessCommunicationEvent.END_TOKEN)

    def stdout(self, msg):
        return self.send(msg, sys.stdout)

    def stderr(self, msg):
        return self.send(msg, sys.stderr)

pcomm = ProcessCommunicationsProtocol()

class EventListenerProtocol:
    def wait(self, stdin=sys.stdin, stdout=sys.stdout):
        self.ready(stdout)
        line = stdin.readline()
        headers = get_headers(line)
        payload = stdin.read(int(headers['len']))
        return headers, payload

    def ready(self, stdout=sys.stdout):
        stdout.write(PEventListenerDispatcher.READY_FOR_EVENTS_TOKEN)
        stdout.flush()

    def ok(self, stdout=sys.stdout):
        self.send('OK', stdout)

    def fail(self, stdout=sys.stdout):
        self.send('FAIL', stdout)

    def send(self, data, stdout=sys.stdout):
        resultlen = len(data)
        result = '%s%s\n%s' % (PEventListenerDispatcher.RESULT_TOKEN_START,
                               str(resultlen),
                               data)
        stdout.write(result)
        stdout.flush()

listener = EventListenerProtocol()

########NEW FILE########
__FILENAME__ = compat
from __future__ import absolute_import

import sys
PY3 = sys.version>'3'

if PY3: # pragma: no cover
    long = int
    basestring = str
    unichr = chr
    raw_input = input
    class unicode(str):
        def __init__(self, string, encoding, errors):
            str.__init__(self, string)
    def as_bytes(s): return s if isinstance(s,bytes) else s.encode('utf8')
    def as_string(s): return s if isinstance(s,str) else s.decode('utf8')
    from functools import reduce

else: # pragma: no cover
    long = long
    raw_input = raw_input
    unicode = unicode
    basestring = basestring
    def as_bytes(s): return s if isinstance(s, str) else s.encode('utf-8')
    def as_string(s): return s if isinstance(s, unicode) else s.decode('utf-8')
    reduce = reduce

def print_function(*args,**kwargs): # pragma: no cover
    kwargs.get('file', sys.stdout).write(' '.join(i for i in args)+kwargs.get('end','\n'))

def total_ordering(cls): # pragma: no cover
    """Class decorator that fills in missing ordering methods"""
    convert = {
        '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
                   ('__le__', lambda self, other: self < other or self == other),
                   ('__ge__', lambda self, other: not self < other)],
        '__le__': [('__ge__', lambda self, other: not self <= other or self == other),
                   ('__lt__', lambda self, other: self <= other and not self == other),
                   ('__gt__', lambda self, other: not self <= other)],
        '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
                   ('__ge__', lambda self, other: self > other or self == other),
                   ('__le__', lambda self, other: not self > other)],
        '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
                   ('__gt__', lambda self, other: self >= other and not self == other),
                   ('__lt__', lambda self, other: not self >= other)]
    }
    roots = set(dir(cls)) & set(convert)
    if not roots:
        raise ValueError('must define at least one ordering operation: < > <= >=')
    root = max(roots)       # prefer __lt__ to __le__ to __gt__ to __ge__
    for opname, opfunc in convert[root]:
        if opname not in roots:
            opfunc.__name__ = opname
            try:
                op = getattr(int, opname)
            except AttributeError: # py25 int has no __gt__
                pass
            else:
                opfunc.__doc__ = op.__doc__
            setattr(cls, opname, opfunc)
    return cls

try: # pragma: no cover
    import xmlrpc.client as xmlrpclib
except ImportError: # pragma: no cover
    import xmlrpclib

try: # pragma: no cover
    import urllib.parse as urlparse
    import urllib.parse as urllib
except ImportError: # pragma: no cover
    import urlparse
    import urllib

if PY3: # pragma: no cover
    from base64 import encodebytes as encodestring
else: # pragma: no cover
    from base64 import encodestring

try: # pragma: no cover
    from hashlib import sha1
except ImportError: # pragma: no cover
    from sha import new as sha1

try: # pragma: no cover
    import syslog
except ImportError: # pragma: no cover
    syslog = None

try: # pragma: no cover
    import configparser as ConfigParser
except ImportError: # pragma: no cover
   import ConfigParser

try: # pragma: no cover
    from StringIO import StringIO
except ImportError: # pragma: no cover
    from io import StringIO

try: # pragma: no cover
    from sys import maxint
except ImportError: # pragma: no cover
    from sys import maxsize as maxint

try: # pragma: no cover
    from urllib.parse import parse_qs, parse_qsl
except ImportError: # pragma: no cover
    from cgi import parse_qs, parse_qsl

try: # pragma: no cover
    import http.client as httplib
except ImportError: # pragma: no cover
    import httplib

try: # pragma: no cover
    from base64 import decodebytes as decodestring, encodebytes as encodestring
except ImportError: # pragma: no cover
    from base64 import decodestring, encodestring


if PY3: # pragma: no cover
    func_attribute = '__func__'
else: # pragma: no cover
    func_attribute = 'im_func'

try: # pragma: no cover
    # Python 2.6 contains a version of cElementTree inside it.
    from xml.etree.ElementTree import iterparse
except ImportError: # pragma: no cover
    try:
        # Failing that, try cElementTree instead.
        from cElementTree import iterparse
    except ImportError:
        iterparse = None

try: # pragma: no cover
    from xmlrpc.client import Fault
except ImportError: # pragma: no cover
    from xmlrpclib import Fault

try: # pragma: no cover
    from string import ascii_letters as letters
except ImportError: # pragma: no cover
    from string import letters

try: # pragma: no cover
    from hashlib import md5
except ImportError: # pragma: no cover
    from md5 import md5

try: # pragma: no cover
    import thread
except ImportError: # pragma: no cover
    import _thread as thread

########NEW FILE########
__FILENAME__ = confecho
import pkg_resources
import sys
from supervisor.compat import as_string

def main(out=sys.stdout):
    config = pkg_resources.resource_string(__name__, 'skel/sample.conf')
    out.write(as_string(config))

########NEW FILE########
__FILENAME__ = datatypes
import grp
import os
import pwd
import signal
import sys
import supervisor.medusa.text_socket as socket
import shlex

from supervisor.compat import urlparse
from supervisor.compat import long
from supervisor.loggers import getLevelNumByDescription

# I dont know why we bother, this doesn't run on Windows, but just
# in case it ever does, avoid this bug magnet by leaving it.
if sys.platform[:3] == "win": # pragma: no cover
    DEFAULT_HOST = "localhost"
else:
    DEFAULT_HOST = ""

here = None

def set_here(v):
    global here
    here = v

def process_or_group_name(name):
    """Ensures that a process or group name is not created with
       characters that break the eventlistener protocol"""
    s = str(name).strip()
    if ' ' in s or ':' in s:
        raise ValueError("Invalid name: " + repr(name))
    return s

def integer(value):
    try:
        return int(value)
    except (ValueError, OverflowError):
        return long(value) # why does this help ValueError? (CM)

TRUTHY_STRINGS = ('yes', 'true', 'on', '1')
FALSY_STRINGS  = ('no', 'false', 'off', '0')

def boolean(s):
    """Convert a string value to a boolean value."""
    ss = str(s).lower()
    if ss in TRUTHY_STRINGS:
        return True
    elif ss in FALSY_STRINGS:
        return False
    else:
        raise ValueError("not a valid boolean value: " + repr(s))

def list_of_strings(arg):
    if not arg:
        return []
    try:
        return [x.strip() for x in arg.split(',')]
    except:
        raise ValueError("not a valid list of strings: " + repr(arg))

def list_of_ints(arg):
    if not arg:
        return []
    else:
        try:
            return list(map(int, arg.split(",")))
        except:
            raise ValueError("not a valid list of ints: " + repr(arg))

def list_of_exitcodes(arg):
    try:
        vals = list_of_ints(arg)
        for val in vals:
            if (val > 255) or (val < 0):
                raise ValueError('Invalid exit code "%s"' % val)
        return vals
    except:
        raise ValueError("not a valid list of exit codes: " + repr(arg))

def dict_of_key_value_pairs(arg):
    """ parse KEY=val,KEY2=val2 into {'KEY':'val', 'KEY2':'val2'}
        Quotes can be used to allow commas in the value
    """
    lexer = shlex.shlex(str(arg), posix=True)
    lexer.wordchars += '/.+-():'

    tokens = list(lexer)
    tokens_len = len(tokens)

    D = {}
    i = 0
    while i < tokens_len:
        k_eq_v = tokens[i:i+3]
        if len(k_eq_v) != 3 or k_eq_v[1] != '=':
            raise ValueError("Unexpected end of key/value pairs")
        D[k_eq_v[0]] = k_eq_v[2]
        i += 4
    return D

class Automatic:
    pass

LOGFILE_NONES = ('none', 'off', None)
LOGFILE_AUTOS = (Automatic, 'auto')

def logfile_name(val):
    if hasattr(val, 'lower'):
        coerced = val.lower()
    else:
        coerced = val

    if coerced in LOGFILE_NONES:
        return None
    elif coerced in LOGFILE_AUTOS:
        return Automatic
    else:
        return existing_dirpath(val)

class RangeCheckedConversion:
    """Conversion helper that range checks another conversion."""

    def __init__(self, conversion, rmin=None, rmax=None):
        self._min = rmin
        self._max = rmax
        self._conversion = conversion

    def __call__(self, value):
        v = self._conversion(value)
        if self._min is not None and v < self._min:
            raise ValueError("%s is below lower bound (%s)"
                             % (repr(v), repr(self._min)))
        if self._max is not None and v > self._max:
            raise ValueError("%s is above upper bound (%s)"
                             % (repr(v), repr(self._max)))
        return v

port_number = RangeCheckedConversion(integer, rmin=1, rmax=0xffff).__call__

def inet_address(s):
    # returns (host, port) tuple
    host = ''
    if ":" in s:
        host, s = s.split(":", 1)
        if not s:
            raise ValueError("no port number specified in %r" % s)
        port = port_number(s)
        host = host.lower()
    else:
        try:
            port = port_number(s)
        except ValueError:
            raise ValueError("not a valid port number: %r " %s)
    if not host or host == '*':
        host = DEFAULT_HOST
    return host, port

class SocketAddress:
    def __init__(self, s):
        # returns (family, address) tuple
        if "/" in s or s.find(os.sep) >= 0 or ":" not in s:
            self.family = getattr(socket, "AF_UNIX", None)
            self.address = s
        else:
            self.family = socket.AF_INET
            self.address = inet_address(s)

class SocketConfig:
    """ Abstract base class which provides a uniform abstraction
    for TCP vs Unix sockets """
    url = '' # socket url
    addr = None #socket addr

    def __repr__(self):
        return '<%s at %s for %s>' % (self.__class__,
                                      id(self),
                                      self.url)

    def __str__(self):
        return str(self.url)

    def __eq__(self, other):
        if not isinstance(other, SocketConfig):
            return False

        if self.url != other.url:
            return False

        return True

    def __ne__(self, other):
        return not self.__eq__(other)

    def addr(self): # pragma: no cover
        raise NotImplementedError

    def create_and_bind(self): # pragma: no cover
        raise NotImplementedError

class InetStreamSocketConfig(SocketConfig):
    """ TCP socket config helper """

    host = None # host name or ip to bind to
    port = None # integer port to bind to

    def __init__(self, host, port):
        self.host = host.lower()
        self.port = port_number(port)
        self.url = 'tcp://%s:%d' % (self.host, self.port)

    def addr(self):
        return self.host, self.port

    def create_and_bind(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.bind(self.addr())
        except:
            sock.close()
            raise
        return sock

class UnixStreamSocketConfig(SocketConfig):
    """ Unix domain socket config helper """

    path = None # Unix domain socket path
    mode = None # Unix permission mode bits for socket
    owner = None # Tuple (uid, gid) for Unix ownership of socket
    sock = None # socket object

    def __init__(self, path, **kwargs):
        self.path = path
        self.url = 'unix://%s' % path
        self.mode = kwargs.get('mode', None)
        self.owner = kwargs.get('owner', None)

    def addr(self):
        return self.path

    def create_and_bind(self):
        if os.path.exists(self.path):
            os.unlink(self.path)
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            sock.bind(self.addr())
            self._chown()
            self._chmod()
        except:
            sock.close()
            if os.path.exists(self.path):
                os.unlink(self.path)
            raise
        return sock

    def get_mode(self):
        return self.mode

    def get_owner(self):
        return self.owner

    def _chmod(self):
        if self.mode is not None:
            try:
                os.chmod(self.path, self.mode)
            except Exception as e:
                raise ValueError("Could not change permissions of socket "
                                    + "file: %s" % e)

    def _chown(self):
        if self.owner is not None:
            try:
                os.chown(self.path, self.owner[0], self.owner[1])
            except Exception as e:
                raise ValueError("Could not change ownership of socket file: "
                                    + "%s" % e)

def colon_separated_user_group(arg):
    """ Find a user ID and group ID from a string like 'user:group'.  Returns
        a tuple (uid, gid).  If the string only contains a user like 'user'
        then (uid, -1) will be returned.  Raises ValueError if either
        the user or group can't be resolved to valid IDs on the system. """
    try:
        parts = arg.split(':', 1)
        if len(parts) == 1:
            uid = name_to_uid(parts[0])
            gid = -1
        else:
            uid = name_to_uid(parts[0])
            gid = name_to_gid(parts[1])
        return (uid, gid)
    except:
        raise ValueError('Invalid user:group definition %s' % arg)

def name_to_uid(name):
    """ Find a user ID from a string containing a user name or ID.
        Raises ValueError if the string can't be resolved to a valid
        user ID on the system. """
    try:
        uid = int(name)
    except ValueError:
        try:
            pwdrec = pwd.getpwnam(name)
        except KeyError:
            raise ValueError("Invalid user name %s" % name)
        uid = pwdrec[2]
    else:
        try:
            pwd.getpwuid(uid) # check if uid is valid
        except KeyError:
            raise ValueError("Invalid user id %s" % name)
    return uid

def name_to_gid(name):
    """ Find a group ID from a string containing a group name or ID.
        Raises ValueError if the string can't be resolved to a valid
        group ID on the system. """
    try:
        gid = int(name)
    except ValueError:
        try:
            grprec = grp.getgrnam(name)
        except KeyError:
            raise ValueError("Invalid group name %s" % name)
        gid = grprec[2]
    else:
        try:
            grp.getgrgid(gid) # check if gid is valid
        except KeyError:
            raise ValueError("Invalid group id %s" % name)
    return gid

def gid_for_uid(uid):
    pwrec = pwd.getpwuid(uid)
    return pwrec[3]

def octal_type(arg):
    try:
        return int(arg, 8)
    except (TypeError, ValueError):
        raise ValueError('%s can not be converted to an octal type' % arg)

def existing_directory(v):
    nv = v % {'here':here}
    nv = os.path.expanduser(nv)
    if os.path.isdir(nv):
        return nv
    raise ValueError('%s is not an existing directory' % v)

def existing_dirpath(v):
    nv = v % {'here':here}
    nv = os.path.expanduser(nv)
    dir = os.path.dirname(nv)
    if not dir:
        # relative pathname with no directory component
        return nv
    if os.path.isdir(dir):
        return nv
    raise ValueError('The directory named as part of the path %s '
                     'does not exist.' % v)

def logging_level(value):
    s = str(value).lower()
    level = getLevelNumByDescription(s)
    if level is None:
        raise ValueError('bad logging level name %r' % value)
    return level

class SuffixMultiplier:
    # d is a dictionary of suffixes to integer multipliers.  If no suffixes
    # match, default is the multiplier.  Matches are case insensitive.  Return
    # values are in the fundamental unit.
    def __init__(self, d, default=1):
        self._d = d
        self._default = default
        # all keys must be the same size
        self._keysz = None
        for k in d.keys():
            if self._keysz is None:
                self._keysz = len(k)
            else:
                assert self._keysz == len(k)

    def __call__(self, v):
        v = v.lower()
        for s, m in self._d.items():
            if v[-self._keysz:] == s:
                return int(v[:-self._keysz]) * m
        return int(v) * self._default

byte_size = SuffixMultiplier({'kb': 1024,
                              'mb': 1024*1024,
                              'gb': 1024*1024*long(1024),})

def url(value):
    # earlier Python 2.6 urlparse (2.6.4 and under) can't parse unix:// URLs,
    # later ones can but we need to straddle
    uri = value.replace('unix://', 'http://', 1).strip()
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
    if scheme and (netloc or path):
        return value
    raise ValueError("value %s is not a URL" % value)

# all valid signal numbers
SIGNUMS = [ getattr(signal, k) for k in dir(signal) if k.startswith('SIG') ]

def signal_number(value):
    try:
        num = int(value)
    except (ValueError, TypeError):
        name = value.strip().upper()
        if not name.startswith('SIG'):
            name = 'SIG' + name
        num = getattr(signal, name, None)
        if num is None:
            raise ValueError('value %s is not a valid signal name' % value)
    if num not in SIGNUMS:
        raise ValueError('value %s is not a valid signal number' % value)
    return num

class RestartWhenExitUnexpected:
    pass

class RestartUnconditionally:
    pass

def auto_restart(value):
    value = str(value.lower())
    computed_value  = value
    if value in TRUTHY_STRINGS:
        computed_value = RestartUnconditionally
    elif value in FALSY_STRINGS:
        computed_value = False
    elif value == 'unexpected':
        computed_value = RestartWhenExitUnexpected
    if computed_value not in (RestartWhenExitUnexpected,
                              RestartUnconditionally, False):
        raise ValueError("invalid 'autorestart' value %r" % value)
    return computed_value

def profile_options(value):
    options = [x.lower() for x in list_of_strings(value) ]
    sort_options = []
    callers = False
    for thing in options:
        if thing != 'callers':
            sort_options.append(thing)
        else:
            callers = True
    return sort_options, callers

########NEW FILE########
__FILENAME__ = dispatchers
import warnings
import errno
from supervisor.medusa.asyncore_25 import compact_traceback

from supervisor.events import notify
from supervisor.events import EventRejectedEvent
from supervisor.events import ProcessLogStderrEvent
from supervisor.events import ProcessLogStdoutEvent
from supervisor.states import EventListenerStates
from supervisor import loggers

def find_prefix_at_end(haystack, needle):
    l = len(needle) - 1
    while l and not haystack.endswith(needle[:l]):
        l -= 1
    return l

class PDispatcher:
    """ Asyncore dispatcher for mainloop, representing a process channel
    (stdin, stdout, or stderr).  This class is abstract. """

    closed = False # True if close() has been called

    def __init__(self, process, channel, fd):
        self.process = process  # process which "owns" this dispatcher
        self.channel = channel  # 'stderr' or 'stdout'
        self.fd = fd
        self.closed = False     # True if close() has been called

    def __repr__(self):
        return '<%s at %s for %s (%s)>' % (self.__class__.__name__,
                                           id(self),
                                           self.process,
                                           self.channel)

    def readable(self):
        raise NotImplementedError

    def writable(self):
        raise NotImplementedError

    def handle_read_event(self):
        raise NotImplementedError

    def handle_write_event(self):
        raise NotImplementedError

    def handle_error(self):
        nil, t, v, tbinfo = compact_traceback()

        self.process.config.options.logger.critical(
            'uncaptured python exception, closing channel %s (%s:%s %s)' % (
                repr(self),
                t,
                v,
                tbinfo
                )
            )
        self.close()

    def close(self):
        if not self.closed:
            self.process.config.options.logger.debug(
                'fd %s closed, stopped monitoring %s' % (self.fd, self))
            self.closed = True

    def flush(self):
        pass

class POutputDispatcher(PDispatcher):
    """
    A Process Output (stdout/stderr) dispatcher. Serves several purposes:

    - capture output sent within <!--XSUPERVISOR:BEGIN--> and
      <!--XSUPERVISOR:END--> tags and signal a ProcessCommunicationEvent
      by calling notify(event).
    - route the output to the appropriate log handlers as specified in the
      config.
    """

    capturemode = False # are we capturing process event data
    mainlog = None #  the process' "normal" logger
    capturelog = None # the logger while we're in capturemode
    childlog = None # the current logger (event or main)
    output_buffer = '' # data waiting to be logged

    def __init__(self, process, event_type, fd):
        """
        Initialize the dispatcher.

        `event_type` should be one of ProcessLogStdoutEvent or
        ProcessLogStderrEvent
        """
        self.process = process
        self.event_type = event_type
        self.fd = fd
        self.channel = channel = self.event_type.channel

        self._setup_logging(process.config, channel)

        capture_maxbytes = getattr(process.config,
                                   '%s_capture_maxbytes' % channel)
        if capture_maxbytes:
            self.capturelog = loggers.handle_boundIO(
                self.process.config.options.getLogger(),
                fmt='%(message)s',
                maxbytes=capture_maxbytes,
                )

        self.childlog = self.mainlog

        # all code below is purely for minor speedups
        begintoken = self.event_type.BEGIN_TOKEN
        endtoken = self.event_type.END_TOKEN
        self.begintoken_data = (begintoken, len(begintoken))
        self.endtoken_data = (endtoken, len(endtoken))
        self.mainlog_level = loggers.LevelsByName.DEBG
        config = self.process.config
        self.log_to_mainlog = config.options.loglevel <= self.mainlog_level
        self.stdout_events_enabled = config.stdout_events_enabled
        self.stderr_events_enabled = config.stderr_events_enabled

    def _setup_logging(self, config, channel):
        """
        Configure the main log according to the process' configuration and
        channel. Sets `mainlog` on self. Returns nothing.
        """

        logfile = getattr(config, '%s_logfile' % channel)
        if not logfile:
            return

        maxbytes = getattr(config, '%s_logfile_maxbytes' % channel)
        backups = getattr(config, '%s_logfile_backups' % channel)
        fmt = '%(message)s'
        if logfile == 'syslog':
            warnings.warn("Specifying 'syslog' for filename is deprecated. "
                "Use %s_syslog instead." % channel, DeprecationWarning)
            fmt = ' '.join((config.name, fmt))
        self.mainlog = loggers.handle_file(
            config.options.getLogger(),
            filename=logfile,
            fmt=fmt,
            rotating=not not maxbytes, # optimization
            maxbytes=maxbytes,
            backups=backups)

        if getattr(config, '%s_syslog' % channel, False):
            fmt = config.name + ' %(message)s'
            loggers.handle_syslog(self.mainlog, fmt)

    def removelogs(self):
        for log in (self.mainlog, self.capturelog):
            if log is not None:
                for handler in log.handlers:
                    handler.remove()
                    handler.reopen()

    def reopenlogs(self):
        for log in (self.mainlog, self.capturelog):
            if log is not None:
                for handler in log.handlers:
                    handler.reopen()

    def _log(self, data):
        if data:
            config = self.process.config
            if config.options.strip_ansi:
                data = stripEscapes(data)
            if self.childlog:
                self.childlog.info(data)
            if self.log_to_mainlog:
                msg = '%(name)r %(channel)s output:\n%(data)s'
                config.options.logger.log(
                    self.mainlog_level, msg, name=config.name,
                    channel=self.channel, data=data)
            if self.channel == 'stdout':
                if self.stdout_events_enabled:
                    notify(
                        ProcessLogStdoutEvent(self.process,
                            self.process.pid, data)
                    )
            else: # channel == stderr
                if self.stderr_events_enabled:
                    notify(
                        ProcessLogStderrEvent(self.process,
                            self.process.pid, data)
                    )

    def record_output(self):
        if self.capturelog is None:
            # shortcut trying to find capture data
            data = self.output_buffer
            self.output_buffer = ''
            self._log(data)
            return

        if self.capturemode:
            token, tokenlen = self.endtoken_data
        else:
            token, tokenlen = self.begintoken_data

        if len(self.output_buffer) <= tokenlen:
            return # not enough data

        data = self.output_buffer
        self.output_buffer = ''

        try:
            before, after = data.split(token, 1)
        except ValueError:
            after = None
            index = find_prefix_at_end(data, token)
            if index:
                self.output_buffer = self.output_buffer + data[-index:]
                data = data[:-index]
            self._log(data)
        else:
            self._log(before)
            self.toggle_capturemode()
            self.output_buffer = after

        if after:
            self.record_output()

    def toggle_capturemode(self):
        self.capturemode = not self.capturemode

        if self.capturelog is not None:
            if self.capturemode:
                self.childlog = self.capturelog
            else:
                for handler in self.capturelog.handlers:
                    handler.flush()
                data = self.capturelog.getvalue()
                channel = self.channel
                procname = self.process.config.name
                event = self.event_type(self.process, self.process.pid, data)
                notify(event)

                msg = "%(procname)r %(channel)s emitted a comm event"
                self.process.config.options.logger.debug(msg,
                                                         procname=procname,
                                                         channel=channel)
                for handler in self.capturelog.handlers:
                    handler.remove()
                    handler.reopen()
                self.childlog = self.mainlog

    def writable(self):
        return False

    def readable(self):
        if self.closed:
            return False
        return True

    def handle_read_event(self):
        data = self.process.config.options.readfd(self.fd)
        self.output_buffer += data
        self.record_output()
        if not data:
            # if we get no data back from the pipe, it means that the
            # child process has ended.  See
            # mail.python.org/pipermail/python-dev/2004-August/046850.html
            self.close()

class PEventListenerDispatcher(PDispatcher):
    """ An output dispatcher that monitors and changes a process'
    listener_state """
    childlog = None # the logger
    state_buffer = ''  # data waiting to be reviewed for state changes

    READY_FOR_EVENTS_TOKEN = 'READY\n'
    RESULT_TOKEN_START = 'RESULT '
    READY_FOR_EVENTS_LEN = len(READY_FOR_EVENTS_TOKEN)
    RESULT_TOKEN_START_LEN = len(RESULT_TOKEN_START)

    def __init__(self, process, channel, fd):
        PDispatcher.__init__(self, process, channel, fd)
        # the initial state of our listener is ACKNOWLEDGED; this is a
        # "busy" state that implies we're awaiting a READY_FOR_EVENTS_TOKEN
        self.process.listener_state = EventListenerStates.ACKNOWLEDGED
        self.process.event = None
        self.result = ''
        self.resultlen = None

        logfile = getattr(process.config, '%s_logfile' % channel)

        if logfile:
            maxbytes = getattr(process.config, '%s_logfile_maxbytes' % channel)
            backups = getattr(process.config, '%s_logfile_backups' % channel)
            self.childlog = loggers.handle_file(
                process.config.options.getLogger(),
                logfile,
                '%(message)s',
                rotating=not not maxbytes, # optimization
                maxbytes=maxbytes,
                backups=backups,
            )

    def removelogs(self):
        if self.childlog is not None:
            for handler in self.childlog.handlers:
                handler.remove()
                handler.reopen()

    def reopenlogs(self):
        if self.childlog is not None:
            for handler in self.childlog.handlers:
                handler.reopen()


    def writable(self):
        return False

    def readable(self):
        if self.closed:
            return False
        return True

    def handle_read_event(self):
        data = self.process.config.options.readfd(self.fd)
        if data:
            self.state_buffer += data
            procname = self.process.config.name
            msg = '%r %s output:\n%s' % (procname, self.channel, data)
            self.process.config.options.logger.debug(msg)

            if self.childlog:
                if self.process.config.options.strip_ansi:
                    data = stripEscapes(data)
                self.childlog.info(data)
        else:
            # if we get no data back from the pipe, it means that the
            # child process has ended.  See
            # mail.python.org/pipermail/python-dev/2004-August/046850.html
            self.close()

        self.handle_listener_state_change()

    def handle_listener_state_change(self):
        data = self.state_buffer

        if not data:
            return

        process = self.process
        procname = process.config.name
        state = process.listener_state

        if state == EventListenerStates.UNKNOWN:
            # this is a fatal state
            self.state_buffer = ''
            return

        if state == EventListenerStates.ACKNOWLEDGED:
            if len(data) < self.READY_FOR_EVENTS_LEN:
                # not enough info to make a decision
                return
            elif data.startswith(self.READY_FOR_EVENTS_TOKEN):
                msg = '%s: ACKNOWLEDGED -> READY' % procname
                process.config.options.logger.debug(msg)
                process.listener_state = EventListenerStates.READY
                tokenlen = self.READY_FOR_EVENTS_LEN
                self.state_buffer = self.state_buffer[tokenlen:]
                process.event = None
            else:
                msg = '%s: ACKNOWLEDGED -> UNKNOWN' % procname
                process.config.options.logger.debug(msg)
                process.listener_state = EventListenerStates.UNKNOWN
                self.state_buffer = ''
                process.event = None
            if self.state_buffer:
                # keep going til its too short
                self.handle_listener_state_change()
            else:
                return

        elif state == EventListenerStates.READY:
            # the process sent some spurious data, be a hardass about it
            msg = '%s: READY -> UNKNOWN' % procname
            process.config.options.logger.debug(msg)
            process.listener_state = EventListenerStates.UNKNOWN
            self.state_buffer = ''
            process.event = None
            return

        elif state == EventListenerStates.BUSY:
            if self.resultlen is None:
                # we haven't begun gathering result data yet
                pos = data.find('\n')
                if pos == -1:
                    # we can't make a determination yet, we dont have a full
                    # results line
                    return

                result_line = self.state_buffer[:pos]
                self.state_buffer = self.state_buffer[pos+1:] # rid LF
                resultlen = result_line[self.RESULT_TOKEN_START_LEN:]
                try:
                    self.resultlen = int(resultlen)
                except ValueError:
                    msg = ('%s: BUSY -> UNKNOWN (bad result line %r)'
                           % (procname, result_line))
                    process.config.options.logger.debug(msg)
                    process.listener_state = EventListenerStates.UNKNOWN
                    self.state_buffer = ''
                    notify(EventRejectedEvent(process, process.event))
                    process.event = None
                    return

            else:
                needed = self.resultlen - len(self.result)

                if needed:
                    self.result += self.state_buffer[:needed]
                    self.state_buffer = self.state_buffer[needed:]
                    needed = self.resultlen - len(self.result)

                if not needed:
                    self.handle_result(self.result)
                    self.process.event = None
                    self.result = ''
                    self.resultlen = None

            if self.state_buffer:
                # keep going til its too short
                self.handle_listener_state_change()

    def handle_result(self, result):
        process = self.process
        procname = process.config.name

        try:
            self.process.group.config.result_handler(process.event, result)
            msg = '%s: BUSY -> ACKNOWLEDGED (processed)' % procname
            process.listener_state = EventListenerStates.ACKNOWLEDGED
        except RejectEvent:
            msg = '%s: BUSY -> ACKNOWLEDGED (rejected)' % procname
            process.listener_state = EventListenerStates.ACKNOWLEDGED
            notify(EventRejectedEvent(process, process.event))
        except:
            msg = '%s: BUSY -> UNKNOWN' % procname
            process.listener_state = EventListenerStates.UNKNOWN
            notify(EventRejectedEvent(process, process.event))

        process.config.options.logger.debug(msg)

class PInputDispatcher(PDispatcher):
    """ Input (stdin) dispatcher """

    def __init__(self, process, channel, fd):
        PDispatcher.__init__(self, process, channel, fd)
        self.input_buffer = ''

    def writable(self):
        if self.input_buffer and not self.closed:
            return True
        return False

    def readable(self):
        return False

    def flush(self):
        # other code depends on this raising EPIPE if the pipe is closed
        sent = self.process.config.options.write(self.fd,
                                                 self.input_buffer)
        self.input_buffer = self.input_buffer[sent:]

    def handle_write_event(self):
        if self.input_buffer:
            try:
                self.flush()
            except OSError as why:
                if why.args[0] == errno.EPIPE:
                    self.input_buffer = ''
                    self.close()
                else:
                    raise

ANSI_ESCAPE_BEGIN = '\x1b['
ANSI_TERMINATORS = ('H', 'f', 'A', 'B', 'C', 'D', 'R', 's', 'u', 'J',
                    'K', 'h', 'l', 'p', 'm')

def stripEscapes(s):
    """
    Remove all ANSI color escapes from the given string.
    """
    result = ''
    show = 1
    i = 0
    L = len(s)
    while i < L:
        if show == 0 and s[i] in ANSI_TERMINATORS:
            show = 1
        elif show:
            n = s.find(ANSI_ESCAPE_BEGIN, i)
            if n == -1:
                return result + s[i:]
            else:
                result = result + s[i:n]
                i = n
                show = 0
        i += 1
    return result

class RejectEvent(Exception):
    """ The exception type expected by a dispatcher when a handler wants
    to reject an event """

def default_handler(event, response):
    if response != 'OK':
        raise RejectEvent(response)

########NEW FILE########
__FILENAME__ = events
from supervisor.states import getProcessStateDescription

callbacks = []

def subscribe(type, callback):
    callbacks.append((type, callback))
    
def notify(event):
    for type, callback in callbacks:
        if isinstance(event, type):
            callback(event)

def clear():
    callbacks[:] = []

class Event:
    """ Abstract event type """
    pass

class ProcessLogEvent(Event):
    """ Abstract """
    channel = None
    def __init__(self, process, pid, data):
        self.process = process
        self.pid = pid
        self.data = data

    def __str__(self):
        groupname = ''
        if self.process.group is not None:
            groupname = self.process.group.config.name
        return 'processname:%s groupname:%s pid:%s channel:%s\n%s' % (
            self.process.config.name,
            groupname,
            self.pid,
            self.channel,
            self.data)

class ProcessLogStdoutEvent(ProcessLogEvent):
    channel = 'stdout'

class ProcessLogStderrEvent(ProcessLogEvent):
    channel = 'stderr'

class ProcessCommunicationEvent(Event):
    """ Abstract """
    # event mode tokens
    BEGIN_TOKEN = '<!--XSUPERVISOR:BEGIN-->'
    END_TOKEN   = '<!--XSUPERVISOR:END-->'

    def __init__(self, process, pid, data):
        self.process = process
        self.pid = pid
        self.data = data

    def __str__(self):
        groupname = ''
        if self.process.group is not None:
            groupname = self.process.group.config.name
        return 'processname:%s groupname:%s pid:%s\n%s' % (
            self.process.config.name,
            groupname,
            self.pid,
            self.data)

class ProcessCommunicationStdoutEvent(ProcessCommunicationEvent):
    channel = 'stdout'

class ProcessCommunicationStderrEvent(ProcessCommunicationEvent):
    channel = 'stderr'

class RemoteCommunicationEvent(Event):
    def __init__(self, type, data):
        self.type = type
        self.data = data

    def __str__(self):
        return 'type:%s\n%s' % (self.type, self.data)

class SupervisorStateChangeEvent(Event):
    """ Abstract class """
    def __str__(self):
        return ''

class SupervisorRunningEvent(SupervisorStateChangeEvent):
    pass

class SupervisorStoppingEvent(SupervisorStateChangeEvent):
    pass

class EventRejectedEvent: # purposely does not subclass Event 
    def __init__(self, process, event):
        self.process = process
        self.event = event

class ProcessStateEvent(Event):
    """ Abstract class, never raised directly """
    frm = None
    to = None
    def __init__(self, process, from_state, expected=True):
        self.process = process
        self.from_state = from_state
        self.expected = expected
        # we eagerly render these so if the process pid, etc changes beneath
        # us, we stash the values at the time the event was sent
        self.extra_values = self.get_extra_values()

    def __str__(self):
        groupname = ''
        if self.process.group is not None:
            groupname = self.process.group.config.name
        L = [('processname', self.process.config.name), ('groupname', groupname),
             ('from_state', getProcessStateDescription(self.from_state))]
        L.extend(self.extra_values)
        s = ' '.join( [ '%s:%s' % (name, val) for (name, val) in L ] )
        return s

    def get_extra_values(self):
        return []

class ProcessStateFatalEvent(ProcessStateEvent):
    pass

class ProcessStateUnknownEvent(ProcessStateEvent):
    pass

class ProcessStateStartingOrBackoffEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('tries', int(self.process.backoff))]

class ProcessStateBackoffEvent(ProcessStateStartingOrBackoffEvent):
    pass

class ProcessStateStartingEvent(ProcessStateStartingOrBackoffEvent):
    pass

class ProcessStateExitedEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('expected', int(self.expected)), ('pid', self.process.pid)]

class ProcessStateRunningEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('pid', self.process.pid)]

class ProcessStateStoppingEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('pid', self.process.pid)]

class ProcessStateStoppedEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('pid', self.process.pid)]

class ProcessGroupEvent(Event):
    def __init__(self, group):
        self.group = group
    def __str__(self):
        return 'groupname:%s\n' % self.group

class ProcessGroupAddedEvent(ProcessGroupEvent):
    pass

class ProcessGroupRemovedEvent(ProcessGroupEvent):
    pass

class TickEvent(Event):
    """ Abstract """
    def __init__(self, when, supervisord):
        self.when = when
        self.supervisord = supervisord

    def __str__(self):
        return 'when:%s' % self.when

class Tick5Event(TickEvent):
    period = 5

class Tick60Event(TickEvent):
    period = 60

class Tick3600Event(TickEvent):
    period = 3600

TICK_EVENTS = [ Tick5Event, Tick60Event, Tick3600Event ] # imported elsewhere

class EventTypes:
    EVENT = Event # abstract
    PROCESS_STATE = ProcessStateEvent # abstract
    PROCESS_STATE_STOPPED = ProcessStateStoppedEvent
    PROCESS_STATE_EXITED = ProcessStateExitedEvent
    PROCESS_STATE_STARTING = ProcessStateStartingEvent
    PROCESS_STATE_STOPPING = ProcessStateStoppingEvent
    PROCESS_STATE_BACKOFF = ProcessStateBackoffEvent
    PROCESS_STATE_FATAL = ProcessStateFatalEvent
    PROCESS_STATE_RUNNING = ProcessStateRunningEvent
    PROCESS_STATE_UNKNOWN = ProcessStateUnknownEvent
    PROCESS_COMMUNICATION = ProcessCommunicationEvent # abstract
    PROCESS_COMMUNICATION_STDOUT = ProcessCommunicationStdoutEvent
    PROCESS_COMMUNICATION_STDERR = ProcessCommunicationStderrEvent
    PROCESS_LOG = ProcessLogEvent
    PROCESS_LOG_STDOUT = ProcessLogStdoutEvent
    PROCESS_LOG_STDERR = ProcessLogStderrEvent     
    REMOTE_COMMUNICATION = RemoteCommunicationEvent
    SUPERVISOR_STATE_CHANGE = SupervisorStateChangeEvent # abstract
    SUPERVISOR_STATE_CHANGE_RUNNING = SupervisorRunningEvent
    SUPERVISOR_STATE_CHANGE_STOPPING = SupervisorStoppingEvent
    TICK = TickEvent # abstract
    TICK_5 = Tick5Event
    TICK_60 = Tick60Event
    TICK_3600 = Tick3600Event
    PROCESS_GROUP = ProcessGroupEvent # abstract
    PROCESS_GROUP_ADDED = ProcessGroupAddedEvent
    PROCESS_GROUP_REMOVED = ProcessGroupRemovedEvent

def getEventNameByType(requested):
    for name, typ in EventTypes.__dict__.items():
        if typ is requested:
            return name

def register(name, event):
    setattr(EventTypes, name, event)

########NEW FILE########
__FILENAME__ = http
import os
import stat
import time
import sys
import supervisor.medusa.text_socket as socket
import errno
import pwd
import weakref

from supervisor.compat import urllib
from supervisor.compat import sha1
from supervisor.compat import as_bytes
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import http_date
from supervisor.medusa import http_server
from supervisor.medusa import producers
from supervisor.medusa import filesys
from supervisor.medusa import default_handler

from supervisor.medusa.auth_handler import auth_handler

class NOT_DONE_YET:
    pass

class deferring_chunked_producer:
    """A producer that implements the 'chunked' transfer coding for HTTP/1.1.
    Here is a sample usage:
            request['Transfer-Encoding'] = 'chunked'
            request.push (
                    producers.chunked_producer (your_producer)
                    )
            request.done()
    """

    def __init__ (self, producer, footers=None):
        self.producer = producer
        self.footers = footers
        self.delay = 0.1

    def more (self):
        if self.producer:
            data = self.producer.more()
            if data is NOT_DONE_YET:
                return NOT_DONE_YET
            elif data:
                return '%x\r\n%s\r\n' % (len(data), data)
            else:
                self.producer = None
                if self.footers:
                    return '\r\n'.join(['0'] + self.footers) + '\r\n\r\n'
                else:
                    return '0\r\n\r\n'
        else:
            return ''

class deferring_composite_producer:
    """combine a fifo of producers into one"""
    def __init__ (self, producers):
        self.producers = producers
        self.delay = 0.1

    def more (self):
        while len(self.producers):
            p = self.producers[0]
            d = p.more()
            if d is NOT_DONE_YET:
                return NOT_DONE_YET
            if d:
                return d
            else:
                self.producers.pop(0)
        else:
            return ''


class deferring_globbing_producer:
    """
    'glob' the output from a producer into a particular buffer size.
    helps reduce the number of calls to send().  [this appears to
    gain about 30% performance on requests to a single channel]
    """

    def __init__ (self, producer, buffer_size=1<<16):
        self.producer = producer
        self.buffer = ''
        self.buffer_size = buffer_size
        self.delay = 0.1

    def more (self):
        while len(self.buffer) < self.buffer_size:
            data = self.producer.more()
            if data is NOT_DONE_YET:
                return NOT_DONE_YET
            if data:
                self.buffer = self.buffer + data
            else:
                break
        r = self.buffer
        self.buffer = ''
        return r


class deferring_hooked_producer:
    """
    A producer that will call <function> when it empties,.
    with an argument of the number of bytes produced.  Useful
    for logging/instrumentation purposes.
    """

    def __init__ (self, producer, function):
        self.producer = producer
        self.function = function
        self.bytes = 0
        self.delay = 0.1

    def more (self):
        if self.producer:
            result = self.producer.more()
            if result is NOT_DONE_YET:
                return NOT_DONE_YET
            if not result:
                self.producer = None
                self.function (self.bytes)
            else:
                self.bytes += len(result)
            return result
        else:
            return ''


class deferring_http_request(http_server.http_request):
    """ The medusa http_request class uses the default set of producers in
    medusa.producers.  We can't use these because they don't know anything
    about deferred responses, so we override various methods here.  This was
    added to support tail -f like behavior on the logtail handler """

    def done(self, *arg, **kw):

        """ I didn't want to override this, but there's no way around
        it in order to support deferreds - CM

        finalize this transaction - send output to the http channel"""

        # ----------------------------------------
        # persistent connection management
        # ----------------------------------------

        #  --- BUCKLE UP! ----

        connection = http_server.get_header(http_server.CONNECTION,self.header)
        connection = connection.lower()

        close_it = 0
        wrap_in_chunking = 0
        globbing = 1

        if self.version == '1.0':
            if connection == 'keep-alive':
                if not 'Content-Length' in self:
                    close_it = 1
                else:
                    self['Connection'] = 'Keep-Alive'
            else:
                close_it = 1
        elif self.version == '1.1':
            if connection == 'close':
                close_it = 1
            elif not 'Content-Length' in self:
                if 'Transfer-Encoding' in self:
                    if not self['Transfer-Encoding'] == 'chunked':
                        close_it = 1
                elif self.use_chunked:
                    self['Transfer-Encoding'] = 'chunked'
                    wrap_in_chunking = 1
                    # globbing slows down tail -f output, so only use it if
                    # we're not in chunked mode
                    globbing = 0
                else:
                    close_it = 1
        elif self.version is None:
            # Although we don't *really* support http/0.9 (because
            # we'd have to use \r\n as a terminator, and it would just
            # yuck up a lot of stuff) it's very common for developers
            # to not want to type a version number when using telnet
            # to debug a server.
            close_it = 1

        outgoing_header = producers.simple_producer(self.build_reply_header())

        if close_it:
            self['Connection'] = 'close'

        if wrap_in_chunking:
            outgoing_producer = deferring_chunked_producer(
                    deferring_composite_producer(self.outgoing)
                    )
            # prepend the header
            outgoing_producer = deferring_composite_producer(
                [outgoing_header, outgoing_producer]
                )
        else:
            # prepend the header
            self.outgoing.insert(0, outgoing_header)
            outgoing_producer = deferring_composite_producer(self.outgoing)

        # hook logging into the output
        outgoing_producer = deferring_hooked_producer(outgoing_producer,
                                                      self.log)

        if globbing:
            outgoing_producer = deferring_globbing_producer(outgoing_producer)

        self.channel.push_with_producer(outgoing_producer)

        self.channel.current_request = None

        if close_it:
            self.channel.close_when_done()

    def log (self, bytes):
        """ We need to override this because UNIX domain sockets return
        an empty string for the addr rather than a (host, port) combination """
        if self.channel.addr:
            host = self.channel.addr[0]
            port = self.channel.addr[1]
        else:
            host = 'localhost'
            port = 0
        self.channel.server.logger.log (
                host,
                '%d - - [%s] "%s" %d %d\n' % (
                        port,
                        self.log_date_string (time.time()),
                        self.request,
                        self.reply_code,
                        bytes
                        )
                )

    def cgi_environment(self):
        env = {}

        # maps request some headers to environment variables.
        # (those that don't start with 'HTTP_')
        header2env= {'content-length'    : 'CONTENT_LENGTH',
                     'content-type'      : 'CONTENT_TYPE',
                     'connection'        : 'CONNECTION_TYPE'}

        workdir = os.getcwd()
        (path, params, query, fragment) = self.split_uri()

        if params:
            path = path + params # undo medusa bug!

        while path and path[0] == '/':
            path = path[1:]
        if '%' in path:
            path = http_server.unquote(path)
        if query:
            query = query[1:]

        server = self.channel.server
        env['REQUEST_METHOD'] = self.command.upper()
        env['SERVER_PORT'] = str(server.port)
        env['SERVER_NAME'] = server.server_name
        env['SERVER_SOFTWARE'] = server.SERVER_IDENT
        env['SERVER_PROTOCOL'] = "HTTP/" + self.version
        env['channel.creation_time'] = self.channel.creation_time
        env['SCRIPT_NAME'] = ''
        env['PATH_INFO'] = '/' + path
        env['PATH_TRANSLATED'] = os.path.normpath(os.path.join(
                workdir, env['PATH_INFO']))
        if query:
            env['QUERY_STRING'] = query
        env['GATEWAY_INTERFACE'] = 'CGI/1.1'
        if self.channel.addr:
            env['REMOTE_ADDR'] = self.channel.addr[0]
        else:
            env['REMOTE_ADDR'] = '127.0.0.1'

        for header in self.header:
            key,value=header.split(":",1)
            key=key.lower()
            value=value.strip()
            if key in header2env and value:
                env[header2env.get(key)]=value
            else:
                key='HTTP_%s' % ("_".join(key.split( "-"))).upper()
                if value and key not in env:
                    env[key]=value
        return env

    def get_server_url(self):
        """ Functionality that medusa's http request doesn't have; set an
        attribute named 'server_url' on the request based on the Host: header
        """
        default_port={'http': '80', 'https': '443'}
        environ = self.cgi_environment()
        if (environ.get('HTTPS') in ('on', 'ON') or
            environ.get('SERVER_PORT_SECURE') == "1"):
            # XXX this will currently never be true
            protocol = 'https'
        else:
            protocol = 'http'

        if 'HTTP_HOST' in environ:
            host = environ['HTTP_HOST'].strip()
            hostname, port = urllib.splitport(host)
        else:
            hostname = environ['SERVER_NAME'].strip()
            port = environ['SERVER_PORT']

        if port is None or default_port[protocol] == port:
            host = hostname
        else:
            host = hostname + ':' + port
        server_url = '%s://%s' % (protocol, host)
        if server_url[-1:]=='/':
            server_url=server_url[:-1]
        return server_url

class deferring_http_channel(http_server.http_channel):

    # use a 4096-byte buffer size instead of the default 65536-byte buffer in
    # order to spew tail -f output faster (speculative)
    ac_out_buffer_size = 4096

    delay = False
    writable_check = time.time()

    def writable(self, t=time.time):
        now = t()
        if self.delay:
            # we called a deferred producer via this channel (see refill_buffer)
            last_writable_check = self.writable_check
            self.writable_check = now
            elapsed = now - last_writable_check
            if elapsed > self.delay:
                return True
            else:
                return False

        return http_server.http_channel.writable(self)

    def refill_buffer (self):
        """ Implement deferreds """
        while 1:
            if len(self.producer_fifo):
                p = self.producer_fifo.first()
                # a 'None' in the producer fifo is a sentinel,
                # telling us to close the channel.
                if p is None:
                    if not self.ac_out_buffer:
                        self.producer_fifo.pop()
                        self.close()
                    return
                elif isinstance(p, str):
                    self.producer_fifo.pop()
                    self.ac_out_buffer += p
                    return

                data = p.more()

                if data is NOT_DONE_YET:
                    self.delay = p.delay
                    return

                elif data:
                    self.ac_out_buffer = self.ac_out_buffer + data
                    self.delay = False
                    return
                else:
                    self.producer_fifo.pop()
            else:
                return

    def found_terminator (self):
        """ We only override this to use 'deferring_http_request' class
        instead of the normal http_request class; it sucks to need to override
        this """
        if self.current_request:
            self.current_request.found_terminator()
        else:
            header = self.in_buffer
            self.in_buffer = ''
            lines = header.split('\r\n')

            # --------------------------------------------------
            # crack the request header
            # --------------------------------------------------

            while lines and not lines[0]:
                # as per the suggestion of http-1.1 section 4.1, (and
                # Eric Parker <eparker@zyvex.com>), ignore a leading
                # blank lines (buggy browsers tack it onto the end of
                # POST requests)
                lines = lines[1:]

            if not lines:
                self.close_when_done()
                return

            request = lines[0]

            command, uri, version = http_server.crack_request (request)
            header = http_server.join_headers (lines[1:])

            # unquote path if necessary (thanks to Skip Montanaro for pointing
            # out that we must unquote in piecemeal fashion).
            rpath, rquery = http_server.splitquery(uri)
            if '%' in rpath:
                if rquery:
                    uri = http_server.unquote (rpath) + '?' + rquery
                else:
                    uri = http_server.unquote (rpath)

            r = deferring_http_request (self, request, command, uri, version,
                                         header)
            self.request_counter.increment()
            self.server.total_requests.increment()

            if command is None:
                self.log_info ('Bad HTTP request: %s' % repr(request), 'error')
                r.error (400)
                return

            # --------------------------------------------------
            # handler selection and dispatch
            # --------------------------------------------------
            for h in self.server.handlers:
                if h.match (r):
                    try:
                        self.current_request = r
                        # This isn't used anywhere.
                        # r.handler = h # CYCLE
                        h.handle_request (r)
                    except:
                        self.server.exceptions.increment()
                        (file, fun, line), t, v, tbinfo = \
                               asyncore.compact_traceback()
                        self.server.log_info(
                            'Server Error: %s, %s: file: %s line: %s' %
                            (t,v,file,line),
                            'error')
                        try:
                            r.error (500)
                        except:
                            pass
                    return

            # no handlers, so complain
            r.error (404)

class supervisor_http_server(http_server.http_server):
    channel_class = deferring_http_channel
    ip = None

    def prebind(self, sock, logger_object):
        """ Override __init__ to do logger setup earlier so it can
        go to our logger object instead of stdout """
        from supervisor.medusa import logger

        if not logger_object:
            logger_object = logger.file_logger(sys.stdout)

        logger_object = logger.unresolving_logger(logger_object)
        self.logger = logger_object

        asyncore.dispatcher.__init__ (self)
        self.set_socket(sock)

        self.handlers = []

        sock.setblocking(0)
        self.set_reuse_addr()

    def postbind(self):
        from supervisor.medusa.counter import counter
        from supervisor.medusa.http_server import VERSION_STRING

        self.listen(1024)

        self.total_clients = counter()
        self.total_requests = counter()
        self.exceptions = counter()
        self.bytes_out = counter()
        self.bytes_in  = counter()

        self.log_info (
                'Medusa (V%s) started at %s'
                '\n\tHostname: %s'
                '\n\tPort:%s'
                '\n' % (
                        VERSION_STRING,
                        time.ctime(time.time()),
                        self.server_name,
                        self.port,
                        )
                )

    def log_info(self, message, type='info'):
        ip = ''
        if getattr(self, 'ip', None) is not None:
            ip = self.ip
        self.logger.log(ip, message)

class supervisor_af_inet_http_server(supervisor_http_server):
    """ AF_INET version of supervisor HTTP server """

    def __init__(self, ip, port, logger_object):
        self.ip = ip
        self.port = port
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.prebind(sock, logger_object)
        self.bind((ip, port))

        if not ip:
            self.log_info('Computing default hostname', 'warning')
            hostname = socket.gethostname()
            try:
                ip = socket.gethostbyname(hostname)
            except socket.error:
                raise ValueError(
                    'Could not determine IP address for hostname %s, '
                    'please try setting an explicit IP address in the "port" '
                    'setting of your [inet_http_server] section.  For example, '
                    'instead of "port = 9001", try "port = 127.0.0.1:9001."'
                    % hostname)
        try:
            self.server_name = socket.gethostbyaddr (ip)[0]
        except socket.error:
            self.log_info('Cannot do reverse lookup', 'warning')
            self.server_name = ip       # use the IP address as the "hostname"

        self.postbind()

class supervisor_af_unix_http_server(supervisor_http_server):
    """ AF_UNIX version of supervisor HTTP server """

    def __init__(self, socketname, sockchmod, sockchown, logger_object):
        self.ip = socketname
        self.port = socketname

        # XXX this is insecure.  We really should do something like
        # http://developer.apple.com/samplecode/CFLocalServer/listing6.html
        # (see also http://developer.apple.com/technotes/tn2005/tn2083.html#SECUNIXDOMAINSOCKETS)
        # but it would be very inconvenient for the user to need to get all
        # the directory setup right.

        tempname = "%s.%d" % (socketname, os.getpid())

        try:
            os.unlink(tempname)
        except OSError:
            pass

        while 1:
            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            try:
                sock.bind(tempname)
                os.chmod(tempname, sockchmod)
                try:
                    # hard link
                    os.link(tempname, socketname)
                except OSError:
                    # Lock contention, or stale socket.
                    used = self.checkused(socketname)
                    if used:
                        # cooperate with 'openhttpserver' in supervisord
                        raise socket.error(errno.EADDRINUSE)

                    # Stale socket -- delete, sleep, and try again.
                    msg = "Unlinking stale socket %s\n" % socketname
                    sys.stderr.write(msg)
                    try:
                        os.unlink(socketname)
                    except:
                        pass
                    sock.close()
                    time.sleep(.3)
                    continue
                else:
                    try:
                        os.chown(socketname, sockchown[0], sockchown[1])
                    except OSError as why:
                        if why.args[0] == errno.EPERM:
                            msg = ('Not permitted to chown %s to uid/gid %s; '
                                   'adjust "sockchown" value in config file or '
                                   'on command line to values that the '
                                   'current user (%s) can successfully chown')
                            raise ValueError(msg % (socketname,
                                                    repr(sockchown),
                                                    pwd.getpwuid(
                                                        os.geteuid())[0],
                                                    ),
                                             )
                        else:
                            raise
                    self.prebind(sock, logger_object)
                    break

            finally:
                try:
                    os.unlink(tempname)
                except OSError:
                    pass

        self.server_name = '<unix domain socket>'
        self.postbind()

    def checkused(self, socketname):
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            s.connect(socketname)
            s.send("GET / HTTP/1.0\r\n\r\n")
            s.recv(1)
            s.close()
        except socket.error:
            return False
        else:
            return True

class tail_f_producer:
    def __init__(self, request, filename, head):
        self.file = open(filename, 'rb')
        self.request = weakref.ref(request)
        self.delay = 0.1
        sz = self.fsize()
        if sz >= head:
            self.sz = sz - head
        else:
            self.sz = 0

    def __del__(self):
        if self.file:
            self.file.close()

    def more(self):
        try:
            newsz = self.fsize()
        except OSError:
            # file descriptor was closed
            return ''
        bytes_added = newsz - self.sz
        if bytes_added < 0:
            self.sz = 0
            return "==> File truncated <==\n"
        if bytes_added > 0:
            self.file.seek(-bytes_added, 2)
            bytes = self.file.read(bytes_added)
            self.sz = newsz
            return bytes
        return NOT_DONE_YET

    def fsize(self):
        return os.fstat(self.file.fileno())[stat.ST_SIZE]

class logtail_handler:
    IDENT = 'Logtail HTTP Request Handler'
    path = '/logtail'

    def __init__(self, supervisord):
        self.supervisord = supervisord

    def match(self, request):
        return request.uri.startswith(self.path)

    def handle_request(self, request):
        if request.command != 'GET':
            request.error (400) # bad request
            return

        path, params, query, fragment = request.split_uri()

        if '%' in path:
            path = http_server.unquote(path)

        # strip off all leading slashes
        while path and path[0] == '/':
            path = path[1:]

        path, process_name_and_channel = path.split('/', 1)

        try:
            process_name, channel = process_name_and_channel.split('/', 1)
        except ValueError:
            # no channel specified, default channel to stdout
            process_name = process_name_and_channel
            channel = 'stdout'

        from supervisor.options import split_namespec
        group_name, process_name = split_namespec(process_name)

        group = self.supervisord.process_groups.get(group_name)
        if group is None:
            request.error(404) # not found
            return

        process = group.processes.get(process_name)
        if process is None:
            request.error(404) # not found
            return

        logfile = getattr(process.config, '%s_logfile' % channel, None)

        if logfile is None or not os.path.exists(logfile):
            # XXX problematic: processes that don't start won't have a log
            # file and we probably don't want to go into fatal state if we try
            # to read the log of a process that did not start.
            request.error(410) # gone
            return

        mtime = os.stat(logfile)[stat.ST_MTIME]
        request['Last-Modified'] = http_date.build_http_date(mtime)
        request['Content-Type'] = 'text/plain'
        # the lack of a Content-Length header makes the outputter
        # send a 'Transfer-Encoding: chunked' response

        request.push(tail_f_producer(request, logfile, 1024))

        request.done()

class mainlogtail_handler:
    IDENT = 'Main Logtail HTTP Request Handler'
    path = '/mainlogtail'

    def __init__(self, supervisord):
        self.supervisord = supervisord

    def match(self, request):
        return request.uri.startswith(self.path)

    def handle_request(self, request):
        if request.command != 'GET':
            request.error (400) # bad request
            return

        logfile = self.supervisord.options.logfile

        if logfile is None or not os.path.exists(logfile):
            request.error(410) # gone
            return

        mtime = os.stat(logfile)[stat.ST_MTIME]
        request['Last-Modified'] = http_date.build_http_date(mtime)
        request['Content-Type'] = 'text/plain'
        # the lack of a Content-Length header makes the outputter
        # send a 'Transfer-Encoding: chunked' response

        request.push(tail_f_producer(request, logfile, 1024))

        request.done()

def make_http_servers(options, supervisord):
    servers = []
    class LogWrapper:
        def log(self, msg):
            if msg.endswith('\n'):
                msg = msg[:-1]
            options.logger.trace(msg)
    wrapper = LogWrapper()

    for config in options.server_configs:
        family = config['family']

        if family == socket.AF_INET:
            host, port = config['host'], config['port']
            hs = supervisor_af_inet_http_server(host, port,
                                                logger_object=wrapper)
        elif family == socket.AF_UNIX:
            socketname = config['file']
            sockchmod = config['chmod']
            sockchown = config['chown']
            hs = supervisor_af_unix_http_server(socketname,sockchmod, sockchown,
                                                logger_object=wrapper)
        else:
            raise ValueError('Cannot determine socket type %r' % family)

        from supervisor.xmlrpc import supervisor_xmlrpc_handler
        from supervisor.xmlrpc import SystemNamespaceRPCInterface
        from supervisor.web import supervisor_ui_handler

        subinterfaces = []
        for name, factory, d in options.rpcinterface_factories:
            try:
                inst = factory(supervisord, **d)
            except:
                import traceback; traceback.print_exc()
                raise ValueError('Could not make %s rpc interface' % name)
            subinterfaces.append((name, inst))
            options.logger.info('RPC interface %r initialized' % name)

        subinterfaces.append(('system',
                              SystemNamespaceRPCInterface(subinterfaces)))
        xmlrpchandler = supervisor_xmlrpc_handler(supervisord, subinterfaces)
        tailhandler = logtail_handler(supervisord)
        maintailhandler = mainlogtail_handler(supervisord)
        uihandler = supervisor_ui_handler(supervisord)
        here = os.path.abspath(os.path.dirname(__file__))
        templatedir = os.path.join(here, 'ui')
        filesystem = filesys.os_filesystem(templatedir)
        defaulthandler = default_handler.default_handler(filesystem)

        username = config['username']
        password = config['password']

        if username:
            # wrap the xmlrpc handler and tailhandler in an authentication
            # handler
            users = {username:password}
            xmlrpchandler = supervisor_auth_handler(users, xmlrpchandler)
            tailhandler = supervisor_auth_handler(users, tailhandler)
            maintailhandler = supervisor_auth_handler(users, maintailhandler)
            uihandler = supervisor_auth_handler(users, uihandler)
            defaulthandler = supervisor_auth_handler(users, defaulthandler)
        else:
            options.logger.critical(
                'Server %r running without any HTTP '
                'authentication checking' % config['section'])
        # defaulthandler must be consulted last as its match method matches
        # everything, so it's first here (indicating last checked)
        hs.install_handler(defaulthandler)
        hs.install_handler(uihandler)
        hs.install_handler(maintailhandler)
        hs.install_handler(tailhandler)
        hs.install_handler(xmlrpchandler) # last for speed (first checked)
        servers.append((config, hs))

    return servers

class encrypted_dictionary_authorizer:
    def __init__ (self, dict):
        self.dict = dict

    def authorize(self, auth_info):
        username, password = auth_info
        if username in self.dict:
            stored_password = self.dict[username]
            if stored_password.startswith('{SHA}'):
                password_hash = sha1(as_bytes(password)).hexdigest()
                return stored_password[5:] == password_hash
            else:
                return stored_password == password
        else:
            return False

class supervisor_auth_handler(auth_handler):
    def __init__(self, dict, handler, realm='default'):
        auth_handler.__init__(self, dict, handler, realm)
        # override the authorizer with one that knows about SHA hashes too
        self.authorizer = encrypted_dictionary_authorizer(dict)


########NEW FILE########
__FILENAME__ = http_client
# this code based on Daniel Krech's RDFLib HTTP client code (see rdflib.net)

import sys
import supervisor.medusa.text_socket as socket

from supervisor.compat import print_function
from supervisor.compat import urlparse
from supervisor.compat import as_bytes
from supervisor.compat import as_string
from supervisor.compat import encodestring
from supervisor.medusa import asynchat_25 as asynchat

CR="\x0d"
LF="\x0a"
CRLF=CR+LF

class Listener(object):

    def status(self, url, status):
        pass

    def error(self, url, error):
        print_function(url, error)

    def response_header(self, url, name, value):
        pass

    def done(self, url):
        pass

    def feed(self, url, data):
        sys.stdout.write(data)
        sys.stdout.flush()

    def close(self, url):
        pass

class HTTPHandler(asynchat.async_chat):
    def __init__(
        self,
        listener,
        username='',
        password=None,
        conn=None,
        map=None
        ):
        asynchat.async_chat.__init__(self, conn, map)
        self.listener = listener
        self.user_agent = 'Supervisor HTTP Client'
        self.buffer = ''
        self.set_terminator(CRLF)
        self.connected = 0
        self.part = self.status_line
        self.chunk_size = 0
        self.chunk_read = 0
        self.length_read = 0
        self.length = 0
        self.encoding = None
        self.username = username
        self.password = password
        self.url = None
        self.error_handled = False

    def get(self, serverurl, path=''):
        if self.url is not None:
            raise AssertionError('Already doing a get')
        self.url = serverurl + path
        scheme, host, path_ignored, params, query, fragment = urlparse.urlparse(
            self.url)
        if not scheme in ("http", "unix"):
            raise NotImplementedError
        self.host = host
        if ":" in host:
            hostname, port = host.split(":", 1)
            port = int(port)
        else:
            hostname = host
            port = 80

        self.path = path
        self.port = port

        if scheme == "http":
            ip = hostname
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
            self.connect((ip, self.port))
        elif scheme == "unix":
            socketname = serverurl[7:]
            self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
            self.connect(socketname)

    def close(self):
        self.listener.close(self.url)
        self.connected = 0
        self.del_channel()
        self.socket.close()
        self.url = "CLOSED"

    def header(self, name, value):
        self.push('%s: %s' % (name, value))
        self.push(CRLF)

    def handle_error(self):
        if self.error_handled:
            return
        if 1 or self.connected:
            t,v,tb = sys.exc_info()
            msg = 'Cannot connect, error: %s (%s)' % (t, v)
            self.listener.error(self.url, msg)
            self.part = self.ignore
            self.close()
            self.error_handled = True
            del t
            del v
            del tb

    def handle_connect(self):
        self.connected = 1
        method = "GET"
        version = "HTTP/1.1"
        self.push("%s %s %s" % (method, self.path, version))
        self.push(CRLF)
        self.header("Host", self.host)

        self.header('Accept-Encoding', 'chunked')
        self.header('Accept', '*/*')
        self.header('User-agent', self.user_agent)
        if self.password:
            auth = '%s:%s' % (self.username, self.password)
            auth = as_string(encodestring(as_bytes(auth))).strip()
            self.header('Authorization', 'Basic %s' % auth)
        self.push(CRLF)
        self.push(CRLF)


    def feed(self, data):
        self.listener.feed(self.url, data)

    def collect_incoming_data(self, bytes):
        self.buffer = self.buffer + bytes
        if self.part==self.body:
            self.feed(self.buffer)
            self.buffer = ''

    def found_terminator(self):
        self.part()
        self.buffer = ''

    def ignore(self):
        self.buffer = ''

    def status_line(self):
        line = self.buffer

        version, status, reason = line.split(None, 2)
        status = int(status)
        if not version.startswith('HTTP/'):
            raise ValueError(line)

        self.listener.status(self.url, status)

        if status == 200:
            self.part = self.headers
        else:
            self.part = self.ignore
            msg = 'Cannot read, status code %s' % status
            self.listener.error(self.url, msg)
            self.close()
        return version, status, reason

    def headers(self):
        line = self.buffer
        if not line:
            if self.encoding=="chunked":
                self.part = self.chunked_size
            else:
                self.part = self.body
                self.set_terminator(self.length)
        else:
            name, value = line.split(":", 1)
            if name and value:
                name = name.lower()
                value = value.strip()
                if name=="transfer-encoding":
                    self.encoding = value
                elif name=="content-length":
                    self.length = int(value)
                self.response_header(name, value)

    def response_header(self, name, value):
        self.listener.response_header(self.url, name, value)

    def body(self):
        self.done()
        self.close()

    def done(self):
        self.listener.done(self.url)

    def chunked_size(self):
        line = self.buffer
        if not line:
            return
        chunk_size = int(line.split()[0], 16)
        if chunk_size==0:
            self.part = self.trailer
        else:
            self.set_terminator(chunk_size)
            self.part = self.chunked_body
        self.length += chunk_size

    def chunked_body(self):
        line = self.buffer
        self.set_terminator(CRLF)
        self.part = self.chunked_size
        self.feed(line)

    def trailer(self):
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
        # trailer        = *(entity-header CRLF)
        line = self.buffer
        if line==CRLF:
            self.done()
            self.close()

########NEW FILE########
__FILENAME__ = loggers
"""
Logger implementation loosely modeled on PEP 282.  We don't use the
PEP 282 logger implementation in the stdlib ('logging') because it's
idiosyncratic and a bit slow for our purposes (we don't use threads).
"""

# This module must not depend on any non-stdlib modules to
# avoid circular import problems

import os
import errno
import sys
import time
import traceback

from supervisor.compat import syslog
from supervisor.compat import long

class LevelsByName:
    CRIT = 50   # messages that probably require immediate user attention
    ERRO = 40   # messages that indicate a potentially ignorable error condition
    WARN = 30   # messages that indicate issues which aren't errors
    INFO = 20   # normal informational output
    DEBG = 10   # messages useful for users trying to debug configurations
    TRAC = 5    # messages useful to developers trying to debug plugins
    BLAT = 3    # messages useful for developers trying to debug supervisor

class LevelsByDescription:
    critical = LevelsByName.CRIT
    error = LevelsByName.ERRO
    warn = LevelsByName.WARN
    info = LevelsByName.INFO
    debug = LevelsByName.DEBG
    trace = LevelsByName.TRAC
    blather = LevelsByName.BLAT

def _levelNumbers():
    bynumber = {}
    for name, number in LevelsByName.__dict__.items():
        if not name.startswith('_'):
            bynumber[number] = name
    return bynumber

LOG_LEVELS_BY_NUM = _levelNumbers()

def getLevelNumByDescription(description):
    num = getattr(LevelsByDescription, description, None)
    return num

class Handler:
    fmt = '%(message)s'
    level = LevelsByName.INFO
    def __init__(self, stream=None):
        self.stream = stream
        self.closed = False

    def setFormat(self, fmt):
        self.fmt = fmt

    def setLevel(self, level):
        self.level = level

    def flush(self):
        try:
            self.stream.flush()
        except IOError as why:
            # if supervisor output is piped, EPIPE can be raised at exit
            if why.args[0] != errno.EPIPE:
                raise

    def close(self):
        if not self.closed:
            if hasattr(self.stream, 'fileno'):
                fd = self.stream.fileno()
                if fd < 3: # don't ever close stdout or stderr
                    return
            self.stream.close()
            self.closed = True

    def emit(self, record):
        try:
            msg = self.fmt % record.asdict()
            try:
                self.stream.write(msg)
            except UnicodeError:
                self.stream.write(msg.encode("UTF-8"))
            self.flush()
        except:
            self.handleError()

    def handleError(self):
        ei = sys.exc_info()
        traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
        del ei

class FileHandler(Handler):
    """File handler which supports reopening of logs.
    """

    def __init__(self, filename, mode="a"):
        Handler.__init__(self, open(filename, mode))
        self.baseFilename = filename
        self.mode = mode

    def reopen(self):
        self.close()
        self.stream = open(self.baseFilename, self.mode)
        self.closed = False

    def remove(self):
        self.close()
        try:
            os.remove(self.baseFilename)
        except OSError as why:
            if why.args[0] != errno.ENOENT:
                raise

class StreamHandler(Handler):
    def __init__(self, strm=None):
        Handler.__init__(self, strm)

    def remove(self):
        if hasattr(self.stream, 'clear'):
            self.stream.clear()

    def reopen(self):
        pass

class BoundIO:
    def __init__(self, maxbytes, buf=''):
        self.maxbytes = maxbytes
        self.buf = buf

    def flush(self):
        pass

    def close(self):
        self.clear()

    def write(self, s):
        slen = len(s)
        if len(self.buf) + slen > self.maxbytes:
            self.buf = self.buf[slen:]
        self.buf += s

    def getvalue(self):
        return self.buf

    def clear(self):
        self.buf = ''

class RotatingFileHandler(FileHandler):
    def __init__(self, filename, mode='a', maxBytes=512*1024*1024,
                 backupCount=10):
        """
        Open the specified file and use it as the stream for logging.

        By default, the file grows indefinitely. You can specify particular
        values of maxBytes and backupCount to allow the file to rollover at
        a predetermined size.

        Rollover occurs whenever the current log file is nearly maxBytes in
        length. If backupCount is >= 1, the system will successively create
        new files with the same pathname as the base file, but with extensions
        ".1", ".2" etc. appended to it. For example, with a backupCount of 5
        and a base file name of "app.log", you would get "app.log",
        "app.log.1", "app.log.2", ... through to "app.log.5". The file being
        written to is always "app.log" - when it gets filled up, it is closed
        and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
        exist, then they are renamed to "app.log.2", "app.log.3" etc.
        respectively.

        If maxBytes is zero, rollover never occurs.
        """
#        FileHandler.__init__(self, filename, mode)
        if maxBytes > 0:
            mode = 'a' # doesn't make sense otherwise!
        FileHandler.__init__(self, filename, mode)
        self.maxBytes = maxBytes
        self.backupCount = backupCount
        self.counter = 0
        self.every = 10

    def __del__(self):
        if self.stream:
            try:
                self.stream.close()
            except OSError:
                pass

    def emit(self, record):
        """
        Emit a record.

        Output the record to the file, catering for rollover as described
        in doRollover().
        """
        FileHandler.emit(self, record)
        self.doRollover()

    def _remove(self, fn): # pragma: no cover
        # this is here to service stubbing in unit tests
        return os.remove(fn)

    def _rename(self, src, tgt): # pragma: no cover
        # this is here to service stubbing in unit tests
        return os.rename(src, tgt)

    def _exists(self, fn): # pragma: no cover
        # this is here to service stubbing in unit tests
        return os.path.exists(fn)

    def removeAndRename(self, sfn, dfn):
        if self._exists(dfn):
            try:
                self._remove(dfn)
            except OSError as why:
                # catch race condition (destination already deleted)
                if why.args[0] != errno.ENOENT:
                    raise
        try:
            self._rename(sfn, dfn)
        except OSError as why:
            # catch exceptional condition (source deleted)
            # E.g. cleanup script removes active log.
            if why.args[0] != errno.ENOENT:
                raise

    def doRollover(self):
        """
        Do a rollover, as described in __init__().
        """
        if self.maxBytes <= 0:
            return

        if not (self.stream.tell() >= self.maxBytes):
            return

        self.stream.close()
        if self.backupCount > 0:
            for i in range(self.backupCount - 1, 0, -1):
                sfn = "%s.%d" % (self.baseFilename, i)
                dfn = "%s.%d" % (self.baseFilename, i + 1)
                if os.path.exists(sfn):
                    self.removeAndRename(sfn, dfn)
            dfn = self.baseFilename + ".1"
            self.removeAndRename(self.baseFilename, dfn)
        self.stream = open(self.baseFilename, 'w')

class LogRecord:
    def __init__(self, level, msg, **kw):
        self.level = level
        self.msg = msg
        self.kw = kw
        self.dictrepr = None

    def asdict(self):
        if self.dictrepr is None:
            now = time.time()
            msecs = (now - long(now)) * 1000
            part1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now))
            asctime = '%s,%03d' % (part1, msecs)
            levelname = LOG_LEVELS_BY_NUM[self.level]
            if self.kw:
                msg = self.msg % self.kw
            else:
                msg = self.msg
            self.dictrepr = {'message':msg, 'levelname':levelname,
                             'asctime':asctime}
        return self.dictrepr

class Logger:
    def __init__(self, level=None, handlers=None):
        if level is None:
            level = LevelsByName.INFO
        self.level = level

        if handlers is None:
            handlers = []
        self.handlers = handlers

    def close(self):
        for handler in self.handlers:
            handler.close()

    def blather(self, msg, **kw):
        if LevelsByName.BLAT >= self.level:
            self.log(LevelsByName.BLAT, msg, **kw)

    def trace(self, msg, **kw):
        if LevelsByName.TRAC >= self.level:
            self.log(LevelsByName.TRAC, msg, **kw)

    def debug(self, msg, **kw):
        if LevelsByName.DEBG >= self.level:
            self.log(LevelsByName.DEBG, msg, **kw)

    def info(self, msg, **kw):
        if LevelsByName.INFO >= self.level:
            self.log(LevelsByName.INFO, msg, **kw)

    def warn(self, msg, **kw):
        if LevelsByName.WARN >= self.level:
            self.log(LevelsByName.WARN, msg, **kw)

    def error(self, msg, **kw):
        if LevelsByName.ERRO >= self.level:
            self.log(LevelsByName.ERRO, msg, **kw)

    def critical(self, msg, **kw):
        if LevelsByName.CRIT >= self.level:
            self.log(LevelsByName.CRIT, msg, **kw)

    def log(self, level, msg, **kw):
        record = LogRecord(level, msg, **kw)
        for handler in self.handlers:
            if level >= handler.level:
                handler.emit(record)

    def addHandler(self, hdlr):
        self.handlers.append(hdlr)

    def getvalue(self):
        raise NotImplementedError

class SyslogHandler(Handler):
    def __init__(self):
        Handler.__init__(self)
        assert syslog is not None, "Syslog module not present"

    def close(self):
        pass

    def reopen(self):
        pass

    def _syslog(self, msg): # pragma: no cover
        # this exists only for unit test stubbing
        syslog.syslog(msg)

    def emit(self, record):
        try:
            params = record.asdict()
            message = params['message']
            for line in message.rstrip('\n').split('\n'):
                params['message'] = line
                msg = self.fmt % params
                try:
                    self._syslog(msg)
                except UnicodeError:
                    self._syslog(msg.encode("UTF-8"))
        except:
            self.handleError()

def getLogger(level=None):
    return Logger(level)

_2MB = 1<<21

def handle_boundIO(logger, fmt, maxbytes=_2MB):
    io = BoundIO(maxbytes)
    handler = StreamHandler(io)
    handler.setLevel(logger.level)
    handler.setFormat(fmt)
    logger.addHandler(handler)
    logger.getvalue = io.getvalue

    return logger

def handle_stdout(logger, fmt):
    handler = StreamHandler(sys.stdout)
    handler.setFormat(fmt)
    handler.setLevel(logger.level)
    logger.addHandler(handler)

def handle_syslog(logger, fmt):
    handler = SyslogHandler()
    handler.setFormat(fmt)
    handler.setLevel(logger.level)
    logger.addHandler(handler)

def handle_file(logger, filename, fmt, rotating=False, maxbytes=0, backups=0):
    if filename == 'syslog':
        handler = SyslogHandler()

    else:
        if rotating is False:
            handler = FileHandler(filename)
        else:
            handler = RotatingFileHandler(filename, 'a', maxbytes, backups)

    handler.setFormat(fmt)
    handler.setLevel(logger.level)
    logger.addHandler(handler)

    return logger

########NEW FILE########
__FILENAME__ = asynchat_25
# -*- Mode: Python; tab-width: 4 -*-
#       Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp
#       Author: Sam Rushing <rushing@nightmare.com>

# ======================================================================
# Copyright 1996 by Sam Rushing
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Sam
# Rushing not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================

r"""A class supporting chat-style (command/response) protocols.

This class adds support for 'chat' style protocols - where one side
sends a 'command', and the other sends a response (examples would be
the common internet protocols - smtp, nntp, ftp, etc..).

The handle_read() method looks at the input stream for the current
'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n'
for multi-line output), calling self.found_terminator() on its
receipt.

for example:
Say you build an async nntp client using this class.  At the start
of the connection, you'll have self.terminator set to '\r\n', in
order to process the single-line greeting.  Just before issuing a
'LIST' command you'll set it to '\r\n.\r\n'.  The output of the LIST
command will be accumulated (using your own 'collect_incoming_data'
method) up to the terminator, and then control will be returned to
you - by calling your self.found_terminator() method.
"""

import supervisor.medusa.text_socket as socket
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.compat import long

class async_chat (asyncore.dispatcher):
    """This is an abstract class.  You must derive from this class, and add
    the two methods collect_incoming_data() and found_terminator()"""

    # these are overridable defaults

    ac_in_buffer_size       = 4096
    ac_out_buffer_size      = 4096

    def __init__ (self, conn=None, map=None):
        self.ac_in_buffer = ''
        self.ac_out_buffer = ''
        self.producer_fifo = fifo()
        asyncore.dispatcher.__init__ (self, conn, map)

    def collect_incoming_data(self, data):
        raise NotImplementedError("must be implemented in subclass")

    def found_terminator(self):
        raise NotImplementedError("must be implemented in subclass")

    def set_terminator (self, term):
        """Set the input delimiter.  Can be a fixed string of any length, an integer, or None"""
        self.terminator = term

    def get_terminator (self):
        return self.terminator

    # grab some more data from the socket,
    # throw it to the collector method,
    # check for the terminator,
    # if found, transition to the next state.

    def handle_read (self):

        try:
            data = self.recv (self.ac_in_buffer_size)
        except socket.error:
            self.handle_error()
            return

        self.ac_in_buffer += data

        # Continue to search for self.terminator in self.ac_in_buffer,
        # while calling self.collect_incoming_data.  The while loop
        # is necessary because we might read several data+terminator
        # combos with a single recv(1024).

        while self.ac_in_buffer:
            lb = len(self.ac_in_buffer)
            terminator = self.get_terminator()
            if not terminator:
                # no terminator, collect it all
                self.collect_incoming_data (self.ac_in_buffer)
                self.ac_in_buffer = ''
            elif isinstance(terminator, int) or isinstance(terminator, long):
                # numeric terminator
                n = terminator
                if lb < n:
                    self.collect_incoming_data (self.ac_in_buffer)
                    self.ac_in_buffer = ''
                    self.terminator -= lb
                else:
                    self.collect_incoming_data (self.ac_in_buffer[:n])
                    self.ac_in_buffer = self.ac_in_buffer[n:]
                    self.terminator = 0
                    self.found_terminator()
            else:
                # 3 cases:
                # 1) end of buffer matches terminator exactly:
                #    collect data, transition
                # 2) end of buffer matches some prefix:
                #    collect data to the prefix
                # 3) end of buffer does not match any prefix:
                #    collect data
                terminator_len = len(terminator)
                index = self.ac_in_buffer.find(terminator)
                if index != -1:
                    # we found the terminator
                    if index > 0:
                        # don't bother reporting the empty string (source of subtle bugs)
                        self.collect_incoming_data (self.ac_in_buffer[:index])
                    self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:]
                    # This does the Right Thing if the terminator is changed here.
                    self.found_terminator()
                else:
                    # check for a prefix of the terminator
                    index = find_prefix_at_end (self.ac_in_buffer, terminator)
                    if index:
                        if index != lb:
                            # we found a prefix, collect up to the prefix
                            self.collect_incoming_data (self.ac_in_buffer[:-index])
                            self.ac_in_buffer = self.ac_in_buffer[-index:]
                        break
                    else:
                        # no prefix, collect it all
                        self.collect_incoming_data (self.ac_in_buffer)
                        self.ac_in_buffer = ''

    def handle_write (self):
        self.initiate_send ()

    def handle_close (self):
        self.close()

    def push (self, data):
        self.producer_fifo.push (simple_producer (data))
        self.initiate_send()

    def push_with_producer (self, producer):
        self.producer_fifo.push (producer)
        self.initiate_send()

    def readable (self):
        """predicate for inclusion in the readable for select()"""
        return len(self.ac_in_buffer) <= self.ac_in_buffer_size

    def writable (self):
        """predicate for inclusion in the writable for select()"""
        # return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
        # this is about twice as fast, though not as clear.
        return not (
                (self.ac_out_buffer == '') and
                self.producer_fifo.is_empty() and
                self.connected
                )

    def close_when_done (self):
        """automatically close this channel once the outgoing queue is empty"""
        self.producer_fifo.push (None)

    # refill the outgoing buffer by calling the more() method
    # of the first producer in the queue
    def refill_buffer (self):
        while 1:
            if len(self.producer_fifo):
                p = self.producer_fifo.first()
                # a 'None' in the producer fifo is a sentinel,
                # telling us to close the channel.
                if p is None:
                    if not self.ac_out_buffer:
                        self.producer_fifo.pop()
                        self.close()
                    return
                elif isinstance(p, str):
                    self.producer_fifo.pop()
                    self.ac_out_buffer += p
                    return
                data = p.more()
                if data:
                    self.ac_out_buffer = self.ac_out_buffer + data
                    return
                else:
                    self.producer_fifo.pop()
            else:
                return

    def initiate_send (self):
        obs = self.ac_out_buffer_size
        # try to refill the buffer
        if len (self.ac_out_buffer) < obs:
            self.refill_buffer()

        if self.ac_out_buffer and self.connected:
            # try to send the buffer
            try:
                num_sent = self.send (self.ac_out_buffer[:obs])
                if num_sent:
                    self.ac_out_buffer = self.ac_out_buffer[num_sent:]

            except socket.error:
                self.handle_error()
                return

    def discard_buffers (self):
        # Emergencies only!
        self.ac_in_buffer = ''
        self.ac_out_buffer = ''
        while self.producer_fifo:
            self.producer_fifo.pop()


class simple_producer:

    def __init__ (self, data, buffer_size=512):
        self.data = data
        self.buffer_size = buffer_size

    def more (self):
        if len (self.data) > self.buffer_size:
            result = self.data[:self.buffer_size]
            self.data = self.data[self.buffer_size:]
            return result
        else:
            result = self.data
            self.data = ''
            return result

class fifo:
    def __init__ (self, list=None):
        if not list:
            self.list = []
        else:
            self.list = list

    def __len__ (self):
        return len(self.list)

    def is_empty (self):
        return self.list == []

    def first (self):
        return self.list[0]

    def push (self, data):
        self.list.append(data)

    def pop (self):
        if self.list:
            return 1, self.list.pop(0)
        else:
            return 0, None

# Given 'haystack', see if any prefix of 'needle' is at its end.  This
# assumes an exact match has already been checked.  Return the number of
# characters matched.
# for example:
# f_p_a_e ("qwerty\r", "\r\n") => 1
# f_p_a_e ("qwertydkjf", "\r\n") => 0
# f_p_a_e ("qwerty\r\n", "\r\n") => <undefined>

# this could maybe be made faster with a computed regex?
# [answer: no; circa Python-2.0, Jan 2001]
# new python:   28961/s
# old python:   18307/s
# re:        12820/s
# regex:     14035/s

def find_prefix_at_end (haystack, needle):
    l = len(needle) - 1
    while l and not haystack.endswith(needle[:l]):
        l -= 1
    return l

########NEW FILE########
__FILENAME__ = asyncore_25
# -*- Mode: Python -*-
#   Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
#   Author: Sam Rushing <rushing@nightmare.com>

# ======================================================================
# Copyright 1996 by Sam Rushing
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Sam
# Rushing not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================

"""Basic infrastructure for asynchronous socket service clients and servers.

There are only two ways to have a program on a single processor do "more
than one thing at a time".  Multi-threaded programming is the simplest and
most popular way to do it, but there is another very different technique,
that lets you have nearly all the advantages of multi-threading, without
actually using multiple threads. it's really only practical if your program
is largely I/O bound. If your program is CPU bound, then preemptive
scheduled threads are probably what you really need. Network servers are
rarely CPU-bound, however.

If your operating system supports the select() system call in its I/O
library (and nearly all do), then you can use it to juggle multiple
communication channels at once; doing other work while your I/O is taking
place in the "background."  Although this strategy can seem strange and
complex, especially at first, it is in many ways easier to understand and
control than multi-threaded programming. The module documented here solves
many of the difficult problems for you, making the task of building
sophisticated high-performance network servers and clients a snap.
"""

import select
import supervisor.medusa.text_socket as socket
import sys
import time

import os
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, \
     ENOTCONN, ESHUTDOWN, EINTR, EISCONN, errorcode

from supervisor.compat import as_string, as_bytes

try:
    socket_map
except NameError:
    socket_map = {}

class ExitNow(Exception):
    pass

def read(obj):
    try:
        obj.handle_read_event()
    except ExitNow:
        raise
    except:
        obj.handle_error()

def write(obj):
    try:
        obj.handle_write_event()
    except ExitNow:
        raise
    except:
        obj.handle_error()

def _exception (obj):
    try:
        obj.handle_expt_event()
    except ExitNow:
        raise
    except:
        obj.handle_error()

def readwrite(obj, flags):
    try:
        if flags & (select.POLLIN | select.POLLPRI):
            obj.handle_read_event()
        if flags & select.POLLOUT:
            obj.handle_write_event()
        if flags & (select.POLLERR | select.POLLHUP | select.POLLNVAL):
            obj.handle_expt_event()
    except ExitNow:
        raise
    except:
        obj.handle_error()

def poll(timeout=0.0, map=None):
    if map is None:
        map = socket_map
    if map:
        r = []; w = []; e = []
        for fd, obj in map.items():
            is_r = obj.readable()
            is_w = obj.writable()
            if is_r:
                r.append(fd)
            if is_w:
                w.append(fd)
            if is_r or is_w:
                e.append(fd)
        if [] == r == w == e:
            time.sleep(timeout)
        else:
            try:
                r, w, e = select.select(r, w, e, timeout)
            except select.error as err:
                if err.args[0] != EINTR:
                    raise
                else:
                    return

        for fd in r:
            obj = map.get(fd)
            if obj is None:
                continue
            read(obj)

        for fd in w:
            obj = map.get(fd)
            if obj is None:
                continue
            write(obj)

        for fd in e:
            obj = map.get(fd)
            if obj is None:
                continue
            _exception(obj)

def poll2(timeout=0.0, map=None):
    # Use the poll() support added to the select module in Python 2.0
    if map is None:
        map = socket_map
    if timeout is not None:
        # timeout is in milliseconds
        timeout = int(timeout*1000)
    pollster = select.poll()
    if map:
        for fd, obj in map.items():
            flags = 0
            if obj.readable():
                flags |= select.POLLIN | select.POLLPRI
            if obj.writable():
                flags |= select.POLLOUT
            if flags:
                # Only check for exceptions if object was either readable
                # or writable.
                flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL
                pollster.register(fd, flags)
        try:
            r = pollster.poll(timeout)
        except select.error as err:
            if err.args[0] != EINTR:
                raise
            r = []
        for fd, flags in r:
            obj = map.get(fd)
            if obj is None:
                continue
            readwrite(obj, flags)

poll3 = poll2                           # Alias for backward compatibility

def loop(timeout=30.0, use_poll=False, map=None, count=None):
    if map is None:
        map = socket_map

    if use_poll and hasattr(select, 'poll'):
        poll_fun = poll2
    else:
        poll_fun = poll

    if count is None:
        while map:
            poll_fun(timeout, map)

    else:
        while map and count > 0:
            poll_fun(timeout, map)
            count -= 1

class dispatcher:

    debug = False
    connected = False
    accepting = False
    closing = False
    addr = None

    def __init__(self, sock=None, map=None):
        if map is None:
            self._map = socket_map
        else:
            self._map = map

        if sock:
            self.set_socket(sock, map)
            # I think it should inherit this anyway
            self.socket.setblocking(0)
            self.connected = True
            # XXX Does the constructor require that the socket passed
            # be connected?
            try:
                self.addr = sock.getpeername()
            except socket.error:
                # The addr isn't crucial
                pass
        else:
            self.socket = None

    def __repr__(self):
        status = [self.__class__.__module__+"."+self.__class__.__name__]
        if self.accepting and self.addr:
            status.append('listening')
        elif self.connected:
            status.append('connected')
        if self.addr is not None:
            try:
                status.append('%s:%d' % self.addr)
            except TypeError:
                status.append(repr(self.addr))
        return '<%s at %#x>' % (' '.join(status), id(self))

    def add_channel(self, map=None):
        #self.log_info('adding channel %s' % self)
        if map is None:
            map = self._map
        map[self._fileno] = self

    def del_channel(self, map=None):
        fd = self._fileno
        if map is None:
            map = self._map
        if fd in map:
            #self.log_info('closing channel %d:%s' % (fd, self))
            del map[fd]
        self._fileno = None

    def create_socket(self, family, type):
        self.family_and_type = family, type
        self.socket = socket.socket(family, type)
        self.socket.setblocking(0)
        self._fileno = self.socket.fileno()
        self.add_channel()

    def set_socket(self, sock, map=None):
        self.socket = sock
##        self.__dict__['socket'] = sock
        self._fileno = sock.fileno()
        self.add_channel(map)

    def set_reuse_addr(self):
        # try to re-use a server port if possible
        try:
            self.socket.setsockopt(
                socket.SOL_SOCKET, socket.SO_REUSEADDR,
                self.socket.getsockopt(socket.SOL_SOCKET,
                                       socket.SO_REUSEADDR) | 1
                )
        except socket.error:
            pass

    # ==================================================
    # predicates for select()
    # these are used as filters for the lists of sockets
    # to pass to select().
    # ==================================================

    def readable(self):
        return True

    def writable(self):
        return True

    # ==================================================
    # socket object methods.
    # ==================================================

    def listen(self, num):
        self.accepting = True
        if os.name == 'nt' and num > 5:
            num = 1
        return self.socket.listen(num)

    def bind(self, addr):
        self.addr = addr
        return self.socket.bind(addr)

    def connect(self, address):
        self.connected = False
        err = self.socket.connect_ex(address)
        # XXX Should interpret Winsock return values
        if err in (EINPROGRESS, EALREADY, EWOULDBLOCK):
            return
        if err in (0, EISCONN):
            self.addr = address
            self.connected = True
            self.handle_connect()
        else:
            raise socket.error(err, errorcode[err])

    def accept(self):
        # XXX can return either an address pair or None
        try:
            conn, addr = self.socket.accept()
            return conn, addr
        except socket.error as why:
            if why.args[0] == EWOULDBLOCK:
                pass
            else:
                raise

    def send(self, data):
        try:
            result = self.socket.send(data)
            return result
        except socket.error as why:
            if why.args[0] == EWOULDBLOCK:
                return 0
            else:
                raise

    def recv(self, buffer_size):
        try:
            data = self.socket.recv(buffer_size)
            if not data:
                # a closed connection is indicated by signaling
                # a read condition, and having recv() return 0.
                self.handle_close()
                return ''
            else:
                return data
        except socket.error as why:
            # winsock sometimes throws ENOTCONN
            if why.args[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:
                self.handle_close()
                return ''
            else:
                raise

    def close(self):
        self.del_channel()
        self.socket.close()

    # cheap inheritance, used to pass all other attribute
    # references to the underlying socket object.
    def __getattr__(self, attr):
        return getattr(self.socket, attr)

    # log and log_info may be overridden to provide more sophisticated
    # logging and warning methods. In general, log is for 'hit' logging
    # and 'log_info' is for informational, warning and error logging.

    def log(self, message):
        sys.stderr.write('log: %s\n' % str(message))

    def log_info(self, message, type='info'):
        if __debug__ or type != 'info':
            print('%s: %s' % (type, message))

    def handle_read_event(self):
        if self.accepting:
            # for an accepting socket, getting a read implies
            # that we are connected
            if not self.connected:
                self.connected = True
            self.handle_accept()
        elif not self.connected:
            self.handle_connect()
            self.connected = True
            self.handle_read()
        else:
            self.handle_read()

    def handle_write_event(self):
        # getting a write implies that we are connected
        if not self.connected:
            self.handle_connect()
            self.connected = True
        self.handle_write()

    def handle_expt_event(self):
        self.handle_expt()

    def handle_error(self):
        nil, t, v, tbinfo = compact_traceback()

        # sometimes a user repr method will crash.
        try:
            self_repr = repr(self)
        except:
            self_repr = '<__repr__(self) failed for object at %0x>' % id(self)

        self.log_info(
            'uncaptured python exception, closing channel %s (%s:%s %s)' % (
                self_repr,
                t,
                v,
                tbinfo
                ),
            'error'
            )
        self.close()

    def handle_expt(self):
        self.log_info('unhandled exception', 'warning')

    def handle_read(self):
        self.log_info('unhandled read event', 'warning')

    def handle_write(self):
        self.log_info('unhandled write event', 'warning')

    def handle_connect(self):
        self.log_info('unhandled connect event', 'warning')

    def handle_accept(self):
        self.log_info('unhandled accept event', 'warning')

    def handle_close(self):
        self.log_info('unhandled close event', 'warning')
        self.close()

# ---------------------------------------------------------------------------
# adds simple buffered output capability, useful for simple clients.
# [for more sophisticated usage use asynchat.async_chat]
# ---------------------------------------------------------------------------

class dispatcher_with_send(dispatcher):

    def __init__(self, sock=None, map=None):
        dispatcher.__init__(self, sock, map)
        self.out_buffer = ''

    def initiate_send(self):
        num_sent = dispatcher.send(self, self.out_buffer[:512])
        self.out_buffer = self.out_buffer[num_sent:]

    def handle_write(self):
        self.initiate_send()

    def writable(self):
        return (not self.connected) or len(self.out_buffer)

    def send(self, data):
        if self.debug:
            self.log_info('sending %s' % repr(data))
        self.out_buffer = self.out_buffer + data
        self.initiate_send()

# ---------------------------------------------------------------------------
# used for debugging.
# ---------------------------------------------------------------------------

def compact_traceback():
    t, v, tb = sys.exc_info()
    tbinfo = []
    assert tb # Must have a traceback
    while tb:
        tbinfo.append((
            tb.tb_frame.f_code.co_filename,
            tb.tb_frame.f_code.co_name,
            str(tb.tb_lineno)
            ))
        tb = tb.tb_next

    # just to be safe
    del tb

    file, function, line = tbinfo[-1]
    info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo])
    return (file, function, line), t, v, info

def close_all(map=None):
    if map is None:
        map = socket_map
    for x in map.values():
        x.socket.close()
    map.clear()

# Asynchronous File I/O:
#
# After a little research (reading man pages on various unixen, and
# digging through the linux kernel), I've determined that select()
# isn't meant for doing asynchronous file i/o.
# Heartening, though - reading linux/mm/filemap.c shows that linux
# supports asynchronous read-ahead.  So _MOST_ of the time, the data
# will be sitting in memory for us already when we go to read it.
#
# What other OS's (besides NT) support async file i/o?  [VMS?]
#
# Regardless, this is useful for pipes, and stdin/stdout...

if os.name == 'posix':
    import fcntl

    class file_wrapper:
        # here we override just enough to make a file
        # look like a socket for the purposes of asyncore.

        def __init__(self, fd):
            self.fd = fd

        def recv(self, buffersize):
            return as_string(os.read(self.fd, buffersize))

        def send(self, s):
            return os.write(self.fd, as_bytes(s))

        read = recv
        write = send

        def close(self):
            os.close(self.fd)

        def fileno(self):
            return self.fd

    class file_dispatcher(dispatcher):

        def __init__(self, fd, map=None):
            dispatcher.__init__(self, None, map)
            self.connected = True
            self.set_file(fd)
            # set it to non-blocking mode
            flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
            flags |= os.O_NONBLOCK
            fcntl.fcntl(fd, fcntl.F_SETFL, flags)

        def set_file(self, fd):
            self._fileno = fd
            self.socket = file_wrapper(fd)
            self.add_channel()

########NEW FILE########
__FILENAME__ = auth_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: auth_handler.py,v 1.6 2002/11/25 19:40:23 akuchling Exp $'

# support for 'basic' authentication.

import re
import time

from supervisor.compat import encodestring, decodestring
from supervisor.compat import md5
from supervisor.compat import as_string, as_bytes
from supervisor.compat import print_function

import supervisor.medusa.counter as counter
import supervisor.medusa.default_handler as default_handler

get_header = default_handler.get_header

import supervisor.medusa.producers as producers

# This is a 'handler' that wraps an authorization method
# around access to the resources normally served up by
# another handler.

# does anyone support digest authentication? (rfc2069)

class auth_handler:
    def __init__ (self, dict, handler, realm='default'):
        self.authorizer = dictionary_authorizer (dict)
        self.handler = handler
        self.realm = realm
        self.pass_count = counter.counter()
        self.fail_count = counter.counter()

    def match (self, request):
        # by default, use the given handler's matcher
        return self.handler.match (request)

    def handle_request (self, request):
        # authorize a request before handling it...
        scheme = get_header (AUTHORIZATION, request.header)

        if scheme:
            scheme = scheme.lower()
            if scheme == 'basic':
                cookie = get_header (AUTHORIZATION, request.header, 2)
                try:
                    decoded = as_string(decodestring(as_bytes(cookie)))
                except:
                    print_function('malformed authorization info <%s>' % cookie)
                    request.error (400)
                    return
                auth_info = decoded.split(':', 1)
                if self.authorizer.authorize (auth_info):
                    self.pass_count.increment()
                    request.auth_info = auth_info
                    self.handler.handle_request (request)
                else:
                    self.handle_unauthorized (request)
            #elif scheme == 'digest':
            #       print 'digest: ',AUTHORIZATION.group(2)
            else:
                print('unknown/unsupported auth method: %s' % scheme)
                self.handle_unauthorized(request)
        else:
            # list both?  prefer one or the other?
            # you could also use a 'nonce' here. [see below]
            #auth = 'Basic realm="%s" Digest realm="%s"' % (self.realm, self.realm)
            #nonce = self.make_nonce (request)
            #auth = 'Digest realm="%s" nonce="%s"' % (self.realm, nonce)
            #request['WWW-Authenticate'] = auth
            #print 'sending header: %s' % request['WWW-Authenticate']
            self.handle_unauthorized (request)

    def handle_unauthorized (self, request):
        # We are now going to receive data that we want to ignore.
        # to ignore the file data we're not interested in.
        self.fail_count.increment()
        request.channel.set_terminator (None)
        request['Connection'] = 'close'
        request['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
        request.error (401)

    def make_nonce (self, request):
        """A digest-authentication <nonce>, constructed as suggested in RFC 2069"""
        ip = request.channel.server.ip
        now = str(long(time.time()))
        if now[-1:] == 'L':
            now = now[:-1]
        private_key = str (id (self))
        nonce = ':'.join([ip, now, private_key])
        return self.apply_hash (nonce)

    def apply_hash (self, s):
        """Apply MD5 to a string <s>, then wrap it in base64 encoding."""
        m = md5()
        m.update (s)
        d = m.digest()
        # base64.encodestring tacks on an extra linefeed.
        return encodestring (d)[:-1]

    def status (self):
        # Thanks to mwm@contessa.phone.net (Mike Meyer)
        r = [
                producers.simple_producer (
                        '<li>Authorization Extension : '
                        '<b>Unauthorized requests:</b> %s<ul>' % self.fail_count
                        )
                ]
        if hasattr (self.handler, 'status'):
            r.append (self.handler.status())
        r.append (
                producers.simple_producer ('</ul>')
                )
        return producers.composite_producer(r)

class dictionary_authorizer:
    def __init__ (self, dict):
        self.dict = dict

    def authorize (self, auth_info):
        [username, password] = auth_info
        if username in self.dict and self.dict[username] == password:
            return 1
        else:
            return 0

AUTHORIZATION = re.compile (
        #               scheme  challenge
        'Authorization: ([^ ]+) (.*)',
        re.IGNORECASE
        )

########NEW FILE########
__FILENAME__ = chat_server
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1997-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID = '$Id: chat_server.py,v 1.4 2002/03/20 17:37:48 amk Exp $'

VERSION = RCS_ID.split()[2]

import supervisor.medusa.text_socket as socket
import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat
import supervisor.medusa.status_handler as status_handler

class chat_channel (asynchat.async_chat):

    def __init__ (self, server, sock, addr):
        asynchat.async_chat.__init__ (self, sock)
        self.server = server
        self.addr = addr
        self.set_terminator ('\r\n')
        self.data = ''
        self.nick = None
        self.push ('nickname?: ')

    def collect_incoming_data (self, data):
        self.data = self.data + data

    def found_terminator (self):
        line = self.data
        self.data = ''
        if self.nick is None:
            self.nick = line.split()[0]
            if not self.nick:
                self.nick = None
                self.push ('huh? gimme a nickname: ')
            else:
                self.greet()
        else:
            if not line:
                pass
            elif line[0] != '/':
                self.server.push_line (self, line)
            else:
                self.handle_command (line)

    def greet (self):
        self.push ('Hello, %s\r\n' % self.nick)
        num_channels = len(self.server.channels)-1
        if num_channels == 0:
            self.push ('[Kinda lonely in here... you\'re the only caller!]\r\n')
        else:
            self.push ('[There are %d other callers]\r\n' % (len(self.server.channels)-1))
            nicks = [x.get_nick() for x in list(self.server.channels.keys())]
            self.push ('\r\n  '.join(nicks) + '\r\n')
            self.server.push_line (self, '[joined]')

    def handle_command (self, command):
        command_line = command.split()
        name = 'cmd_%s' % command_line[0][1:]
        if hasattr (self, name):
            # make sure it's a method...
            method = getattr (self, name)
            if type(method) == type(self.handle_command):
                method (command_line[1:])
            else:
                self.push ('unknown command: %s' % command_line[0])

    def cmd_quit (self, args):
        self.server.push_line (self, '[left]')
        self.push ('Goodbye!\r\n')
        self.close_when_done()

    # alias for '/quit' - '/q'
    cmd_q = cmd_quit

    def push_line (self, nick, line):
        self.push ('%s: %s\r\n' % (nick, line))

    def handle_close (self):
        self.close()

    def close (self):
        del self.server.channels[self]
        asynchat.async_chat.close (self)

    def get_nick (self):
        if self.nick is not None:
            return self.nick
        else:
            return 'Unknown'

class chat_server (asyncore.dispatcher):

    SERVER_IDENT = 'Chat Server (V%s)' % VERSION

    channel_class = chat_channel

    spy = 1

    def __init__ (self, ip='', port=8518):
        asyncore.dispatcher.__init__(self)
        self.port = port
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.bind ((ip, port))
        print('%s started on port %d' % (self.SERVER_IDENT, port))
        self.listen (5)
        self.channels = {}
        self.count = 0

    def handle_accept (self):
        conn, addr = self.accept()
        self.count += 1
        print('client #%d - %s:%d' % (self.count, addr[0], addr[1]))
        self.channels[self.channel_class (self, conn, addr)] = 1

    def push_line (self, from_channel, line):
        nick = from_channel.get_nick()
        if self.spy:
            print('%s: %s' % (nick, line))
        for c in self.channels.keys():
            if c is not from_channel:
                c.push ('%s: %s\r\n' % (nick, line))

    def status (self):
        lines = [
                '<h2>%s</h2>'                                           % self.SERVER_IDENT,
                '<br>Listening on Port: %d'                     % self.port,
                '<br><b>Total Sessions:</b> %d'         % self.count,
                '<br><b>Current Sessions:</b> %d'       % (len(self.channels))
                ]
        return status_handler.lines_producer (lines)

    def writable (self):
        return 0

if __name__ == '__main__':
    import sys

    if len(sys.argv) > 1:
        port = int(sys.argv[1])
    else:
        port = 8518

    s = chat_server ('', port)
    asyncore.loop()

########NEW FILE########
__FILENAME__ = counter
# -*- Mode: Python -*-

# It is tempting to add an __int__ method to this class, but it's not
# a good idea.  This class tries to gracefully handle integer
# overflow, and to hide this detail from both the programmer and the
# user.  Note that the __str__ method can be relied on for printing out
# the value of a counter:
#
# >>> print 'Total Client: %s' % self.total_clients
#
# If you need to do arithmetic with the value, then use the 'as_long'
# method, the use of long arithmetic is a reminder that the counter
# will overflow.

class counter:
    """general-purpose counter"""

    def __init__ (self, initial_value=0):
        self.value = initial_value

    def increment (self, delta=1):
        result = self.value
        try:
            self.value = self.value + delta
        except OverflowError:
            self.value = long(self.value) + delta
        return result

    def decrement (self, delta=1):
        result = self.value
        try:
            self.value = self.value - delta
        except OverflowError:
            self.value = long(self.value) - delta
        return result

    def as_long (self):
        return long(self.value)

    def __nonzero__ (self):
        return self.value != 0

    __bool__ = __nonzero__

    def __repr__ (self):
        return '<counter value=%s at %x>' % (self.value, id(self))

    def __str__ (self):
        s = str(long(self.value))
        if s[-1:] == 'L':
            s = s[:-1]
        return s


########NEW FILE########
__FILENAME__ = default_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1997 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID = '$Id: default_handler.py,v 1.8 2002/08/01 18:15:45 akuchling Exp $'

# standard python modules
import mimetypes
import re
import stat

# medusa modules
import supervisor.medusa.http_date as http_date
import supervisor.medusa.http_server as http_server
import supervisor.medusa.status_handler as status_handler
import supervisor.medusa.producers as producers

unquote = http_server.unquote

# This is the 'default' handler.  it implements the base set of
# features expected of a simple file-delivering HTTP server.  file
# services are provided through a 'filesystem' object, the very same
# one used by the FTP server.
#
# You can replace or modify this handler if you want a non-standard
# HTTP server.  You can also derive your own handler classes from
# it.
#
# support for handling POST requests is available in the derived
# class <default_with_post_handler>, defined below.
#

from supervisor.medusa.counter import counter

class default_handler:

    valid_commands = ['GET', 'HEAD']

    IDENT = 'Default HTTP Request Handler'

    # Pathnames that are tried when a URI resolves to a directory name
    directory_defaults = [
            'index.html',
            'default.html'
            ]

    default_file_producer = producers.file_producer

    def __init__ (self, filesystem):
        self.filesystem = filesystem
        # count total hits
        self.hit_counter = counter()
        # count file deliveries
        self.file_counter = counter()
        # count cache hits
        self.cache_counter = counter()

    hit_counter = 0

    def __repr__ (self):
        return '<%s (%s hits) at %x>' % (
                self.IDENT,
                self.hit_counter,
                id (self)
                )

    # always match, since this is a default
    def match (self, request):
        return 1

    # handle a file request, with caching.

    def handle_request (self, request):

        if request.command not in self.valid_commands:
            request.error (400) # bad request
            return

        self.hit_counter.increment()

        path, params, query, fragment = request.split_uri()

        if '%' in path:
            path = unquote (path)

        # strip off all leading slashes
        while path and path[0] == '/':
            path = path[1:]

        if self.filesystem.isdir (path):
            if path and path[-1] != '/':
                request['Location'] = 'http://%s/%s/' % (
                        request.channel.server.server_name,
                        path
                        )
                request.error (301)
                return

            # we could also generate a directory listing here,
            # may want to move this into another method for that
            # purpose
            found = 0
            if path and path[-1] != '/':
                path += '/'
            for default in self.directory_defaults:
                p = path + default
                if self.filesystem.isfile (p):
                    path = p
                    found = 1
                    break
            if not found:
                request.error (404) # Not Found
                return

        elif not self.filesystem.isfile (path):
            request.error (404) # Not Found
            return

        file_length = self.filesystem.stat (path)[stat.ST_SIZE]

        ims = get_header_match (IF_MODIFIED_SINCE, request.header)

        length_match = 1
        if ims:
            length = ims.group (4)
            if length:
                try:
                    length = int(length)
                    if length != file_length:
                        length_match = 0
                except:
                    pass

        ims_date = 0

        if ims:
            ims_date = http_date.parse_http_date (ims.group (1))

        try:
            mtime = self.filesystem.stat (path)[stat.ST_MTIME]
        except:
            request.error (404)
            return

        if length_match and ims_date:
            if mtime <= ims_date:
                request.reply_code = 304
                request.done()
                self.cache_counter.increment()
                return
        try:
            file = self.filesystem.open (path, 'rb')
        except IOError:
            request.error (404)
            return

        request['Last-Modified'] = http_date.build_http_date (mtime)
        request['Content-Length'] = file_length
        self.set_content_type (path, request)

        if request.command == 'GET':
            request.push (self.default_file_producer (file))

        self.file_counter.increment()
        request.done()

    def set_content_type (self, path, request):
        typ, encoding = mimetypes.guess_type(path)
        if typ is not None:
            request['Content-Type'] = typ
        else:
            # TODO: test a chunk off the front of the file for 8-bit
            # characters, and use application/octet-stream instead.
            request['Content-Type'] = 'text/plain'

    def status (self):
        return producers.simple_producer (
                '<li>%s' % status_handler.html_repr (self)
                + '<ul>'
                + '  <li><b>Total Hits:</b> %s'                 % self.hit_counter
                + '  <li><b>Files Delivered:</b> %s'    % self.file_counter
                + '  <li><b>Cache Hits:</b> %s'                 % self.cache_counter
                + '</ul>'
                )

# HTTP/1.0 doesn't say anything about the "; length=nnnn" addition
# to this header.  I suppose its purpose is to avoid the overhead
# of parsing dates...
IF_MODIFIED_SINCE = re.compile (
        'If-Modified-Since: ([^;]+)((; length=([0-9]+)$)|$)',
        re.IGNORECASE
        )

USER_AGENT = re.compile ('User-Agent: (.*)', re.IGNORECASE)

CONTENT_TYPE = re.compile (
        r'Content-Type: ([^;]+)((; boundary=([A-Za-z0-9\'\(\)+_,./:=?-]+)$)|$)',
        re.IGNORECASE
        )

get_header = http_server.get_header
get_header_match = http_server.get_header_match

def get_extension (path):
    dirsep = path.rfind('/')
    dotsep = path.rfind('.')
    if dotsep > dirsep:
        return path[dotsep+1:]
    else:
        return ''

########NEW FILE########
__FILENAME__ = publish
# -*- Mode: Python -*-

# Demonstrates use of the auth and put handlers to support publishing
# web pages via HTTP.

# It is also possible to set up the ftp server to do essentially the
# same thing.

# Security Note: Using HTTP with the 'Basic' authentication scheme is
# only slightly more secure than using FTP: both techniques involve
# sending a unencrypted password of the network (http basic auth
# base64-encodes the username and password).  The 'Digest' scheme is
# much more secure, but not widely supported yet. <sigh>

from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import default_handler
from supervisor.medusa import http_server
from supervisor.medusa import put_handler
from supervisor.medusa import auth_handler
from supervisor.medusa import filesys

# For this demo, we'll just use a dictionary of usernames/passwords.
# You can of course use anything that supports the mapping interface,
# and it would be pretty easy to set this up to use the crypt module
# on unix.

users = { 'mozart' : 'jupiter', 'beethoven' : 'pastoral' }

# The filesystem we will be giving access to
fs = filesys.os_filesystem('/home/medusa')

# The 'default' handler - delivers files for the HTTP GET method.
dh = default_handler.default_handler(fs)

# Supports the HTTP PUT method...
ph = put_handler.put_handler(fs, '/.*')

# ... but be sure to wrap it with an auth handler:
ah = auth_handler.auth_handler(users, ph)

# Create a Web Server
hs = http_server.http_server(ip='', port=8080)

# install the handlers we created:

hs.install_handler(dh) # for GET
hs.install_handler(ah) # for PUT

asyncore.loop()

########NEW FILE########
__FILENAME__ = script_server
# -*- Mode: Python -*-

import re, sys
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import http_server
from supervisor.medusa import default_handler
from supervisor.medusa import logger
from supervisor.medusa import script_handler
from supervisor.medusa import filesys

PUBLISHING_ROOT='/home/medusa'
CONTENT_LENGTH = re.compile ('Content-Length: ([0-9]+)', re.IGNORECASE)

class sample_input_collector:
    def __init__ (self, request, length):
        self.request = request
        self.length = length

    def collect_incoming_data (self, data):
        print('data from %s: <%s>' % (self.request, repr(data)))


class post_script_handler (script_handler.script_handler):

    def handle_request (self, request):
        if request.command == 'post':
            cl = default_handler.get_header(CONTENT_LENGTH, request.header)
            ic = sample_input_collector(request, cl)
            request.collector = ic
            print(request.header)

        return script_handler.script_handler.handle_request (self, request)

lg = logger.file_logger (sys.stdout)
fs = filesys.os_filesystem (PUBLISHING_ROOT)
dh = default_handler.default_handler (fs)
ph = post_script_handler (fs)
hs = http_server.http_server ('', 8081, logger_object = lg)

hs.install_handler (dh)
hs.install_handler (ph)

asyncore.loop()

########NEW FILE########
__FILENAME__ = simple_anon_ftpd
# -*- Mode: Python -*-

from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import ftp_server

# create a 'dummy' authorizer (one that lets everyone in) that returns
# a read-only filesystem rooted at '/home/ftp'

authorizer = ftp_server.dummy_authorizer('/home/ftp')

# Create an ftp server using this authorizer, running on port 8021
# [the standard port is 21, but you are probably already running
#  a server there]

fs = ftp_server.ftp_server(authorizer, port=8021)

# Run the async main loop
asyncore.loop()

# to test this server, try
# $ ftp myhost 8021
# when using the standard bsd ftp client,
# $ ncftp -p 8021 myhost
# when using ncftp, and
# ftp://myhost:8021/
# from a web browser.


########NEW FILE########
__FILENAME__ = start_medusa
# -*- Mode: Python -*-

#
# Sample/Template Medusa Startup Script.
#
# This file acts as a configuration file and startup script for Medusa.
#
# You should make a copy of this file, then add, change or comment out
# appropriately.  Then you can start up the server by simply typing
#
# $ python start_medusa.py
#

import os
import sys

from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import http_server
from supervisor.medusa import ftp_server
from supervisor.medusa import chat_server
from supervisor.medusa import monitor
from supervisor.medusa import filesys
from supervisor.medusa import default_handler
from supervisor.medusa import status_handler
from supervisor.medusa import resolver
from supervisor.medusa import logger

if len(sys.argv) > 1:
    # process a few convenient arguments
    [HOSTNAME, IP_ADDRESS, PUBLISHING_ROOT] = sys.argv[1:]
else:
    HOSTNAME                        = 'www.nightmare.com'
    # This is the IP address of the network interface you want
    # your servers to be visible from.  This can be changed to ''
    # to listen on all interfaces.
    IP_ADDRESS                      = ''

    # Root of the http and ftp server's published filesystems.
    PUBLISHING_ROOT         = '/home/www'

HTTP_PORT               = 8080 # The standard port is 80
FTP_PORT                = 8021 # The standard port is 21
CHAT_PORT               = 8888
MONITOR_PORT    = 9999

# ===========================================================================
# Caching DNS Resolver
# ===========================================================================
# The resolver is used to resolve incoming IP address (for logging),
# and also to resolve hostnames for HTTP Proxy requests.  I recommend
# using a nameserver running on the local machine, but you can also
# use a remote nameserver.

rs = resolver.caching_resolver ('127.0.0.1')

# ===========================================================================
# Logging.
# ===========================================================================

# There are several types of logging objects. Multiple loggers may be combined,
# See 'logger.py' for more details.

# This will log to stdout:
lg = logger.file_logger (sys.stdout)

# This will log to syslog:
#lg = logger.syslog_logger ('/dev/log')

# This will wrap the logger so that it will
#  1) keep track of the last 500 entries
#  2) display an entry in the status report with a hyperlink
#     to view these log entries.
#
#  If you decide to comment this out, be sure to remove the
#  logger object from the list of status objects below.
#

lg = status_handler.logger_for_status (lg)

# ===========================================================================
# Filesystem Object.
# ===========================================================================
# An abstraction for the file system.  Filesystem objects can be
# combined and implemented in interesting ways.  The default type
# simply remaps a directory to root.

fs = filesys.os_filesystem (PUBLISHING_ROOT)

# ===========================================================================
# Default HTTP handler
# ===========================================================================

# The 'default' handler for the HTTP server is one that delivers
# files normally - this is the expected behavior of a web server.
# Note that you needn't use it:  Your web server might not want to
# deliver files!

# This default handler uses the filesystem object we just constructed.

dh = default_handler.default_handler (fs)

# ===========================================================================
# HTTP Server
# ===========================================================================
hs = http_server.http_server (IP_ADDRESS, HTTP_PORT, rs, lg)

# Here we install the default handler created above.
hs.install_handler (dh)

# ===========================================================================
# Unix user `public_html' directory support
# ===========================================================================
if os.name == 'posix':
    from supervisor.medusa import unix_user_handler
    uh = unix_user_handler.unix_user_handler ('public_html')
    hs.install_handler (uh)

# ===========================================================================
# FTP Server
# ===========================================================================

# Here we create an 'anonymous' ftp server.
# Note: the ftp server is read-only by default. [in this mode, all
# 'write-capable' commands are unavailable]

ftp = ftp_server.ftp_server (
        ftp_server.anon_authorizer (
                PUBLISHING_ROOT
                ),
        ip=IP_ADDRESS,
        port=FTP_PORT,
        resolver=rs,
        logger_object=lg
        )

# ===========================================================================
# Monitor Server:
# ===========================================================================

# This creates a secure monitor server, binding to the loopback
# address on port 9999, with password 'fnord'.  The monitor server
# can be used to examine and control the server while it is running.
# If you wish to access the server from another machine, you will
# need to use '' or some other IP instead of '127.0.0.1'.
ms = monitor.secure_monitor_server ('fnord', '127.0.0.1', MONITOR_PORT)

# ===========================================================================
# Chat Server
# ===========================================================================

# The chat server is a simple IRC-like server: It is meant as a
# demonstration of how to write new servers and plug them into medusa.
# It's a very simple server (it took about 2 hours to write), but it
# could be easily extended. For example, it could be integrated with
# the web server, perhaps providing navigational tools to browse
# through a series of discussion groups, listing the number of current
# users, authentication, etc...

cs = chat_server.chat_server (IP_ADDRESS, CHAT_PORT)

# ===========================================================================
# Status Handler
# ===========================================================================

# These are objects that can report their status via the HTTP server.
# You may comment out any of these, or add more of your own.  The only
# requirement for a 'status-reporting' object is that it have a method
# 'status' that will return a producer, which will generate an HTML
# description of the status of the object.

status_objects = [
        hs,
        ftp,
        ms,
        cs,
        rs,
        lg
        ]

# Create a status handler.  By default it binds to the URI '/status'...
sh = status_handler.status_extension(status_objects)
# ... and install it on the web server.
hs.install_handler (sh)

# become 'nobody'
try:
    if os.name == 'posix':
        if hasattr (os, 'seteuid'):
            import pwd
            [uid, gid] = pwd.getpwnam ('nobody')[2:4]
            os.setegid (gid)
            os.seteuid (uid)
except Exception:
    pass

# Finally, start up the server loop!  This loop will not exit until
# all clients and servers are closed.  You may cleanly shut the system
# down by sending SIGINT (a.k.a. KeyboardInterrupt).
asyncore.loop()

########NEW FILE########
__FILENAME__ = winFTPserver
#
# winFTPServer.py -- FTP server that uses Win32 user API
#
# Contributed by John Abel
#
# For it to authenticate users correctly, the user running the
# script must be added to the security policy "Act As Part Of The OS".
# This is needed for the LogonUser to work.  A pain, but something that MS
# forgot to mention in the API.


import win32security, win32con, win32api, win32net
import ntsecuritycon, pywintypes
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import ftp_server, filesys

class Win32Authorizer:


    def authorize (self, channel, userName, passWord):
        self.AdjustPrivilege( ntsecuritycon.SE_CHANGE_NOTIFY_NAME )
        self.AdjustPrivilege( ntsecuritycon.SE_ASSIGNPRIMARYTOKEN_NAME )
        self.AdjustPrivilege( ntsecuritycon.SE_TCB_NAME )
        try:
            logonHandle = win32security.LogonUser( userName,
                                                   None,
                                                   passWord,
                                                    win32con.LOGON32_LOGON_INTERACTIVE,
                                                    win32con.LOGON32_PROVIDER_DEFAULT )
        except pywintypes.error, ErrorMsg:
            return 0, ErrorMsg.args[2], None

        userInfo = win32net.NetUserGetInfo( None, userName, 1 )

        return 1, 'Login successful', filesys.os_filesystem( userInfo[ 'home_dir' ] )

    def AdjustPrivilege( self, priv ):
        flags = ntsecuritycon.TOKEN_ADJUST_PRIVILEGES | ntsecuritycon.TOKEN_QUERY
        htoken =  win32security.OpenProcessToken(win32api.GetCurrentProcess(), flags)
        id = win32security.LookupPrivilegeValue(None, priv)
        newPrivileges = [(id, ntsecuritycon.SE_PRIVILEGE_ENABLED)]
        win32security.AdjustTokenPrivileges(htoken, 0, newPrivileges)

def start_Server():
#    ftpServ = ftp_server.ftp_server( ftp_server.anon_authorizer( "D:\MyDocuments\MyDownloads"), port=21 )
    ftpServ = ftp_server.ftp_server( Win32Authorizer(), port=21 )
    asyncore.loop()

if __name__ == "__main__":
    print "Starting FTP Server"
    start_Server()

########NEW FILE########
__FILENAME__ = event_loop
# -*- Mode: Python -*-

# This is an alternative event loop that supports 'schedulable events'.
# You can specify an event callback to take place after <n> seconds.

# Important usage note: The granularity of the time-check is limited
# by the <timeout> argument to 'go()'; if there is little or no
# activity and you specify a 30-second timeout interval, then the
# schedule of events may only be checked at those 30-second intervals.
# In other words, if you need 1-second resolution, you will have to
# poll at 1-second intervals.  This facility is more useful for longer
# timeouts ("if the channel doesn't close in 5 minutes, then forcibly
# close it" would be a typical usage).

import supervisor.medusa.asyncore_25 as asyncore
import bisect
import time

socket_map = asyncore.socket_map

class event_loop:

    def __init__ (self):
        self.events = []
        self.num_channels = 0
        self.max_channels = 0

    def go (self, timeout=30.0, granularity=15):
        global socket_map
        last_event_check = 0
        while socket_map:
            now = int(time.time())
            if (now - last_event_check) >= granularity:
                last_event_check = now
                fired = []
                # yuck. i want my lisp.
                i = j = 0
                while i < len(self.events):
                    when, what = self.events[i]
                    if now >= when:
                        fired.append (what)
                        j = i + 1
                    else:
                        break
                    i += 1
                if fired:
                    self.events = self.events[j:]
                    for what in fired:
                        what (self, now)
            # sample the number of channels
            n = len(asyncore.socket_map)
            self.num_channels = n
            if n > self.max_channels:
                self.max_channels = n
            asyncore.poll (timeout)

    def schedule (self, delta, callback):
        now = int (time.time())
        bisect.insort (self.events, (now + delta, callback))

    def __len__ (self):
        return len(self.events)

class test (asyncore.dispatcher):

    def __init__ (self):
        asyncore.dispatcher.__init__ (self)

    def handle_connect (self):
        print('Connected!')

    def writable (self):
        return not self.connected

    def connect_timeout_callback (self, event_loop, when):
        if not self.connected:
            print('Timeout on connect')
            self.close()

    def periodic_thing_callback (self, event_loop, when):
        print('A Periodic Event has Occurred!')
        # re-schedule it.
        event_loop.schedule (self, 15, self.periodic_thing_callback)

if __name__ == '__main__':
    import supervisor.medusa.text_socket as socket
    el = event_loop()
    t = test ()
    t.create_socket (socket.AF_INET, socket.SOCK_STREAM)
    el.schedule (10, t.connect_timeout_callback)
    el.schedule (15, t.periodic_thing_callback)
    t.connect (('squirl', 80))
    el.go(1.0)

########NEW FILE########
__FILENAME__ = filesys
# -*- Mode: Python -*-
#       $Id: filesys.py,v 1.9 2003/12/24 16:10:56 akuchling Exp $
#       Author: Sam Rushing <rushing@nightmare.com>
#
# Generic filesystem interface.
#

# We want to provide a complete wrapper around any and all
# filesystem operations.

# this class is really just for documentation,
# identifying the API for a filesystem object.

# opening files for reading, and listing directories, should
# return a producer.

from supervisor.compat import long

class abstract_filesystem:
    def __init__ (self):
        pass

    def current_directory (self):
        """Return a string representing the current directory."""
        pass

    def listdir (self, path, long=0):
        """Return a listing of the directory at 'path' The empty string
        indicates the current directory.  If 'long' is set, instead
        return a list of (name, stat_info) tuples
        """
        pass

    def open (self, path, mode):
        """Return an open file object"""
        pass

    def stat (self, path):
        """Return the equivalent of os.stat() on the given path."""
        pass

    def isdir (self, path):
        """Does the path represent a directory?"""
        pass

    def isfile (self, path):
        """Does the path represent a plain file?"""
        pass

    def cwd (self, path):
        """Change the working directory."""
        pass

    def cdup (self):
        """Change to the parent of the current directory."""
        pass


    def longify (self, path):
        """Return a 'long' representation of the filename
        [for the output of the LIST command]"""
        pass

# standard wrapper around a unix-like filesystem, with a 'false root'
# capability.

# security considerations: can symbolic links be used to 'escape' the
# root?  should we allow it?  if not, then we could scan the
# filesystem on startup, but that would not help if they were added
# later.  We will probably need to check for symlinks in the cwd method.

# what to do if wd is an invalid directory?

import os
import stat
import re

def safe_stat (path):
    try:
        return path, os.stat (path)
    except:
        return None

class os_filesystem:
    path_module = os.path

    # set this to zero if you want to disable pathname globbing.
    # [we currently don't glob, anyway]
    do_globbing = 1

    def __init__ (self, root, wd='/'):
        self.root = root
        self.wd = wd

    def current_directory (self):
        return self.wd

    def isfile (self, path):
        p = self.normalize (self.path_module.join (self.wd, path))
        return self.path_module.isfile (self.translate(p))

    def isdir (self, path):
        p = self.normalize (self.path_module.join (self.wd, path))
        return self.path_module.isdir (self.translate(p))

    def cwd (self, path):
        p = self.normalize (self.path_module.join (self.wd, path))
        translated_path = self.translate(p)
        if not self.path_module.isdir (translated_path):
            return 0
        else:
            old_dir = os.getcwd()
            # temporarily change to that directory, in order
            # to see if we have permission to do so.
            can = 0
            try:
                try:
                    os.chdir (translated_path)
                    can = 1
                    self.wd = p
                except:
                    pass
            finally:
                if can:
                    os.chdir (old_dir)
            return can

    def cdup (self):
        return self.cwd ('..')

    def listdir (self, path, long=0):
        p = self.translate (path)
        # I think we should glob, but limit it to the current
        # directory only.
        ld = os.listdir (p)
        if not long:
            return list_producer (ld, None)
        else:
            old_dir = os.getcwd()
            try:
                os.chdir (p)
                # if os.stat fails we ignore that file.
                result = [_f for _f in map (safe_stat, ld) if _f]
            finally:
                os.chdir (old_dir)
            return list_producer (result, self.longify)

    # TODO: implement a cache w/timeout for stat()
    def stat (self, path):
        p = self.translate (path)
        return os.stat (p)

    def open (self, path, mode):
        p = self.translate (path)
        return open (p, mode)

    def unlink (self, path):
        p = self.translate (path)
        return os.unlink (p)

    def mkdir (self, path):
        p = self.translate (path)
        return os.mkdir (p)

    def rmdir (self, path):
        p = self.translate (path)
        return os.rmdir (p)

    def rename(self, src, dst):
        return os.rename(self.translate(src),self.translate(dst))

    # utility methods
    def normalize (self, path):
        # watch for the ever-sneaky '/+' path element
        path = re.sub('/+', '/', path)
        p = self.path_module.normpath (path)
        # remove 'dangling' cdup's.
        if len(p) > 2 and p[:3] == '/..':
            p = '/'
        return p

    def translate (self, path):
        # we need to join together three separate
        # path components, and do it safely.
        # <real_root>/<current_directory>/<path>
        # use the operating system's path separator.
        path = os.sep.join(path.split('/'))
        p = self.normalize (self.path_module.join (self.wd, path))
        p = self.normalize (self.path_module.join (self.root, p[1:]))
        return p

    def longify (self, path_stat_info_tuple):
        (path, stat_info) = path_stat_info_tuple
        return unix_longify (path, stat_info)

    def __repr__ (self):
        return '<unix-style fs root:%s wd:%s>' % (
                self.root,
                self.wd
                )

if os.name == 'posix':

    class unix_filesystem (os_filesystem):
        pass

    class schizophrenic_unix_filesystem (os_filesystem):
        PROCESS_UID     = os.getuid()
        PROCESS_EUID    = os.geteuid()
        PROCESS_GID     = os.getgid()
        PROCESS_EGID    = os.getegid()

        def __init__ (self, root, wd='/', persona=(None, None)):
            os_filesystem.__init__ (self, root, wd)
            self.persona = persona

        def become_persona (self):
            if self.persona is not (None, None):
                uid, gid = self.persona
                # the order of these is important!
                os.setegid (gid)
                os.seteuid (uid)

        def become_nobody (self):
            if self.persona is not (None, None):
                os.seteuid (self.PROCESS_UID)
                os.setegid (self.PROCESS_GID)

        # cwd, cdup, open, listdir
        def cwd (self, path):
            try:
                self.become_persona()
                return os_filesystem.cwd (self, path)
            finally:
                self.become_nobody()

        def cdup (self):
            try:
                self.become_persona()
                return os_filesystem.cdup (self)
            finally:
                self.become_nobody()

        def open (self, filename, mode):
            try:
                self.become_persona()
                return os_filesystem.open (self, filename, mode)
            finally:
                self.become_nobody()

        def listdir (self, path, long=0):
            try:
                self.become_persona()
                return os_filesystem.listdir (self, path, long)
            finally:
                self.become_nobody()

# For the 'real' root, we could obtain a list of drives, and then
# use that.  Doesn't win32 provide such a 'real' filesystem?
# [yes, I think something like this "\\.\c\windows"]

class msdos_filesystem (os_filesystem):
    def longify (self, path_stat_info_tuple):
        (path, stat_info) = path_stat_info_tuple
        return msdos_longify (path, stat_info)

# A merged filesystem will let you plug other filesystems together.
# We really need the equivalent of a 'mount' capability - this seems
# to be the most general idea.  So you'd use a 'mount' method to place
# another filesystem somewhere in the hierarchy.

# Note: this is most likely how I will handle ~user directories
# with the http server.

class merged_filesystem:
    def __init__ (self, *fsys):
        pass

# this matches the output of NT's ftp server (when in
# MSDOS mode) exactly.

def msdos_longify (file, stat_info):
    if stat.S_ISDIR (stat_info[stat.ST_MODE]):
        dir = '<DIR>'
    else:
        dir = '     '
    date = msdos_date (stat_info[stat.ST_MTIME])
    return '%s       %s %8d %s' % (
            date,
            dir,
            stat_info[stat.ST_SIZE],
            file
            )

def msdos_date (t):
    try:
        info = time.gmtime (t)
    except:
        info = time.gmtime (0)
    # year, month, day, hour, minute, second, ...
    hour = info[3]
    if hour > 11:
        merid = 'PM'
        hour -= 12
    else:
        merid = 'AM'
    return '%02d-%02d-%02d  %02d:%02d%s' % (
            info[1],
            info[2],
            info[0]%100,
            hour,
            info[4],
            merid
            )

months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

mode_table = {
        '0':'---',
        '1':'--x',
        '2':'-w-',
        '3':'-wx',
        '4':'r--',
        '5':'r-x',
        '6':'rw-',
        '7':'rwx'
        }

import time

def unix_longify (file, stat_info):
    # for now, only pay attention to the lower bits
    mode = ('%o' % stat_info[stat.ST_MODE])[-3:]
    mode = ''.join([mode_table[x] for x in mode])
    if stat.S_ISDIR (stat_info[stat.ST_MODE]):
        dirchar = 'd'
    else:
        dirchar = '-'
    date = ls_date (long(time.time()), stat_info[stat.ST_MTIME])
    return '%s%s %3d %-8d %-8d %8d %s %s' % (
            dirchar,
            mode,
            stat_info[stat.ST_NLINK],
            stat_info[stat.ST_UID],
            stat_info[stat.ST_GID],
            stat_info[stat.ST_SIZE],
            date,
            file
            )

# Emulate the unix 'ls' command's date field.
# it has two formats - if the date is more than 180
# days in the past, then it's like this:
# Oct 19  1995
# otherwise, it looks like this:
# Oct 19 17:33

def ls_date (now, t):
    try:
        info = time.gmtime (t)
    except:
        info = time.gmtime (0)
    # 15,600,000 == 86,400 * 180
    if (now - t) > 15600000:
        return '%s %2d  %d' % (
                months[info[1]-1],
                info[2],
                info[0]
                )
    else:
        return '%s %2d %02d:%02d' % (
                months[info[1]-1],
                info[2],
                info[3],
                info[4]
                )

# ===========================================================================
# Producers
# ===========================================================================

class list_producer:
    def __init__ (self, list, func=None):
        self.list = list
        self.func = func

    # this should do a pushd/popd
    def more (self):
        if not self.list:
            return ''
        else:
            # do a few at a time
            bunch = self.list[:50]
            if self.func is not None:
                bunch = map (self.func, bunch)
            self.list = self.list[50:]
            return '\r\n'.join(bunch) + '\r\n'


########NEW FILE########
__FILENAME__ = ftp_server
# -*- Mode: Python -*-

#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: ftp_server.py,v 1.11 2003/12/24 16:05:28 akuchling Exp $'

# An extensible, configurable, asynchronous FTP server.
#
# All socket I/O is non-blocking, however file I/O is currently
# blocking.  Eventually file I/O may be made non-blocking, too, if it
# seems necessary.  Currently the only CPU-intensive operation is
# getting and formatting a directory listing.  [this could be moved
# into another process/directory server, or another thread?]
#
# Only a subset of RFC 959 is implemented, but much of that RFC is
# vestigial anyway.  I've attempted to include the most commonly-used
# commands, using the feature set of wu-ftpd as a guide.

import os
import stat
import sys
import time

from supervisor.compat import letters

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat
import supervisor.medusa.text_socket as socket
from supervisor.medusa.producers import file_producer

# TODO: implement a directory listing cache.  On very-high-load
# servers this could save a lot of disk abuse, and possibly the
# work of computing emulated unix ls output.

# Potential security problem with the FTP protocol?  I don't think
# there's any verification of the origin of a data connection.  Not
# really a problem for the server (since it doesn't send the port
# command, except when in PASV mode) But I think a data connection
# could be spoofed by a program with access to a sniffer - it could
# watch for a PORT command to go over a command channel, and then
# connect to that port before the server does.

# Unix user id's:
# In order to support assuming the id of a particular user,
# it seems there are two options:
# 1) fork, and seteuid in the child
# 2) carefully control the effective uid around filesystem accessing
#    methods, using try/finally. [this seems to work]

VERSION = RCS_ID.split()[2]

from supervisor.medusa.counter import counter
import supervisor.medusa.producers as producers
import supervisor.medusa.status_handler as status_handler
import supervisor.medusa.logger as logger

class ftp_channel (asynchat.async_chat):

    # defaults for a reliable __repr__
    addr = ('unknown','0')

    # unset this in a derived class in order
    # to enable the commands in 'self.write_commands'
    read_only = 1
    write_commands = ['appe','dele','mkd','rmd','rnfr','rnto','stor','stou']

    restart_position = 0

    # comply with (possibly troublesome) RFC959 requirements
    # This is necessary to correctly run an active data connection
    # through a firewall that triggers on the source port (expected
    # to be 'L-1', or 20 in the normal case).
    bind_local_minus_one = 0

    def __init__ (self, server, conn, addr):
        self.server = server
        self.current_mode = 'a'
        self.addr = addr
        asynchat.async_chat.__init__ (self, conn)
        self.set_terminator ('\r\n')

        # client data port.  Defaults to 'the same as the control connection'.
        self.client_addr = (addr[0], 21)

        self.client_dc = None
        self.in_buffer = ''
        self.closing = 0
        self.passive_acceptor = None
        self.passive_connection = None
        self.filesystem = None
        self.authorized = 0
        # send the greeting
        self.respond (
                '220 %s FTP server (Medusa Async V%s [experimental]) ready.' % (
                        self.server.hostname,
                        VERSION
                        )
                )

#       def __del__ (self):
#               print 'ftp_channel.__del__()'

    # --------------------------------------------------
    # async-library methods
    # --------------------------------------------------

    def handle_expt (self):
        # this is handled below.  not sure what I could
        # do here to make that code less kludgish.
        pass

    def collect_incoming_data (self, data):
        self.in_buffer = self.in_buffer + data
        if len(self.in_buffer) > 4096:
            # silently truncate really long lines
            # (possible denial-of-service attack)
            self.in_buffer = ''

    def found_terminator (self):

        line = self.in_buffer

        if not len(line):
            return

        sp = line.find(' ')
        if sp != -1:
            line = [line[:sp], line[sp+1:]]
        else:
            line = [line]

        command = line[0].lower()
        # watch especially for 'urgent' abort commands.
        if command.find('abor') != -1:
            # strip off telnet sync chars and the like...
            while command and command[0] not in letters:
                command = command[1:]

        fun_name = 'cmd_%s' % command
        if command != 'pass':
            self.log ('<== %s' % repr(self.in_buffer)[1:-1])
        else:
            self.log ('<== %s' % line[0]+' <password>')
        self.in_buffer = ''
        if not hasattr (self, fun_name):
            self.command_not_understood (line[0])
            return
        if hasattr(self,'_rnfr_src') and fun_name!='cmd_rnto':
            del self._rnfr_src
            self.respond ('503 RNTO Command expected!')
            return

        fun = getattr (self, fun_name)
        if (not self.authorized) and (command not in ('user', 'pass', 'help', 'quit')):
            self.respond ('530 Please log in with USER and PASS')
        elif not self.check_command_authorization (command):
            self.command_not_authorized (command)
        else:
            try:
                fun(*(line,))
            except:
                self.server.total_exceptions.increment()
                (file, fun, line), t,v, tbinfo = asyncore.compact_traceback()
                if self.client_dc:
                    try:
                        self.client_dc.close()
                    except:
                        pass
                self.respond (
                        '451 Server Error: %s, %s: file: %s line: %s' % (
                                t,v,file,line,
                                )
                        )

    closed = 0
    def close (self):
        if not self.closed:
            self.closed = 1
            if self.passive_acceptor:
                self.passive_acceptor.close()
            if self.client_dc:
                self.client_dc.close()
            self.server.closed_sessions.increment()
            asynchat.async_chat.close (self)

    # --------------------------------------------------
    # filesystem interface functions.
    # override these to provide access control or perform
    # other functions.
    # --------------------------------------------------

    def cwd (self, line):
        return self.filesystem.cwd (line[1])

    def cdup (self, line):
        return self.filesystem.cdup()

    def open (self, path, mode):
        return self.filesystem.open (path, mode)

    # returns a producer
    def listdir (self, path, long=0):
        return self.filesystem.listdir (path, long)

    def get_dir_list (self, line, long=0):
        # we need to scan the command line for arguments to '/bin/ls'...
        args = line[1:]
        path_args = []
        for arg in args:
            if arg[0] != '-':
                path_args.append (arg)
            else:
                # ignore arguments
                pass
        if len(path_args) < 1:
            dir = '.'
        else:
            dir = path_args[0]
        return self.listdir (dir, long)

    # --------------------------------------------------
    # authorization methods
    # --------------------------------------------------

    def check_command_authorization (self, command):
        if command in self.write_commands and self.read_only:
            return 0
        else:
            return 1

    # --------------------------------------------------
    # utility methods
    # --------------------------------------------------

    def log (self, message):
        self.server.logger.log (
                self.addr[0],
                '%d %s' % (
                        self.addr[1], message
                        )
                )

    def respond (self, resp):
        self.log ('==> %s' % resp)
        self.push (resp + '\r\n')

    def command_not_understood (self, command):
        self.respond ("500 '%s': command not understood." % command)

    def command_not_authorized (self, command):
        self.respond (
                "530 You are not authorized to perform the '%s' command" % (
                        command
                        )
                )

    def make_xmit_channel (self):
        # In PASV mode, the connection may or may _not_ have been made
        # yet.  [although in most cases it is... FTP Explorer being
        # the only exception I've yet seen].  This gets somewhat confusing
        # because things may happen in any order...
        pa = self.passive_acceptor
        if pa:
            if pa.ready:
                # a connection has already been made.
                conn, addr = self.passive_acceptor.ready
                cdc = xmit_channel (self, addr)
                cdc.set_socket (conn)
                cdc.connected = 1
                self.passive_acceptor.close()
                self.passive_acceptor = None
            else:
                # we're still waiting for a connect to the PASV port.
                cdc = xmit_channel (self)
        else:
            # not in PASV mode.
            ip, port = self.client_addr
            cdc = xmit_channel (self, self.client_addr)
            cdc.create_socket (socket.AF_INET, socket.SOCK_STREAM)
            if self.bind_local_minus_one:
                cdc.bind (('', self.server.port - 1))
            try:
                cdc.connect ((ip, port))
            except socket.error:
                self.respond ("425 Can't build data connection")
        self.client_dc = cdc

    # pretty much the same as xmit, but only right on the verge of
    # being worth a merge.
    def make_recv_channel (self, fd):
        pa = self.passive_acceptor
        if pa:
            if pa.ready:
                # a connection has already been made.
                conn, addr = pa.ready
                cdc = recv_channel (self, addr, fd)
                cdc.set_socket (conn)
                cdc.connected = 1
                self.passive_acceptor.close()
                self.passive_acceptor = None
            else:
                # we're still waiting for a connect to the PASV port.
                cdc = recv_channel (self, None, fd)
        else:
            # not in PASV mode.
            ip, port = self.client_addr
            cdc = recv_channel (self, self.client_addr, fd)
            cdc.create_socket (socket.AF_INET, socket.SOCK_STREAM)
            try:
                cdc.connect ((ip, port))
            except socket.error:
                self.respond ("425 Can't build data connection")
        self.client_dc = cdc

    type_map = {
            'a':'ASCII',
            'i':'Binary',
            'e':'EBCDIC',
            'l':'Binary'
            }

    type_mode_map = {
            'a':'t',
            'i':'b',
            'e':'b',
            'l':'b'
            }

    # --------------------------------------------------
    # command methods
    # --------------------------------------------------

    def cmd_type (self, line):
        """specify data transfer type"""
        # ascii, ebcdic, image, local <byte size>
        t = line[1].lower()
        # no support for EBCDIC
        # if t not in ['a','e','i','l']:
        if t not in ['a','i','l']:
            self.command_not_understood(line.join())
        elif t == 'l' and (len(line) > 2 and line[2] != '8'):
            self.respond ('504 Byte size must be 8')
        else:
            self.current_mode = t
            self.respond ('200 Type set to %s.' % self.type_map[t])


    def cmd_quit (self, line):
        """terminate session"""
        self.respond ('221 Goodbye.')
        self.close_when_done()

    def cmd_port (self, line):
        """specify data connection port"""
        info = line[1].split (',')
        ip = '.'.join (info[:4])
        port = int(info[4])*256 + int(info[5])
        # how many data connections at a time?
        # I'm assuming one for now...
        # TODO: we should (optionally) verify that the
        # ip number belongs to the client.  [wu-ftpd does this?]
        self.client_addr = (ip, port)
        self.respond ('200 PORT command successful.')

    def new_passive_acceptor (self):
        # ensure that only one of these exists at a time.
        if self.passive_acceptor is not None:
            self.passive_acceptor.close()
            self.passive_acceptor = None
        self.passive_acceptor = passive_acceptor (self)
        return self.passive_acceptor

    def cmd_pasv (self, line):
        """prepare for server-to-server transfer"""
        pc = self.new_passive_acceptor()
        port = pc.addr[1]
        ip_addr = pc.control_channel.getsockname()[0]
        self.respond (
                '227 Entering Passive Mode (%s,%d,%d)' % (
                        ip_addr.replace('.', ','),
                        port/256,
                        port%256
                        )
                )
        self.client_dc = None

    def cmd_nlst (self, line):
        """give name list of files in directory"""
        # ncftp adds the -FC argument for the user-visible 'nlist'
        # command.  We could try to emulate ls flags, but not just yet.
        if '-FC' in line:
            line.remove ('-FC')
        try:
            dir_list_producer = self.get_dir_list (line, 0)
        except os.error as why:
            self.respond ('550 Could not list directory: %s' % why)
            return
        self.respond (
                '150 Opening %s mode data connection for file list' % (
                        self.type_map[self.current_mode]
                        )
                )
        self.make_xmit_channel()
        self.client_dc.push_with_producer (dir_list_producer)
        self.client_dc.close_when_done()

    def cmd_list (self, line):
        """give a list of files in a directory"""
        try:
            dir_list_producer = self.get_dir_list (line, 1)
        except os.error as why:
            self.respond ('550 Could not list directory: %s' % why)
            return
        self.respond (
                '150 Opening %s mode data connection for file list' % (
                        self.type_map[self.current_mode]
                        )
                )
        self.make_xmit_channel()
        self.client_dc.push_with_producer (dir_list_producer)
        self.client_dc.close_when_done()

    def cmd_cwd (self, line):
        """change working directory"""
        if self.cwd (line):
            self.respond ('250 CWD command successful.')
        else:
            self.respond ('550 No such directory.')

    def cmd_cdup (self, line):
        """change to parent of current working directory"""
        if self.cdup(line):
            self.respond ('250 CDUP command successful.')
        else:
            self.respond ('550 No such directory.')

    def cmd_pwd (self, line):
        """print the current working directory"""
        self.respond (
                '257 "%s" is the current directory.' % (
                        self.filesystem.current_directory()
                        )
                )

    # modification time
    # example output:
    # 213 19960301204320
    def cmd_mdtm (self, line):
        """show last modification time of file"""
        filename = line[1]
        if not self.filesystem.isfile (filename):
            self.respond ('550 "%s" is not a file' % filename)
        else:
            mtime = time.gmtime(self.filesystem.stat(filename)[stat.ST_MTIME])
            self.respond (
                    '213 %4d%02d%02d%02d%02d%02d' % (
                            mtime[0],
                            mtime[1],
                            mtime[2],
                            mtime[3],
                            mtime[4],
                            mtime[5]
                            )
                    )

    def cmd_noop (self, line):
        """do nothing"""
        self.respond ('200 NOOP command successful.')

    def cmd_size (self, line):
        """return size of file"""
        filename = line[1]
        if not self.filesystem.isfile (filename):
            self.respond ('550 "%s" is not a file' % filename)
        else:
            self.respond (
                    '213 %d' % (self.filesystem.stat(filename)[stat.ST_SIZE])
                    )

    def cmd_retr (self, line):
        """retrieve a file"""
        if len(line) < 2:
            self.command_not_understood(line.join())
        else:
            file = line[1]
            if not self.filesystem.isfile (file):
                self.log_info ('checking %s' % file)
                self.respond ('550 No such file')
            else:
                try:
                    # FIXME: for some reason, 'rt' isn't working on win95
                    mode = 'r'+self.type_mode_map[self.current_mode]
                    fd = self.open (file, mode)
                except IOError as why:
                    self.respond ('553 could not open file for reading: %s' % (repr(why)))
                    return
                self.respond (
                        "150 Opening %s mode data connection for file '%s'" % (
                                self.type_map[self.current_mode],
                                file
                                )
                        )
                self.make_xmit_channel()

                if self.restart_position:
                    # try to position the file as requested, but
                    # give up silently on failure (the 'file object'
                    # may not support seek())
                    try:
                        fd.seek (self.restart_position)
                    except:
                        pass
                    self.restart_position = 0

                self.client_dc.push_with_producer (
                        file_producer (fd)
                        )
                self.client_dc.close_when_done()

    def cmd_stor (self, line, mode='wb'):
        """store a file"""
        if len (line) < 2:
            self.command_not_understood(line.join())
        else:
            if self.restart_position:
                self.restart_position = 0
                self.respond ('553 restart on STOR not yet supported')
                return
            file = line[1]
            # todo: handle that type flag
            try:
                fd = self.open (file, mode)
            except IOError as why:
                self.respond ('553 could not open file for writing: %s' % (repr(why)))
                return
            self.respond (
                    '150 Opening %s connection for %s' % (
                            self.type_map[self.current_mode],
                            file
                            )
                    )
            self.make_recv_channel (fd)

    def cmd_abor (self, line):
        """abort operation"""
        if self.client_dc:
            self.client_dc.close()
        self.respond ('226 ABOR command successful.')

    def cmd_appe (self, line):
        """append to a file"""
        return self.cmd_stor (line, 'ab')

    def cmd_dele (self, line):
        if len (line) != 2:
            self.command_not_understood(line.join())
        else:
            file = line[1]
            if self.filesystem.isfile (file):
                try:
                    self.filesystem.unlink (file)
                    self.respond ('250 DELE command successful.')
                except:
                    self.respond ('550 error deleting file.')
            else:
                self.respond ('550 %s: No such file.' % file)

    def cmd_mkd (self, line):
        if len (line) != 2:
            self.command_not_understood(line.join())
        else:
            path = line[1]
            try:
                self.filesystem.mkdir (path)
                self.respond ('257 MKD command successful.')
            except:
                self.respond ('550 error creating directory.')

    def cmd_rnfr (self, line):
        if not hasattr(self.filesystem,'rename'):
            self.respond('502 RNFR not implemented.')
            return

        if len(line)!=2:
            self.command_not_understood(line.join())
        else:
            src = line[1]
            try:
                assert self.filesystem.isfile(src)
                self._rfnr_src = src
                self.respond('350 RNFR file exists, ready for destination name.')
            except:
                self.respond('550 %s: No such file.' % src)

    def cmd_rnto (self, line):
        src = getattr(self,'_rfnr_src',None)
        if not src:
            self.respond('503 RNTO command unexpected.')
            return

        if len(line)!=2:
            self.command_not_understood(line.join())
        else:
            dst = line[1]
            try:
                self.filesystem.rename(src,dst)
                self.respond('250 RNTO command successful.')
            except:
                t, v = sys.exc_info[:2]
                self.respond('550 %s: %s.' % (str(t),str(v)))
        try:
            del self._rfnr_src
        except:
            pass

    def cmd_rmd (self, line):
        if len (line) != 2:
            self.command_not_understood(line.join())
        else:
            path = line[1]
            try:
                self.filesystem.rmdir (path)
                self.respond ('250 RMD command successful.')
            except:
                self.respond ('550 error removing directory.')

    def cmd_user (self, line):
        """specify user name"""
        if len(line) > 1:
            self.user = line[1]
            self.respond ('331 Password required.')
        else:
            self.command_not_understood(line.join())

    def cmd_pass (self, line):
        """specify password"""
        if len(line) < 2:
            pw = ''
        else:
            pw = line[1]
        result, message, fs = self.server.authorizer.authorize (self, self.user, pw)
        if result:
            self.respond ('230 %s' % message)
            self.filesystem = fs
            self.authorized = 1
            self.log_info('Successful login: Filesystem=%s' % repr(fs))
        else:
            self.respond ('530 %s' % message)

    def cmd_rest (self, line):
        """restart incomplete transfer"""
        try:
            pos = int(line[1])
        except ValueError:
            self.command_not_understood(line.join())
        self.restart_position = pos
        self.respond ('350 Restarting at %d. Send STORE or RETRIEVE to initiate transfer.' % pos)

    def cmd_stru (self, line):
        """obsolete - set file transfer structure"""
        if line[1] in 'fF':
            # f == 'file'
            self.respond ('200 STRU F Ok')
        else:
            self.respond ('504 Unimplemented STRU type')

    def cmd_mode (self, line):
        """obsolete - set file transfer mode"""
        if line[1] in 'sS':
            # f == 'file'
            self.respond ('200 MODE S Ok')
        else:
            self.respond ('502 Unimplemented MODE type')

# The stat command has two personalities.  Normally it returns status
# information about the current connection.  But if given an argument,
# it is equivalent to the LIST command, with the data sent over the
# control connection.  Strange.  But wuftpd, ftpd, and nt's ftp server
# all support it.
#
##      def cmd_stat (self, line):
##              'return status of server'
##              pass

    def cmd_syst (self, line):
        """show operating system type of server system"""
        # Replying to this command is of questionable utility, because
        # this server does not behave in a predictable way w.r.t. the
        # output of the LIST command.  We emulate Unix ls output, but
        # on win32 the pathname can contain drive information at the front
        # Currently, the combination of ensuring that os.sep == '/'
        # and removing the leading slash when necessary seems to work.
        # [cd'ing to another drive also works]
        #
        # This is how wuftpd responds, and is probably
        # the most expected.  The main purpose of this reply is so that
        # the client knows to expect Unix ls-style LIST output.
        self.respond ('215 UNIX Type: L8')
        # one disadvantage to this is that some client programs
        # assume they can pass args to /bin/ls.
        # a few typical responses:
        # 215 UNIX Type: L8 (wuftpd)
        # 215 Windows_NT version 3.51
        # 215 VMS MultiNet V3.3
        # 500 'SYST': command not understood. (SVR4)

    def cmd_help (self, line):
        """give help information"""
        # find all the methods that match 'cmd_xxxx',
        # use their docstrings for the help response.
        attrs = dir(self.__class__)
        help_lines = []
        for attr in attrs:
            if attr[:4] == 'cmd_':
                x = getattr (self, attr)
                if type(x) == type(self.cmd_help):
                    if x.__doc__:
                        help_lines.append ('\t%s\t%s' % (attr[4:], x.__doc__))
        if help_lines:
            self.push ('214-The following commands are recognized\r\n')
            self.push_with_producer (producers.lines_producer (help_lines))
            self.push ('214\r\n')
        else:
            self.push ('214-\r\n\tHelp Unavailable\r\n214\r\n')

class ftp_server (asyncore.dispatcher):
    # override this to spawn a different FTP channel class.
    ftp_channel_class = ftp_channel

    SERVER_IDENT = 'FTP Server (V%s)' % VERSION

    def __init__ (
            self,
            authorizer,
            hostname        =None,
            ip              ='',
            port            =21,
            resolver        =None,
            logger_object=logger.file_logger (sys.stdout)
            ):
        self.ip = ip
        self.port = port
        self.authorizer = authorizer

        if hostname is None:
            self.hostname = socket.gethostname()
        else:
            self.hostname = hostname

        # statistics
        self.total_sessions = counter()
        self.closed_sessions = counter()
        self.total_files_out = counter()
        self.total_files_in = counter()
        self.total_bytes_out = counter()
        self.total_bytes_in = counter()
        self.total_exceptions = counter()
        #
        asyncore.dispatcher.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)

        self.set_reuse_addr()
        self.bind ((self.ip, self.port))
        self.listen (5)

        if not logger_object:
            logger_object = sys.stdout

        if resolver:
            self.logger = logger.resolving_logger (resolver, logger_object)
        else:
            self.logger = logger.unresolving_logger (logger_object)

        self.log_info('FTP server started at %s\n\tAuthorizer:%s\n\tHostname: %s\n\tPort: %d' % (
                time.ctime(time.time()),
                repr (self.authorizer),
                self.hostname,
                self.port)
                )

    def writable (self):
        return 0

    def handle_read (self):
        pass

    def handle_connect (self):
        pass

    def handle_accept (self):
        conn, addr = self.accept()
        self.total_sessions.increment()
        self.log_info('Incoming connection from %s:%d' % (addr[0], addr[1]))
        self.ftp_channel_class (self, conn, addr)

    # return a producer describing the state of the server
    def status (self):

        def nice_bytes (n):
            return ''.join(status_handler.english_bytes(n))

        return producers.lines_producer (
                ['<h2>%s</h2>'                          % self.SERVER_IDENT,
                 '<br>Listening on <b>Host:</b> %s' % self.hostname,
                 '<b>Port:</b> %d'                      % self.port,
                 '<br>Sessions',
                 '<b>Total:</b> %s'                     % self.total_sessions,
                 '<b>Current:</b> %d'           % (self.total_sessions.as_long() - self.closed_sessions.as_long()),
                 '<br>Files',
                 '<b>Sent:</b> %s'                      % self.total_files_out,
                 '<b>Received:</b> %s'          % self.total_files_in,
                 '<br>Bytes',
                 '<b>Sent:</b> %s'                      % nice_bytes (self.total_bytes_out.as_long()),
                 '<b>Received:</b> %s'          % nice_bytes (self.total_bytes_in.as_long()),
                 '<br>Exceptions: %s'           % self.total_exceptions,
                 ]
                )

# ======================================================================
#                                                Data Channel Classes
# ======================================================================

# This socket accepts a data connection, used when the server has been
# placed in passive mode.  Although the RFC implies that we ought to
# be able to use the same acceptor over and over again, this presents
# a problem: how do we shut it off, so that we are accepting
# connections only when we expect them?  [we can't]
#
# wuftpd, and probably all the other servers, solve this by allowing
# only one connection to hit this acceptor.  They then close it.  Any
# subsequent data-connection command will then try for the default
# port on the client side [which is of course never there].  So the
# 'always-send-PORT/PASV' behavior seems required.
#
# Another note: wuftpd will also be listening on the channel as soon
# as the PASV command is sent.  It does not wait for a data command
# first.

# --- we need to queue up a particular behavior:
#  1) xmit : queue up producer[s]
#  2) recv : the file object
#
# It would be nice if we could make both channels the same.  Hmmm..
#

class passive_acceptor (asyncore.dispatcher):
    ready = None

    def __init__ (self, control_channel):
        # connect_fun (conn, addr)
        asyncore.dispatcher.__init__ (self)
        self.control_channel = control_channel
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        # bind to an address on the interface that the
        # control connection is coming from.
        self.bind ((
                self.control_channel.getsockname()[0],
                0
                ))
        self.addr = self.getsockname()
        self.listen (1)

#       def __del__ (self):
#               print 'passive_acceptor.__del__()'

    def log (self, *ignore):
        pass

    def handle_accept (self):
        conn, addr = self.accept()
        dc = self.control_channel.client_dc
        if dc is not None:
            dc.set_socket (conn)
            dc.addr = addr
            dc.connected = 1
            self.control_channel.passive_acceptor = None
        else:
            self.ready = conn, addr
        self.close()


class xmit_channel (asynchat.async_chat):

    # for an ethernet, you want this to be fairly large, in fact, it
    # _must_ be large for performance comparable to an ftpd.  [64k] we
    # ought to investigate automatically-sized buffers...

    ac_out_buffer_size = 16384
    bytes_out = 0

    def __init__ (self, channel, client_addr=None):
        self.channel = channel
        self.client_addr = client_addr
        asynchat.async_chat.__init__ (self)

#       def __del__ (self):
#               print 'xmit_channel.__del__()'

    def log (self, *args):
        pass

    def readable (self):
        return not self.connected

    def writable (self):
        return 1

    def send (self, data):
        result = asynchat.async_chat.send (self, data)
        self.bytes_out += result
        return result

    def handle_error (self):
        # usually this is to catch an unexpected disconnect.
        self.log_info ('unexpected disconnect on data xmit channel', 'error')
        try:
            self.close()
        except:
            pass

    # TODO: there's a better way to do this.  we need to be able to
    # put 'events' in the producer fifo.  to do this cleanly we need
    # to reposition the 'producer' fifo as an 'event' fifo.

    def close (self):
        c = self.channel
        s = c.server
        c.client_dc = None
        s.total_files_out.increment()
        s.total_bytes_out.increment (self.bytes_out)
        if not len(self.producer_fifo):
            c.respond ('226 Transfer complete')
        elif not c.closed:
            c.respond ('426 Connection closed; transfer aborted')
        del c
        del s
        del self.channel
        asynchat.async_chat.close (self)

class recv_channel (asyncore.dispatcher):
    def __init__ (self, channel, client_addr, fd):
        self.channel = channel
        self.client_addr = client_addr
        self.fd = fd
        asyncore.dispatcher.__init__ (self)
        self.bytes_in = counter()

    def log (self, *ignore):
        pass

    def handle_connect (self):
        pass

    def writable (self):
        return 0

    def recv (self, *args):
        result = asyncore.dispatcher.recv(self, *args)
        self.bytes_in.increment(len(result))
        return result

    buffer_size = 8192

    def handle_read (self):
        block = self.recv (self.buffer_size)
        if block:
            try:
                self.fd.write (block)
            except IOError:
                self.log_info ('got exception writing block...', 'error')

    def handle_close (self):
        s = self.channel.server
        s.total_files_in.increment()
        s.total_bytes_in.increment(self.bytes_in.as_long())
        self.fd.close()
        self.channel.respond ('226 Transfer complete.')
        self.close()

import supervisor.medusa.filesys as filesys

# not much of a doorman! 8^)
class dummy_authorizer:
    def __init__ (self, root='/'):
        self.root = root

    def authorize (self, channel, username, password):
        channel.persona = -1, -1
        channel.read_only = 1
        return 1, 'Ok.', filesys.os_filesystem (self.root)

class anon_authorizer:
    def __init__ (self, root='/'):
        self.root = root

    def authorize (self, channel, username, password):
        if username in ('ftp', 'anonymous'):
            channel.persona = -1, -1
            channel.read_only = 1
            return 1, 'Ok.', filesys.os_filesystem (self.root)
        else:
            return 0, 'Password invalid.', None

# ===========================================================================
# Unix-specific improvements
# ===========================================================================

if os.name == 'posix':

    class unix_authorizer:
        # return a trio of (success, reply_string, filesystem)
        def authorize (self, channel, username, password):
            import crypt
            import pwd
            try:
                info = pwd.getpwnam (username)
            except KeyError:
                return 0, 'No such user.', None
            mangled = info[1]
            if crypt.crypt (password, mangled[:2]) == mangled:
                channel.read_only = 0
                fs = filesys.schizophrenic_unix_filesystem (
                        '/',
                        info[5],
                        persona = (info[2], info[3])
                        )
                return 1, 'Login successful.', fs
            else:
                return 0, 'Password invalid.', None

        def __repr__ (self):
            return '<standard unix authorizer>'

    # simple anonymous ftp support
    class unix_authorizer_with_anonymous (unix_authorizer):
        def __init__ (self, root=None, real_users=0):
            self.root = root
            self.real_users = real_users

        def authorize (self, channel, username, password):
            if username.lower() in ('anonymous', 'ftp'):
                import pwd
                try:
                    # ok, here we run into lots of confusion.
                    # on some os', anon runs under user 'nobody',
                    # on others as 'ftp'.  ownership is also critical.
                    # need to investigate.
                    # linux: new linuxen seem to have nobody's UID=-1,
                    #    which is an illegal value.  Use ftp.
                    ftp_user_info = pwd.getpwnam ('ftp')
                    if os.uname()[0].lower() == 'linux':
                        nobody_user_info = pwd.getpwnam ('ftp')
                    else:
                        nobody_user_info = pwd.getpwnam ('nobody')
                    channel.read_only = 1
                    if self.root is None:
                        self.root = ftp_user_info[5]
                    fs = filesys.unix_filesystem (self.root, '/')
                    return 1, 'Anonymous Login Successful', fs
                except KeyError:
                    return 0, 'Anonymous account not set up', None
            elif self.real_users:
                return unix_authorizer.authorize (
                        self,
                        channel,
                        username,
                        password
                        )
            else:
                return 0, 'User logins not allowed', None

# usage: ftp_server /PATH/TO/FTP/ROOT PORT
# for example:
# $ ftp_server /home/users/ftp 8021

if os.name == 'posix':
    def test (port='8021'):
        fs = ftp_server (
                unix_authorizer(),
                port=int(port)
                )
        try:
            asyncore.loop()
        except KeyboardInterrupt:
            fs.log_info('FTP server shutting down. (received SIGINT)', 'warning')
            # close everything down on SIGINT.
            # of course this should be a cleaner shutdown.
            asyncore.close_all()

    if __name__ == '__main__':
        test (sys.argv[1])
# not unix
else:
    def test ():
        ftp_server (dummy_authorizer())
    if __name__ == '__main__':
        test ()

# this is the command list from the wuftpd man page
# '*' means we've implemented it.
# '!' requires write access
#
command_documentation = {
        'abor': 'abort previous command',                                                       #*
        'acct': 'specify account (ignored)',
        'allo': 'allocate storage (vacuously)',
        'appe': 'append to a file',                                                                     #*!
        'cdup': 'change to parent of current working directory',        #*
        'cwd':  'change working directory',                                                     #*
        'dele': 'delete a file',                                                                        #!
        'help': 'give help information',                                                        #*
        'list': 'give list files in a directory',                                       #*
        'mkd':  'make a directory',                                                                     #!
        'mdtm': 'show last modification time of file',                          #*
        'mode': 'specify data transfer mode',
        'nlst': 'give name list of files in directory',                         #*
        'noop': 'do nothing',                                                                           #*
        'pass': 'specify password',                                                                     #*
        'pasv': 'prepare for server-to-server transfer',                        #*
        'port': 'specify data connection port',                                         #*
        'pwd':  'print the current working directory',                          #*
        'quit': 'terminate session',                                                            #*
        'rest': 'restart incomplete transfer',                                          #*
        'retr': 'retrieve a file',                                                                      #*
        'rmd':  'remove a directory',                                                           #!
        'rnfr': 'specify rename-from file name',                                        #*!
        'rnto': 'specify rename-to file name',                                          #*!
        'site': 'non-standard commands (see next section)',
        'size': 'return size of file',                                                          #*
        'stat': 'return status of server',                                                      #*
        'stor': 'store a file',                                                                         #*!
        'stou': 'store a file with a unique name',                                      #!
        'stru': 'specify data transfer structure',
        'syst': 'show operating system type of server system',          #*
        'type': 'specify data transfer type',                                           #*
        'user': 'specify user name',                                                            #*
        'xcup': 'change to parent of current working directory (deprecated)',
        'xcwd': 'change working directory (deprecated)',
        'xmkd': 'make a directory (deprecated)',                                        #!
        'xpwd': 'print the current working directory (deprecated)',
        'xrmd': 'remove a directory (deprecated)',                                      #!
}


# debugging aid (linux)
def get_vm_size ():
    return int(open('/proc/self/stat').readline().split()[22])

def print_vm():
    print('vm: %8dk' % (get_vm_size()/1024))

########NEW FILE########
__FILENAME__ = http_date
# -*- Mode: Python -*-

import re
import time

def concat (*args):
    return ''.join (args)

def join (seq, field=' '):
    return field.join (seq)

def group (s):
    return '(' + s + ')'

short_days = ['sun','mon','tue','wed','thu','fri','sat']
long_days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']

short_day_reg = group (join (short_days, '|'))
long_day_reg = group (join (long_days, '|'))

daymap = {}
for i in range(7):
    daymap[short_days[i]] = i
    daymap[long_days[i]] = i

hms_reg = join (3 * [group('[0-9][0-9]')], ':')

months = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec']

monmap = {}
for i in range(12):
    monmap[months[i]] = i+1

months_reg = group (join (months, '|'))

# From draft-ietf-http-v11-spec-07.txt/3.3.1
#       Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
#       Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
#       Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format

# rfc822 format
rfc822_date = join (
        [concat (short_day_reg,','),    # day
         group('[0-9][0-9]?'),          # date
         months_reg,                    # month
         group('[0-9]+'),               # year
         hms_reg,                       # hour minute second
         'gmt'
         ],
        ' '
        )

rfc822_reg = re.compile (rfc822_date)

def unpack_rfc822(m):
    g = m.group
    i = int
    return (
            i(g(4)),        # year
            monmap[g(3)],   # month
            i(g(2)),        # day
            i(g(5)),        # hour
            i(g(6)),        # minute
            i(g(7)),        # second
            0,
            0,
            0
            )

# rfc850 format
rfc850_date = join (
        [concat (long_day_reg,','),
         join (
                 [group ('[0-9][0-9]?'),
                  months_reg,
                  group ('[0-9]+')
                  ],
                 '-'
                 ),
         hms_reg,
         'gmt'
         ],
        ' '
        )

rfc850_reg = re.compile (rfc850_date)
# they actually unpack the same way
def unpack_rfc850(m):
    g = m.group
    i = int
    return (
            i(g(4)),        # year
            monmap[g(3)],   # month
            i(g(2)),        # day
            i(g(5)),        # hour
            i(g(6)),        # minute
            i(g(7)),        # second
            0,
            0,
            0
            )

# parsedate.parsedate    - ~700/sec.
# parse_http_date       - ~1333/sec.

def build_http_date (when):
    return time.strftime ('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(when))

def parse_http_date (d):
    d = d.lower()
    tz = time.timezone
    m = rfc850_reg.match (d)
    if m and m.end() == len(d):
        retval = int (time.mktime (unpack_rfc850(m)) - tz)
    else:
        m = rfc822_reg.match (d)
        if m and m.end() == len(d):
            retval = int (time.mktime (unpack_rfc822(m)) - tz)
        else:
            return 0
    # Thanks to Craig Silverstein <csilvers@google.com> for pointing
    # out the DST discrepancy
    if time.daylight and time.localtime(retval)[-1] == 1: # DST correction
        retval += tz - time.altzone
    return retval

########NEW FILE########
__FILENAME__ = http_server
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#
RCS_ID =  '$Id: http_server.py,v 1.12 2004/04/21 15:11:44 akuchling Exp $'

# python modules
import re
import supervisor.medusa.text_socket as socket
import sys
import time

# async modules
import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

# medusa modules
import supervisor.medusa.http_date as http_date
import supervisor.medusa.producers as producers
import supervisor.medusa.logger as logger

VERSION_STRING = RCS_ID.split()[2]

from supervisor.medusa.counter import counter
try:
    from urllib import unquote, splitquery
except ImportError:
    from urllib.parse import unquote, splitquery

# ===========================================================================
#                                                       Request Object
# ===========================================================================

class http_request:

    # default reply code
    reply_code = 200

    request_counter = counter()

    # Whether to automatically use chunked encoding when
    #
    #   HTTP version is 1.1
    #   Content-Length is not set
    #   Chunked encoding is not already in effect
    #
    # If your clients are having trouble, you might want to disable this.
    use_chunked = 1

    # by default, this request object ignores user data.
    collector = None

    def __init__ (self, *args):
        # unpack information about the request
        (self.channel, self.request,
         self.command, self.uri, self.version,
         self.header) = args

        self.outgoing = []
        self.reply_headers = {
                'Server'        : 'Medusa/%s' % VERSION_STRING,
                'Date'          : http_date.build_http_date (time.time())
                }

        # New reply header list (to support multiple
        # headers with same name)
        self.__reply_header_list = []

        self.request_number = http_request.request_counter.increment()
        self._split_uri = None
        self._header_cache = {}

    # --------------------------------------------------
    # reply header management
    # --------------------------------------------------
    def __setitem__ (self, key, value):
        self.reply_headers[key] = value

    def __getitem__ (self, key):
        return self.reply_headers[key]

    def __contains__(self, key):
        return key in self.reply_headers

    def has_key (self, key):
        return key in self.reply_headers

    def build_reply_header (self):
        header_items = ['%s: %s' % item for item in self.reply_headers.items()]
        return '\r\n'.join (
            [self.response(self.reply_code)] + header_items) + '\r\n\r\n'

    ####################################################
    # multiple reply header management
    ####################################################
    # These are intended for allowing multiple occurrences
    # of the same header.
    # Usually you can fold such headers together, separating
    # their contents by a comma (e.g. Accept: text/html, text/plain)
    # but the big exception is the Set-Cookie header.
    # dictionary centric.
    #---------------------------------------------------

    def add_header(self, name, value):
        """ Adds a header to the reply headers """
        self.__reply_header_list.append((name, value))

    def clear_headers(self):
        """ Clears the reply header list """

        # Remove things from the old dict as well
        self.reply_headers.clear()

        self.__reply_header_list[:] = []

    def remove_header(self, name, value=None):
        """ Removes the specified header.
        If a value is provided, the name and
        value must match to remove the header.
        If the value is None, removes all headers
        with that name."""

        found_it = 0

        # Remove things from the old dict as well
        if (name in self.reply_headers and
            (value is None or
             self.reply_headers[name] == value)):
            del self.reply_headers[name]
            found_it = 1


        removed_headers = []
        if not value is None:
            if (name, value) in self.__reply_header_list:
                removed_headers = [(name, value)]
                found_it = 1
        else:
            for h in self.__reply_header_list:
                if h[0] == name:
                    removed_headers.append(h)
                    found_it = 1

        if not found_it:
            if value is None:
                search_value = "%s" % name
            else:
                search_value = "%s: %s" % (name, value)

            raise LookupError("Header '%s' not found" % search_value)

        for h in removed_headers:
            self.__reply_header_list.remove(h)


    def get_reply_headers(self):
        """ Get the tuple of headers that will be used
        for generating reply headers"""
        header_tuples = self.__reply_header_list[:]

        # The idea here is to insert the headers from
        # the old header dict into the new header list,
        # UNLESS there's already an entry in the list
        # that would have overwritten the dict entry
        # if the dict was the only storage...
        header_names = [n for n,v in header_tuples]
        for n,v in self.reply_headers.items():
            if n not in header_names:
                header_tuples.append((n,v))
                header_names.append(n)
        # Ok, that should do it.  Now, if there were any
        # headers in the dict that weren't in the list,
        # they should have been copied in.  If the name
        # was already in the list, we didn't copy it,
        # because the value from the dict has been
        # 'overwritten' by the one in the list.

        return header_tuples

    def get_reply_header_text(self):
        """ Gets the reply header (including status and
        additional crlf)"""

        header_tuples = self.get_reply_headers()

        headers = [self.response(self.reply_code)]
        headers += ["%s: %s" % h for h in header_tuples]
        return '\r\n'.join(headers) + '\r\n\r\n'

    #---------------------------------------------------
    # This is the end of the new reply header
    # management section.
    ####################################################


    # --------------------------------------------------
    # split a uri
    # --------------------------------------------------

    # <path>;<params>?<query>#<fragment>
    path_regex = re.compile (
    #      path      params    query   fragment
            r'([^;?#]*)(;[^?#]*)?(\?[^#]*)?(#.*)?'
            )

    def split_uri (self):
        if self._split_uri is None:
            m = self.path_regex.match (self.uri)
            if m.end() != len(self.uri):
                raise ValueError("Broken URI")
            else:
                self._split_uri = m.groups()
        return self._split_uri

    def get_header_with_regex (self, head_reg, group):
        for line in self.header:
            m = head_reg.match (line)
            if m.end() == len(line):
                return m.group (group)
        return ''

    def get_header (self, header):
        header = header.lower()
        hc = self._header_cache
        if header not in hc:
            h = header + ': '
            hl = len(h)
            for line in self.header:
                if line[:hl].lower() == h:
                    r = line[hl:]
                    hc[header] = r
                    return r
            hc[header] = None
            return None
        else:
            return hc[header]

    # --------------------------------------------------
    # user data
    # --------------------------------------------------

    def collect_incoming_data (self, data):
        if self.collector:
            self.collector.collect_incoming_data (data)
        else:
            self.log_info(
                    'Dropping %d bytes of incoming request data' % len(data),
                    'warning'
                    )

    def found_terminator (self):
        if self.collector:
            self.collector.found_terminator()
        else:
            self.log_info (
                    'Unexpected end-of-record for incoming request',
                    'warning'
                    )

    def push (self, thing):
        if type(thing) == type(''):
            self.outgoing.append(producers.simple_producer(thing,
              buffer_size=len(thing)))
        else:
            self.outgoing.append(thing)

    def response (self, code=200):
        message = self.responses[code]
        self.reply_code = code
        return 'HTTP/%s %d %s' % (self.version, code, message)

    def error (self, code):
        self.reply_code = code
        message = self.responses[code]
        s = self.DEFAULT_ERROR_MESSAGE % {
                'code': code,
                'message': message,
                }
        self['Content-Length'] = len(s)
        self['Content-Type'] = 'text/html'
        # make an error reply
        self.push (s)
        self.done()

    # can also be used for empty replies
    reply_now = error

    def done (self):
        """finalize this transaction - send output to the http channel"""

        # ----------------------------------------
        # persistent connection management
        # ----------------------------------------

        #  --- BUCKLE UP! ----

        connection = get_header(CONNECTION, self.header).lower()

        close_it = 0
        wrap_in_chunking = 0

        if self.version == '1.0':
            if connection == 'keep-alive':
                if 'Content-Length' not in self:
                    close_it = 1
                else:
                    self['Connection'] = 'Keep-Alive'
            else:
                close_it = 1
        elif self.version == '1.1':
            if connection == 'close':
                close_it = 1
            elif 'Content-Length' not in self:
                if 'Transfer-Encoding' in self:
                    if not self['Transfer-Encoding'] == 'chunked':
                        close_it = 1
                elif self.use_chunked:
                    self['Transfer-Encoding'] = 'chunked'
                    wrap_in_chunking = 1
                else:
                    close_it = 1
        elif self.version is None:
            # Although we don't *really* support http/0.9 (because we'd have to
            # use \r\n as a terminator, and it would just yuck up a lot of stuff)
            # it's very common for developers to not want to type a version number
            # when using telnet to debug a server.
            close_it = 1

        outgoing_header = producers.simple_producer(self.get_reply_header_text())

        if close_it:
            self['Connection'] = 'close'

        if wrap_in_chunking:
            outgoing_producer = producers.chunked_producer (
                    producers.composite_producer (self.outgoing)
                    )
            # prepend the header
            outgoing_producer = producers.composite_producer(
                [outgoing_header, outgoing_producer]
                )
        else:
            # prepend the header
            self.outgoing.insert(0, outgoing_header)
            outgoing_producer = producers.composite_producer (self.outgoing)

        # apply a few final transformations to the output
        self.channel.push_with_producer (
                # globbing gives us large packets
                producers.globbing_producer (
                        # hooking lets us log the number of bytes sent
                        producers.hooked_producer (
                                outgoing_producer,
                                self.log
                                )
                        )
                )

        self.channel.current_request = None

        if close_it:
            self.channel.close_when_done()

    def log_date_string (self, when):
        gmt = time.gmtime(when)
        if time.daylight and gmt[8]:
            tz = time.altzone
        else:
            tz = time.timezone
        if tz > 0:
            neg = 1
        else:
            neg = 0
            tz = -tz
        h, rem = divmod (tz, 3600)
        m, rem = divmod (rem, 60)
        if neg:
            offset = '-%02d%02d' % (h, m)
        else:
            offset = '+%02d%02d' % (h, m)

        return time.strftime ( '%d/%b/%Y:%H:%M:%S ', gmt) + offset

    def log (self, bytes):
        self.channel.server.logger.log (
                self.channel.addr[0],
                '%d - - [%s] "%s" %d %d\n' % (
                        self.channel.addr[1],
                        self.log_date_string (time.time()),
                        self.request,
                        self.reply_code,
                        bytes
                        )
                )

    responses = {
            100: "Continue",
            101: "Switching Protocols",
            200: "OK",
            201: "Created",
            202: "Accepted",
            203: "Non-Authoritative Information",
            204: "No Content",
            205: "Reset Content",
            206: "Partial Content",
            300: "Multiple Choices",
            301: "Moved Permanently",
            302: "Moved Temporarily",
            303: "See Other",
            304: "Not Modified",
            305: "Use Proxy",
            400: "Bad Request",
            401: "Unauthorized",
            402: "Payment Required",
            403: "Forbidden",
            404: "Not Found",
            405: "Method Not Allowed",
            406: "Not Acceptable",
            407: "Proxy Authentication Required",
            408: "Request Time-out",
            409: "Conflict",
            410: "Gone",
            411: "Length Required",
            412: "Precondition Failed",
            413: "Request Entity Too Large",
            414: "Request-URI Too Large",
            415: "Unsupported Media Type",
            500: "Internal Server Error",
            501: "Not Implemented",
            502: "Bad Gateway",
            503: "Service Unavailable",
            504: "Gateway Time-out",
            505: "HTTP Version not supported"
            }

    # Default error message
    DEFAULT_ERROR_MESSAGE = '\r\n'.join(
            ('<head>',
             '<title>Error response</title>',
             '</head>',
             '<body>',
             '<h1>Error response</h1>',
             '<p>Error code %(code)d.',
             '<p>Message: %(message)s.',
             '</body>',
             ''
             )
            )

    def log_info(self, msg, level):
        pass


# ===========================================================================
#                                                HTTP Channel Object
# ===========================================================================

class http_channel (asynchat.async_chat):

    # use a larger default output buffer
    ac_out_buffer_size = 1<<16

    current_request = None
    channel_counter = counter()

    def __init__ (self, server, conn, addr):
        self.channel_number = http_channel.channel_counter.increment()
        self.request_counter = counter()
        asynchat.async_chat.__init__ (self, conn)
        self.server = server
        self.addr = addr
        self.set_terminator ('\r\n\r\n')
        self.in_buffer = ''
        self.creation_time = int (time.time())
        self.check_maintenance()

    def __repr__ (self):
        ar = asynchat.async_chat.__repr__(self)[1:-1]
        return '<%s channel#: %s requests:%s>' % (
                ar,
                self.channel_number,
                self.request_counter
                )

    # Channel Counter, Maintenance Interval...
    maintenance_interval = 500

    def check_maintenance (self):
        if not self.channel_number % self.maintenance_interval:
            self.maintenance()

    def maintenance (self):
        self.kill_zombies()

    # 30-minute zombie timeout.  status_handler also knows how to kill zombies.
    zombie_timeout = 30 * 60

    def kill_zombies (self):
        now = int (time.time())
        for channel in asyncore.socket_map.values():
            if channel.__class__ == self.__class__:
                if (now - channel.creation_time) > channel.zombie_timeout:
                    channel.close()

    # --------------------------------------------------
    # send/recv overrides, good place for instrumentation.
    # --------------------------------------------------

    # this information needs to get into the request object,
    # so that it may log correctly.
    def send (self, data):
        result = asynchat.async_chat.send (self, data)
        self.server.bytes_out.increment (len(data))
        return result

    def recv (self, buffer_size):
        try:
            result = asynchat.async_chat.recv (self, buffer_size)
            self.server.bytes_in.increment (len(result))
            return result
        except MemoryError:
            # --- Save a Trip to Your Service Provider ---
            # It's possible for a process to eat up all the memory of
            # the machine, and put it in an extremely wedged state,
            # where medusa keeps running and can't be shut down.  This
            # is where MemoryError tends to get thrown, though of
            # course it could get thrown elsewhere.
            sys.exit ("Out of Memory!")

    def handle_error (self):
        t, v = sys.exc_info()[:2]
        if t is SystemExit:
            raise t(v)
        else:
            asynchat.async_chat.handle_error (self)

    def log (self, *args):
        pass

    # --------------------------------------------------
    # async_chat methods
    # --------------------------------------------------

    def collect_incoming_data (self, data):
        if self.current_request:
            # we are receiving data (probably POST data) for a request
            self.current_request.collect_incoming_data (data)
        else:
            # we are receiving header (request) data
            self.in_buffer = self.in_buffer + data

    def found_terminator (self):
        if self.current_request:
            self.current_request.found_terminator()
        else:
            header = self.in_buffer
            self.in_buffer = ''
            lines = header.split('\r\n')

            # --------------------------------------------------
            # crack the request header
            # --------------------------------------------------

            while lines and not lines[0]:
                # as per the suggestion of http-1.1 section 4.1, (and
                # Eric Parker <eparker@zyvex.com>), ignore a leading
                # blank lines (buggy browsers tack it onto the end of
                # POST requests)
                lines = lines[1:]

            if not lines:
                self.close_when_done()
                return

            request = lines[0]

            command, uri, version = crack_request (request)
            header = join_headers (lines[1:])

            # unquote path if necessary (thanks to Skip Montanaro for pointing
            # out that we must unquote in piecemeal fashion).
            rpath, rquery = splitquery(uri)
            if '%' in rpath:
                if rquery:
                    uri = unquote (rpath) + '?' + rquery
                else:
                    uri = unquote (rpath)

            r = http_request (self, request, command, uri, version, header)
            self.request_counter.increment()
            self.server.total_requests.increment()

            if command is None:
                self.log_info ('Bad HTTP request: %s' % repr(request), 'error')
                r.error (400)
                return

            # --------------------------------------------------
            # handler selection and dispatch
            # --------------------------------------------------
            for h in self.server.handlers:
                if h.match (r):
                    try:
                        self.current_request = r
                        # This isn't used anywhere.
                        # r.handler = h # CYCLE
                        h.handle_request (r)
                    except:
                        self.server.exceptions.increment()
                        (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
                        self.log_info(
                                        'Server Error: %s, %s: file: %s line: %s' % (t,v,file,line),
                                        'error')
                        try:
                            r.error (500)
                        except:
                            pass
                    return

            # no handlers, so complain
            r.error (404)

    def writable_for_proxy (self):
        # this version of writable supports the idea of a 'stalled' producer
        # [i.e., it's not ready to produce any output yet] This is needed by
        # the proxy, which will be waiting for the magic combination of
        # 1) hostname resolved
        # 2) connection made
        # 3) data available.
        if self.ac_out_buffer:
            return 1
        elif len(self.producer_fifo):
            p = self.producer_fifo.first()
            if hasattr (p, 'stalled'):
                return not p.stalled()
            else:
                return 1

# ===========================================================================
#                                                HTTP Server Object
# ===========================================================================

class http_server (asyncore.dispatcher):

    SERVER_IDENT = 'HTTP Server (V%s)' % VERSION_STRING

    channel_class = http_channel

    def __init__ (self, ip, port, resolver=None, logger_object=None):
        self.ip = ip
        self.port = port
        asyncore.dispatcher.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)

        self.handlers = []

        if not logger_object:
            logger_object = logger.file_logger (sys.stdout)

        self.set_reuse_addr()
        self.bind ((ip, port))

        # lower this to 5 if your OS complains
        self.listen (1024)

        host, port = self.socket.getsockname()
        if not ip:
            self.log_info('Computing default hostname', 'warning')
            ip = socket.gethostbyname (socket.gethostname())
        try:
            self.server_name = socket.gethostbyaddr (ip)[0]
        except socket.error:
            self.log_info('Cannot do reverse lookup', 'warning')
            self.server_name = ip       # use the IP address as the "hostname"

        self.server_port = port
        self.total_clients = counter()
        self.total_requests = counter()
        self.exceptions = counter()
        self.bytes_out = counter()
        self.bytes_in  = counter()

        if not logger_object:
            logger_object = logger.file_logger (sys.stdout)

        if resolver:
            self.logger = logger.resolving_logger (resolver, logger_object)
        else:
            self.logger = logger.unresolving_logger (logger_object)

        self.log_info (
                'Medusa (V%s) started at %s'
                '\n\tHostname: %s'
                '\n\tPort:%d'
                '\n' % (
                        VERSION_STRING,
                        time.ctime(time.time()),
                        self.server_name,
                        port,
                        )
                )

    def writable (self):
        return 0

    def handle_read (self):
        pass

    def readable (self):
        return self.accepting

    def handle_connect (self):
        pass

    def handle_accept (self):
        self.total_clients.increment()
        try:
            conn, addr = self.accept()
        except socket.error:
            # linux: on rare occasions we get a bogus socket back from
            # accept.  socketmodule.c:makesockaddr complains that the
            # address family is unknown.  We don't want the whole server
            # to shut down because of this.
            self.log_info ('warning: server accept() threw an exception', 'warning')
            return
        except TypeError:
            # unpack non-sequence.  this can happen when a read event
            # fires on a listening socket, but when we call accept()
            # we get EWOULDBLOCK, so dispatcher.accept() returns None.
            # Seen on FreeBSD3.
            self.log_info ('warning: server accept() threw EWOULDBLOCK', 'warning')
            return

        self.channel_class (self, conn, addr)

    def install_handler (self, handler, back=0):
        if back:
            self.handlers.append (handler)
        else:
            self.handlers.insert (0, handler)

    def remove_handler (self, handler):
        self.handlers.remove (handler)

    def status (self):
        from supervisor.medusa.status_handler import english_bytes
        def nice_bytes (n):
            return ''.join(english_bytes (n))

        handler_stats = [_f for _f in map (maybe_status, self.handlers) if _f]

        if self.total_clients:
            ratio = self.total_requests.as_long() / float(self.total_clients.as_long())
        else:
            ratio = 0.0

        return producers.composite_producer (
                [producers.lines_producer (
                        ['<h2>%s</h2>'                                                  % self.SERVER_IDENT,
                        '<br>Listening on: <b>Host:</b> %s'             % self.server_name,
                        '<b>Port:</b> %d'                                               % self.port,
                         '<p><ul>'
                         '<li>Total <b>Clients:</b> %s'                 % self.total_clients,
                         '<b>Requests:</b> %s'                                  % self.total_requests,
                         '<b>Requests/Client:</b> %.1f'                 % ratio,
                         '<li>Total <b>Bytes In:</b> %s'        % (nice_bytes (self.bytes_in.as_long())),
                         '<b>Bytes Out:</b> %s'                         % (nice_bytes (self.bytes_out.as_long())),
                         '<li>Total <b>Exceptions:</b> %s'              % self.exceptions,
                         '</ul><p>'
                         '<b>Extension List</b><ul>',
                         ])] + handler_stats + [producers.simple_producer('</ul>')]
                )

def maybe_status (thing):
    if hasattr (thing, 'status'):
        return thing.status()
    else:
        return None

CONNECTION = re.compile ('Connection: (.*)', re.IGNORECASE)

# merge multi-line headers
# [486dx2: ~500/sec]
def join_headers (headers):
    r = []
    for i in range(len(headers)):
        if headers[i][0] in ' \t':
            r[-1] = r[-1] + headers[i][1:]
        else:
            r.append (headers[i])
    return r

def get_header (head_reg, lines, group=1):
    for line in lines:
        m = head_reg.match (line)
        if m and m.end() == len(line):
            return m.group (group)
    return ''

def get_header_match (head_reg, lines):
    for line in lines:
        m = head_reg.match (line)
        if m and m.end() == len(line):
            return m
    return ''

REQUEST = re.compile ('([^ ]+) ([^ ]+)(( HTTP/([0-9.]+))$|$)')

def crack_request (r):
    m = REQUEST.match (r)
    if m and m.end() == len(r):
        if m.group(3):
            version = m.group(5)
        else:
            version = None
        return m.group(1), m.group(2), version
    else:
        return None, None, None

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('usage: %s <root> <port>' % (sys.argv[0]))
    else:
        import supervisor.medusa.monitor as monitor
        import supervisor.medusa.filesys as filesys
        import supervisor.medusa.default_handler as default_handler
        import supervisor.medusa.status_handler as status_handler
        import supervisor.medusa.ftp_server as ftp_server
        import supervisor.medusa.chat_server as chat_server
        import supervisor.medusa.resolver as resolver
        rs = resolver.caching_resolver ('127.0.0.1')
        lg = logger.file_logger (sys.stdout)
        ms = monitor.secure_monitor_server ('fnord', '127.0.0.1', 9999)
        fs = filesys.os_filesystem (sys.argv[1])
        dh = default_handler.default_handler (fs)
        hs = http_server('', int(sys.argv[2]), rs, lg)
        hs.install_handler (dh)
        ftp = ftp_server.ftp_server (
                ftp_server.dummy_authorizer(sys.argv[1]),
                port=8021,
                resolver=rs,
                logger_object=lg
                )
        cs = chat_server.chat_server ('', 7777)
        sh = status_handler.status_extension([hs,ms,ftp,cs,rs])
        hs.install_handler (sh)
        if '-p' in sys.argv:
            def profile_loop ():
                try:
                    asyncore.loop()
                except KeyboardInterrupt:
                    pass
            import profile
            profile.run ('profile_loop()', 'profile.out')
        else:
            asyncore.loop()

########NEW FILE########
__FILENAME__ = logger
# -*- Mode: Python -*-

import supervisor.medusa.asynchat_25 as asynchat
import supervisor.medusa.text_socket as socket
import time         # these three are for the rotating logger
import os           # |
import stat         # v

#
# three types of log:
# 1) file
#    with optional flushing.  Also, one that rotates the log.
# 2) socket
#    dump output directly to a socket connection. [how do we
#    keep it open?]
# 3) syslog
#    log to syslog via tcp.  this is a per-line protocol.
#

#
# The 'standard' interface to a logging object is simply
# log_object.log (message)
#

# a file-like object that captures output, and
# makes sure to flush it always...  this could
# be connected to:
#  o    stdio file
#  o    low-level file
#  o    socket channel
#  o    syslog output...

class file_logger:

    # pass this either a path or a file object.
    def __init__ (self, file, flush=1, mode='a'):
        if isinstance(file, str):
            if file == '-':
                import sys
                self.file = sys.stdout
            else:
                self.file = open (file, mode)
        else:
            self.file = file
        self.do_flush = flush

    def __repr__ (self):
        return '<file logger: %s>' % self.file

    def write (self, data):
        self.file.write (data)
        self.maybe_flush()

    def writeline (self, line):
        self.file.writeline (line)
        self.maybe_flush()

    def writelines (self, lines):
        self.file.writelines (lines)
        self.maybe_flush()

    def maybe_flush (self):
        if self.do_flush:
            self.file.flush()

    def flush (self):
        self.file.flush()

    def softspace (self, *args):
        pass

    def log (self, message):
        if message[-1] not in ('\r', '\n'):
            self.write (message + '\n')
        else:
            self.write (message)

# like a file_logger, but it must be attached to a filename.
# When the log gets too full, or a certain time has passed,
# it backs up the log and starts a new one.  Note that backing
# up the log is done via "mv" because anything else (cp, gzip)
# would take time, during which medusa would do nothing else.

class rotating_file_logger (file_logger):

    # If freq is non-None we back up "daily", "weekly", or "monthly".
    # Else if maxsize is non-None we back up whenever the log gets
    # to big.  If both are None we never back up.
    def __init__ (self, file, freq=None, maxsize=None, flush=1, mode='a'):
        file_logger.__init__ (self, file, flush, mode)
        self.filename = file
        self.mode = mode
        self.freq = freq
        self.maxsize = maxsize
        self.rotate_when = self.next_backup(self.freq)

    def __repr__ (self):
        return '<rotating-file logger: %s>' % self.file

    # We back up at midnight every 1) day, 2) monday, or 3) 1st of month
    def next_backup (self, freq):
        (yr, mo, day, hr, min, sec, wd, jday, dst) = time.localtime(time.time())
        if freq == 'daily':
            return time.mktime((yr,mo,day+1, 0,0,0, 0,0,-1))
        elif freq == 'weekly':
            return time.mktime((yr,mo,day-wd+7, 0,0,0, 0,0,-1)) # wd(monday)==0
        elif freq == 'monthly':
            return time.mktime((yr,mo+1,1, 0,0,0, 0,0,-1))
        else:
            return None                  # not a date-based backup

    def maybe_flush (self):              # rotate first if necessary
        self.maybe_rotate()
        if self.do_flush:                # from file_logger()
            self.file.flush()

    def maybe_rotate (self):
        if self.freq and time.time() > self.rotate_when:
            self.rotate()
            self.rotate_when = self.next_backup(self.freq)
        elif self.maxsize:               # rotate when we get too big
            try:
                if os.stat(self.filename)[stat.ST_SIZE] > self.maxsize:
                    self.rotate()
            except os.error:             # file not found, probably
                self.rotate()            # will create a new file

    def rotate (self):
        (yr, mo, day, hr, min, sec, wd, jday, dst) = time.localtime(time.time())
        try:
            self.file.close()
            newname = '%s.ends%04d%02d%02d' % (self.filename, yr, mo, day)
            try:
                open(newname, "r").close()      # check if file exists
                newname += "-%02d%02d%02d" % (hr, min, sec)
            except:                             # YEAR_MONTH_DAY is unique
                pass
            os.rename(self.filename, newname)
            self.file = open(self.filename, self.mode)
        except:
            pass

# syslog is a line-oriented log protocol - this class would be
# appropriate for FTP or HTTP logs, but not for dumping stderr to.

# TODO: a simple safety wrapper that will ensure that the line sent
# to syslog is reasonable.

# TODO: async version of syslog_client: now, log entries use blocking
# send()

import supervisor.medusa.m_syslog as m_syslog
syslog_logger = m_syslog.syslog_client

class syslog_logger (m_syslog.syslog_client):
    def __init__ (self, address, facility='user'):
        m_syslog.syslog_client.__init__ (self, address)
        self.facility = m_syslog.facility_names[facility]
        self.address=address

    def __repr__ (self):
        return '<syslog logger address=%s>' % (repr(self.address))

    def log(self, message):
        m_syslog.syslog_client.log (
                self,
                message,
                facility=self.facility,
                priority=m_syslog.LOG_INFO
                )

# log to a stream socket, asynchronously

class socket_logger (asynchat.async_chat):

    def __init__ (self, address):
        asynchat.async_chat.__init__(self)
        if isinstance(address, str):
            self.create_socket (socket.AF_UNIX, socket.SOCK_STREAM)
        else:
            self.create_socket (socket.AF_INET, socket.SOCK_STREAM)

        self.connect (address)
        self.address = address

    def __repr__ (self):
        return '<socket logger: address=%s>' % self.address

    def log (self, message):
        if message[-2:] != '\r\n':
            self.socket.push (message + '\r\n')
        else:
            self.socket.push (message)

# log to multiple places
class multi_logger:
    def __init__ (self, loggers):
        self.loggers = loggers

    def __repr__ (self):
        return '<multi logger: %s>' % (repr(self.loggers))

    def log (self, message):
        for logger in self.loggers:
            logger.log (message)

class resolving_logger:
    """Feed (ip, message) combinations into this logger to get a
    resolved hostname in front of the message.  The message will not
    be logged until the PTR request finishes (or fails)."""

    def __init__ (self, resolver, logger):
        self.resolver = resolver
        self.logger = logger

    class logger_thunk:
        def __init__ (self, message, logger):
            self.message = message
            self.logger = logger

        def __call__ (self, host, ttl, answer):
            if not answer:
                answer = host
            self.logger.log ('%s:%s' % (answer, self.message))

    def log (self, ip, message):
        self.resolver.resolve_ptr (
                ip,
                self.logger_thunk (
                        message,
                        self.logger
                        )
                )

class unresolving_logger:
    """Just in case you don't want to resolve"""
    def __init__ (self, logger):
        self.logger = logger

    def log (self, ip, message):
        self.logger.log ('%s:%s' % (ip, message))


def strip_eol (line):
    while line and line[-1] in '\r\n':
        line = line[:-1]
    return line

class tail_logger:
    """Keep track of the last <size> log messages"""
    def __init__ (self, logger, size=500):
        self.size = size
        self.logger = logger
        self.messages = []

    def log (self, message):
        self.messages.append (strip_eol (message))
        if len (self.messages) > self.size:
            del self.messages[0]
        self.logger.log (message)

########NEW FILE########
__FILENAME__ = medusa_gif
# -*- Mode: Python -*-

# the medusa icon as a python source file.

width = 97
height = 61

data = 'GIF89aa\000=\000\204\000\000\000\000\000\255\255\255\245\245\245ssskkkccc111)))\326\326\326!!!\316\316\316\300\300\300\204\204\000\224\224\224\214\214\214\200\200\200RRR\377\377\377JJJ\367\367\367BBB\347\347\347\000\204\000\020\020\020\265\265\265\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000!\371\004\001\000\000\021\000,\000\000\000\000a\000=\000\000\005\376`$\216di\236h\252\256l\353\276p,\317tm\337x\256\357|m\001@\240E\305\000\364\2164\206R)$\005\201\214\007r\012{X\255\312a\004\260\\>\026\3240\353)\224n\001W+X\334\373\231~\344.\303b\216\024\027x<\273\307\255G,rJiWN\014{S}k"?ti\013EdPQ\207G@_%\000\026yy\\\201\202\227\224<\221Fs$pOjWz\241<r@vO\236\231\233k\247M\2544\203F\177\235\236L#\247\256Z\270,\266BxJ[\276\256A]iE\304\305\262\273E\313\201\275i#\\\303\321\'h\203V\\\177\326\276\216\220P~\335\230_\264\013\342\275\344KF\233\360Q\212\352\246\000\367\274s\361\236\334\347T\341;\341\246\2202\177\3142\211`\242o\325@S\202\264\031\252\207\260\323\256\205\311\036\236\270\002\'\013\302\177\274H\010\324X\002\0176\212\037\376\321\360\032\226\207\244\2674(+^\202\346r\205J\0211\375\241Y#\256f\0127\315>\272\002\325\307g\012(\007\205\312#j\317(\012A\200\224.\241\003\346GS\247\033\245\344\264\366\015L\'PXQl]\266\263\243\232\260?\245\316\371\362\225\035\332\243J\273\332Q\263\357-D\241T\327\270\265\013W&\330\010u\371b\322IW0\214\261]\003\033Va\365Z#\207\213a\030k\2647\262\014p\354\024[n\321N\363\346\317\003\037P\000\235C\302\000\3228(\244\363YaA\005\022\255_\237@\260\000A\212\326\256qbp\321\332\266\011\334=T\023\010"!B\005\003A\010\224\020\220 H\002\337#\020 O\276E\357h\221\327\003\\\000b@v\004\351A.h\365\354\342B\002\011\257\025\\ \220\340\301\353\006\000\024\214\200pA\300\353\012\364\241k/\340\033C\202\003\000\310fZ\011\003V\240R\005\007\354\376\026A\000\000\360\'\202\177\024\004\210\003\000\305\215\360\000\000\015\220\240\332\203\027@\'\202\004\025VpA\000%\210x\321\206\032J\341\316\010\262\211H"l\333\341\200\200>"]P\002\212\011\010`\002\0066FP\200\001\'\024p]\004\027(8B\221\306]\000\201w>\002iB\001\007\340\260"v7J1\343(\257\020\251\243\011\242i\263\017\215\337\035\220\200\221\365m4d\015\016D\251\341iN\354\346Ng\253\200I\240\031\35609\245\2057\311I\302\2007t\231"&`\314\310\244\011e\226(\236\010w\212\300\234\011\012HX(\214\253\311@\001\233^\222pg{% \340\035\224&H\000\246\201\362\215`@\001"L\340\004\030\234\022\250\'\015(V:\302\235\030\240q\337\205\224\212h@\177\006\000\250\210\004\007\310\207\337\005\257-P\346\257\367]p\353\203\271\256:\203\236\211F\340\247\010\3329g\244\010\307*=A\000\203\260y\012\304s#\014\007D\207,N\007\304\265\027\021C\233\207%B\366[m\353\006\006\034j\360\306+\357\274a\204\000\000;'

########NEW FILE########
__FILENAME__ = monitor
# -*- Mode: Python -*-
#       Author: Sam Rushing <rushing@nightmare.com>

#
# python REPL channel.
#
RCS_ID = '$Id: monitor.py,v 1.5 2002/03/23 15:08:06 amk Exp $'

from supervisor.compat import md5
from supervisor.compat import print_function

import supervisor.medusa.text_socket as socket
import sys
import time

VERSION = RCS_ID.split()[2]

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

from supervisor.medusa.counter import counter
import supervisor.medusa.producers as producers

class monitor_channel (asynchat.async_chat):
    try_linemode = 1

    def __init__ (self, server, sock, addr):
        asynchat.async_chat.__init__ (self, sock)
        self.server = server
        self.addr = addr
        self.set_terminator ('\r\n')
        self.data = ''
        # local bindings specific to this channel
        self.local_env = sys.modules['__main__'].__dict__.copy()
        self.push ('Python ' + sys.version + '\r\n')
        self.push (sys.copyright+'\r\n')
        self.push ('Welcome to %s\r\n' % self)
        self.push ("[Hint: try 'from __main__ import *']\r\n")
        self.prompt()
        self.number = server.total_sessions.as_long()
        self.line_counter = counter()
        self.multi_line = []

    def handle_connect (self):
        # send IAC DO LINEMODE
        self.push ('\377\375\"')

    def close (self):
        self.server.closed_sessions.increment()
        asynchat.async_chat.close(self)

    def prompt (self):
        self.push ('>>> ')

    def collect_incoming_data (self, data):
        self.data = self.data + data
        if len(self.data) > 1024:
            # denial of service.
            self.push ('BCNU\r\n')
            self.close_when_done()

    def found_terminator (self):
        line = self.clean_line (self.data)
        self.data = ''
        self.line_counter.increment()
        # check for special case inputs...
        if not line and not self.multi_line:
            self.prompt()
            return
        if line in ['\004', 'exit']:
            self.push ('BCNU\r\n')
            self.close_when_done()
            return
        oldout = sys.stdout
        olderr = sys.stderr
        try:
            p = output_producer(self, olderr)
            sys.stdout = p
            sys.stderr = p
            try:
                # this is, of course, a blocking operation.
                # if you wanted to thread this, you would have
                # to synchronize, etc... and treat the output
                # like a pipe.  Not Fun.
                #
                # try eval first.  If that fails, try exec.  If that fails,
                # hurl.
                try:
                    if self.multi_line:
                        # oh, this is horrible...
                        raise SyntaxError
                    co = compile (line, repr(self), 'eval')
                    result = eval (co, self.local_env)
                    method = 'eval'
                    if result is not None:
                        print(repr(result))
                    self.local_env['_'] = result
                except SyntaxError:
                    try:
                        if self.multi_line:
                            if line and line[0] in [' ','\t']:
                                self.multi_line.append (line)
                                self.push ('... ')
                                return
                            else:
                                self.multi_line.append (line)
                                line = '\n'.join(self.multi_line)
                                co = compile (line, repr(self), 'exec')
                                self.multi_line = []
                        else:
                            co = compile (line, repr(self), 'exec')
                    except SyntaxError as why:
                        if why.args[0] == 'unexpected EOF while parsing':
                            self.push ('... ')
                            self.multi_line.append (line)
                            return
                        else:
                            t,v,tb = sys.exc_info()
                            del tb
                            raise t(v)
                    exec(co in self.local_env)
                    method = 'exec'
            except:
                method = 'exception'
                self.multi_line = []
                (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
                self.log_info('%s %s %s' %(t, v, tbinfo), 'warning')
        finally:
            sys.stdout = oldout
            sys.stderr = olderr
        self.log_info('%s:%s (%s)> %s' % (
                self.number,
                self.line_counter,
                method,
                repr(line))
                )
        self.push_with_producer (p)
        self.prompt()

    # for now, we ignore any telnet option stuff sent to
    # us, and we process the backspace key ourselves.
    # gee, it would be fun to write a full-blown line-editing
    # environment, etc...
    def clean_line (self, line):
        chars = []
        for ch in line:
            oc = ord(ch)
            if oc < 127:
                if oc in [8,177]:
                    # backspace
                    chars = chars[:-1]
                else:
                    chars.append (ch)
        return ''.join(chars)

class monitor_server (asyncore.dispatcher):

    SERVER_IDENT = 'Monitor Server (V%s)' % VERSION

    channel_class = monitor_channel

    def __init__ (self, hostname='127.0.0.1', port=8023):
        asyncore.dispatcher.__init__(self)
        self.hostname = hostname
        self.port = port
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind ((hostname, port))
        self.log_info('%s started on port %d' % (self.SERVER_IDENT, port))
        self.listen (5)
        self.closed             = 0
        self.failed_auths = 0
        self.total_sessions = counter()
        self.closed_sessions = counter()

    def writable (self):
        return 0

    def handle_accept (self):
        conn, addr = self.accept()
        self.log_info('Incoming monitor connection from %s:%d' % addr)
        self.channel_class (self, conn, addr)
        self.total_sessions.increment()

    def status (self):
        return producers.simple_producer (
                '<h2>%s</h2>'                                           % self.SERVER_IDENT
                + '<br><b>Total Sessions:</b> %s'               % self.total_sessions
                + '<br><b>Current Sessions:</b> %d'     % (
                        self.total_sessions.as_long()-self.closed_sessions.as_long()
                        )
                )

def hex_digest (s):
    m = md5()
    m.update(s)
    return ''.join([hex (ord (x))[2:] for x in list(m.digest())])

class secure_monitor_channel (monitor_channel):
    authorized = 0

    def __init__ (self, server, sock, addr):
        asynchat.async_chat.__init__ (self, sock)
        self.server = server
        self.addr = addr
        self.set_terminator ('\r\n')
        self.data = ''
        # local bindings specific to this channel
        self.local_env = {}
        # send timestamp string
        self.timestamp = str(time.time())
        self.count = 0
        self.line_counter = counter()
        self.number = int(server.total_sessions.as_long())
        self.multi_line = []
        self.push (self.timestamp + '\r\n')

    def found_terminator (self):
        if not self.authorized:
            if hex_digest ('%s%s' % (self.timestamp, self.server.password)) != self.data:
                self.log_info ('%s: failed authorization' % self, 'warning')
                self.server.failed_auths += 1
                self.close()
            else:
                self.authorized = 1
                self.push ('Python ' + sys.version + '\r\n')
                self.push (sys.copyright+'\r\n')
                self.push ('Welcome to %s\r\n' % self)
                self.prompt()
                self.data = ''
        else:
            monitor_channel.found_terminator (self)

class secure_encrypted_monitor_channel (secure_monitor_channel):
    """Wrap send() and recv() with a stream cipher"""

    def __init__ (self, server, conn, addr):
        key = server.password
        self.outgoing = server.cipher.new (key)
        self.incoming = server.cipher.new (key)
        secure_monitor_channel.__init__ (self, server, conn, addr)

    def send (self, data):
        # send the encrypted data instead
        ed = self.outgoing.encrypt (data)
        return secure_monitor_channel.send (self, ed)

    def recv (self, block_size):
        data = secure_monitor_channel.recv (self, block_size)
        if data:
            dd = self.incoming.decrypt (data)
            return dd
        else:
            return data

class secure_monitor_server (monitor_server):
    channel_class = secure_monitor_channel

    def __init__ (self, password, hostname='', port=8023):
        monitor_server.__init__ (self, hostname, port)
        self.password = password

    def status (self):
        p = monitor_server.status (self)
        # kludge
        p.data += '<br><b>Failed Authorizations:</b> %d' % self.failed_auths
        return p

# don't try to print from within any of the methods
# of this object. 8^)

class output_producer:
    def __init__ (self, channel, real_stderr):
        self.channel = channel
        self.data = ''
        # use _this_ for debug output
        self.stderr = real_stderr

    def check_data (self):
        if len(self.data) > 1<<16:
            # runaway output, close it.
            self.channel.close()

    def write (self, data):
        lines = data.split('\n')
        data = '\r\n'.join(lines)
        self.data += data
        self.check_data()

    def writeline (self, line):
        self.data = self.data + line + '\r\n'
        self.check_data()

    def writelines (self, lines):
        self.data = self.data + '\r\n'.join(lines) + '\r\n'
        self.check_data()

    def flush (self):
        pass

    def softspace (self, *args):
        pass

    def more (self):
        if self.data:
            result = self.data[:512]
            self.data = self.data[512:]
            return result
        else:
            return ''

if __name__ == '__main__':
    if '-s' in sys.argv:
        sys.argv.remove ('-s')
        print_function('Enter password: ', end='')
        password = raw_input()
    else:
        password = None

    if '-e' in sys.argv:
        sys.argv.remove ('-e')
        encrypt = 1
    else:
        encrypt = 0

    if len(sys.argv) > 1:
        port = int(sys.argv[1])
    else:
        port = 8023

    if password is not None:
        s = secure_monitor_server (password, '', port)
        if encrypt:
            s.channel_class = secure_encrypted_monitor_channel
            import sapphire
            s.cipher = sapphire
    else:
        s = monitor_server ('', port)

    asyncore.loop(use_poll=1)

########NEW FILE########
__FILENAME__ = monitor_client
# -*- Mode: Python -*-

# monitor client, unix version.
import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat
import supervisor.medusa.text_socket as socket
import sys
import os

from supervisor.compat import md5
from supervisor.compat import print_function

class stdin_channel (asyncore.file_dispatcher):
    def handle_read (self):
        data = self.recv(512)
        if not data:
            print_function('\nclosed.')
            self.sock_channel.close()
            try:
                self.close()
            except:
                pass

        data = data.replace('\n', '\r\n')
        self.sock_channel.push (data)

    def writable (self):
        return 0

    def log (self, *ignore):
        pass

class monitor_client (asynchat.async_chat):
    def __init__ (self, password, addr=('',8023), socket_type=socket.AF_INET):
        asynchat.async_chat.__init__ (self)
        self.create_socket (socket_type, socket.SOCK_STREAM)
        self.terminator = '\r\n'
        self.connect (addr)
        self.sent_auth = 0
        self.timestamp = ''
        self.password = password

    def collect_incoming_data (self, data):
        if not self.sent_auth:
            self.timestamp = self.timestamp + data
        else:
            sys.stdout.write (data)
            sys.stdout.flush()

    def found_terminator (self):
        if not self.sent_auth:
            self.push (hex_digest (self.timestamp + self.password) + '\r\n')
            self.sent_auth = 1
        else:
            print_function()

    def handle_close (self):
        # close all the channels, which will make the standard main
        # loop exit.
        for c in asyncore.socket_map.values():
            c.close()

    def log (self, *ignore):
        pass

class encrypted_monitor_client (monitor_client):
    """Wrap push() and recv() with a stream cipher"""

    def init_cipher (self, cipher, key):
        self.outgoing = cipher.new (key)
        self.incoming = cipher.new (key)

    def push (self, data):
        # push the encrypted data instead
        return monitor_client.push (self, self.outgoing.encrypt (data))

    def recv (self, block_size):
        data = monitor_client.recv (self, block_size)
        if data:
            return self.incoming.decrypt (data)
        else:
            return data

def hex_digest (s):
    m = md5.md5()
    m.update(s)
    return ''.join([hex (ord (x))[2:] for x in m.digest()])

if __name__ == '__main__':
    if len(sys.argv) == 1:
        print_function('Usage: %s host port' % sys.argv[0])
        sys.exit(0)

    if '-e' in sys.argv:
        encrypt = 1
        sys.argv.remove ('-e')
    else:
        encrypt = 0

    sys.stderr.write ('Enter Password: ')
    sys.stderr.flush()
    try:
        os.system ('stty -echo')
        p = raw_input()
        print_function()
    finally:
        os.system ('stty echo')
    stdin = stdin_channel (0)
    if len(sys.argv) > 1:
        if encrypt:
            client = encrypted_monitor_client(p, (sys.argv[1], int(sys.argv[2])))
            import sapphire
            client.init_cipher (sapphire, p)
        else:
            client = monitor_client (p, (sys.argv[1], int(sys.argv[2])))
    else:
        # default to local host, 'standard' port
        client = monitor_client (p)
    stdin.sock_channel = client
    asyncore.loop()

########NEW FILE########
__FILENAME__ = monitor_client_win32
# -*- Mode: Python -*-

# monitor client, win32 version

# since we can't do select() on stdin/stdout, we simply
# use threads and blocking sockets.  <sigh>

import supervisor.medusa.text_socket as socket
import sys
import supervisor.medusa.thread as thread

from supervisor.compat import md5
from supervisor.compat import print_function

def hex_digest (s):
    m = md5.md5()
    m.update (s)
    return ''.join([hex (ord (x))[2:] for x in list(m.digest())])

def reader (lock, sock, password):
    # first grab the timestamp
    ts = sock.recv (1024)[:-2]
    sock.send (hex_digest (ts+password) + '\r\n')
    while 1:
        d = sock.recv (1024)
        if not d:
            lock.release()
            print('Connection closed.  Hit <return> to exit')
            thread.exit()
        sys.stdout.write (d)
        sys.stdout.flush()

def writer (lock, sock, barrel="just kidding"):
    while lock.locked():
        sock.send (
                sys.stdin.readline()[:-1] + '\r\n'
                )

if __name__ == '__main__':
    if len(sys.argv) == 1:
        print('Usage: %s host port')
        sys.exit(0)
    print_function('Enter Password: ',end='')
    p = raw_input()
    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    s.connect((sys.argv[1], int(sys.argv[2])))
    l = thread.allocate_lock()
    l.acquire()
    thread.start_new_thread (reader, (l, s, p))
    writer (l, s)

########NEW FILE########
__FILENAME__ = m_syslog
# -*- Mode: Python -*-

# ======================================================================
# Copyright 1997 by Sam Rushing
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Sam
# Rushing not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================

"""socket interface to unix syslog.
On Unix, there are usually two ways of getting to syslog: via a
local unix-domain socket, or via the TCP service.

Usually "/dev/log" is the unix domain socket.  This may be different
for other systems.

>>> my_client = syslog_client ('/dev/log')

Otherwise, just use the UDP version, port 514.

>>> my_client = syslog_client (('my_log_host', 514))

On win32, you will have to use the UDP version.  Note that
you can use this to log to other hosts (and indeed, multiple
hosts).

This module is not a drop-in replacement for the python
<syslog> extension module - the interface is different.

Usage:

>>> c = syslog_client()
>>> c = syslog_client ('/strange/non_standard_log_location')
>>> c = syslog_client (('other_host.com', 514))
>>> c.log ('testing', facility='local0', priority='debug')

"""

# TODO: support named-pipe syslog.
# [see ftp://sunsite.unc.edu/pub/Linux/system/Daemons/syslog-fifo.tar.z]

# from <linux/sys/syslog.h>:
# ===========================================================================
# priorities/facilities are encoded into a single 32-bit quantity, where the
# bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
# (0-big number).  Both the priorities and the facilities map roughly
# one-to-one to strings in the syslogd(8) source code.  This mapping is
# included in this file.
#
# priorities (these are ordered)

LOG_EMERG               = 0             #  system is unusable
LOG_ALERT               = 1             #  action must be taken immediately
LOG_CRIT                = 2             #  critical conditions
LOG_ERR                 = 3             #  error conditions
LOG_WARNING             = 4             #  warning conditions
LOG_NOTICE              = 5             #  normal but significant condition
LOG_INFO                = 6             #  informational
LOG_DEBUG               = 7             #  debug-level messages

#  facility codes
LOG_KERN                = 0             #  kernel messages
LOG_USER                = 1             #  random user-level messages
LOG_MAIL                = 2             #  mail system
LOG_DAEMON              = 3             #  system daemons
LOG_AUTH                = 4             #  security/authorization messages
LOG_SYSLOG              = 5             #  messages generated internally by syslogd
LOG_LPR                 = 6             #  line printer subsystem
LOG_NEWS                = 7             #  network news subsystem
LOG_UUCP                = 8             #  UUCP subsystem
LOG_CRON                = 9             #  clock daemon
LOG_AUTHPRIV    = 10    #  security/authorization messages (private)

#  other codes through 15 reserved for system use
LOG_LOCAL0              = 16            #  reserved for local use
LOG_LOCAL1              = 17            #  reserved for local use
LOG_LOCAL2              = 18            #  reserved for local use
LOG_LOCAL3              = 19            #  reserved for local use
LOG_LOCAL4              = 20            #  reserved for local use
LOG_LOCAL5              = 21            #  reserved for local use
LOG_LOCAL6              = 22            #  reserved for local use
LOG_LOCAL7              = 23            #  reserved for local use

priority_names = {
        "alert":        LOG_ALERT,
        "crit":         LOG_CRIT,
        "debug":        LOG_DEBUG,
        "emerg":        LOG_EMERG,
        "err":          LOG_ERR,
        "error":        LOG_ERR,                #  DEPRECATED
        "info":         LOG_INFO,
        "notice":       LOG_NOTICE,
        "panic":        LOG_EMERG,              #  DEPRECATED
        "warn":         LOG_WARNING,            #  DEPRECATED
        "warning":      LOG_WARNING,
        }

facility_names = {
        "auth":         LOG_AUTH,
        "authpriv":     LOG_AUTHPRIV,
        "cron":         LOG_CRON,
        "daemon":       LOG_DAEMON,
        "kern":         LOG_KERN,
        "lpr":          LOG_LPR,
        "mail":         LOG_MAIL,
        "news":         LOG_NEWS,
        "security":     LOG_AUTH,               #  DEPRECATED
        "syslog":       LOG_SYSLOG,
        "user":         LOG_USER,
        "uucp":         LOG_UUCP,
        "local0":       LOG_LOCAL0,
        "local1":       LOG_LOCAL1,
        "local2":       LOG_LOCAL2,
        "local3":       LOG_LOCAL3,
        "local4":       LOG_LOCAL4,
        "local5":       LOG_LOCAL5,
        "local6":       LOG_LOCAL6,
        "local7":       LOG_LOCAL7,
        }

import supervisor.medusa.text_socket as socket

class syslog_client:
    def __init__ (self, address='/dev/log'):
        self.address = address
        self.stream = 0
        if isinstance(address, type('')):
            try:
                self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
                self.socket.connect(address)
            except socket.error:
                # Some Linux installations have /dev/log
                # a stream socket instead of a datagram socket.
                self.socket = socket.socket (socket.AF_UNIX,
                                             socket.SOCK_STREAM)
                self.stream = 1
        else:
            self.socket = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)

    # curious: when talking to the unix-domain '/dev/log' socket, a
    #   zero-terminator seems to be required.  this string is placed
    #   into a class variable so that it can be overridden if
    #   necessary.

    log_format_string = '<%d>%s\000'

    def log (self, message, facility=LOG_USER, priority=LOG_INFO):
        message = self.log_format_string % (
                self.encode_priority (facility, priority),
                message
                )
        if self.stream:
            self.socket.send (message)
        else:
            self.socket.sendto (message, self.address)

    def encode_priority (self, facility, priority):
        if type(facility) == type(''):
            facility = facility_names[facility]
        if type(priority) == type(''):
            priority = priority_names[priority]
        return (facility<<3) | priority

    def close (self):
        if self.stream:
            self.socket.close()

########NEW FILE########
__FILENAME__ = producers
# -*- Mode: Python -*-

RCS_ID = '$Id: producers.py,v 1.9 2004/04/21 13:56:28 akuchling Exp $'

"""
A collection of producers.
Each producer implements a particular feature:  They can be combined
in various ways to get interesting and useful behaviors.

For example, you can feed dynamically-produced output into the compressing
producer, then wrap this with the 'chunked' transfer-encoding producer.
"""

from asynchat import find_prefix_at_end

class simple_producer:
    """producer for a string"""
    def __init__ (self, data, buffer_size=1024):
        self.data = data
        self.buffer_size = buffer_size

    def more (self):
        if len (self.data) > self.buffer_size:
            result = self.data[:self.buffer_size]
            self.data = self.data[self.buffer_size:]
            return result
        else:
            result = self.data
            self.data = ''
            return result

class scanning_producer:
    """like simple_producer, but more efficient for large strings"""
    def __init__ (self, data, buffer_size=1024):
        self.data = data
        self.buffer_size = buffer_size
        self.pos = 0

    def more (self):
        if self.pos < len(self.data):
            lp = self.pos
            rp = min (
                    len(self.data),
                    self.pos + self.buffer_size
                    )
            result = self.data[lp:rp]
            self.pos += len(result)
            return result
        else:
            return ''

class lines_producer:
    """producer for a list of lines"""

    def __init__ (self, lines):
        self.lines = lines

    def more (self):
        if self.lines:
            chunk = self.lines[:50]
            self.lines = self.lines[50:]
            return '\r\n'.join(chunk) + '\r\n'
        else:
            return ''

class buffer_list_producer:
    """producer for a list of strings"""

    # i.e., data == ''.join(buffers)

    def __init__ (self, buffers):
        self.index = 0
        self.buffers = buffers

    def more (self):
        if self.index >= len(self.buffers):
            return ''
        else:
            data = self.buffers[self.index]
            self.index += 1
            return data

class file_producer:
    """producer wrapper for file[-like] objects"""

    # match http_channel's outgoing buffer size
    out_buffer_size = 1<<16

    def __init__ (self, file):
        self.done = 0
        self.file = file

    def more (self):
        if self.done:
            return ''
        else:
            data = self.file.read (self.out_buffer_size)
            if not data:
                self.file.close()
                del self.file
                self.done = 1
                return ''
            else:
                return data

# A simple output producer.  This one does not [yet] have
# the safety feature builtin to the monitor channel:  runaway
# output will not be caught.

# don't try to print from within any of the methods
# of this object.

class output_producer:
    """Acts like an output file; suitable for capturing sys.stdout"""
    def __init__ (self):
        self.data = ''

    def write (self, data):
        lines = data.split('\n')
        data = '\r\n'.join(lines)
        self.data += data

    def writeline (self, line):
        self.data = self.data + line + '\r\n'

    def writelines (self, lines):
        self.data = self.data + '\r\n'.join(lines) + '\r\n'

    def flush (self):
        pass

    def softspace (self, *args):
        pass

    def more (self):
        if self.data:
            result = self.data[:512]
            self.data = self.data[512:]
            return result
        else:
            return ''

class composite_producer:
    """combine a fifo of producers into one"""
    def __init__ (self, producers):
        self.producers = producers

    def more (self):
        while len(self.producers):
            p = self.producers[0]
            d = p.more()
            if d:
                return d
            else:
                self.producers.pop(0)
        else:
            return ''


class globbing_producer:
    """
    'glob' the output from a producer into a particular buffer size.
    helps reduce the number of calls to send().  [this appears to
    gain about 30% performance on requests to a single channel]
    """

    def __init__ (self, producer, buffer_size=1<<16):
        self.producer = producer
        self.buffer = ''
        self.buffer_size = buffer_size

    def more (self):
        while len(self.buffer) < self.buffer_size:
            data = self.producer.more()
            if data:
                self.buffer = self.buffer + data
            else:
                break
        r = self.buffer
        self.buffer = ''
        return r


class hooked_producer:
    """
    A producer that will call <function> when it empties,.
    with an argument of the number of bytes produced.  Useful
    for logging/instrumentation purposes.
    """

    def __init__ (self, producer, function):
        self.producer = producer
        self.function = function
        self.bytes = 0

    def more (self):
        if self.producer:
            result = self.producer.more()
            if not result:
                self.producer = None
                self.function (self.bytes)
            else:
                self.bytes += len(result)
            return result
        else:
            return ''

# HTTP 1.1 emphasizes that an advertised Content-Length header MUST be
# correct.  In the face of Strange Files, it is conceivable that
# reading a 'file' may produce an amount of data not matching that
# reported by os.stat() [text/binary mode issues, perhaps the file is
# being appended to, etc..]  This makes the chunked encoding a True
# Blessing, and it really ought to be used even with normal files.
# How beautifully it blends with the concept of the producer.

class chunked_producer:
    """A producer that implements the 'chunked' transfer coding for HTTP/1.1.
    Here is a sample usage:
            request['Transfer-Encoding'] = 'chunked'
            request.push (
                    producers.chunked_producer (your_producer)
                    )
            request.done()
    """

    def __init__ (self, producer, footers=None):
        self.producer = producer
        self.footers = footers

    def more (self):
        if self.producer:
            data = self.producer.more()
            if data:
                return '%x\r\n%s\r\n' % (len(data), data)
            else:
                self.producer = None
                if self.footers:
                    return '\r\n'.join(['0'] + self.footers) + '\r\n\r\n'
                else:
                    return '0\r\n\r\n'
        else:
            return ''

try:
    import zlib
except ImportError:
    zlib = None

class compressed_producer:
    """
    Compress another producer on-the-fly, using ZLIB
    """

    # Note: It's not very efficient to have the server repeatedly
    # compressing your outgoing files: compress them ahead of time, or
    # use a compress-once-and-store scheme.  However, if you have low
    # bandwidth and low traffic, this may make more sense than
    # maintaining your source files compressed.
    #
    # Can also be used for compressing dynamically-produced output.

    def __init__ (self, producer, level=5):
        self.producer = producer
        self.compressor = zlib.compressobj (level)

    def more (self):
        if self.producer:
            cdata = ''
            # feed until we get some output
            while not cdata:
                data = self.producer.more()
                if not data:
                    self.producer = None
                    return self.compressor.flush()
                else:
                    cdata = self.compressor.compress (data)
            return cdata
        else:
            return ''

class escaping_producer:

    """A producer that escapes a sequence of characters"""
    # Common usage: escaping the CRLF.CRLF sequence in SMTP, NNTP, etc...

    def __init__ (self, producer, esc_from='\r\n.', esc_to='\r\n..'):
        self.producer = producer
        self.esc_from = esc_from
        self.esc_to = esc_to
        self.buffer = ''
        self.find_prefix_at_end = find_prefix_at_end

    def more (self):
        esc_from = self.esc_from
        esc_to   = self.esc_to

        buffer = self.buffer + self.producer.more()

        if buffer:
            buffer = buffer.replace(esc_from, esc_to)
            i = self.find_prefix_at_end (buffer, esc_from)
            if i:
                # we found a prefix
                self.buffer = buffer[-i:]
                return buffer[:-i]
            else:
                # no prefix, return it all
                self.buffer = ''
                return buffer
        else:
            return buffer

########NEW FILE########
__FILENAME__ = put_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: put_handler.py,v 1.4 2002/08/01 18:15:45 akuchling Exp $'

import re

import supervisor.medusa.default_handler as default_handler
unquote         = default_handler.unquote
get_header      = default_handler.get_header

last_request = None

class put_handler:
    def __init__ (self, filesystem, uri_regex):
        self.filesystem = filesystem
        if type (uri_regex) == type(''):
            self.uri_regex = re.compile (uri_regex)
        else:
            self.uri_regex = uri_regex

    def match (self, request):
        uri = request.uri
        if request.command == 'PUT':
            m = self.uri_regex.match (uri)
            if m and m.end() == len(uri):
                return 1
        return 0

    def handle_request (self, request):

        path, params, query, fragment = request.split_uri()

        # strip off leading slashes
        while path and path[0] == '/':
            path = path[1:]

        if '%' in path:
            path = unquote (path)

        # make sure there's a content-length header
        cl = get_header (CONTENT_LENGTH, request.header)
        if not cl:
            request.error (411)
            return
        else:
            cl = int(cl)

        # don't let the try to overwrite a directory
        if self.filesystem.isdir (path):
            request.error (405)
            return

        is_update = self.filesystem.isfile (path)

        try:
            output_file = self.filesystem.open (path, 'wb')
        except:
            request.error (405)
            return

        request.collector = put_collector (output_file, cl, request, is_update)

        # no terminator while receiving PUT data
        request.channel.set_terminator (None)

        # don't respond yet, wait until we've received the data...

class put_collector:
    def __init__ (self, file, length, request, is_update):
        self.file               = file
        self.length             = length
        self.request    = request
        self.is_update  = is_update
        self.bytes_in   = 0

    def collect_incoming_data (self, data):
        ld = len(data)
        bi = self.bytes_in
        if (bi + ld) >= self.length:
            # last bit of data
            chunk = self.length - bi
            self.file.write (data[:chunk])
            self.file.close()

            if chunk != ld:
                print('orphaned %d bytes: <%s>' % (ld - chunk, repr(data[chunk:])))

            # do some housekeeping
            r = self.request
            ch = r.channel
            ch.current_request = None
            # set the terminator back to the default
            ch.set_terminator ('\r\n\r\n')
            if self.is_update:
                r.reply_code = 204 # No content
                r.done()
            else:
                r.reply_now (201) # Created
            # avoid circular reference
            del self.request
        else:
            self.file.write (data)
            self.bytes_in += ld

    def found_terminator (self):
        # shouldn't be called
        pass

CONTENT_LENGTH = re.compile ('Content-Length: ([0-9]+)', re.IGNORECASE)

########NEW FILE########
__FILENAME__ = redirecting_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: redirecting_handler.py,v 1.4 2002/03/20 17:37:48 amk Exp $'

import re
import supervisor.medusa.counter as counter

class redirecting_handler:

    def __init__ (self, pattern, redirect, regex_flag=re.IGNORECASE):
        self.pattern = pattern
        self.redirect = redirect
        self.patreg = re.compile (pattern, regex_flag)
        self.hits = counter.counter()

    def match (self, request):
        m = self.patreg.match (request.uri)
        return m and (m.end() == len(request.uri))

    def handle_request (self, request):
        self.hits.increment()
        m = self.patreg.match (request.uri)
        part = m.group(1)

        request['Location'] = self.redirect % part
        request.error (302) # moved temporarily

    def __repr__ (self):
        return '<Redirecting Handler at %08x [%s => %s]>' % (
                id(self),
                repr(self.pattern),
                repr(self.redirect)
                )

    def status (self):
        import supervisor.medusa.producers as producers
        return producers.simple_producer (
                '<li> Redirecting Handler %s => %s <b>Hits</b>: %s' % (
                        self.pattern, self.redirect, self.hits
                        )
                )

########NEW FILE########
__FILENAME__ = resolver
# -*- Mode: Python -*-

#
#       Author: Sam Rushing <rushing@nightmare.com>
#
RCS_ID =  '$Id: resolver.py,v 1.4 2002/03/20 17:37:48 amk Exp $'


# Fast, low-overhead asynchronous name resolver.  uses 'pre-cooked'
# DNS requests, unpacks only as much as it needs of the reply.

# see rfc1035 for details

from supervisor.compat import reduce

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.text_socket as socket
import sys
import time
from supervisor.medusa.counter import counter

VERSION = RCS_ID.split()[2]

# header
#                                    1  1  1  1  1  1
#      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                      ID                       |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                    QDCOUNT                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                    ANCOUNT                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                    NSCOUNT                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                    ARCOUNT                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+


# question
#                                    1  1  1  1  1  1
#      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                                               |
#    /                     QNAME                     /
#    /                                               /
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                     QTYPE                     |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                     QCLASS                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

# build a DNS address request, _quickly_
def fast_address_request (host, id=0):
    return (
            '%c%c' % (chr((id>>8)&0xff),chr(id&0xff))
            + '\001\000\000\001\000\000\000\000\000\000%s\000\000\001\000\001' % (
                    ''.join (['%c%s' % (chr(len(part)),part) for part in host.split('.')])
                    )
            )

def fast_ptr_request (host, id=0):
    return (
            '%c%c' % (chr((id>>8)&0xff),chr(id&0xff))
            + '\001\000\000\001\000\000\000\000\000\000%s\000\000\014\000\001' %
            ''.join(['%c%s' % (chr(len(part)),part) for part in host.split('.')])
            )

def unpack_name (r,pos):
    n = []
    while 1:
        ll = ord(r[pos])
        if ll&0xc0:
            # compression
            pos = (ll&0x3f << 8) + (ord(r[pos+1]))
        elif ll == 0:
            break
        else:
            pos += 1
            n.append (r[pos:pos+ll])
            pos += ll
    return '.'.join (n)

def skip_name (r,pos):
    while 1:
        ll = ord(r[pos])
        if ll&0xc0:
            # compression
            return pos + 2
        elif ll == 0:
            pos += 1
            break
        else:
            pos = pos + ll + 1
    return pos

def unpack_ttl (r,pos):
    return reduce (
            lambda x,y: (x<<8)|y,
            map (ord, r[pos:pos+4])
            )

# resource record
#                                    1  1  1  1  1  1
#      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                                               |
#    /                                               /
#    /                      NAME                     /
#    |                                               |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                      TYPE                     |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                     CLASS                     |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                      TTL                      |
#    |                                               |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                   RDLENGTH                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
#    /                     RDATA                     /
#    /                                               /
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

def unpack_address_reply (r):
    ancount = (ord(r[6])<<8) + (ord(r[7]))
    # skip question, first name starts at 12,
    # this is followed by QTYPE and QCLASS
    pos = skip_name (r, 12) + 4
    if ancount:
        # we are looking very specifically for
        # an answer with TYPE=A, CLASS=IN (\000\001\000\001)
        for an in range(ancount):
            pos = skip_name (r, pos)
            if r[pos:pos+4] == '\000\001\000\001':
                return (
                        unpack_ttl (r,pos+4),
                        '%d.%d.%d.%d' % tuple(map(ord,r[pos+10:pos+14]))
                        )
            # skip over TYPE, CLASS, TTL, RDLENGTH, RDATA
            pos += 8
            rdlength = (ord(r[pos])<<8) + (ord(r[pos+1]))
            pos = pos + 2 + rdlength
        return 0, None
    else:
        return 0, None

def unpack_ptr_reply (r):
    ancount = (ord(r[6])<<8) + (ord(r[7]))
    # skip question, first name starts at 12,
    # this is followed by QTYPE and QCLASS
    pos = skip_name (r, 12) + 4
    if ancount:
        # we are looking very specifically for
        # an answer with TYPE=PTR, CLASS=IN (\000\014\000\001)
        for an in range(ancount):
            pos = skip_name (r, pos)
            if r[pos:pos+4] == '\000\014\000\001':
                return (
                        unpack_ttl (r,pos+4),
                        unpack_name (r, pos+10)
                        )
            # skip over TYPE, CLASS, TTL, RDLENGTH, RDATA
            pos += 8
            rdlength = (ord(r[pos])<<8) + (ord(r[pos+1]))
            pos = pos + 2 + rdlength
        return 0, None
    else:
        return 0, None


# This is a UDP (datagram) resolver.

#
# It may be useful to implement a TCP resolver.  This would presumably
# give us more reliable behavior when things get too busy.  A TCP
# client would have to manage the connection carefully, since the
# server is allowed to close it at will (the RFC recommends closing
# after 2 minutes of idle time).
#
# Note also that the TCP client will have to prepend each request
# with a 2-byte length indicator (see rfc1035).
#

class resolver (asyncore.dispatcher):
    id = counter()
    def __init__ (self, server='127.0.0.1'):
        asyncore.dispatcher.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_DGRAM)
        self.server = server
        self.request_map = {}
        self.last_reap_time = int(time.time())      # reap every few minutes

    def writable (self):
        return 0

    def log (self, *args):
        pass

    def handle_close (self):
        self.log_info('closing!')
        self.close()

    def handle_error (self):      # don't close the connection on error
        file_fun_line, t, v, tbinfo = asyncore.compact_traceback()
        self.log_info(
                        'Problem with DNS lookup (%s:%s %s)' % (t, v, tbinfo),
                        'error')

    def get_id (self):
        return self.id.as_long() % (1<<16)

    def reap (self):          # find DNS requests that have timed out
        now = int(time.time())
        if now - self.last_reap_time > 180:        # reap every 3 minutes
            self.last_reap_time = now              # update before we forget
            for k,(host,unpack,callback,when) in list(self.request_map.items()):
                if now - when > 180:               # over 3 minutes old
                    del self.request_map[k]
                    try:                           # same code as in handle_read
                        callback (host, 0, None)   # timeout val is (0,None)
                    except:
                        file_fun_line, t, v, tbinfo = asyncore.compact_traceback()
                        self.log_info('%s %s %s' % (t,v,tbinfo), 'error')

    def resolve (self, host, callback):
        self.reap()                                # first, get rid of old guys
        self.socket.sendto (
                fast_address_request (host, self.get_id()),
                (self.server, 53)
                )
        self.request_map [self.get_id()] = (
                host, unpack_address_reply, callback, int(time.time()))
        self.id.increment()

    def resolve_ptr (self, host, callback):
        self.reap()                                # first, get rid of old guys
        ip = host.split('.')
        ip.reverse()
        ip = '.'.join(ip) + '.in-addr.arpa'
        self.socket.sendto (
                fast_ptr_request (ip, self.get_id()),
                (self.server, 53)
                )
        self.request_map [self.get_id()] = (
                host, unpack_ptr_reply, callback, int(time.time()))
        self.id.increment()

    def handle_read (self):
        reply, whence = self.socket.recvfrom (512)
        # for security reasons we may want to double-check
        # that <whence> is the server we sent the request to.
        id = (ord(reply[0])<<8) + ord(reply[1])
        if id in self.request_map:
            host, unpack, callback, when = self.request_map[id]
            del self.request_map[id]
            ttl, answer = unpack (reply)
            try:
                callback (host, ttl, answer)
            except:
                file_fun_line, t, v, tbinfo = asyncore.compact_traceback()
                self.log_info('%s %s %s' % ( t,v,tbinfo), 'error')

class rbl (resolver):

    def resolve_maps (self, host, callback):
        ip = host.split('.')
        ip.reverse()
        ip = '.'.join(ip) + '.rbl.maps.vix.com'
        self.socket.sendto (
                fast_ptr_request (ip, self.get_id()),
                (self.server, 53)
                )
        self.request_map [self.get_id()] = host, self.check_reply, callback
        self.id.increment()

    def check_reply (self, r):
        # we only need to check RCODE.
        rcode = (ord(r[3])&0xf)
        self.log_info('MAPS RBL; RCODE =%02x\n %s' % (rcode, repr(r)))
        return 0, rcode # (ttl, answer)


class hooked_callback:
    def __init__ (self, hook, callback):
        self.hook, self.callback = hook, callback

    def __call__ (self, *args):
        self.hook(*args)
        self.callback(*args)

class caching_resolver (resolver):
    """Cache DNS queries.  Will need to honor the TTL value in the replies"""

    def __init__ (self, server='127.0.0.1'):
        resolver.__init__(self, server)
        self.cache = {}
        self.forward_requests = counter()
        self.reverse_requests = counter()
        self.cache_hits = counter()

    def resolve (self, host, callback):
        self.forward_requests.increment()
        if host in self.cache:
            when, ttl, answer = self.cache[host]
            # ignore TTL for now
            callback (host, ttl, answer)
            self.cache_hits.increment()
        else:
            resolver.resolve (
                    self,
                    host,
                    hooked_callback (
                            self.callback_hook,
                            callback
                            )
                    )

    def resolve_ptr (self, host, callback):
        self.reverse_requests.increment()
        if host in self.cache:
            when, ttl, answer = self.cache[host]
            # ignore TTL for now
            callback (host, ttl, answer)
            self.cache_hits.increment()
        else:
            resolver.resolve_ptr (
                    self,
                    host,
                    hooked_callback (
                            self.callback_hook,
                            callback
                            )
                    )

    def callback_hook (self, host, ttl, answer):
        self.cache[host] = time.time(), ttl, answer

    SERVER_IDENT = 'Caching DNS Resolver (V%s)' % VERSION

    def status (self):
        import supervisor.medusa.producers as producers
        return producers.simple_producer (
                '<h2>%s</h2>'                                   % self.SERVER_IDENT
                + '<br>Server: %s'                              % self.server
                + '<br>Cache Entries: %d'               % len(self.cache)
                + '<br>Outstanding Requests: %d' % len(self.request_map)
                + '<br>Forward Requests: %s'    % self.forward_requests
                + '<br>Reverse Requests: %s'    % self.reverse_requests
                + '<br>Cache Hits: %s'                  % self.cache_hits
                )

#test_reply = """\000\000\205\200\000\001\000\001\000\002\000\002\006squirl\011nightmare\003com\000\000\001\000\001\300\014\000\001\000\001\000\001Q\200\000\004\315\240\260\005\011nightmare\003com\000\000\002\000\001\000\001Q\200\000\002\300\014\3006\000\002\000\001\000\001Q\200\000\015\003ns1\003iag\003net\000\300\014\000\001\000\001\000\001Q\200\000\004\315\240\260\005\300]\000\001\000\001\000\000\350\227\000\004\314\033\322\005"""
# def test_unpacker ():
#       print unpack_address_reply (test_reply)
#
# import time
# class timer:
#       def __init__ (self):
#               self.start = time.time()
#       def end (self):
#               return time.time() - self.start
#
# # I get ~290 unpacks per second for the typical case, compared to ~48
# # using dnslib directly.  also, that latter number does not include
# # picking the actual data out.
#
# def benchmark_unpacker():
#
#       r = range(1000)
#       t = timer()
#       for i in r:
#               unpack_address_reply (test_reply)
#       print '%.2f unpacks per second' % (1000.0 / t.end())

if __name__ == '__main__':
    if len(sys.argv) == 1:
        print('usage: %s [-r] [-s <server_IP>] host [host ...]' % sys.argv[0])
        sys.exit(0)
    elif '-s' in sys.argv:
        i = sys.argv.index('-s')
        server = sys.argv[i+1]
        del sys.argv[i:i+2]
    else:
        server = '127.0.0.1'

    if '-r' in sys.argv:
        reverse = 1
        i = sys.argv.index('-r')
        del sys.argv[i]
    else:
        reverse = 0

    if '-m' in sys.argv:
        maps = 1
        sys.argv.remove ('-m')
    else:
        maps = 0

    if maps:
        r = rbl (server)
    else:
        r = caching_resolver(server)

    count = len(sys.argv) - 1

    def print_it (host, ttl, answer):
        global count
        print('%s: %s' % (host, answer))
        count -= 1
        if not count:
            r.close()

    for host in sys.argv[1:]:
        if reverse:
            r.resolve_ptr (host, print_it)
        elif maps:
            r.resolve_maps (host, print_it)
        else:
            r.resolve (host, print_it)

    # hooked asyncore.loop()
    while asyncore.socket_map:
        asyncore.poll (30.0)
        print('requests outstanding: %d' % len(r.request_map))

########NEW FILE########
__FILENAME__ = rpc_client
# -*- Mode: Python -*-

# Copyright 1999, 2000 by eGroups, Inc.
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# eGroups not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# EGROUPS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL EGROUPS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

import marshal
import supervisor.medusa.text_socket as socket

try:
    from exceptions import StandardError
except ImportError:
    StandardError = Exception
import sys

#
# there are three clients in here.
#
# 1) rpc client
# 2) fastrpc client
# 3) async fastrpc client
#
# we hope that *whichever* choice you make, that you will enjoy the
# excellent hand-made construction, and return to do business with us
# again in the near future.
#

class RPC_Error(StandardError):
    pass

# ===========================================================================
#                                                         RPC Client
# ===========================================================================

# request types:
# 0 call
# 1 getattr
# 2 setattr
# 3 repr
# 4 del


class rpc_proxy:

    DEBUG = 0

    def __init__ (self, conn, oid):
        # route around __setattr__
        self.__dict__['conn'] = conn
        self.__dict__['oid'] = oid

    # Warning: be VERY CAREFUL with attribute references, keep
    #             this __getattr__ in mind!

    def __getattr__ (self, attr):
        # __getattr__ and __call__
        if attr == '__call__':
            # 0 == __call__
            return self.__remote_call__
        elif attr == '__repr__':
            # 3 == __repr__
            return self.__remote_repr__
        elif attr == '__getitem__':
            return self.__remote_getitem__
        elif attr == '__setitem__':
            return self.__remote_setitem__
        elif attr == '__len__':
            return self.__remote_len__
        else:
            # 1 == __getattr__
            return self.__send_request__ (1, attr)

    def __setattr__ (self, attr, value):
        return self.__send_request__ (2, (attr, value))

    def __del__ (self):
        self.__send_request__ (4, None)

    def __remote_repr__ (self):
        r = self.__send_request__ (3, None)
        return '<remote object [%s]>' % r[1:-1]

    def __remote_call__ (self, *args):
        return self.__send_request__ (0, args)

    def __remote_getitem__ (self, key):
        return self.__send_request__ (5, key)

    def __remote_setitem__ (self, key, value):
        return self.__send_request__ (6, (key, value))

    def __remote_len__ (self):
        return self.__send_request__ (7, None)

    _request_types_ = ['call', 'getattr', 'setattr', 'repr', 'del', 'getitem', 'setitem', 'len']

    def __send_request__ (self, *args):
        if self.DEBUG:
            kind = args[0]
            print (
                    'RPC: ==> %s:%08x:%s:%s' % (
                            self.conn.address,
                            self.oid,
                            self._request_types_[kind],
                            repr(args[1:])
                            )
                    )
        packet = marshal.dumps ((self.oid,)+args)
        # send request
        self.conn.send_packet (packet)
        # get response
        data = self.conn.receive_packet()
        # types of response:
        # 0: proxy
        # 1: error
        # 2: marshall'd data

        kind, value = marshal.loads (data)

        if kind == 0:
            # proxy (value == oid)
            if self.DEBUG:
                print('RPC: <== proxy(%08x)' % value)
            return rpc_proxy (self.conn, value)
        elif kind == 1:
            raise RPC_Error(value)
        else:
            if self.DEBUG:
                print('RPC: <== %s' % (repr(value)))
            return value

class rpc_connection:

    cache = {}

    def __init__ (self, address):
        self.address = address
        self.connect ()

    def connect (self):
        s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
        s.connect (self.address)
        self.socket = s

    def receive_packet (self):
        packet_len = int(self.socket.recv(8), 16)
        packet = []
        while packet_len:
            data = self.socket.recv (8192)
            packet.append (data)
            packet_len -= len(data)
        return ''.join(packet)

    def send_packet (self, packet):
        self.socket.send ('%08x%s' % (len(packet), packet))

def rpc_connect (address = ('localhost', 8746)):
    if address not in rpc_connection.cache:
        conn = rpc_connection (address)
        # get oid of remote object
        data = conn.receive_packet()
        (oid,) = marshal.loads (data)
        rpc_connection.cache[address] = rpc_proxy (conn, oid)
    return rpc_connection.cache[address]

# ===========================================================================
#                       fastrpc client
# ===========================================================================

class fastrpc_proxy:

    def __init__ (self, conn, path=()):
        self.conn = conn
        self.path = path

    def __getattr__ (self, attr):
        if attr == '__call__':
            return self.__method_caller__
        else:
            return fastrpc_proxy (self.conn, self.path + (attr,))

    def __method_caller__ (self, *args):
        # send request
        packet = marshal.dumps ((self.path, args))
        self.conn.send_packet (packet)
        # get response
        data = self.conn.receive_packet()
        error, result = marshal.loads (data)
        if error is None:
            return result
        else:
            raise RPC_Error(error)

    def __repr__ (self):
        return '<remote-method-%s at %x>' % ('.'.join(self.path), id (self))

def fastrpc_connect (address = ('localhost', 8748)):
    if address not in rpc_connection.cache:
        conn = rpc_connection (address)
        rpc_connection.cache[address] = fastrpc_proxy (conn)
    return rpc_connection.cache[address]

# ===========================================================================
#                                                async fastrpc client
# ===========================================================================

import supervisor.medusa.asynchat_25 as asynchat

class async_fastrpc_client (asynchat.async_chat):

    STATE_LENGTH = 'length state'
    STATE_PACKET = 'packet state'

    def __init__ (self, address=('idb', 3001)):

        asynchat.async_chat.__init__ (self)

        if type(address) is type(''):
            family = socket.AF_UNIX
        else:
            family = socket.AF_INET

        self.create_socket (family, socket.SOCK_STREAM)
        self.address = address
        self.request_fifo = []
        self.buffer = []
        self.pstate = self.STATE_LENGTH
        self.set_terminator (8)
        self._connected = 0
        self.connect (self.address)

    def log (self, *args):
        pass

    def handle_connect (self):
        self._connected = 1

    def close (self):
        self._connected = 0
        self.flush_pending_requests ('lost connection to rpc server')
        asynchat.async_chat.close(self)

    def flush_pending_requests (self, why):
        f = self.request_fifo
        while len(f):
            callback = f.pop(0)
            callback (why, None)

    def collect_incoming_data (self, data):
        self.buffer.append (data)

    def found_terminator (self):
        self.buffer, data = [], ''.join(self.buffer)

        if self.pstate is self.STATE_LENGTH:
            packet_length = int(data, 16)
            self.set_terminator (packet_length)
            self.pstate = self.STATE_PACKET
        else:
            # modified to fix socket leak in chat server, 2000-01-27, schiller@eGroups.net
            #self.set_terminator (8)
            #self.pstate = self.STATE_LENGTH
            error, result = marshal.loads (data)
            callback = self.request_fifo.pop(0)
            callback (error, result)
            self.close()    # for chat server

    def call_method (self, method, args, callback):
        if not self._connected:
            # might be a unix socket...
            family, type = self.family_and_type
            self.create_socket (family, type)
            self.connect (self.address)
        # push the request out the socket
        path = method.split('.')
        packet = marshal.dumps ((path, args))
        self.push ('%08x%s' % (len(packet), packet))
        self.request_fifo.append(callback)


if __name__ == '__main__':
    if '-f' in sys.argv:
        connect = fastrpc_connect
    else:
        connect = rpc_connect

    print('connecting...')
    c = connect()
    print('calling <remote>.calc.sum (1,2,3)')
    print(c.calc.sum(1, 2, 3))
    print('calling <remote>.calc.nonexistent(), expect an exception!')
    print(c.calc.nonexistent())

########NEW FILE########
__FILENAME__ = rpc_server
# -*- Mode: Python -*-

# Copyright 1999, 2000 by eGroups, Inc.
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# eGroups not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# EGROUPS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL EGROUPS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

# There are two RPC implementations here.

# The first ('rpc') attempts to be as transparent as possible, and
# passes along 'internal' methods like __getattr__, __getitem__, and
# __del__.  It is rather 'chatty', and may not be suitable for a
# high-performance system.

# The second ('fastrpc') is less flexible, but has much less overhead,
# and is easier to use from an asynchronous client.

import marshal
import supervisor.medusa.text_socket as socket
import sys

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

from supervisor.medusa.producers import scanning_producer
from supervisor.medusa.counter import counter

from supervisor.compat import reduce

MY_NAME = socket.gethostname().split('.')[0]

# ===========================================================================
#                                                         RPC server
# ===========================================================================

# marshal is good for low-level data structures.
# but when passing an 'object' (any non-marshallable object)
# we really want to pass a 'reference', which will act on
# the other side as a proxy.  How transparent can we make this?

class rpc_channel (asynchat.async_chat):

    """Simple RPC server."""

    # a 'packet': NNNNNNNNmmmmmmmmmmmmmmmm
    # (hex length in 8 bytes, followed by marshal'd packet data)
    # same protocol used in both directions.

    STATE_LENGTH = 'length state'
    STATE_PACKET = 'packet state'

    ac_out_buffer_size = 65536

    request_counter = counter()
    exception_counter = counter()
    client_counter = counter()

    def __init__ (self, root, conn, addr):
        self.root = root
        self.addr = addr
        asynchat.async_chat.__init__ (self, conn)
        self.pstate = self.STATE_LENGTH
        self.set_terminator (8)
        self.buffer = []
        self.proxies = {}
        rid = id(root)
        self.new_reference (root)
        p = marshal.dumps ((rid,))
        # send root oid to the other side
        self.push ('%08x%s' % (len(p), p))
        self.client_counter.increment()

    def new_reference (self, object):
        oid = id(object)
        ignore, refcnt = self.proxies.get (oid, (None, 0))
        self.proxies[oid] = (object, refcnt + 1)

    def forget_reference (self, oid):
        object, refcnt = self.proxies.get (oid, (None, 0))
        if refcnt > 1:
            self.proxies[oid] = (object, refcnt - 1)
        else:
            del self.proxies[oid]

    def log (self, *ignore):
        pass

    def collect_incoming_data (self, data):
        self.buffer.append (data)

    def found_terminator (self):
        self.buffer, data = [], ''.join(self.buffer)

        if self.pstate is self.STATE_LENGTH:
            packet_length = int(data, 16)
            self.set_terminator (packet_length)
            self.pstate = self.STATE_PACKET
        else:

            self.set_terminator (8)
            self.pstate = self.STATE_LENGTH

            oid, kind, arg = marshal.loads (data)

            obj, refcnt = self.proxies[oid]
            reply_kind = 2

            try:
                if kind == 0:
                    # __call__
                    result = obj(*arg)
                elif kind == 1:
                    # __getattr__
                    result = getattr (obj, arg)
                elif kind == 2:
                    # __setattr__
                    key, value = arg
                    setattr (obj, key, value)
                    result = None
                elif kind == 3:
                    # __repr__
                    result = repr(obj)
                elif kind == 4:
                    # __del__
                    self.forget_reference (oid)
                    result = None
                elif kind == 5:
                    # __getitem__
                    result = obj[arg]
                elif kind == 6:
                    # __setitem__
                    (key, value) = arg
                    obj[key] = value
                    result = None
                elif kind == 7:
                    # __len__
                    result = len(obj)
                else:
                    result = None

            except:
                reply_kind = 1
                (file,fun,line), t, v, tbinfo = asyncore.compact_traceback()
                result = '%s:%s:%s:%s (%s:%s)' % (MY_NAME, file, fun, line, t, str(v))
                self.log_info (result, 'error')
                self.exception_counter.increment()

            self.request_counter.increment()

            try:
                rb = marshal.dumps ((reply_kind, result))
            except ValueError:
                # unmarshallable object, return a reference
                rid = id(result)
                self.new_reference (result)
                rb = marshal.dumps ((0, rid))

            self.push_with_producer (
                    scanning_producer (
                            ('%08x' % len(rb)) + rb,
                            buffer_size = 65536
                            )
                    )

class rpc_server_root:
    pass

class rpc_server (asyncore.dispatcher):

    def __init__ (self, root, address = ('', 8746)):
        asyncore.dispatcher.__init__(self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind (address)
        self.listen (128)
        self.root = root

    def handle_accept (self):
        conn, addr = self.accept()
        rpc_channel (self.root, conn, addr)


# ===========================================================================
#                                                  Fast RPC server
# ===========================================================================

# no proxies, request consists
# of a 'chain' of getattrs terminated by a __call__.

# Protocol:
# <path>.<to>.<object> ( <param1>, <param2>, ... )
# => ( <value1>, <value2>, ... )
#
#
# (<path>, <params>)
# path: tuple of strings
# params: tuple of objects

class fastrpc_channel (asynchat.async_chat):

    """Simple RPC server"""

    # a 'packet': NNNNNNNNmmmmmmmmmmmmmmmm
    # (hex length in 8 bytes, followed by marshal'd packet data)
    # same protocol used in both directions.

    # A request consists of (<path-tuple>, <args-tuple>)
    # where <path-tuple> is a list of strings (eqv to 'a.b.c'.split('.'))

    STATE_LENGTH = 'length state'
    STATE_PACKET = 'packet state'

    def __init__ (self, root, conn, addr):
        self.root = root
        self.addr = addr
        asynchat.async_chat.__init__ (self, conn)
        self.pstate = self.STATE_LENGTH
        self.set_terminator (8)
        self.buffer = []

    def log (*ignore):
        pass

    def collect_incoming_data (self, data):
        self.buffer.append (data)

    def found_terminator (self):
        self.buffer, data = [], ''.join(self.buffer)

        if self.pstate is self.STATE_LENGTH:
            packet_length = int(data, 16)
            self.set_terminator (packet_length)
            self.pstate = self.STATE_PACKET
        else:
            self.set_terminator (8)
            self.pstate = self.STATE_LENGTH
            (path, params) = marshal.loads (data)
            o = self.root

            e = None

            try:
                for p in path:
                    o = getattr (o, p)
                result = o(*params)
            except:
                e = repr (asyncore.compact_traceback())
                result = None

            rb = marshal.dumps ((e,result))
            self.push (('%08x' % len(rb)) + rb)

class fastrpc_server (asyncore.dispatcher):

    def __init__ (self, root, address = ('', 8748)):
        asyncore.dispatcher.__init__(self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind (address)
        self.listen (128)
        self.root = root

    def handle_accept (self):
        conn, addr = self.accept()
        fastrpc_channel (self.root, conn, addr)

# ===========================================================================

if __name__ == '__main__':

    class thing:
        def __del__ (self):
            print('a thing has gone away %08x' % id(self))

    class sample_calc:

        def product (self, *values):
            return reduce (lambda a,b: a*b, values, 1)

        def sum (self, *values):
            return reduce (lambda a,b: a+b, values, 0)

        def eval (self, string):
            return eval (string)

        def make_a_thing (self):
            return thing()

    if '-f' in sys.argv:
        server_class = fastrpc_server
        address = ('', 8748)
    else:
        server_class = rpc_server
        address = ('', 8746)

    root = rpc_server_root()
    root.calc = sample_calc()
    root.sys = sys
    rs = server_class (root, address)
    asyncore.loop()

########NEW FILE########
__FILENAME__ = script_handler
# -*- Mode: Python -*-

# This is a simple python server-side script handler.

# A note about performance: This is really only suited for 'fast'
# scripts: The script should generate its output quickly, since the
# whole web server will stall otherwise.  This doesn't mean you have
# to write 'fast code' or anything, it simply means that you shouldn't
# call any long-running code, [like say something that opens up an
# internet connection, or a database query that will hold up the
# server].  If you need this sort of feature, you can support it using
# the asynchronous I/O 'api' that the rest of medusa is built on.  [or
# you could probably use threads]

# Put your script into your web docs directory (like a cgi-bin
# script), make sure it has the correct extension [see the overridable
# script_handler.extension member below].
#
# There's lots of things that can be done to tweak the restricted
# execution model.  Also, of course you could just use 'execfile'
# instead (this is now the default, see class variable
# script_handler.restricted)

import re
import sys

from supervisor.compat import StringIO

import supervisor.medusa.counter as counter
import supervisor.medusa.default_handler as default_handler
import supervisor.medusa.producers as producers

unquote    = default_handler.unquote

class script_handler:

    extension = 'mpy'
    restricted = 0

    script_regex = re.compile (
            r'.*/([^/]+\.%s)' % extension,
            re.IGNORECASE
            )

    def __init__ (self, filesystem):
        self.filesystem = filesystem
        self.hits = counter.counter()
        self.exceptions = counter.counter()

    def match (self, request):
        path = request.split_uri()[0]
        m = self.script_regex.match (path)
        return m and (m.end() == len(path))

    def handle_request (self, request):

        path = request.split_uri()[0]

        while path and path[0] == '/':
            path = path[1:]

        if '%' in path:
            path = unquote (path)

        if not self.filesystem.isfile (path):
            request.error (404)
            return
        else:

            self.hits.increment()

            request.script_filename = self.filesystem.translate (path)

            if request.command in ('PUT', 'POST'):
                # look for a Content-Length header.
                cl = request.get_header ('content-length')
                length = int(cl)
                if not cl:
                    request.error (411)
                else:
                    collector (self, length, request)
            else:
                self.continue_request (
                        request,
                        StringIO() # empty stdin
                        )

    def continue_request (self, request, stdin):
        temp_files = stdin, StringIO(), StringIO()
        old_files = sys.stdin, sys.stdout, sys.stderr

        try:
            sys.request = request
            sys.stdin, sys.stdout, sys.stderr = temp_files
            try:
                exec(compile(open(request.script_filename).read(), request.script_filename, 'exec'))
                request.reply_code = 200
            except:
                request.reply_code = 500
                self.exceptions.increment()
        finally:
            sys.stdin, sys.stdout, sys.stderr = old_files
            del sys.request

        i,o,e = temp_files

        if request.reply_code != 200:
            s = e.getvalue()
        else:
            s = o.getvalue()

        request['Content-Length'] = len(s)
        request.push (s)
        request.done()

    def status (self):
        return producers.simple_producer (
                '<li>Server-Side Script Handler'
                + '<ul>'
                + '  <li><b>Hits:</b> %s' % self.hits
                + '  <li><b>Exceptions:</b> %s' % self.exceptions
                + '</ul>'
                )


class persistent_script_handler:

    def __init__ (self):
        self.modules = {}
        self.hits = counter.counter()
        self.exceptions = counter.counter()

    def add_module (self, name, module):
        self.modules[name] = module

    def del_module (self, name):
        del self.modules[name]

    def match (self, request):
        path = request.split_uri()[0]
        parts = path.split ('/')
        if (len(parts)>1) and parts[1] in self.modules:
            module = self.modules[parts[1]]
            request.module = module
            return 1
        else:
            return 0

    def handle_request (self, request):
        if request.command in ('PUT', 'POST'):
            # look for a Content-Length header.
            cl = request.get_header ('content-length')
            length = int(cl)
            if not cl:
                request.error (411)
            else:
                collector (self, length, request)
        else:
            self.continue_request (request, StringIO())

    def continue_request (self, request, input_data):
        temp_files = input_data, StringIO(), StringIO()
        old_files = sys.stdin, sys.stdout, sys.stderr

        try:
            sys.stdin, sys.stdout, sys.stderr = temp_files
            # provide a default
            request['Content-Type'] = 'text/html'
            try:
                request.module.main (request)
                request.reply_code = 200
            except:
                request.reply_code = 500
                self.exceptions.increment()
        finally:
            sys.stdin, sys.stdout, sys.stderr = old_files

        i,o,e = temp_files

        if request.reply_code != 200:
            s = e.getvalue()
        else:
            s = o.getvalue()

        request['Content-Length'] = len(s)
        request.push (s)
        request.done()

class collector:

    def __init__ (self, handler, length, request):
        self.handler = handler
        self.request = request
        self.request.collector = self
        self.request.channel.set_terminator (length)
        self.buffer = StringIO()

    def collect_incoming_data (self, data):
        self.buffer.write (data)

    def found_terminator (self):
        self.buffer.seek(0)
        self.request.collector = None
        self.request.channel.set_terminator ('\r\n\r\n')
        self.handler.continue_request (
                self.request,
                self.buffer
                )

########NEW FILE########
__FILENAME__ = status_handler
# -*- Mode: Python -*-

VERSION_STRING = "$Id: status_handler.py,v 1.7 2003/12/24 16:08:16 akuchling Exp $"

#
# medusa status extension
#

import time
import re
from cgi import escape

from supervisor.compat import StringIO
from supervisor.compat import long

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.http_server as http_server
import supervisor.medusa.medusa_gif as medusa_gif
import supervisor.medusa.producers as producers
from supervisor.medusa.counter import counter

START_TIME = long(time.time())

class status_extension:
    hit_counter = counter()

    def __init__ (self, objects, statusdir='/status', allow_emergency_debug=0):
        self.objects = objects
        self.statusdir = statusdir
        self.allow_emergency_debug = allow_emergency_debug
        # We use /status instead of statusdir here because it's too
        # hard to pass statusdir to the logger, who makes the HREF
        # to the object dir.  We don't need the security-through-
        # obscurity here in any case, because the id is obscurity enough
        self.hyper_regex = re.compile('/status/object/([0-9]+)/.*')
        self.hyper_objects = []
        for object in objects:
            self.register_hyper_object (object)

    def __repr__ (self):
        return '<Status Extension (%s hits) at %x>' % (
                self.hit_counter,
                id(self)
                )

    def match (self, request):
        path, params, query, fragment = request.split_uri()
        # For reasons explained above, we don't use statusdir for /object
        return (path[:len(self.statusdir)] == self.statusdir or
                        path[:len("/status/object/")] == '/status/object/')

    # Possible Targets:
    # /status
    # /status/channel_list
    # /status/medusa.gif

    # can we have 'clickable' objects?
    # [yes, we can use id(x) and do a linear search]

    # Dynamic producers:
    # HTTP/1.0: we must close the channel, because it's dynamic output
    # HTTP/1.1: we can use the chunked transfer-encoding, and leave
    #   it open.

    def handle_request (self, request):
        path = request.split_uri()[0]
        self.hit_counter.increment()
        if path == self.statusdir:          # and not a subdirectory
            up_time = ''.join(english_time(long(time.time()) - START_TIME))
            request['Content-Type'] = 'text/html'
            request.push (
                    '<html>'
                    '<title>Medusa Status Reports</title>'
                    '<body bgcolor="#ffffff">'
                    '<h1>Medusa Status Reports</h1>'
                    '<b>Up:</b> %s' % up_time
                    )
            for i in range(len(self.objects)):
                try:
                    request.push (self.objects[i].status())
                except:
                    import traceback
                    stream = StringIO()
                    traceback.print_exc(None,stream)
                    request.push('<h2><font color="red">Error in Channel %3d: %s</font><pre>%s</pre>' % (i,escape(repr(self.objects[i])), escape(stream.getvalue())))
                request.push ('<hr>\r\n')
            request.push (
                    '<p><a href="%s/channel_list">Channel List</a>'
                    '<hr>'
                    '<img src="%s/medusa.gif" align=right width=%d height=%d>'
                    '</body></html>' % (
                            self.statusdir,
                            self.statusdir,
                            medusa_gif.width,
                            medusa_gif.height
                            )
                    )
            request.done()
        elif path == self.statusdir + '/channel_list':
            request['Content-Type'] = 'text/html'
            request.push ('<html><body>')
            request.push(channel_list_producer(self.statusdir))
            request.push (
                    '<hr>'
                    '<img src="%s/medusa.gif" align=right width=%d height=%d>' % (
                            self.statusdir,
                            medusa_gif.width,
                            medusa_gif.height
                            ) +
                    '</body></html>'
                    )
            request.done()

        elif path == self.statusdir + '/medusa.gif':
            request['Content-Type'] = 'image/gif'
            request['Content-Length'] = len(medusa_gif.data)
            request.push (medusa_gif.data)
            request.done()

        elif path == self.statusdir + '/close_zombies':
            message = (
                    '<h2>Closing all zombie http client connections...</h2>'
                    '<p><a href="%s">Back to the status page</a>' % self.statusdir
                    )
            request['Content-Type'] = 'text/html'
            request['Content-Length'] = len (message)
            request.push (message)
            now = int (time.time())
            for channel in asyncore.socket_map.keys():
                if channel.__class__ == http_server.http_channel:
                    if channel != request.channel:
                        if (now - channel.creation_time) > channel.zombie_timeout:
                            channel.close()
            request.done()

        # Emergency Debug Mode
        # If a server is running away from you, don't KILL it!
        # Move all the AF_INET server ports and perform an autopsy...
        # [disabled by default to protect the innocent]
        elif self.allow_emergency_debug and path == self.statusdir + '/emergency_debug':
            request.push ('<html>Moving All Servers...</html>')
            request.done()
            for channel in asyncore.socket_map.keys():
                if channel.accepting:
                    if type(channel.addr) is type(()):
                        ip, port = channel.addr
                        channel.socket.close()
                        channel.del_channel()
                        channel.addr = (ip, port+10000)
                        fam, typ = channel.family_and_type
                        channel.create_socket (fam, typ)
                        channel.set_reuse_addr()
                        channel.bind (channel.addr)
                        channel.listen(5)

        else:
            m = self.hyper_regex.match (path)
            if m:
                oid = int(m.group(1))
                for object in self.hyper_objects:
                    if id (object) == oid:
                        if hasattr (object, 'hyper_respond'):
                            object.hyper_respond (self, path, request)
            else:
                request.error (404)
                return

    def status (self):
        return producers.simple_producer (
                '<li>Status Extension <b>Hits</b> : %s' % self.hit_counter
                )

    def register_hyper_object (self, object):
        if not object in self.hyper_objects:
            self.hyper_objects.append (object)

import supervisor.medusa.logger as logger

class logger_for_status (logger.tail_logger):

    def status (self):
        return 'Last %d log entries for: %s' % (
                len (self.messages),
                html_repr (self)
                )

    def hyper_respond (self, sh, path, request):
        request['Content-Type'] = 'text/plain'
        messages = self.messages[:]
        messages.reverse()
        request.push (lines_producer (messages))
        request.done()

class lines_producer:
    def __init__ (self, lines):
        self.lines = lines

    def more (self):
        if self.lines:
            chunk = self.lines[:50]
            self.lines = self.lines[50:]
            return '\r\n'.join(chunk) + '\r\n'
        else:
            return ''

class channel_list_producer (lines_producer):
    def __init__ (self, statusdir):
        channel_reprs = ['&lt;' + repr(x)[1:-1] + '&gt;' for x in asyncore.socket_map.values()]
        channel_reprs.sort()
        lines_producer.__init__ (
                self,
                ['<h1>Active Channel List</h1>',
                 '<pre>'
                 ] + channel_reprs + [
                         '</pre>',
                         '<p><a href="%s">Status Report</a>' % statusdir
                         ]
                )


def html_repr (object):
    so = escape (repr (object))
    if hasattr (object, 'hyper_respond'):
        return '<a href="/status/object/%d/">%s</a>' % (id (object), so)
    else:
        return so

def html_reprs (list, front='', back=''):
    reprs = list(map (
            lambda x,f=front,b=back: '%s%s%s' % (f,x,b),
            [escape (html_repr(x)) for x in list]
            ))
    reprs.sort()
    return reprs

# for example, tera, giga, mega, kilo
# p_d (n, (1024, 1024, 1024, 1024))
# smallest divider goes first - for example
# minutes, hours, days
# p_d (n, (60, 60, 24))

def progressive_divide (n, parts):
    result = []
    for part in parts:
        n, rem = divmod (n, part)
        result.append (rem)
    result.append (n)
    return result

# b,k,m,g,t
def split_by_units (n, units, dividers, format_string):
    divs = progressive_divide (n, dividers)
    result = []
    for i in range(len(units)):
        if divs[i]:
            result.append (format_string % (divs[i], units[i]))
    result.reverse()
    if not result:
        return [format_string % (0, units[0])]
    else:
        return result

def english_bytes (n):
    return split_by_units (
            n,
            ('','K','M','G','T'),
            (1024, 1024, 1024, 1024, 1024),
            '%d %sB'
            )

def english_time (n):
    return split_by_units (
            n,
            ('secs', 'mins', 'hours', 'days', 'weeks', 'years'),
            (         60,     60,      24,     7,       52),
            '%d %s'
            )

########NEW FILE########
__FILENAME__ = asyn_http_bench
# -*- Mode: Python -*-

from supervisor.compat import as_bytes

import asyncore
import socket
import sys

def blurt (thing):
    sys.stdout.write (thing)
    sys.stdout.flush ()

total_sessions = 0

class http_client (asyncore.dispatcher_with_send):
    def __init__ (self, host='127.0.0.1', port=80, uri='/', num=10):
        asyncore.dispatcher_with_send.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.host = host
        self.port = port
        self.uri = uri
        self.num = num
        self.bytes = 0
        self.connect ((host, port))

    def log (self, *info):
        pass

    def handle_connect (self):
        self.connected = 1
#               blurt ('o')
        self.send (as_bytes('GET %s HTTP/1.0\r\n\r\n' % self.uri))

    def handle_read (self):
#               blurt ('.')
        d = self.recv (8192)
        self.bytes += len(d)

    def handle_close (self):
        global total_sessions
#               blurt ('(%d)' % (self.bytes))
        self.close()
        total_sessions += 1
        if self.num:
            http_client (self.host, self.port, self.uri, self.num-1)

import time
class timer:
    def __init__ (self):
        self.start = time.time()
    def end (self):
        return time.time() - self.start

from asyncore import socket_map, poll

MAX = 0

def loop (timeout=30.0):
    global MAX
    while socket_map:
        if len(socket_map) > MAX:
            MAX = len(socket_map)
        poll (timeout)

if __name__ == '__main__':
    if len(sys.argv) < 6:
        print('usage: %s <host> <port> <uri> <hits> <num_clients>' % sys.argv[0])
    else:
        [host, port, uri, hits, num] = sys.argv[1:]
        hits = int(hits)
        num = int(num)
        port = int(port)
        t = timer()
        clients = [http_client(host, port, uri, hits-1) for x in range(num)]
        #import profile
        #profile.run ('loop')
        loop()
        total_time = t.end()
        print (
                '\n%d clients\n%d hits/client\n'
                'total_hits:%d\n%.3f seconds\ntotal hits/sec:%.3f' % (
                        num,
                        hits,
                        total_sessions,
                        total_time,
                        total_sessions / total_time
                        )
                )
        print('Max. number of concurrent sessions: %d' % MAX)


# linux 2.x, talking to medusa
# 50 clients
# 1000 hits/client
# total_hits:50000
# 2255.858 seconds
# total hits/sec:22.165
# Max. number of concurrent sessions: 50

########NEW FILE########
__FILENAME__ = bench
# -*- Mode: Python -*-

# benchmark a single channel, pipelined

request = 'GET /index.html HTTP/1.0\r\nConnection: Keep-Alive\r\n\r\n'
last_request = 'GET /index.html HTTP/1.0\r\nConnection: close\r\n\r\n'

import supervisor.medusa.text_socket as socket
import time

class timer:
    def __init__ (self):
        self.start = time.time()
    def end (self):
        return time.time() - self.start

def bench (host, port=80, n=100):
    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    s.connect ((host, port))
    t = timer()
    s.send ((request * n) + last_request)
    while 1:
        d = s.recv(65536)
        if not d:
            break
    total = t.end()
    print('time: %.2f seconds  (%.2f hits/sec)' % (total, n / total))

if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3:
        print('usage: %s <host> <port> <count>' % (sys.argv[0]))
    else:
        bench(sys.argv[1], int(sys.argv[2]), int(sys.argv[3]))

########NEW FILE########
__FILENAME__ = max_sockets
# -*- Mode: Python -*-

import supervisor.medusa.text_socket as socket
import select

# several factors here we might want to test:
# 1) max we can create
# 2) max we can bind
# 3) max we can listen on
# 4) max we can connect

def max_server_sockets():
    sl = []
    while 1:
        try:
            s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
            s.bind (('',0))
            s.listen(5)
            sl.append (s)
        except:
            break
    num = len(sl)
    for s in sl:
        s.close()
    del sl
    return num

def max_client_sockets():
    # make a server socket
    server = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    server.bind (('', 9999))
    server.listen (5)
    sl = []
    while 1:
        try:
            s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
            s.connect (('', 9999))
            conn, addr = server.accept()
            sl.append ((s,conn))
        except:
            break
    num = len(sl)
    for s,c in sl:
        s.close()
        c.close()
    del sl
    return num

def max_select_sockets():
    sl = []
    while 1:
        try:
            s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
            s.bind (('',0))
            s.listen(5)
            sl.append (s)
            select.select(sl,[],[],0)
        except:
            break
    num = len(sl) - 1
    for s in sl:
        s.close()
    del sl
    return num

########NEW FILE########
__FILENAME__ = test_11
# -*- Mode: Python -*-

import supervisor.medusa.text_socket as socket
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import asynchat_25 as asynchat

# get some performance figures for an HTTP/1.1 server.
# use pipelining.

class test_client (asynchat.async_chat):

    ac_in_buffer_size = 16384
    ac_out_buffer_size = 16384

    total_in = 0

    concurrent = 0
    max_concurrent = 0

    def __init__ (self, addr, chain):
        asynchat.async_chat.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.set_terminator ('\r\n\r\n')
        self.connect (addr)
        self.push (chain)

    def handle_connect (self):
        test_client.concurrent += 1
        if test_client.concurrent > test_client.max_concurrent:
            test_client.max_concurrent = test_client.concurrent

    def handle_expt (self):
        print('unexpected FD_EXPT thrown.  closing()')
        self.close()

    def close (self):
        test_client.concurrent -= 1
        asynchat.async_chat.close(self)

    def collect_incoming_data (self, data):
        test_client.total_in += len(data)

    def found_terminator (self):
        pass

    def log (self, *args):
        pass


import time

class timer:
    def __init__ (self):
        self.start = time.time()

    def end (self):
        return time.time() - self.start

def build_request_chain (num, host, request_size):
    s = 'GET /test%d.html HTTP/1.1\r\nHost: %s\r\n\r\n' % (request_size, host)
    sl = [s] * (num-1)
    sl.append (
            'GET /test%d.html HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n' % (
                    request_size, host
                    )
            )
    return ''.join(sl)

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 6:
        print('usage: %s <host> <port> <request-size> <num-requests> <num-connections>\n' % sys.argv[0])
    else:
        host = sys.argv[1]

        ip = socket.gethostbyname (host)

        port, request_size, num_requests, num_conns = [int(i) for i in sys.argv[2:]]

        chain = build_request_chain (num_requests, host, request_size)

        t = timer()
        for i in range (num_conns):
            test_client ((host,port), chain)
        asyncore.loop()
        total_time = t.end()

        # ok, now do some numbers
        total_bytes = test_client.total_in
        num_trans = num_requests * num_conns
        throughput = float (total_bytes) / total_time
        trans_per_sec = num_trans / total_time

        sys.stderr.write ('total time: %.2f\n' % total_time)
        sys.stderr.write ('number of transactions: %d\n' % num_trans)
        sys.stderr.write ('total bytes sent: %d\n' % total_bytes)
        sys.stderr.write ('total throughput (bytes/sec): %.2f\n' % throughput)
        sys.stderr.write ('transactions/second: %.2f\n' % trans_per_sec)
        sys.stderr.write ('max concurrent connections: %d\n' % test_client.max_concurrent)

        sys.stdout.write (
            ','.join (str(i) for i in (num_conns, num_requests, request_size, throughput, trans_per_sec)) + '\n'
        )

########NEW FILE########
__FILENAME__ = test_lb
# -*- Mode: Python -*-

# Get a lower bound for Medusa performance with a simple async
# client/server benchmark built on the async lib.  The idea is to test
# all the underlying machinery [select, asyncore, asynchat, etc...] in
# a context where there is virtually no processing of the data.

import supervisor.medusa.text_socket as socket
import sys

# ==================================================
# server
# ==================================================

from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import asynchat_25 as asynchat

class test_channel (asynchat.async_chat):

    ac_in_buffer_size = 16384
    ac_out_buffer_size = 16384

    total_in = 0

    def __init__ (self, conn, addr):
        asynchat.async_chat.__init__ (self, conn)
        self.set_terminator ('\r\n\r\n')
        self.buffer = ''

    def collect_incoming_data (self, data):
        self.buffer = self.buffer + data
        test_channel.total_in += len(data)

    def found_terminator (self):
        # we've gotten the data, now send it back
        data = self.buffer
        self.buffer = ''
        self.push (data+'\r\n\r\n')

    def handle_close (self):
        sys.stdout.write ('.'); sys.stdout.flush()
        self.close()

    def log (self, *args):
        pass

class test_server (asyncore.dispatcher):
    def __init__ (self, addr):
        asyncore.dispatcher.__init__(self)
        if type(addr) == type(''):
            f = socket.AF_UNIX
        else:
            f = socket.AF_INET

        self.create_socket (f, socket.SOCK_STREAM)
        self.bind (addr)
        self.listen (5)
        print('server started on %s' % str(addr))

    def handle_accept (self):
        conn, addr = self.accept()
        test_channel (conn, addr)

# ==================================================
# client
# ==================================================

# pretty much the same behavior, except that we kick
# off the exchange and decide when to quit

class test_client (test_channel):

    def __init__ (self, addr, packet, number):
        if type(addr) == type(''):
            f = socket.AF_UNIX
        else:
            f = socket.AF_INET

        asynchat.async_chat.__init__ (self)
        self.create_socket (f, socket.SOCK_STREAM)
        self.set_terminator ('\r\n\r\n')
        self.buffer = ''
        self.connect (addr)
        self.push (packet + '\r\n\r\n')
        self.number = number
        self.count = 0

    def handle_connect (self):
        pass

    def found_terminator (self):
        self.count += 1
        if self.count == self.number:
            sys.stdout.write('.'); sys.stdout.flush()
            self.close()
        else:
            test_channel.found_terminator (self)

import time

class timer:
    def __init__ (self):
        self.start = time.time()

    def end (self):
        return time.time() - self.start

if __name__ == '__main__':
    if '--poll' in sys.argv:
        sys.argv.remove ('--poll')
        use_poll=1
    else:
        use_poll=0

    if len(sys.argv) == 1:
        print('usage: %s\n'\
              '  (as a server) [--poll] -s <ip> <port>\n'\
              '  (as a client) [--poll] -c <ip> <port> <packet-size> <num-packets> <num-connections>\n' % sys.argv[0])
        sys.exit(0)
    if sys.argv[1] == '-s':
        s = test_server((sys.argv[2], int(sys.argv[3])))
        asyncore.loop(use_poll=use_poll)
    elif sys.argv[1] == '-c':
        # create the packet
        packet = int(sys.argv[4]) * 'B'
        host = sys.argv[2]
        port = int(sys.argv[3])
        num_packets = int(sys.argv[5])
        num_conns = int(sys.argv[6])

        t = timer()
        for i in range (num_conns):
            test_client ((host,port), packet, num_packets)
        asyncore.loop(use_poll=use_poll)
        total_time = t.end()

        # ok, now do some numbers
        bytes = test_client.total_in
        num_trans = num_packets * num_conns
        total_bytes = num_trans * len(packet)
        throughput = float (total_bytes) / total_time
        trans_per_sec = num_trans / total_time

        sys.stderr.write ('total time: %.2f\n' % total_time)
        sys.stderr.write ( 'number of transactions: %d\n' % num_trans)
        sys.stderr.write ( 'total bytes sent: %d\n' % total_bytes)
        sys.stderr.write ( 'total throughput (bytes/sec): %.2f\n' % throughput)
        sys.stderr.write ( ' [note, throughput is this amount in each direction]\n')
        sys.stderr.write ( 'transactions/second: %.2f\n' % trans_per_sec)

        sys.stdout.write (
            ','.join([str(i) for i in (num_conns, num_packets, len(packet), throughput, trans_per_sec)]) + '\n'
        )

########NEW FILE########
__FILENAME__ = test_medusa
# -*- Mode: Python -*-

import supervisor.medusa.text_socket as socket
import time
from supervisor.medusa import http_date

now = http_date.build_http_date (time.time())

cache_request = '\r\n'.join(
        ['GET / HTTP/1.0',
         'If-Modified-Since: %s' % now,
         ]) + '\r\n\r\n'

nocache_request = 'GET / HTTP/1.0\r\n\r\n'

def get (request, host='', port=8080):
    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    s.send(request)
    while 1:
        d = s.recv (8192)
        if not d:
            break
    s.close()

class timer:
    def __init__ (self):
        self.start = time.time()
    def end (self):
        return time.time() - self.start

def test_cache (n=1000):
    t = timer()
    for i in range (n):
        get(cache_request)
    end = t.end()
    print('cache: %d requests, %.2f seconds, %.2f hits/sec' % (n, end, n / end))


def test_nocache (n=1000):
    t = timer()
    for i in range (n):
        get(nocache_request)
    end = t.end()
    print('nocache: %d requests, %.2f seconds, %.2f hits/sec' % (n, end, n / end))

if __name__ == '__main__':
    test_cache()
    test_nocache()

########NEW FILE########
__FILENAME__ = test_producers
##
## Test script for producers.py
##
#
#__revision__ = "$Id: test_producers.py,v 1.2 2002/09/18 20:16:40 akuchling Exp $"
#
#import StringIO, zlib
#from sancho.unittest import TestScenario, parse_args, run_scenarios
#
#tested_modules = ["supervisor.medusa.producers"]
#
#
#from supervisor.medusa import producers
#
#test_string = ''
#for i in range(16385):
#    test_string += chr(48 + (i%10))
#
#class ProducerTest (TestScenario):
#
#    def setup (self):
#        pass
#
#    def shutdown (self):
#        pass
#
#    def _check_all (self, p, expected_string):
#        # Check that a producer returns all of the string,
#        # and that it's the unchanged string.
#        count = 0
#        data = ""
#        while 1:
#            s = p.more()
#            if s == "":
#                break
#            count += len(s)
#            data += s
#        self.test_val('count', len(expected_string))
#        self.test_val('data', expected_string)
#        self.test_val('p.more()', '')
#        return data
#
#    def check_simple (self):
#        p = producers.simple_producer(test_string)
#        self.test_val('p.more()', test_string[:1024])
#
#        p = producers.simple_producer(test_string, buffer_size = 5)
#        self._check_all(p, test_string)
#
#    def check_scanning (self):
#        p = producers.scanning_producer(test_string)
#        self.test_val('p.more()', test_string[:1024])
#
#        p = producers.scanning_producer(test_string, buffer_size = 5)
#        self._check_all(p, test_string)
#
#    def check_lines (self):
#        p = producers.lines_producer(['a']* 65)
#        self._check_all(p, 'a\r\n'*65)
#
#    def check_buffer (self):
#        p = producers.buffer_list_producer(['a']* 1027)
#        self._check_all(p, 'a'*1027)
#
#    def check_file (self):
#        f = StringIO.StringIO(test_string)
#        p = producers.file_producer(f)
#        self._check_all(p, test_string)
#
#    def check_output (self):
#        p = producers.output_producer()
#        for i in range(0,66):
#            p.write('a')
#        for i in range(0,65):
#            p.write('b\n')
#        self._check_all(p, 'a'*66 + 'b\r\n'*65)
#
#    def check_composite (self):
#        p1 = producers.simple_producer('a'*66, buffer_size = 5)
#        p2 = producers.lines_producer(['b']*65)
#        p = producers.composite_producer([p1, p2])
#        self._check_all(p, 'a'*66 + 'b\r\n'*65)
#
#    def check_glob (self):
#        p1 = producers.simple_producer(test_string, buffer_size = 5)
#        p = producers.globbing_producer(p1, buffer_size = 1024)
#        self.test_true('1024 <= len(p.more())')
#
#    def check_hooked (self):
#        def f (num_bytes):
#            self.test_val('num_bytes', len(test_string))
#        p1 = producers.simple_producer(test_string, buffer_size = 5)
#        p = producers.hooked_producer(p1, f)
#        self._check_all(p, test_string)
#
#    def check_chunked (self):
#        p1 = producers.simple_producer('the quick brown fox', buffer_size = 5)
#        p = producers.chunked_producer(p1, footers=['FOOTER'])
#        self._check_all(p, """5\r
#the q\r
#5\r
#uick \r
#5\r
#brown\r
#4\r
# fox\r
#0\r
#FOOTER\r
#\r\n""")
#
#    def check_compressed (self):
#        p1 = producers.simple_producer(test_string, buffer_size = 5)
#        p = producers.compressed_producer(p1)
#        compr_data = self._check_all(p, zlib.compress(test_string, 5))
#        self.test_val('zlib.decompress(compr_data)', test_string)
#
#    def check_escaping (self):
#        p1 = producers.simple_producer('the quick brown fox', buffer_size = 5)
#        p = producers.escaping_producer(p1,
#                                        esc_from = ' ',
#                                        esc_to = '_')
#        self._check_all(p, 'the_quick_brown_fox')
#
## class ProducerTest
#
#
#if __name__ == "__main__":
#    (scenarios, options) = parse_args()
#    run_scenarios(scenarios, options)

########NEW FILE########
__FILENAME__ = test_single_11
# -*- Mode: Python -*-

# no-holds barred, test a single channel's pipelining speed

import supervisor.medusa.text_socket as socket

def build_request_chain (num, host, request_size):
    s = 'GET /test%d.html HTTP/1.1\r\nHost: %s\r\n\r\n' % (request_size, host)
    sl = [s] * (num-1)
    sl.append (
            'GET /test%d.html HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n' % (
                    request_size, host
                    )
            )
    return ''.join(sl)

import time

class timer:
    def __init__ (self):
        self.start = time.time()

    def end (self):
        return time.time() - self.start

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 5:
        print('usage: %s <host> <port> <request-size> <num-requests>' % (sys.argv[0]))
    else:
        host = sys.argv[1]
        port, request_size, num_requests = (int(i) for i in sys.argv[2:])
        chain = build_request_chain (num_requests, host, request_size)
        s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
        s.connect ((host,port))
        t = timer()
        s.send (chain)
        num_bytes = 0
        while 1:
            data = s.recv(16384)
            if not data:
                break
            else:
                num_bytes += len(data)
        total_time = t.end()
        print('total bytes received: %d' % num_bytes)
        print('total time: %.2f sec' % total_time)
        print('transactions/sec: %.2f' % (num_requests / total_time))

########NEW FILE########
__FILENAME__ = text_socket
# -*- Mode: Python -*-

__author__ = 'Scott Maxwell'

from supervisor.compat import PY3
from supervisor.compat import as_string, as_bytes

from socket import * # relied on to be imported from elsewhere

if PY3:
    bin_socket = socket
    class text_socket(socket):
        def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
            bin_socket.__init__(self, family, type, proto, fileno)

        def recv(self, *args, **kwargs):
            return as_string(bin_socket.recv(self, *args, **kwargs))

        def recvfrom(self, *args, **kwargs):
            reply, whence = bin_socket.recvfrom(self, *args, **kwargs)
            reply = as_string(reply)
            return reply, whence

        def send(self, data, *args, **kwargs):
            b = as_bytes(data)
            return bin_socket.send(self, b, *args, **kwargs)

        def sendall(self, data, *args, **kwargs):
            return bin_socket.sendall(self, as_bytes(data), *args, **kwargs)

        def sendto(self, data, *args, **kwargs):
            return bin_socket.sendto(self, as_bytes(data), *args, **kwargs)

        def accept(self):
#            sock, addr = bin_socket.accept(self)
#            sock = text_socket(self.family, self.type, self.proto, fileno=sock.fileno())
            fd, addr = self._accept()
            sock = text_socket(self.family, self.type, self.proto, fileno=fd)
            # Issue #7995: if no default timeout is set and the listening
            # socket had a (non-zero) timeout, force the new socket in blocking
            # mode to override platform-specific socket flags inheritance.
            if getdefaulttimeout() is None and self.gettimeout():
                sock.setblocking(True)
            return sock, addr

    text_socket.__init__.__doc__ = bin_socket.__init__.__doc__
    socket = text_socket

########NEW FILE########
__FILENAME__ = pi_module
# -*- Mode: Python -*-

# [reworking of the version in Python-1.5.1/Demo/scripts/pi.py]

# Print digits of pi forever.
#
# The algorithm, using Python's 'long' integers ("bignums"), works
# with continued fractions, and was conceived by Lambert Meertens.
#
# See also the ABC Programmer's Handbook, by Geurts, Meertens & Pemberton,
# published by Prentice-Hall (UK) Ltd., 1990.

StopException = "Stop!"

from supervisor.compat import long

def go (file):
    try:
        k, a, b, a1, b1 = long(2), long(4), long(1), long(12), long(4)
        while 1:
            # Next approximation
            p, q, k = k*k, long(2)*k+long(1), k+long(1)
            a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
            # Print common digits
            d, d1 = a/b, a1/b1
            while d == d1:
                if file.write (str(int(d))):
                    raise StopException
                a, a1 = long(10)*(a%b), long(10)*(a1%b1)
                d, d1 = a/b, a1/b1
    except StopException:
        return

class line_writer:
    """partition the endless line into 80-character ones"""

    def __init__ (self, file, digit_limit=10000):
        self.file = file
        self.buffer = ''
        self.count = 0
        self.digit_limit = digit_limit

    def write (self, data):
        self.buffer = self.buffer + data
        if len(self.buffer) > 80:
            line, self.buffer = self.buffer[:80], self.buffer[80:]
            self.file.write (line+'\r\n')
            self.count += 80
        if self.count > self.digit_limit:
            return 1
        else:
            return 0

def main (env, stdin, stdout):
    parts = env['REQUEST_URI'].split('/')
    if len(parts) >= 3:
        ndigits = int(parts[2])
    else:
        ndigits = 5000
    stdout.write ('Content-Type: text/plain\r\n\r\n')
    go (line_writer (stdout, ndigits))

########NEW FILE########
__FILENAME__ = select_trigger
# -*- Mode: 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
#
##############################################################################

__revision__ = "$Id: select_trigger.py,v 1.4 2003/01/09 15:49:15 akuchling Exp $"
from supervisor.compat import thread
from supervisor.compat import as_bytes

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

import os
import supervisor.medusa.text_socket as socket

if os.name == 'posix':

    class trigger (asyncore.file_dispatcher):

        """Wake up a call to select() running in the main thread"""

        # This is useful in a context where you are using Medusa's I/O
        # subsystem to deliver data, but the data is generated by another
        # thread.  Normally, if Medusa is in the middle of a call to
        # select(), new output data generated by another thread will have
        # to sit until the call to select() either times out or returns.
        # If the trigger is 'pulled' by another thread, it should immediately
        # generate a READ event on the trigger object, which will force the
        # select() invocation to return.

        # A common use for this facility: letting Medusa manage I/O for a
        # large number of connections; but routing each request through a
        # thread chosen from a fixed-size thread pool.  When a thread is
        # acquired, a transaction is performed, but output data is
        # accumulated into buffers that will be emptied more efficiently
        # by Medusa. [picture a server that can process database queries
        # rapidly, but doesn't want to tie up threads waiting to send data
        # to low-bandwidth connections]

        # The other major feature provided by this class is the ability to
        # move work back into the main thread: if you call pull_trigger()
        # with a thunk argument, when select() wakes up and receives the
        # event it will call your thunk from within that thread.  The main
        # purpose of this is to remove the need to wrap thread locks around
        # Medusa's data structures, which normally do not need them.  [To see
        # why this is true, imagine this scenario: A thread tries to push some
        # new data onto a channel's outgoing data queue at the same time that
        # the main thread is trying to remove some]

        def __init__ (self):
            r, w = self._fds = os.pipe()
            self.trigger = w
            asyncore.file_dispatcher.__init__(self, r)
            self.lock = thread.allocate_lock()
            self.thunks = []
            self._closed = 0

        # Override the asyncore close() method, because it seems that
        # it would only close the r file descriptor and not w.  The
        # constructor calls file_dispatcher.__init__ and passes r,
        # which would get stored in a file_wrapper and get closed by
        # the default close.  But that would leave w open...

        def close(self):
            if not self._closed:
                self._closed = 1
                self.del_channel()
                for fd in self._fds:
                    os.close(fd)
                self._fds = []

        def __repr__ (self):
            return '<select-trigger (pipe) at %x>' % id(self)

        def readable (self):
            return 1

        def writable (self):
            return 0

        def handle_connect (self):
            pass

        def handle_close(self):
            self.close()

        def pull_trigger (self, thunk=None):
            # print 'PULL_TRIGGER: ', len(self.thunks)
            if thunk:
                self.lock.acquire()
                try:
                    self.thunks.append(thunk)
                finally:
                    self.lock.release()
            os.write(self.trigger, as_bytes('x'))

        def handle_read (self):
            try:
                self.recv(8192)
            except socket.error:
                return
            self.lock.acquire()
            try:
                for thunk in self.thunks:
                    try:
                        thunk()
                    except:
                        nil, t, v, tbinfo = asyncore.compact_traceback()
                        print('exception in trigger thunk: (%s:%s %s)' % (t, v, tbinfo))
                self.thunks = []
            finally:
                self.lock.release()

else:

    # win32-safe version

    # XXX Should define a base class that has the common methods and
    # then put the platform-specific in a subclass named trigger.

    HOST = '127.0.0.1'
    MINPORT = 19950
    NPORTS = 50

    class trigger (asyncore.dispatcher):
        portoffset = 0

        def __init__ (self):
            a = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            w = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            # set TCP_NODELAY to true to avoid buffering
            w.setsockopt(socket.IPPROTO_TCP, 1, 1)

            # tricky: get a pair of connected sockets
            for i in range(NPORTS):
                trigger.portoffset = (trigger.portoffset + 1) % NPORTS
                port = MINPORT + trigger.portoffset
                address = (HOST, port)
                try:
                    a.bind(address)
                except socket.error:
                    continue
                else:
                    break
            else:
                raise RuntimeError('Cannot bind trigger!')

            a.listen(1)
            w.setblocking(0)
            try:
                w.connect(address)
            except:
                pass
            r, addr = a.accept()
            a.close()
            w.setblocking(1)
            self.trigger = w

            asyncore.dispatcher.__init__(self, r)
            self.lock = thread.allocate_lock()
            self.thunks = []
            self._trigger_connected = 0

        def __repr__ (self):
            return '<select-trigger (loopback) at %x>' % id(self)

        def readable (self):
            return 1

        def writable (self):
            return 0

        def handle_connect (self):
            pass

        def pull_trigger (self, thunk=None):
            if thunk:
                self.lock.acquire()
                try:
                    self.thunks.append(thunk)
                finally:
                    self.lock.release()
            self.trigger.send('x')

        def handle_read (self):
            try:
                self.recv(8192)
            except socket.error:
                return
            self.lock.acquire()
            try:
                for thunk in self.thunks:
                    try:
                        thunk()
                    except:
                        nil, t, v, tbinfo = asyncore.compact_traceback()
                        print ('exception in trigger thunk:'
                               ' (%s:%s %s)' % (t, v, tbinfo))
                self.thunks = []
            finally:
                self.lock.release()


the_trigger = None

class trigger_file:

    """A 'triggered' file object"""

    buffer_size = 4096

    def __init__ (self, parent):
        global the_trigger
        if the_trigger is None:
            the_trigger = trigger()
        self.parent = parent
        self.buffer = ''

    def write (self, data):
        self.buffer = self.buffer + data
        if len(self.buffer) > self.buffer_size:
            d, self.buffer = self.buffer, ''
            the_trigger.pull_trigger (
                    lambda d=d,p=self.parent: p.push (d)
                    )

    def writeline (self, line):
        self.write (line+'\r\n')

    def writelines(self, lines):
        self.write('\r\n'.join(lines) + '\r\n')

    def flush (self):
        if self.buffer:
            d, self.buffer = self.buffer, ''
            the_trigger.pull_trigger (
                    lambda p=self.parent,d=d: p.push (d)
                    )

    def softspace (self, *args):
        pass

    def close (self):
        # in a derived class, you may want to call trigger_close() instead.
        self.flush()
        self.parent = None

    def trigger_close (self):
        d, self.buffer = self.buffer, ''
        p, self.parent = self.parent, None
        the_trigger.pull_trigger (
                lambda p=p,d=d: (p.push(d), p.close_when_done())
                )

if __name__ == '__main__':

    import time

    def thread_function (output_file, i, n):
        print('entering thread_function')
        while n:
            time.sleep (5)
            output_file.write ('%2d.%2d %s\r\n' % (i, n, output_file))
            output_file.flush()
            n -= 1
        output_file.close()
        print('exiting thread_function')

    class thread_parent (asynchat.async_chat):

        def __init__ (self, conn, addr):
            self.addr = addr
            asynchat.async_chat.__init__ (self, conn)
            self.set_terminator ('\r\n')
            self.buffer = ''
            self.count = 0

        def collect_incoming_data (self, data):
            self.buffer = self.buffer + data

        def found_terminator (self):
            data, self.buffer = self.buffer, ''
            if not data:
                asyncore.close_all()
                print("done")
                return
            n = int(data.split()[0])
            tf = trigger_file (self)
            self.count += 1
            thread.start_new_thread (thread_function, (tf, self.count, n))

    class thread_server (asyncore.dispatcher):

        def __init__ (self, family=socket.AF_INET, address=('', 9003)):
            asyncore.dispatcher.__init__ (self)
            self.create_socket (family, socket.SOCK_STREAM)
            self.set_reuse_addr()
            self.bind (address)
            self.listen (5)

        def handle_accept (self):
            conn, addr = self.accept()
            thread_parent (conn, addr)

    thread_server()
    #asyncore.loop(1.0, use_poll=1)
    try:
        asyncore.loop ()
    except:
        asyncore.close_all()

########NEW FILE########
__FILENAME__ = test_module
# -*- Mode: Python -*-

import pprint

def main (env, stdin, stdout):

    stdout.write (
            '<html><body><h1>Test CGI Module</h1>\r\n'
            '<br>The Environment:<pre>\r\n'
            )
    pprint.pprint (env, stdout)
    stdout.write ('</pre></body></html>\r\n')

########NEW FILE########
__FILENAME__ = thread_channel
# -*- Mode: Python -*-

VERSION_STRING = "$Id: thread_channel.py,v 1.3 2002/03/19 22:49:40 amk Exp $"

# This will probably only work on Unix.

# The disadvantage to this technique is that it wastes file
# descriptors (especially when compared to select_trigger.py)

# May be possible to do it on Win32, using TCP localhost sockets.
# [does winsock support 'socketpair'?]

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

import fcntl
import os
import supervisor.medusa.text_socket as socket
try:
    import _thread as thread
except ImportError:
    import thread

# this channel slaves off of another one.  it starts a thread which
# pumps its output through the 'write' side of the pipe.  The 'read'
# side of the pipe will then notify us when data is ready.  We push
# this data on the owning data channel's output queue.

class thread_channel (asyncore.file_dispatcher):

    buffer_size = 8192

    def __init__ (self, channel, function, *args):
        self.parent = channel
        self.function = function
        self.args = args
        self.pipe = rfd, wfd = os.pipe()
        asyncore.file_dispatcher.__init__ (self, rfd)

    def start (self):
        rfd, wfd = self.pipe

        # The read side of the pipe is set to non-blocking I/O; it is
        # 'owned' by medusa.

        flags = fcntl.fcntl (rfd, fcntl.F_GETFL, 0)
        fcntl.fcntl (rfd, fcntl.F_SETFL, flags | os.O_NDELAY)

        # The write side of the pipe is left in blocking mode; it is
        # 'owned' by the thread.  However, we wrap it up as a file object.
        # [who wants to 'write()' to a number?]

        of = os.fdopen (wfd, 'w')

        thread.start_new_thread (
                self.function,
                # put the output file in front of the other arguments
                (of,) + self.args
                )

    def writable (self):
        return 0

    def readable (self):
        return 1

    def handle_read (self):
        data = self.recv (self.buffer_size)
        self.parent.push (data)

    def handle_close (self):
        # Depending on your intentions, you may want to close
        # the parent channel here.
        self.close()

# Yeah, it's bad when the test code is bigger than the library code.

if __name__ == '__main__':

    import time

    def thread_function (output_file, i, n):
        print('entering thread_function')
        while n:
            time.sleep (5)
            output_file.write ('%2d.%2d %s\r\n' % (i, n, output_file))
            output_file.flush()
            n -= 1
        output_file.close()
        print('exiting thread_function')

    class thread_parent (asynchat.async_chat):

        def __init__ (self, conn, addr):
            self.addr = addr
            asynchat.async_chat.__init__ (self, conn)
            self.set_terminator ('\r\n')
            self.buffer = ''
            self.count = 0

        def collect_incoming_data (self, data):
            self.buffer = self.buffer + data

        def found_terminator (self):
            data, self.buffer = self.buffer, ''
            n = int(data.split()[0])
            tc = thread_channel (self, thread_function, self.count, n)
            self.count += 1
            tc.start()

    class thread_server (asyncore.dispatcher):

        def __init__ (self, family=socket.AF_INET, address=('127.0.0.1', 9003)):
            asyncore.dispatcher.__init__ (self)
            self.create_socket (family, socket.SOCK_STREAM)
            self.set_reuse_addr()
            self.bind (address)
            self.listen (5)

        def handle_accept (self):
            conn, addr = self.accept()
            thread_parent(conn, addr)

    thread_server()
    #asyncore.loop(1.0, use_poll=1)
    asyncore.loop ()

########NEW FILE########
__FILENAME__ = thread_handler
# -*- Mode: Python -*-

import re
try:
    from StringIO import StringIO
except:
    from io import StringIO
import sys

import supervisor.medusa.thread.select_trigger as select_trigger
from supervisor.medusa import counter
from supervisor.medusa.default_handler import unquote

import threading

class request_queue:

    def __init__ (self):
        self.mon = threading.RLock()
        self.cv = threading.Condition (self.mon)
        self.queue = []

    def put (self, item):
        self.cv.acquire()
        self.queue.append(item)
        self.cv.notify()
        self.cv.release()

    def get(self):
        self.cv.acquire()
        while not self.queue:
            self.cv.wait()
        result = self.queue.pop(0)
        self.cv.release()
        return result

header2env= {
        'Content-Length'        : 'CONTENT_LENGTH',
        'Content-Type'          : 'CONTENT_TYPE',
        'Referer'                       : 'HTTP_REFERER',
        'User-Agent'            : 'HTTP_USER_AGENT',
        'Accept'                        : 'HTTP_ACCEPT',
        'Accept-Charset'        : 'HTTP_ACCEPT_CHARSET',
        'Accept-Language'       : 'HTTP_ACCEPT_LANGUAGE',
        'Host'                          : 'HTTP_HOST',
        'Connection'            : 'CONNECTION_TYPE',
        'Authorization'         : 'HTTP_AUTHORIZATION',
        'Cookie'                        : 'HTTP_COOKIE',
        }

# convert keys to lower case for case-insensitive matching
for (key,value) in list(header2env.items()):
    del header2env[key]
    key = key.lower()
    header2env[key]=value

class thread_output_file (select_trigger.trigger_file):

    def close (self):
        self.trigger_close()

class script_handler:

    def __init__ (self, queue, document_root=""):
        self.modules = {}
        self.document_root = document_root
        self.queue = queue

    def add_module (self, module, *names):
        if not names:
            names = ["/%s" % module.__name__]
        for name in names:
            self.modules['/'+name] = module

    def match (self, request):
        uri = request.uri

        i = uri.find("/", 1)
        if i != -1:
            uri = uri[:i]

        i = uri.find("?", 1)
        if i != -1:
            uri = uri[:i]

        if uri in self.modules:
            request.module = self.modules[uri]
            return 1
        else:
            return 0

    def handle_request (self, request):

        [path, params, query, fragment] = request.split_uri()

        while path and path[0] == '/':
            path = path[1:]

        if '%' in path:
            path = unquote (path)

        env = {}

        env['REQUEST_URI'] = "/" + path
        env['REQUEST_METHOD']   = request.command.upper()
        env['SERVER_PORT']       = str(request.channel.server.port)
        env['SERVER_NAME']       = request.channel.server.server_name
        env['SERVER_SOFTWARE'] = request['Server']
        env['DOCUMENT_ROOT']     = self.document_root

        parts = path.split("/")

        # are script_name and path_info ok?

        env['SCRIPT_NAME']      = "/" + parts[0]

        if query and query[0] == "?":
            query = query[1:]

        env['QUERY_STRING']     = query

        try:
            path_info = "/" + "/".join(parts[1:])
        except:
            path_info = ''

        env['PATH_INFO']                = path_info
        env['GATEWAY_INTERFACE']='CGI/1.1'                         # what should this really be?
        env['REMOTE_ADDR']              = request.channel.addr[0]
        env['REMOTE_HOST']              = request.channel.addr[0]  # TODO: connect to resolver

        for header in request.header:
            key, value = header.split(": ", 1)
            key = key.lower()

            if key in header2env:
                if header2env[key]:
                    env[header2env[key]]=value
            else:
                key = 'HTTP_' + "_".join(key.split ("-")).upper()
                env[key] = value

        ## remove empty environment variables
        for key in env.keys():
            if env[key]=="" or env[key] is None:
                del env[key]

        try:
            httphost = env['HTTP_HOST']
            parts = httphost.split(":")
            env['HTTP_HOST'] = parts[0]
        except KeyError:
            pass

        if request.command in ('put', 'post'):
            # PUT data requires a correct Content-Length: header
            # (though I bet with http/1.1 we can expect chunked encoding)
            request.collector = collector (self, request, env)
            request.channel.set_terminator (None)
        else:
            sin = StringIO ('')
            self.continue_request (sin, request, env)

    def continue_request (self, stdin, request, env):
        stdout = header_scanning_file (
                request,
                thread_output_file (request.channel)
                )
        self.queue.put (
                (request.module.main, (env, stdin, stdout))
                )

HEADER_LINE = re.compile ('([A-Za-z0-9-]+): ([^\r\n]+)')

# A file wrapper that handles the CGI 'Status:' header hack
# by scanning the output.

class header_scanning_file:

    def __init__ (self, request, file):
        self.buffer = ''
        self.request = request
        self.file = file
        self.got_header = 0
        self.bytes_out = counter.counter()

    def write (self, data):
        if self.got_header:
            self._write (data)
        else:
            # CGI scripts may optionally provide extra headers.
            #
            # If they do not, then the output is assumed to be
            # text/html, with an HTTP reply code of '200 OK'.
            #
            # If they do, we need to scan those headers for one in
            # particular: the 'Status:' header, which will tell us
            # to use a different HTTP reply code [like '302 Moved']
            #
            self.buffer = self.buffer + data
            lines = self.buffer.split('\n')
            # ignore the last piece, it is either empty, or a partial line
            lines = lines[:-1]
            # look for something un-header-like
            for i in range(len(lines)):
                li = lines[i]
                if (not li) or (HEADER_LINE.match (li) is None):
                    # this is either the header separator, or it
                    # is not a header line.
                    self.got_header = 1
                    h = self.build_header (lines[:i])
                    self._write (h)
                    # rejoin the rest of the data
                    d = '\n'.join(lines[i:])
                    self._write (d)
                    self.buffer = ''
                    break

    def build_header (self, lines):
        status = '200 OK'
        saw_content_type = 0
        hl = HEADER_LINE
        for line in lines:
            mo = hl.match (line)
            if mo is not None:
                h = mo.group(1).lower()
                if h == 'status':
                    status = mo.group(2)
                elif h == 'content-type':
                    saw_content_type = 1
        lines.insert (0, 'HTTP/1.0 %s' % status)
        lines.append ('Server: ' + self.request['Server'])
        lines.append ('Date: ' + self.request['Date'])
        if not saw_content_type:
            lines.append ('Content-Type: text/html')
        lines.append ('Connection: close')
        return '\r\n'.join(lines) + '\r\n\r\n'

    def _write (self, data):
        self.bytes_out.increment (len(data))
        self.file.write (data)

    def writelines(self, lst):
        self.write(''.join(lst))

    def flush(self):
        pass

    def close (self):
        if not self.got_header:
            # managed to slip through our header detectors
            self._write (self.build_header (['Status: 502', 'Content-Type: text/html']))
            self._write (
                    '<html><h1>Server Error</h1>\r\n'
                    '<b>Bad Gateway:</b> No Header from CGI Script\r\n'
                    '<pre>Data: %s</pre>'
                    '</html>\r\n' % (repr(self.buffer))
                    )
        self.request.log (int(self.bytes_out.as_long()))
        self.file.close()
        self.request.channel.current_request = None


class collector:

    """gathers input for PUT requests"""

    def __init__ (self, handler, request, env):
        self.handler    = handler
        self.env = env
        self.request    = request
        self.data = StringIO()

        # make sure there's a content-length header
        self.cl = request.get_header ('content-length')

        if not self.cl:
            request.error (411)
            return
        else:
            self.cl = int(self.cl)

    def collect_incoming_data (self, data):
        self.data.write (data)
        if self.data.tell() >= self.cl:
            self.data.seek(0)

            h=self.handler
            r=self.request

            # set the terminator back to the default
            self.request.channel.set_terminator ('\r\n\r\n')
            del self.handler
            del self.request

            h.continue_request (self.data, r, self.env)


class request_loop_thread (threading.Thread):

    def __init__ (self, queue):
        threading.Thread.__init__ (self)
        self.setDaemon(1)
        self.queue = queue

    def run (self):
        while 1:
            function, (env, stdin, stdout) = self.queue.get()
            function (env, stdin, stdout)
            stdout.close()

# ===========================================================================
#                                                          Testing
# ===========================================================================

if __name__ == '__main__':

    if len(sys.argv) < 2:
        print('Usage: %s <worker_threads>' % sys.argv[0])
    else:
        nthreads = int(sys.argv[1])

        import supervisor.medusa.asyncore_25 as asyncore
        from supervisor.medusa import http_server
        # create a generic web server
        hs = http_server.http_server ('', 7080)

        # create a request queue
        q = request_queue()

        # create a script handler
        sh = script_handler (q)

        # install the script handler on the web server
        hs.install_handler (sh)

        # get a couple of CGI modules
        import supervisor.medusa.thread.test_module as test_module
        import supervisor.medusa.thread.pi_module as pi_module

        # install the module on the script handler
        sh.add_module (test_module, 'test')
        sh.add_module (pi_module, 'pi')

        # fire up the worker threads
        for i in range (nthreads):
            rt = request_loop_thread (q)
            rt.start()

        # start the main event loop
        asyncore.loop()

########NEW FILE########
__FILENAME__ = unix_user_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996, 1997 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: unix_user_handler.py,v 1.4 2002/11/25 00:09:23 akuchling Exp $'

# support for `~user/public_html'.

import re
import supervisor.medusa.default_handler as default_handler
import supervisor.medusa.filesys as filesys
import os
import pwd

get_header = default_handler.get_header

user_dir = re.compile ('/~([^/]+)(.*)')

class unix_user_handler (default_handler.default_handler):

    def __init__ (self, public_html = 'public_html'):
        self.public_html = public_html
        default_handler.default_handler.__init__ (self, None)

    # cache userdir-filesystem objects
    fs_cache = {}

    def match (self, request):
        m = user_dir.match (request.uri)
        return m and (m.end() == len (request.uri))

    def handle_request (self, request):
        # get the user name
        m = user_dir.match (request.uri)
        user = m.group(1)
        rest = m.group(2)

        # special hack to catch those lazy URL typers
        if not rest:
            request['Location'] = '/~%s/' % user
            request.error (301)
            return

        # have we already built a userdir fs for this user?
        if user in self.fs_cache:
            fs = self.fs_cache[user]
        else:
            # no, well then, let's build one.
            # first, find out where the user directory is
            try:
                info = pwd.getpwnam (user)
            except KeyError:
                request.error (404)
                return
            ud = info[5] + '/' + self.public_html
            if os.path.isdir (ud):
                fs = filesys.os_filesystem (ud)
                self.fs_cache[user] = fs
            else:
                request.error (404)
                return

        # fake out default_handler
        self.filesystem = fs
        # massage the request URI
        request.uri = '/' + rest
        return default_handler.default_handler.handle_request (self, request)

    def __repr__ (self):
        return '<Unix User Directory Handler at %08x [~user/%s, %d filesystems loaded]>' % (
                id(self),
                self.public_html,
                len(self.fs_cache)
                )

########NEW FILE########
__FILENAME__ = virtual_handler
# -*- Mode: Python -*-

import supervisor.medusa.text_socket as socket
import supervisor.medusa.default_handler as default_handler
import re

HOST = re.compile ('Host: ([^:/]+).*', re.IGNORECASE)

get_header = default_handler.get_header

class virtual_handler:

    """HTTP request handler for an HTTP/1.0-style virtual host.  Each
    Virtual host must have a different IP"""

    def __init__ (self, handler, hostname):
        self.handler = handler
        self.hostname = hostname
        try:
            self.ip = socket.gethostbyname (hostname)
        except socket.error:
            raise ValueError("Virtual Hostname %s does not appear to be registered in the DNS" % hostname)

    def match (self, request):
        if request.channel.addr[0] == self.ip:
            return 1
        else:
            return 0

    def handle_request (self, request):
        return self.handler.handle_request (request)

    def __repr__ (self):
        return '<virtual request handler for %s>' % self.hostname


class virtual_handler_with_host:

    """HTTP request handler for HTTP/1.1-style virtual hosts.  This
    matches by checking the value of the 'Host' header in the request.
    You actually don't _have_ to support HTTP/1.1 to use this, since
    many browsers now send the 'Host' header.  This is a Good Thing."""

    def __init__ (self, handler, hostname):
        self.handler = handler
        self.hostname = hostname

    def match (self, request):
        host = get_header (HOST, request.header)
        if host == self.hostname:
            return 1
        else:
            return 0

    def handle_request (self, request):
        return self.handler.handle_request (request)

    def __repr__ (self):
        return '<virtual request handler for %s>' % self.hostname

########NEW FILE########
__FILENAME__ = xmlrpc_handler
# -*- Mode: Python -*-

# See http://www.xml-rpc.com/
#     http://www.pythonware.com/products/xmlrpc/

# Based on "xmlrpcserver.py" by Fredrik Lundh (fredrik@pythonware.com)

VERSION = "$Id: xmlrpc_handler.py,v 1.6 2004/04/21 14:09:24 akuchling Exp $"

import supervisor.medusa.http_server as http_server
try:
    import xmlrpclib
except:
    import xmlrpc.client as xmlrpclib

import sys

class xmlrpc_handler:

    def match (self, request):
        # Note: /RPC2 is not required by the spec, so you may override this method.
        if request.uri[:5] == '/RPC2':
            return 1
        else:
            return 0

    def handle_request (self, request):
        if request.command == 'POST':
            request.collector = collector (self, request)
        else:
            request.error (400)

    def continue_request (self, data, request):
        params, method = xmlrpclib.loads (data)
        try:
            # generate response
            try:
                response = self.call (method, params)
                if type(response) != type(()):
                    response = (response,)
            except:
                # report exception back to server
                response = xmlrpclib.dumps (
                        xmlrpclib.Fault (1, "%s:%s" % (sys.exc_info()[0], sys.exc_info()[1]))
                        )
            else:
                response = xmlrpclib.dumps (response, methodresponse=1)
        except:
            # internal error, report as HTTP server error
            request.error (500)
        else:
            # got a valid XML RPC response
            request['Content-Type'] = 'text/xml'
            request.push (response)
            request.done()

    def call (self, method, params):
        # override this method to implement RPC methods
        raise Exception("NotYetImplemented")

class collector:

    """gathers input for POST and PUT requests"""

    def __init__ (self, handler, request):

        self.handler = handler
        self.request = request
        self.data = []

        # make sure there's a content-length header
        cl = request.get_header ('content-length')

        if not cl:
            request.error (411)
        else:
            cl = int(cl)
            # using a 'numeric' terminator
            self.request.channel.set_terminator (cl)

    def collect_incoming_data (self, data):
        self.data.append(data)

    def found_terminator (self):
        # set the terminator back to the default
        self.request.channel.set_terminator ('\r\n\r\n')
        self.handler.continue_request ("".join(self.data), self.request)

if __name__ == '__main__':

    class rpc_demo (xmlrpc_handler):

        def call (self, method, params):
            print('method="%s" params=%s' % (method, params))
            return "Sure, that works"

    import supervisor.medusa.asyncore_25 as asyncore

    hs = http_server.http_server ('', 8000)
    rpc = rpc_demo()
    hs.install_handler (rpc)

    asyncore.loop()

########NEW FILE########
__FILENAME__ = options
import supervisor.medusa.text_socket as socket
import getopt
import os
import sys
import tempfile
import errno
import signal
import re
import pwd
import grp
import resource
import stat
import pkg_resources
import select
import glob
import platform
import warnings

from fcntl import fcntl
from fcntl import F_SETFL, F_GETFL

from supervisor.compat import print_function
from supervisor.compat import PY3
from supervisor.compat import ConfigParser
from supervisor.compat import as_bytes, as_string
from supervisor.compat import xmlrpclib
from supervisor.compat import StringIO
from supervisor.compat import basestring

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.datatypes import process_or_group_name
from supervisor.datatypes import boolean
from supervisor.datatypes import integer
from supervisor.datatypes import name_to_uid
from supervisor.datatypes import gid_for_uid
from supervisor.datatypes import existing_dirpath
from supervisor.datatypes import byte_size
from supervisor.datatypes import signal_number
from supervisor.datatypes import list_of_exitcodes
from supervisor.datatypes import dict_of_key_value_pairs
from supervisor.datatypes import logfile_name
from supervisor.datatypes import list_of_strings
from supervisor.datatypes import octal_type
from supervisor.datatypes import existing_directory
from supervisor.datatypes import logging_level
from supervisor.datatypes import colon_separated_user_group
from supervisor.datatypes import inet_address
from supervisor.datatypes import InetStreamSocketConfig
from supervisor.datatypes import UnixStreamSocketConfig
from supervisor.datatypes import url
from supervisor.datatypes import Automatic
from supervisor.datatypes import auto_restart
from supervisor.datatypes import profile_options
from supervisor.datatypes import set_here

from supervisor import loggers
from supervisor import states
from supervisor import xmlrpc
from supervisor import read_file

mydir = os.path.abspath(os.path.dirname(__file__))
version_txt = os.path.join(mydir, 'version.txt')

VERSION = read_file(version_txt).strip()

def normalize_path(v):
    return os.path.normpath(os.path.abspath(os.path.expanduser(v)))

class Dummy:
    pass

class Options:
    stderr = sys.stderr
    stdout = sys.stdout
    exit = sys.exit
    warnings = warnings

    uid = gid = None

    progname = sys.argv[0]
    configfile = None
    schemadir = None
    configroot = None
    here = None

    # Class variable deciding whether positional arguments are allowed.
    # If you want positional arguments, set this to 1 in your subclass.
    positional_args_allowed = 0

    def __init__(self, require_configfile=True):
        """Constructor.

        Params:
        require_configfile -- whether we should fail on no config file.
        """
        self.names_list = []
        self.short_options = []
        self.long_options = []
        self.options_map = {}
        self.default_map = {}
        self.required_map = {}
        self.environ_map = {}
        self.attr_priorities = {}
        self.require_configfile = require_configfile
        self.add(None, None, "h", "help", self.help)
        self.add("configfile", None, "c:", "configuration=")

        here = os.path.dirname(os.path.dirname(sys.argv[0]))
        searchpaths = [os.path.join(here, 'etc', 'supervisord.conf'),
                       os.path.join(here, 'supervisord.conf'),
                       'supervisord.conf',
                       'etc/supervisord.conf',
                       '/etc/supervisord.conf']
        self.searchpaths = searchpaths

    def default_configfile(self):
        """Return the name of the found config file or print usage/exit."""
        config = None
        for path in self.searchpaths:
            if os.path.exists(path):
                config = path
                break
        if config is None and self.require_configfile:
            self.usage('No config file found at default paths (%s); '
                       'use the -c option to specify a config file '
                       'at a different path' % ', '.join(self.searchpaths))
        return config

    def help(self, dummy):
        """Print a long help message to stdout and exit(0).

        Occurrences of "%s" in are replaced by self.progname.
        """
        help = self.doc + "\n"
        if help.find("%s") > 0:
            help = help.replace("%s", self.progname)
        print_function(help, end='', file=self.stdout)
        self.exit(0)

    def usage(self, msg):
        """Print a brief error message to stderr and exit(2)."""
        self.stderr.write("Error: %s\n" % str(msg))
        self.stderr.write("For help, use %s -h\n" % self.progname)
        self.exit(2)

    def add(self,
            name=None,                  # attribute name on self
            confname=None,              # dotted config path name
            short=None,                 # short option name
            long=None,                  # long option name
            handler=None,               # handler (defaults to string)
            default=None,               # default value
            required=None,              # message if not provided
            flag=None,                  # if not None, flag value
            env=None,                   # if not None, environment variable
            ):
        """Add information about a configuration option.

        This can take several forms:

        add(name, confname)
            Configuration option 'confname' maps to attribute 'name'
        add(name, None, short, long)
            Command line option '-short' or '--long' maps to 'name'
        add(None, None, short, long, handler)
            Command line option calls handler
        add(name, None, short, long, handler)
            Assign handler return value to attribute 'name'

        In addition, one of the following keyword arguments may be given:

        default=...  -- if not None, the default value
        required=... -- if nonempty, an error message if no value provided
        flag=...     -- if not None, flag value for command line option
        env=...      -- if not None, name of environment variable that
                        overrides the configuration file or default
        """
        if flag is not None:
            if handler is not None:
                raise ValueError("use at most one of flag= and handler=")
            if not long and not short:
                raise ValueError("flag= requires a command line flag")
            if short and short.endswith(":"):
                raise ValueError("flag= requires a command line flag")
            if long and long.endswith("="):
                raise ValueError("flag= requires a command line flag")
            handler = lambda arg, flag=flag: flag

        if short and long:
            if short.endswith(":") != long.endswith("="):
                raise ValueError("inconsistent short/long options: %r %r" % (
                    short, long))

        if short:
            if short[0] == "-":
                raise ValueError("short option should not start with '-'")
            key, rest = short[:1], short[1:]
            if rest not in ("", ":"):
                raise ValueError("short option should be 'x' or 'x:'")
            key = "-" + key
            if key in self.options_map:
                raise ValueError("duplicate short option key '%s'" % key)
            self.options_map[key] = (name, handler)
            self.short_options.append(short)

        if long:
            if long[0] == "-":
                raise ValueError("long option should not start with '-'")
            key = long
            if key[-1] == "=":
                key = key[:-1]
            key = "--" + key
            if key in self.options_map:
                raise ValueError("duplicate long option key '%s'" % key)
            self.options_map[key] = (name, handler)
            self.long_options.append(long)

        if env:
            self.environ_map[env] = (name, handler)

        if name:
            if not hasattr(self, name):
                setattr(self, name, None)
            self.names_list.append((name, confname))
            if default is not None:
                self.default_map[name] = default
            if required:
                self.required_map[name] = required

    def _set(self, attr, value, prio):
        current = self.attr_priorities.get(attr, -1)
        if prio >= current:
            setattr(self, attr, value)
            self.attr_priorities[attr] = prio

    def realize(self, args=None, doc=None,
                progname=None, raise_getopt_errs=True):
        """Realize a configuration.

        Optional arguments:

        args     -- the command line arguments, less the program name
                    (default is sys.argv[1:])

        doc      -- usage message (default is __main__.__doc__)
        """
        # Provide dynamic default method arguments
        if args is None:
            args = sys.argv[1:]
        if progname is None:
            progname = sys.argv[0]
        if doc is None:
            try:
                import __main__
                doc = __main__.__doc__
            except Exception:
                pass
        self.progname = progname
        self.doc = doc

        self.options = []
        self.args = []

        # Call getopt
        try:
            self.options, self.args = getopt.getopt(
                args, "".join(self.short_options), self.long_options)
        except getopt.error as msg:
            if raise_getopt_errs:
                self.usage(msg)

        # Check for positional args
        if self.args and not self.positional_args_allowed:
            self.usage("positional arguments are not supported")

        # Process options returned by getopt
        for opt, arg in self.options:
            name, handler = self.options_map[opt]
            if handler is not None:
                try:
                    arg = handler(arg)
                except ValueError as msg:
                    self.usage("invalid value for %s %r: %s" % (opt, arg, msg))
            if name and arg is not None:
                if getattr(self, name) is not None:
                    self.usage("conflicting command line option %r" % opt)
                self._set(name, arg, 2)

        # Process environment variables
        for envvar in self.environ_map.keys():
            name, handler = self.environ_map[envvar]
            if envvar in os.environ:
                value = os.environ[envvar]
                if handler is not None:
                    try:
                        value = handler(value)
                    except ValueError as msg:
                        self.usage("invalid environment value for %s %r: %s"
                                   % (envvar, value, msg))
                if name and value is not None:
                    self._set(name, value, 1)

        if self.configfile is None:
            uid = os.getuid()
            if uid == 0 and "supervisord" in self.progname: # pragma: no cover
                self.warnings.warn(
                    'Supervisord is running as root and it is searching '
                    'for its configuration file in default locations '
                    '(including its current working directory); you '
                    'probably want to specify a "-c" argument specifying an '
                    'absolute path to a configuration file for improved '
                    'security.'
                    )

            self.configfile = self.default_configfile()

        self.process_config()

    def process_config(self, do_usage=True):
        """Process configuration data structure.

        This includes reading config file if necessary, setting defaults etc.
        """
        if self.configfile:
            self.process_config_file(do_usage)

        # Copy config options to attributes of self.  This only fills
        # in options that aren't already set from the command line.
        for name, confname in self.names_list:
            if confname:
                parts = confname.split(".")
                obj = self.configroot
                for part in parts:
                    if obj is None:
                        break
                    # Here AttributeError is not a user error!
                    obj = getattr(obj, part)
                self._set(name, obj, 0)

        # Process defaults
        for name, value in self.default_map.items():
            if getattr(self, name) is None:
                setattr(self, name, value)

        # Process required options
        for name, message in self.required_map.items():
            if getattr(self, name) is None:
                self.usage(message)

    def process_config_file(self, do_usage):
        # Process config file
        if not hasattr(self.configfile, 'read'):
            self.here = os.path.abspath(os.path.dirname(self.configfile))
            set_here(self.here)
        try:
            self.read_config(self.configfile)
        except ValueError as msg:
            if do_usage:
                # if this is not called from an RPC method, run usage and exit.
                self.usage(str(msg))
            else:
                # if this is called from an RPC method, raise an error
                raise ValueError(msg)

    def get_plugins(self, parser, factory_key, section_prefix):
        factories = []

        for section in parser.sections():
            if not section.startswith(section_prefix):
                continue
            name = section.split(':', 1)[1]
            factory_spec = parser.saneget(section, factory_key, None)
            if factory_spec is None:
                raise ValueError('section [%s] does not specify a %s'  %
                                 (section, factory_key))
            try:
                factory = self.import_spec(factory_spec)
            except ImportError:
                raise ValueError('%s cannot be resolved within [%s]' % (
                    factory_spec, section))
            items = parser.items(section)
            items.remove((factory_key, factory_spec))
            factories.append((name, factory, dict(items)))

        return factories

    def import_spec(self, spec):
        return pkg_resources.EntryPoint.parse("x="+spec).load(False)


class ServerOptions(Options):
    user = None
    sockchown = None
    sockchmod = None
    logfile = None
    loglevel = None
    pidfile = None
    passwdfile = None
    nodaemon = None
    environment = None
    httpservers = ()
    unlink_socketfiles = True
    mood = states.SupervisorStates.RUNNING

    def __init__(self):
        Options.__init__(self)
        self.configroot = Dummy()
        self.configroot.supervisord = Dummy()

        self.add(None, None, "v", "version", self.version)
        self.add("nodaemon", "supervisord.nodaemon", "n", "nodaemon", flag=1,
                 default=0)
        self.add("user", "supervisord.user", "u:", "user=")
        self.add("umask", "supervisord.umask", "m:", "umask=",
                 octal_type, default='022')
        self.add("directory", "supervisord.directory", "d:", "directory=",
                 existing_directory)
        self.add("logfile", "supervisord.logfile", "l:", "logfile=",
                 existing_dirpath, default="supervisord.log")
        self.add("logfile_maxbytes", "supervisord.logfile_maxbytes",
                 "y:", "logfile_maxbytes=", byte_size,
                 default=50 * 1024 * 1024) # 50MB
        self.add("logfile_backups", "supervisord.logfile_backups",
                 "z:", "logfile_backups=", integer, default=10)
        self.add("loglevel", "supervisord.loglevel", "e:", "loglevel=",
                 logging_level, default="info")
        self.add("pidfile", "supervisord.pidfile", "j:", "pidfile=",
                 existing_dirpath, default="supervisord.pid")
        self.add("identifier", "supervisord.identifier", "i:", "identifier=",
                 str, default="supervisor")
        self.add("childlogdir", "supervisord.childlogdir", "q:", "childlogdir=",
                 existing_directory, default=tempfile.gettempdir())
        self.add("minfds", "supervisord.minfds",
                 "a:", "minfds=", int, default=1024)
        self.add("minprocs", "supervisord.minprocs",
                 "", "minprocs=", int, default=200)
        self.add("nocleanup", "supervisord.nocleanup",
                 "k", "nocleanup", flag=1, default=0)
        self.add("strip_ansi", "supervisord.strip_ansi",
                 "t", "strip_ansi", flag=1, default=0)
        self.add("profile_options", "supervisord.profile_options",
                 "", "profile_options=", profile_options, default=None)
        self.pidhistory = {}
        self.process_group_configs = []
        self.parse_warnings = []
        self.signal_receiver = SignalReceiver()

    def version(self, dummy):
        """Print version to stdout and exit(0).
        """
        self.stdout.write('%s\n' % VERSION)
        self.exit(0)

    def getLogger(self, *args, **kwargs):
        """
        A proxy to loggers.getLogger so the options might customize log setup.
        Used by tests to mock log setup.
        """
        return loggers.getLogger(*args, **kwargs)

    def realize(self, *arg, **kw):
        Options.realize(self, *arg, **kw)
        section = self.configroot.supervisord

        # Additional checking of user option; set uid and gid
        if self.user is not None:
            try:
                uid = name_to_uid(self.user)
            except ValueError as msg:
                self.usage(msg) # invalid user
            self.uid = uid
            self.gid = gid_for_uid(uid)

        if not self.loglevel:
            self.loglevel = section.loglevel

        if self.logfile:
            logfile = self.logfile
        else:
            logfile = section.logfile

        self.logfile = normalize_path(logfile)

        if self.pidfile:
            pidfile = self.pidfile
        else:
            pidfile = section.pidfile

        self.pidfile = normalize_path(pidfile)

        self.rpcinterface_factories = section.rpcinterface_factories

        self.serverurl = None

        self.server_configs = sconfigs = section.server_configs

        # we need to set a fallback serverurl that process.spawn can use

        # prefer a unix domain socket
        for config in [ config for config in sconfigs if
                        config['family'] is socket.AF_UNIX ]:
            path = config['file']
            self.serverurl = 'unix://%s' % path
            break

        # fall back to an inet socket
        if self.serverurl is None:
            for config in [ config for config in sconfigs if
                            config['family'] is socket.AF_INET]:
                host = config['host']
                port = config['port']
                if not host:
                    host = 'localhost'
                self.serverurl = 'http://%s:%s' % (host, port)

        # self.serverurl may still be None if no servers at all are
        # configured in the config file

        self.identifier = section.identifier

    def process_config(self, do_usage=True):
        Options.process_config(self, do_usage=do_usage)

        new = self.configroot.supervisord.process_group_configs
        self.process_group_configs = new

    def read_config(self, fp):
        # Clear parse warnings, since we may be re-reading the
        # config a second time after a reload.
        self.parse_warnings = []

        section = self.configroot.supervisord
        need_close = False
        if not hasattr(fp, 'read'):
            if not os.path.exists(fp):
                raise ValueError("could not find config file %s" % fp)
            try:
                fp = open(fp, 'r')
                need_close = True
            except (IOError, OSError):
                raise ValueError("could not read config file %s" % fp)
        kwargs = {}
        if PY3:
            kwargs['inline_comment_prefixes'] = (';','#')
        parser = UnhosedConfigParser(**kwargs)
        try:
            try:
                parser.read_file(fp)
            except AttributeError:
                parser.readfp(fp)
        except ConfigParser.ParsingError as why:
            raise ValueError(str(why))
        finally:
            if need_close:
                fp.close()

        if parser.has_section('include'):
            if not parser.has_option('include', 'files'):
                raise ValueError(".ini file has [include] section, but no "
                "files setting")
            files = parser.get('include', 'files')
            files = files.split()
            if hasattr(fp, 'name'):
                base = os.path.dirname(os.path.abspath(fp.name))
            else:
                base = '.'
            for pattern in files:
                pattern = os.path.join(base, pattern)
                filenames = glob.glob(pattern)
                if not filenames:
                    self.parse_warnings.append(
                        'No file matches via include "%s"' % pattern)
                    continue
                for filename in filenames:
                    self.parse_warnings.append(
                        'Included extra file "%s" during parsing' % filename)
                    try:
                        parser.read(filename)
                    except ConfigParser.ParsingError as why:
                        raise ValueError(str(why))

        sections = parser.sections()
        if not 'supervisord' in sections:
            raise ValueError('.ini file does not include supervisord section')
        get = parser.getdefault
        section.minfds = integer(get('minfds', 1024))
        section.minprocs = integer(get('minprocs', 200))

        directory = get('directory', None)
        if directory is None:
            section.directory = None
        else:
            section.directory = existing_directory(directory)

        section.user = get('user', None)
        section.umask = octal_type(get('umask', '022'))
        section.logfile = existing_dirpath(get('logfile', 'supervisord.log'))
        section.logfile_maxbytes = byte_size(get('logfile_maxbytes', '50MB'))
        section.logfile_backups = integer(get('logfile_backups', 10))
        section.loglevel = logging_level(get('loglevel', 'info'))
        section.pidfile = existing_dirpath(get('pidfile', 'supervisord.pid'))
        section.identifier = get('identifier', 'supervisor')
        section.nodaemon = boolean(get('nodaemon', 'false'))

        tempdir = tempfile.gettempdir()
        section.childlogdir = existing_directory(get('childlogdir', tempdir))
        section.nocleanup = boolean(get('nocleanup', 'false'))
        section.strip_ansi = boolean(get('strip_ansi', 'false'))

        expansions = {'here':self.here}
        expansions.update(environ_expansions())
        environ_str = get('environment', '')
        environ_str = expand(environ_str, expansions, 'environment')
        section.environment = dict_of_key_value_pairs(environ_str)
        # Process rpcinterface plugins before groups to allow custom events to
        # be registered.
        section.rpcinterface_factories = self.get_plugins(
            parser,
            'supervisor.rpcinterface_factory',
            'rpcinterface:'
            )
        section.process_group_configs = self.process_groups_from_parser(parser)
        for group in section.process_group_configs:
            for proc in group.process_configs:
                env = section.environment.copy()
                env.update(proc.environment)
                proc.environment = env
        section.server_configs = self.server_configs_from_parser(parser)
        section.profile_options = None
        return section

    def process_groups_from_parser(self, parser):
        groups = []
        all_sections = parser.sections()
        homogeneous_exclude = []
        get = parser.saneget

        # process heterogeneous groups
        for section in all_sections:
            if not section.startswith('group:'):
                continue
            group_name = process_or_group_name(section.split(':', 1)[1])
            programs = list_of_strings(get(section, 'programs', None))
            priority = integer(get(section, 'priority', 999))
            group_processes = []
            for program in programs:
                program_section = "program:%s" % program
                if not program_section in all_sections:
                    raise ValueError(
                        '[%s] names unknown program %s' % (section, program))
                homogeneous_exclude.append(program_section)
                processes = self.processes_from_section(parser, program_section,
                                                        group_name,
                                                        ProcessConfig)
                group_processes.extend(processes)
            groups.append(
                ProcessGroupConfig(self, group_name, priority, group_processes)
                )

        # process "normal" homogeneous groups
        for section in all_sections:
            if ( (not section.startswith('program:') )
                 or section in homogeneous_exclude ):
                continue
            program_name = process_or_group_name(section.split(':', 1)[1])
            priority = integer(get(section, 'priority', 999))
            processes=self.processes_from_section(parser, section, program_name,
                                                  ProcessConfig)
            groups.append(
                ProcessGroupConfig(self, program_name, priority, processes)
                )

        # process "event listener" homogeneous groups
        for section in all_sections:
            if not section.startswith('eventlistener:'):
                continue
            pool_name = section.split(':', 1)[1]
            # give listeners a "high" default priority so they are started first
            # and stopped last at mainloop exit
            priority = integer(get(section, 'priority', -1))
            buffer_size = integer(get(section, 'buffer_size', 10))
            result_handler = get(section, 'result_handler',
                                       'supervisor.dispatchers:default_handler')
            try:
                result_handler = self.import_spec(result_handler)
            except ImportError:
                raise ValueError('%s cannot be resolved within [%s]' % (
                    result_handler, section))
            pool_event_names = [x.upper() for x in
                                list_of_strings(get(section, 'events', ''))]
            pool_event_names = set(pool_event_names)
            if not pool_event_names:
                raise ValueError('[%s] section requires an "events" line' %
                                 section)
            from supervisor.events import EventTypes
            pool_events = []
            for pool_event_name in pool_event_names:
                pool_event = getattr(EventTypes, pool_event_name, None)
                if pool_event is None:
                    raise ValueError('Unknown event type %s in [%s] events' %
                                     (pool_event_name, section))
                pool_events.append(pool_event)
            processes=self.processes_from_section(parser, section, pool_name,
                                                  EventListenerConfig)

            groups.append(
                EventListenerPoolConfig(self, pool_name, priority, processes,
                                        buffer_size, pool_events,
                                        result_handler)
                )

        # process fastcgi homogeneous groups
        for section in all_sections:
            if ( (not section.startswith('fcgi-program:') )
                 or section in homogeneous_exclude ):
                continue
            program_name = process_or_group_name(section.split(':', 1)[1])
            priority = integer(get(section, 'priority', 999))

            # find proc_uid from "user" option
            proc_user = get(section, 'user', None)
            if proc_user is None:
                proc_uid = None
            else:
                proc_uid = name_to_uid(proc_user)

            socket_owner = get(section, 'socket_owner', None)
            if socket_owner is not None:
                try:
                    socket_owner = colon_separated_user_group(socket_owner)
                except ValueError:
                    raise ValueError('Invalid socket_owner value %s'
                                                                % socket_owner)

            socket_mode = get(section, 'socket_mode', None)
            if socket_mode is not None:
                try:
                    socket_mode = octal_type(socket_mode)
                except (TypeError, ValueError):
                    raise ValueError('Invalid socket_mode value %s'
                                                                % socket_mode)

            socket = get(section, 'socket', None)
            if not socket:
                raise ValueError('[%s] section requires a "socket" line' %
                                 section)

            expansions = {'here':self.here,
                          'program_name':program_name}
            expansions.update(environ_expansions())
            socket = expand(socket, expansions, 'socket')
            try:
                socket_config = self.parse_fcgi_socket(socket, proc_uid,
                                                    socket_owner, socket_mode)
            except ValueError as e:
                raise ValueError('%s in [%s] socket' % (str(e), section))

            processes=self.processes_from_section(parser, section, program_name,
                                                  FastCGIProcessConfig)
            groups.append(
                FastCGIGroupConfig(self, program_name, priority, processes,
                                   socket_config)
                )

        groups.sort()
        return groups

    def parse_fcgi_socket(self, sock, proc_uid, socket_owner, socket_mode):
        if sock.startswith('unix://'):
            path = sock[7:]
            #Check it's an absolute path
            if not os.path.isabs(path):
                raise ValueError("Unix socket path %s is not an absolute path",
                                 path)
            path = normalize_path(path)

            if socket_owner is None:
                uid = os.getuid()
                if proc_uid is not None and proc_uid != uid:
                    socket_owner = (proc_uid, gid_for_uid(proc_uid))

            if socket_mode is None:
                socket_mode = 448 # 0700 in Py2, 0o700 Py3

            return UnixStreamSocketConfig(path, owner=socket_owner,
                                                mode=socket_mode)

        if socket_owner is not None or socket_mode is not None:
            raise ValueError("socket_owner and socket_mode params should"
                    + " only be used with a Unix domain socket")

        m = re.match(r'tcp://([^\s:]+):(\d+)$', sock)
        if m:
            host = m.group(1)
            port = int(m.group(2))
            return InetStreamSocketConfig(host, port)

        raise ValueError("Bad socket format %s", sock)

    def processes_from_section(self, parser, section, group_name,
                               klass=None):
        if klass is None:
            klass = ProcessConfig
        programs = []
        get = parser.saneget
        program_name = process_or_group_name(section.split(':', 1)[1])
        priority = integer(get(section, 'priority', 999))
        autostart = boolean(get(section, 'autostart', 'true'))
        autorestart = auto_restart(get(section, 'autorestart', 'unexpected'))
        startsecs = integer(get(section, 'startsecs', 1))
        startretries = integer(get(section, 'startretries', 3))
        stopsignal = signal_number(get(section, 'stopsignal', 'TERM'))
        stopwaitsecs = integer(get(section, 'stopwaitsecs', 10))
        stopasgroup = boolean(get(section, 'stopasgroup', 'false'))
        killasgroup = boolean(get(section, 'killasgroup', stopasgroup))
        exitcodes = list_of_exitcodes(get(section, 'exitcodes', '0,2'))
        redirect_stderr = boolean(get(section, 'redirect_stderr','false'))
        numprocs = integer(get(section, 'numprocs', 1))
        numprocs_start = integer(get(section, 'numprocs_start', 0))
        environment_str = get(section, 'environment', '')
        stdout_cmaxbytes = byte_size(get(section,'stdout_capture_maxbytes','0'))
        stdout_events = boolean(get(section, 'stdout_events_enabled','false'))
        stderr_cmaxbytes = byte_size(get(section,'stderr_capture_maxbytes','0'))
        stderr_events = boolean(get(section, 'stderr_events_enabled','false'))
        serverurl = get(section, 'serverurl', None)
        if serverurl and serverurl.strip().upper() == 'AUTO':
            serverurl = None

        # find uid from "user" option
        user = get(section, 'user', None)
        if user is None:
            uid = None
        else:
            uid = name_to_uid(user)

        umask = get(section, 'umask', None)
        if umask is not None:
            umask = octal_type(umask)

        command = get(section, 'command', None)
        if command is None:
            raise ValueError(
                'program section %s does not specify a command' % section)

        process_name = process_or_group_name(
            get(section, 'process_name', '%(program_name)s'))

        if numprocs > 1:
            if not '%(process_num)' in process_name:
                # process_name needs to include process_num when we
                # represent a group of processes
                raise ValueError(
                    '%(process_num) must be present within process_name when '
                    'numprocs > 1')

        if stopasgroup and not killasgroup:
            raise ValueError(
                "Cannot set stopasgroup=true and killasgroup=false"
                )

        host_node_name = platform.node()
        for process_num in range(numprocs_start, numprocs + numprocs_start):
            expansions = {'here':self.here,
                          'process_num':process_num,
                          'program_name':program_name,
                          'host_node_name':host_node_name,
                          'group_name':group_name}
            expansions.update(environ_expansions())

            environment = dict_of_key_value_pairs(
                expand(environment_str, expansions, 'environment'))

            directory = get(section, 'directory', None)
            if directory:
                directory = expand(directory, expansions, 'directory')

            logfiles = {}

            for k in ('stdout', 'stderr'):
                n = '%s_logfile' % k
                lf_val = get(section, n, Automatic)
                if isinstance(lf_val, basestring):
                    lf_val = expand(lf_val, expansions, n)
                lf_val = logfile_name(lf_val)
                logfiles[n] = lf_val

                bu_key = '%s_logfile_backups' % k
                backups = integer(get(section, bu_key, 10))
                logfiles[bu_key] = backups

                mb_key = '%s_logfile_maxbytes' % k
                maxbytes = byte_size(get(section, mb_key, '50MB'))
                logfiles[mb_key] = maxbytes

                sy_key = '%s_syslog' % k
                syslog = boolean(get(section, sy_key, False))
                logfiles[sy_key] = syslog

                if lf_val is Automatic and not maxbytes:
                    self.parse_warnings.append(
                        'For [%s], AUTO logging used for %s without '
                        'rollover, set maxbytes > 0 to avoid filling up '
                        'filesystem unintentionally' % (section, n))

            pconfig = klass(
                self,
                name=expand(process_name, expansions, 'process_name'),
                command=expand(command, expansions, 'command'),
                directory=directory,
                umask=umask,
                priority=priority,
                autostart=autostart,
                autorestart=autorestart,
                startsecs=startsecs,
                startretries=startretries,
                uid=uid,
                stdout_logfile=logfiles['stdout_logfile'],
                stdout_capture_maxbytes = stdout_cmaxbytes,
                stdout_events_enabled = stdout_events,
                stdout_logfile_backups=logfiles['stdout_logfile_backups'],
                stdout_logfile_maxbytes=logfiles['stdout_logfile_maxbytes'],
                stdout_syslog=logfiles['stdout_syslog'],
                stderr_logfile=logfiles['stderr_logfile'],
                stderr_capture_maxbytes = stderr_cmaxbytes,
                stderr_events_enabled = stderr_events,
                stderr_logfile_backups=logfiles['stderr_logfile_backups'],
                stderr_logfile_maxbytes=logfiles['stderr_logfile_maxbytes'],
                stderr_syslog=logfiles['stderr_syslog'],
                stopsignal=stopsignal,
                stopwaitsecs=stopwaitsecs,
                stopasgroup=stopasgroup,
                killasgroup=killasgroup,
                exitcodes=exitcodes,
                redirect_stderr=redirect_stderr,
                environment=environment,
                serverurl=serverurl)

            programs.append(pconfig)

        programs.sort() # asc by priority
        return programs

    def _parse_servernames(self, parser, stype):
        options = []
        for section in parser.sections():
            if section.startswith(stype):
                parts = section.split(':', 1)
                if len(parts) > 1:
                    name = parts[1]
                else:
                    name = None # default sentinel
                options.append((name, section))
        return options

    def _parse_username_and_password(self, parser, section):
        get = parser.saneget
        username = get(section, 'username', None)
        password = get(section, 'password', None)
        if username is None and password is not None:
            raise ValueError(
                'Must specify username if password is specified in [%s]'
                % section)
        return {'username':username, 'password':password}

    def server_configs_from_parser(self, parser):
        configs = []
        inet_serverdefs = self._parse_servernames(parser, 'inet_http_server')
        for name, section in inet_serverdefs:
            config = {}
            get = parser.saneget
            config.update(self._parse_username_and_password(parser, section))
            config['name'] = name
            config['family'] = socket.AF_INET
            port = get(section, 'port', None)
            if port is None:
                raise ValueError('section [%s] has no port value' % section)
            host, port = inet_address(port)
            config['host'] = host
            config['port'] = port
            config['section'] = section
            configs.append(config)

        unix_serverdefs = self._parse_servernames(parser, 'unix_http_server')
        for name, section in unix_serverdefs:
            config = {}
            get = parser.saneget
            sfile = get(section, 'file', None)
            if sfile is None:
                raise ValueError('section [%s] has no file value' % section)
            sfile = sfile.strip()
            config['name'] = name
            config['family'] = socket.AF_UNIX
            sfile = expand(sfile, {'here':self.here}, 'socket file')
            config['file'] = normalize_path(sfile)
            config.update(self._parse_username_and_password(parser, section))
            chown = get(section, 'chown', None)
            if chown is not None:
                try:
                    chown = colon_separated_user_group(chown)
                except ValueError:
                    raise ValueError('Invalid sockchown value %s' % chown)
            else:
                chown = (-1, -1)
            config['chown'] = chown
            chmod = get(section, 'chmod', None)
            if chmod is not None:
                try:
                    chmod = octal_type(chmod)
                except (TypeError, ValueError):
                    raise ValueError('Invalid chmod value %s' % chmod)
            else:
                chmod = 448 # 0700 on py2, 0o700 on py3
            config['chmod'] = chmod
            config['section'] = section
            configs.append(config)

        return configs

    def daemonize(self):
        # To daemonize, we need to become the leader of our own session
        # (process) group.  If we do not, signals sent to our
        # parent process will also be sent to us.   This might be bad because
        # signals such as SIGINT can be sent to our parent process during
        # normal (uninteresting) operations such as when we press Ctrl-C in the
        # parent terminal window to escape from a logtail command.
        # To disassociate ourselves from our parent's session group we use
        # os.setsid.  It means "set session id", which has the effect of
        # disassociating a process from is current session and process group
        # and setting itself up as a new session leader.
        #
        # Unfortunately we cannot call setsid if we're already a session group
        # leader, so we use "fork" to make a copy of ourselves that is
        # guaranteed to not be a session group leader.
        #
        # We also change directories, set stderr and stdout to null, and
        # change our umask.
        #
        # This explanation was (gratefully) garnered from
        # http://www.hawklord.uklinux.net/system/daemons/d3.htm

        pid = os.fork()
        if pid != 0:
            # Parent
            self.logger.blather("supervisord forked; parent exiting")
            os._exit(0)
        # Child
        self.logger.info("daemonizing the supervisord process")
        if self.directory:
            try:
                os.chdir(self.directory)
            except OSError as err:
                self.logger.critical("can't chdir into %r: %s"
                                     % (self.directory, err))
            else:
                self.logger.info("set current directory: %r"
                                 % self.directory)
        os.close(0)
        self.stdin = sys.stdin = sys.__stdin__ = open("/dev/null")
        os.close(1)
        self.stdout = sys.stdout = sys.__stdout__ = open("/dev/null", "w")
        os.close(2)
        self.stderr = sys.stderr = sys.__stderr__ = open("/dev/null", "w")
        os.setsid()
        os.umask(self.umask)
        # XXX Stevens, in his Advanced Unix book, section 13.3 (page
        # 417) recommends calling umask(0) and closing unused
        # file descriptors.  In his Network Programming book, he
        # additionally recommends ignoring SIGHUP and forking again
        # after the setsid() call, for obscure SVR4 reasons.

    def write_pidfile(self):
        pid = os.getpid()
        try:
            f = open(self.pidfile, 'w')
            f.write('%s\n' % pid)
            f.close()
        except (IOError, OSError):
            self.logger.critical('could not write pidfile %s' % self.pidfile)
        else:
            self.logger.info('supervisord started with pid %s' % pid)

    def cleanup(self):
        try:
            for config, server in self.httpservers:
                if config['family'] == socket.AF_UNIX:
                    if self.unlink_socketfiles:
                        socketname = config['file']
                        try:
                            os.unlink(socketname)
                        except OSError:
                            pass
        except OSError:
            pass
        try:
            os.unlink(self.pidfile)
        except OSError:
            pass

    def close_httpservers(self):
        for config, server in self.httpservers:
            server.close()
            map = self.get_socket_map()
            # server._map is a reference to the asyncore socket_map
            for dispatcher in map.values():
                # For unknown reasons, sometimes an http_channel
                # dispatcher in the socket map related to servers
                # remains open *during a reload*.  If one of these
                # exists at this point, we need to close it by hand
                # (thus removing it from the asyncore.socket_map).  If
                # we don't do this, 'cleanup_fds' will cause its file
                # descriptor to be closed, but it will still remain in
                # the socket_map, and eventually its file descriptor
                # will be passed to # select(), which will bomb.  See
                # also http://www.plope.com/software/collector/253
                dispatcher_server = getattr(dispatcher, 'server', None)
                if dispatcher_server is server:
                    dispatcher.close()

    def close_logger(self):
        self.logger.close()

    def setsignals(self):
        receive = self.signal_receiver.receive
        signal.signal(signal.SIGTERM, receive)
        signal.signal(signal.SIGINT, receive)
        signal.signal(signal.SIGQUIT, receive)
        signal.signal(signal.SIGHUP, receive)
        signal.signal(signal.SIGCHLD, receive)
        signal.signal(signal.SIGUSR2, receive)

    def get_signal(self):
        return self.signal_receiver.get_signal()

    def openhttpservers(self, supervisord):
        try:
            self.httpservers = self.make_http_servers(supervisord)
        except socket.error as why:
            if why.args[0] == errno.EADDRINUSE:
                self.usage('Another program is already listening on '
                           'a port that one of our HTTP servers is '
                           'configured to use.  Shut this program '
                           'down first before starting supervisord.')
            else:
                help = 'Cannot open an HTTP server: socket.error reported'
                errorname = errno.errorcode.get(why.args[0])
                if errorname is None:
                    self.usage('%s %s' % (help, why.args[0]))
                else:
                    self.usage('%s errno.%s (%d)' %
                               (help, errorname, why.args[0]))
            self.unlink_socketfiles = False
        except ValueError as why:
            self.usage(why.args[0])

    def get_autochildlog_name(self, name, identifier, channel):
        prefix='%s-%s---%s-' % (name, channel, identifier)
        logfile = self.mktempfile(
            suffix='.log',
            prefix=prefix,
            dir=self.childlogdir)
        return logfile

    def clear_autochildlogdir(self):
        # must be called after realize()
        childlogdir = self.childlogdir
        fnre = re.compile(r'.+?---%s-\S+\.log\.{0,1}\d{0,4}' % self.identifier)
        try:
            filenames = os.listdir(childlogdir)
        except (IOError, OSError):
            self.logger.warn('Could not clear childlog dir')
            return

        for filename in filenames:
            if fnre.match(filename):
                pathname = os.path.join(childlogdir, filename)
                try:
                    os.remove(pathname)
                except (OSError, IOError):
                    self.logger.warn('Failed to clean up %r' % pathname)

    def get_socket_map(self):
        return asyncore.socket_map

    def cleanup_fds(self):
        # try to close any leaked file descriptors (for reload)
        start = 5
        for x in range(start, self.minfds):
            try:
                os.close(x)
            except OSError:
                pass

    def select(self, r, w, x, timeout):
        return select.select(r, w, x, timeout)

    def kill(self, pid, signal):
        os.kill(pid, signal)

    def set_uid(self):
        if self.uid is None:
            if os.getuid() == 0:
                return 'Supervisor running as root (no user in config file)'
            return None
        msg = self.dropPrivileges(self.uid)
        if msg is None:
            return 'Set uid to user %s' % self.uid
        return msg

    def dropPrivileges(self, user):
        # Drop root privileges if we have them
        if user is None:
            return "No user specified to setuid to!"

        # get uid for user, which can be a number or username
        try:
            uid = int(user)
        except ValueError:
            try:
                pwrec = pwd.getpwnam(user)
            except KeyError:
                return "Can't find username %r" % user
            uid = pwrec[2]
        else:
            try:
                pwrec = pwd.getpwuid(uid)
            except KeyError:
                return "Can't find uid %r" % uid

        current_uid = os.getuid()

        if current_uid == uid:
            # do nothing and return successfully if the uid is already the
            # current one.  this allows a supervisord running as an
            # unprivileged user "foo" to start a process where the config
            # has "user=foo" (same user) in it.
            return

        if current_uid != 0:
            return "Can't drop privilege as nonroot user"

        gid = pwrec[3]
        if hasattr(os, 'setgroups'):
            user = pwrec[0]
            groups = [grprec[2] for grprec in grp.getgrall() if user in
                      grprec[3]]

            # always put our primary gid first in this list, otherwise we can
            # lose group info since sometimes the first group in the setgroups
            # list gets overwritten on the subsequent setgid call (at least on
            # freebsd 9 with python 2.7 - this will be safe though for all unix
            # /python version combos)
            groups.insert(0, gid)
            try:
                os.setgroups(groups)
            except OSError:
                return 'Could not set groups of effective user'
        try:
            os.setgid(gid)
        except OSError:
            return 'Could not set group id of effective user'
        os.setuid(uid)

    def waitpid(self):
        # need pthread_sigmask here to avoid concurrent sigchild, but
        # Python doesn't offer it as it's not standard across UNIX versions.
        # there is still a race condition here; we can get a sigchild while
        # we're sitting in the waitpid call.
        try:
            pid, sts = os.waitpid(-1, os.WNOHANG)
        except OSError as why:
            err = why.args[0]
            if err not in (errno.ECHILD, errno.EINTR):
                self.logger.critical(
                    'waitpid error; a process may not be cleaned up properly')
            if err == errno.EINTR:
                self.logger.blather('EINTR during reap')
            pid, sts = None, None
        return pid, sts

    def set_rlimits(self):
        limits = []
        if hasattr(resource, 'RLIMIT_NOFILE'):
            limits.append(
                {
                'msg':('The minimum number of file descriptors required '
                       'to run this process is %(min)s as per the "minfds" '
                       'command-line argument or config file setting. '
                       'The current environment will only allow you '
                       'to open %(hard)s file descriptors.  Either raise '
                       'the number of usable file descriptors in your '
                       'environment (see README.rst) or lower the '
                       'minfds setting in the config file to allow '
                       'the process to start.'),
                'min':self.minfds,
                'resource':resource.RLIMIT_NOFILE,
                'name':'RLIMIT_NOFILE',
                })
        if hasattr(resource, 'RLIMIT_NPROC'):
            limits.append(
                {
                'msg':('The minimum number of available processes required '
                       'to run this program is %(min)s as per the "minprocs" '
                       'command-line argument or config file setting. '
                       'The current environment will only allow you '
                       'to open %(hard)s processes.  Either raise '
                       'the number of usable processes in your '
                       'environment (see README.rst) or lower the '
                       'minprocs setting in the config file to allow '
                       'the program to start.'),
                'min':self.minprocs,
                'resource':resource.RLIMIT_NPROC,
                'name':'RLIMIT_NPROC',
                })

        msgs = []

        for limit in limits:

            lmin = limit['min']
            res = limit['resource']
            msg = limit['msg']
            name = limit['name']
            name = name # name is used below by locals()

            soft, hard = resource.getrlimit(res)

            if (soft < lmin) and (soft != -1): # -1 means unlimited
                if (hard < lmin) and (hard != -1):
                    # setrlimit should increase the hard limit if we are
                    # root, if not then setrlimit raises and we print usage
                    hard = lmin

                try:
                    resource.setrlimit(res, (lmin, hard))
                    msgs.append('Increased %(name)s limit to %(lmin)s' %
                                locals())
                except (resource.error, ValueError):
                    self.usage(msg % locals())
        return msgs

    def make_logger(self, critical_messages, warn_messages, info_messages):
        # must be called after realize() and after supervisor does setuid()
        format = '%(asctime)s %(levelname)s %(message)s\n'
        self.logger = loggers.getLogger(self.loglevel)
        if self.nodaemon:
            loggers.handle_stdout(self.logger, format)
        loggers.handle_file(
            self.logger,
            self.logfile,
            format,
            rotating=True,
            maxbytes=self.logfile_maxbytes,
            backups=self.logfile_backups,
        )
        for msg in critical_messages:
            self.logger.critical(msg)
        for msg in warn_messages:
            self.logger.warn(msg)
        for msg in info_messages:
            self.logger.info(msg)

    def make_http_servers(self, supervisord):
        from supervisor.http import make_http_servers
        return make_http_servers(self, supervisord)

    def close_fd(self, fd):
        try:
            os.close(fd)
        except OSError:
            pass

    def fork(self):
        return os.fork()

    def dup2(self, frm, to):
        return os.dup2(frm, to)

    def setpgrp(self):
        return os.setpgrp()

    def stat(self, filename):
        return os.stat(filename)

    def write(self, fd, data):
        return os.write(fd, as_bytes(data))

    def execve(self, filename, argv, env):
        return os.execve(filename, argv, env)

    def mktempfile(self, suffix, prefix, dir):
        # set os._urandomfd as a hack around bad file descriptor bug
        # seen in the wild, see
        # http://www.plope.com/software/collector/252
        os._urandomfd = None
        fd, filename = tempfile.mkstemp(suffix, prefix, dir)
        os.close(fd)
        return filename

    def remove(self, path):
        os.remove(path)

    def exists(self, path):
        return os.path.exists(path)

    def _exit(self, code):
        os._exit(code)

    def setumask(self, mask):
        os.umask(mask)

    def get_path(self):
        """Return a list corresponding to $PATH, or a default."""
        path = ["/bin", "/usr/bin", "/usr/local/bin"]
        if "PATH" in os.environ:
            p = os.environ["PATH"]
            if p:
                path = p.split(os.pathsep)
        return path

    def get_pid(self):
        return os.getpid()

    def check_execv_args(self, filename, argv, st):
        if st is None:
            raise NotFound("can't find command %r" % filename)

        elif stat.S_ISDIR(st[stat.ST_MODE]):
            raise NotExecutable("command at %r is a directory" % filename)

        elif not (stat.S_IMODE(st[stat.ST_MODE]) & 73):
            # 73 is spelled 0111 in py2, 0o111 in py3
            raise NotExecutable("command at %r is not executable" % filename)

        elif not os.access(filename, os.X_OK):
            raise NoPermission("no permission to run command %r" % filename)

    def reopenlogs(self):
        self.logger.info('supervisord logreopen')
        for handler in self.logger.handlers:
            if hasattr(handler, 'reopen'):
                handler.reopen()

    def readfd(self, fd):
        try:
            data = os.read(fd, 2 << 16) # 128K
        except OSError as why:
            if why.args[0] not in (errno.EWOULDBLOCK, errno.EBADF, errno.EINTR):
                raise
            data = ''
        return as_string(data)

    def process_environment(self):
        os.environ.update(self.environment or {})

    def open(self, fn, mode='r'):
        return open(fn, mode)

    def chdir(self, dir):
        os.chdir(dir)

    def make_pipes(self, stderr=True):
        """ Create pipes for parent to child stdin/stdout/stderr
        communications.  Open fd in non-blocking mode so we can read them
        in the mainloop without blocking.  If stderr is False, don't
        create a pipe for stderr. """

        pipes = {'child_stdin':None,
                 'stdin':None,
                 'stdout':None,
                 'child_stdout':None,
                 'stderr':None,
                 'child_stderr':None}
        try:
            stdin, child_stdin = os.pipe()
            pipes['child_stdin'], pipes['stdin'] = stdin, child_stdin
            stdout, child_stdout = os.pipe()
            pipes['stdout'], pipes['child_stdout'] = stdout, child_stdout
            if stderr:
                stderr, child_stderr = os.pipe()
                pipes['stderr'], pipes['child_stderr'] = stderr, child_stderr
            for fd in (pipes['stdout'], pipes['stderr'], pipes['stdin']):
                if fd is not None:
                    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | os.O_NDELAY)
            return pipes
        except OSError:
            for fd in pipes.values():
                if fd is not None:
                    self.close_fd(fd)

    def close_parent_pipes(self, pipes):
        for fdname in ('stdin', 'stdout', 'stderr'):
            fd = pipes[fdname]
            if fd is not None:
                self.close_fd(fd)

    def close_child_pipes(self, pipes):
        for fdname in ('child_stdin', 'child_stdout', 'child_stderr'):
            fd = pipes[fdname]
            if fd is not None:
                self.close_fd(fd)

class ClientOptions(Options):
    positional_args_allowed = 1

    interactive = None
    prompt = None
    serverurl = None
    username = None
    password = None
    history_file = None

    def __init__(self):
        Options.__init__(self, require_configfile=False)
        self.configroot = Dummy()
        self.configroot.supervisorctl = Dummy()
        self.configroot.supervisorctl.interactive = None
        self.configroot.supervisorctl.prompt = 'supervisor'
        self.configroot.supervisorctl.serverurl = None
        self.configroot.supervisorctl.username = None
        self.configroot.supervisorctl.password = None
        self.configroot.supervisorctl.history_file = None

        from supervisor.supervisorctl import DefaultControllerPlugin
        default_factory = ('default', DefaultControllerPlugin, {})
        # we always add the default factory. If you want to a supervisorctl
        # without the default plugin, please write your own supervisorctl.
        self.plugin_factories = [default_factory]

        self.add("interactive", "supervisorctl.interactive", "i",
                 "interactive", flag=1, default=0)
        self.add("prompt", "supervisorctl.prompt", default="supervisor")
        self.add("serverurl", "supervisorctl.serverurl", "s:", "serverurl=",
                 url, default="http://localhost:9001")
        self.add("username", "supervisorctl.username", "u:", "username=")
        self.add("password", "supervisorctl.password", "p:", "password=")
        self.add("history", "supervisorctl.history_file", "r:", "history_file=")

    def realize(self, *arg, **kw):
        Options.realize(self, *arg, **kw)
        if not self.args:
            self.interactive = 1

    def read_config(self, fp):
        section = self.configroot.supervisorctl
        need_close = False
        if not hasattr(fp, 'read'):
            self.here = os.path.dirname(normalize_path(fp))
            if not os.path.exists(fp):
                raise ValueError("could not find config file %s" % fp)
            try:
                fp = open(fp, 'r')
                need_close = True
            except (IOError, OSError):
                raise ValueError("could not read config file %s" % fp)
        config = UnhosedConfigParser()
        config.mysection = 'supervisorctl'
        try:
            config.read_file(fp)
        except AttributeError:
            config.readfp(fp)
        if need_close:
            fp.close()
        sections = config.sections()
        if not 'supervisorctl' in sections:
            raise ValueError('.ini file does not include supervisorctl section')
        serverurl = config.getdefault('serverurl', 'http://localhost:9001')
        if serverurl.startswith('unix://'):
            sf = serverurl[7:]
            path = expand(sf, {'here':self.here}, 'serverurl')
            path = normalize_path(path)
            serverurl = 'unix://%s' % path
        section.serverurl = serverurl

        # The defaults used below are really set in __init__ (since
        # section==self.configroot.supervisorctl)
        section.prompt = config.getdefault('prompt', section.prompt)
        section.username = config.getdefault('username', section.username)
        section.password = config.getdefault('password', section.password)
        history_file = config.getdefault('history_file', section.history_file)

        if history_file:
            history_file = normalize_path(history_file)
            section.history_file = history_file
            self.history_file = history_file
        else:
            section.history_file = None
            self.history_file = None

        self.plugin_factories += self.get_plugins(
            config,
            'supervisor.ctl_factory',
            'ctlplugin:'
            )

        return section

    def getServerProxy(self):
        # mostly put here for unit testing
        return xmlrpclib.ServerProxy(
            # dumbass ServerProxy won't allow us to pass in a non-HTTP url,
            # so we fake the url we pass into it and always use the transport's
            # 'serverurl' to figure out what to attach to
            'http://127.0.0.1',
            transport = xmlrpc.SupervisorTransport(self.username,
                                                   self.password,
                                                   self.serverurl)
            )

_marker = []

class UnhosedConfigParser(ConfigParser.RawConfigParser):
    mysection = 'supervisord'
    def read_string(self, s):
        if not PY3 and isinstance(s, str):
            s = unicode(s)
        s = StringIO(s)
        try:
            return self.read_file(s)
        except AttributeError:
            return self.readfp(s)

    def getdefault(self, option, default=_marker):
        try:
            return self.get(self.mysection, option)
        except ConfigParser.NoOptionError:
            if default is _marker:
                raise
            else:
                return default

    def saneget(self, section, option, default=_marker):
        try:
            return self.get(section, option)
        except ConfigParser.NoOptionError:
            if default is _marker:
                raise
            else:
                return default

class Config(object):
    def __ne__(self, other):
        return not self.__eq__(other)

    def __lt__(self, other):
        if self.priority == other.priority:
            return self.name < other.name

        return self.priority < other.priority

    def __le__(self, other):
        if self.priority == other.priority:
            return self.name <= other.name

        return self.priority <= other.priority

    def __gt__(self, other):
        if self.priority == other.priority:
            return self.name > other.name

        return self.priority > other.priority

    def __ge__(self, other):
        if self.priority == other.priority:
            return self.name >= other.name

        return self.priority >= other.priority

    def __repr__(self):
        return '<%s instance at %s named %s>' % (self.__class__, id(self),
                                                 self.name)

class ProcessConfig(Config):
    req_param_names = [
        'name', 'uid', 'command', 'directory', 'umask', 'priority',
        'autostart', 'autorestart', 'startsecs', 'startretries',
        'stdout_logfile', 'stdout_capture_maxbytes',
        'stdout_events_enabled', 'stdout_syslog',
        'stdout_logfile_backups', 'stdout_logfile_maxbytes',
        'stderr_logfile', 'stderr_capture_maxbytes',
        'stderr_logfile_backups', 'stderr_logfile_maxbytes',
        'stderr_events_enabled', 'stderr_syslog',
        'stopsignal', 'stopwaitsecs', 'stopasgroup', 'killasgroup',
        'exitcodes', 'redirect_stderr' ]
    optional_param_names = [ 'environment', 'serverurl' ]

    def __init__(self, options, **params):
        self.options = options
        for name in self.req_param_names:
            setattr(self, name, params[name])
        for name in self.optional_param_names:
            setattr(self, name, params.get(name, None))

    def __eq__(self, other):
        if not isinstance(other, ProcessConfig):
            return False

        for name in self.req_param_names + self.optional_param_names:
            if Automatic in [getattr(self, name), getattr(other, name)] :
                continue
            if getattr(self, name) != getattr(other, name):
                return False

        return True

    def create_autochildlogs(self):
        # temporary logfiles which are erased at start time
        get_autoname = self.options.get_autochildlog_name
        sid = self.options.identifier
        name = self.name
        if self.stdout_logfile is Automatic:
            self.stdout_logfile = get_autoname(name, sid, 'stdout')
        if self.stderr_logfile is Automatic:
            self.stderr_logfile = get_autoname(name, sid, 'stderr')

    def make_process(self, group=None):
        from supervisor.process import Subprocess
        process = Subprocess(self)
        process.group = group
        return process

    def make_dispatchers(self, proc):
        use_stderr = not self.redirect_stderr
        p = self.options.make_pipes(use_stderr)
        stdout_fd,stderr_fd,stdin_fd = p['stdout'],p['stderr'],p['stdin']
        dispatchers = {}
        from supervisor.dispatchers import POutputDispatcher
        from supervisor.dispatchers import PInputDispatcher
        from supervisor import events
        if stdout_fd is not None:
            etype = events.ProcessCommunicationStdoutEvent
            dispatchers[stdout_fd] = POutputDispatcher(proc, etype, stdout_fd)
        if stderr_fd is not None:
            etype = events.ProcessCommunicationStderrEvent
            dispatchers[stderr_fd] = POutputDispatcher(proc,etype, stderr_fd)
        if stdin_fd is not None:
            dispatchers[stdin_fd] = PInputDispatcher(proc, 'stdin', stdin_fd)
        return dispatchers, p

class EventListenerConfig(ProcessConfig):
    def make_dispatchers(self, proc):
        use_stderr = not self.redirect_stderr
        p = self.options.make_pipes(use_stderr)
        stdout_fd,stderr_fd,stdin_fd = p['stdout'],p['stderr'],p['stdin']
        dispatchers = {}
        from supervisor.dispatchers import PEventListenerDispatcher
        from supervisor.dispatchers import PInputDispatcher
        from supervisor.dispatchers import POutputDispatcher
        from supervisor import events
        if stdout_fd is not None:
            dispatchers[stdout_fd] = PEventListenerDispatcher(proc, 'stdout',
                                                              stdout_fd)
        if stderr_fd is not None:
            etype = events.ProcessCommunicationStderrEvent
            dispatchers[stderr_fd] = POutputDispatcher(proc, etype, stderr_fd)
        if stdin_fd is not None:
            dispatchers[stdin_fd] = PInputDispatcher(proc, 'stdin', stdin_fd)
        return dispatchers, p

class FastCGIProcessConfig(ProcessConfig):

    def make_process(self, group=None):
        if group is None:
            raise NotImplementedError('FastCGI programs require a group')
        from supervisor.process import FastCGISubprocess
        process = FastCGISubprocess(self)
        process.group = group
        return process

    def make_dispatchers(self, proc):
        dispatchers, p = ProcessConfig.make_dispatchers(self, proc)
        #FastCGI child processes expect the FastCGI socket set to
        #file descriptor 0, so supervisord cannot use stdin
        #to communicate with the child process
        stdin_fd = p['stdin']
        if stdin_fd is not None:
            dispatchers[stdin_fd].close()
        return dispatchers, p

class ProcessGroupConfig(Config):
    def __init__(self, options, name, priority, process_configs):
        self.options = options
        self.name = name
        self.priority = priority
        self.process_configs = process_configs

    def __eq__(self, other):
        if not isinstance(other, ProcessGroupConfig):
            return False

        if self.name != other.name:
            return False
        if self.priority != other.priority:
            return False
        if self.process_configs != other.process_configs:
            return False

        return True

    def after_setuid(self):
        for config in self.process_configs:
            config.create_autochildlogs()

    def make_group(self):
        from supervisor.process import ProcessGroup
        return ProcessGroup(self)

class EventListenerPoolConfig(Config):
    def __init__(self, options, name, priority, process_configs, buffer_size,
                 pool_events, result_handler):
        self.options = options
        self.name = name
        self.priority = priority
        self.process_configs = process_configs
        self.buffer_size = buffer_size
        self.pool_events = pool_events
        self.result_handler = result_handler

    def __eq__(self, other):
        if not isinstance(other, EventListenerPoolConfig):
            return False

        if (self.name == other.name) and (self.priority == other.priority):
            return True

        return False

    def after_setuid(self):
        for config in self.process_configs:
            config.create_autochildlogs()

    def make_group(self):
        from supervisor.process import EventListenerPool
        return EventListenerPool(self)

class FastCGIGroupConfig(ProcessGroupConfig):
    def __init__(self, options, name, priority, process_configs, socket_config):
        ProcessGroupConfig.__init__(
            self,
            options,
            name,
            priority,
            process_configs,
            )
        self.socket_config = socket_config

    def __eq__(self, other):
        if not isinstance(other, FastCGIGroupConfig):
            return False

        if self.socket_config != other.socket_config:
            return False

        return ProcessGroupConfig.__eq__(self, other)

    def make_group(self):
        from supervisor.process import FastCGIProcessGroup
        return FastCGIProcessGroup(self)

def readFile(filename, offset, length):
    """ Read length bytes from the file named by filename starting at
    offset """

    absoffset = abs(offset)
    abslength = abs(length)

    f = None
    try:
        f = open(filename, 'rb')
        if absoffset != offset:
            # negative offset returns offset bytes from tail of the file
            if length:
                raise ValueError('BAD_ARGUMENTS')
            f.seek(0, 2)
            sz = f.tell()
            pos = int(sz - absoffset)
            if pos < 0:
                pos = 0
            f.seek(pos)
            data = f.read(absoffset)
        else:
            if abslength != length:
                raise ValueError('BAD_ARGUMENTS')
            if length == 0:
                f.seek(offset)
                data = f.read()
            else:
                f.seek(offset)
                data = f.read(length)
    except (OSError, IOError):
        raise ValueError('FAILED')

    finally:
        if f:
            f.close()
    return data

def tailFile(filename, offset, length):
    """
    Read length bytes from the file named by filename starting at
    offset, automatically increasing offset and setting overflow
    flag if log size has grown beyond (offset + length).  If length
    bytes are not available, as many bytes as are available are returned.
    """

    overflow = False
    f = None
    try:
        f = open(filename, 'rb')
        f.seek(0, 2)
        sz = f.tell()

        if sz > (offset + length):
            overflow = True
            offset   = sz - 1

        if (offset + length) > sz:
            if offset > (sz - 1):
                length = 0
            offset = sz - length

        if offset < 0: offset = 0
        if length < 0: length = 0

        if length == 0:
            data = ''
        else:
            f.seek(offset)
            data = f.read(length)

        offset = sz
        return [as_string(data), offset, overflow]

    except (OSError, IOError):
        return ['', offset, False]
    finally:
        if f:
            f.close()

# Helpers for dealing with signals and exit status

def decode_wait_status(sts):
    """Decode the status returned by wait() or waitpid().

    Return a tuple (exitstatus, message) where exitstatus is the exit
    status, or -1 if the process was killed by a signal; and message
    is a message telling what happened.  It is the caller's
    responsibility to display the message.
    """
    if os.WIFEXITED(sts):
        es = os.WEXITSTATUS(sts) & 0xffff
        msg = "exit status %s" % es
        return es, msg
    elif os.WIFSIGNALED(sts):
        sig = os.WTERMSIG(sts)
        msg = "terminated by %s" % signame(sig)
        if hasattr(os, "WCOREDUMP"):
            iscore = os.WCOREDUMP(sts)
        else:
            iscore = sts & 0x80
        if iscore:
            msg += " (core dumped)"
        return -1, msg
    else:
        msg = "unknown termination cause 0x%04x" % sts
        return -1, msg

_signames = None

def signame(sig):
    """Return a symbolic name for a signal.

    Return "signal NNN" if there is no corresponding SIG name in the
    signal module.
    """

    if _signames is None:
        _init_signames()
    return _signames.get(sig) or "signal %d" % sig

def _init_signames():
    global _signames
    d = {}
    for k, v in signal.__dict__.items():
        k_startswith = getattr(k, "startswith", None)
        if k_startswith is None:
            continue
        if k_startswith("SIG") and not k_startswith("SIG_"):
            d[v] = k
    _signames = d

class SignalReceiver:
    def __init__(self):
        self._signals_recvd = []

    def receive(self, sig, frame):
        if sig not in self._signals_recvd:
            self._signals_recvd.append(sig)

    def get_signal(self):
        if self._signals_recvd:
            sig = self._signals_recvd.pop(0)
        else:
            sig = None
        return sig

# miscellaneous utility functions

def expand(s, expansions, name):
    try:
        return s % expansions
    except KeyError:
        raise ValueError(
            'Format string %r for %r contains names which cannot be '
            'expanded' % (s, name))
    except:
        raise ValueError(
            'Format string %r for %r is badly formatted' % (s, name)
            )

_environ_expansions = None

def environ_expansions():
    """Return dict of environment variables, suitable for use in string
    expansions.

    Every environment variable is prefixed by 'ENV_'.
    """
    global _environ_expansions

    if _environ_expansions:
        return _environ_expansions

    _environ_expansions = {}
    for key, value in os.environ.items():
        _environ_expansions['ENV_%s' % key] = value

    return _environ_expansions

def make_namespec(group_name, process_name):
    # we want to refer to the process by its "short name" (a process named
    # process1 in the group process1 has a name "process1").  This is for
    # backwards compatibility
    if group_name == process_name:
        name = process_name
    else:
        name = '%s:%s' % (group_name, process_name)
    return name

def split_namespec(namespec):
    names = namespec.split(':', 1)
    if len(names) == 2:
        # group and process name differ
        group_name, process_name = names
        if not process_name or process_name == '*':
            process_name = None
    else:
        # group name is same as process name
        group_name, process_name = namespec, namespec
    return group_name, process_name

# exceptions

class ProcessException(Exception):
    """ Specialized exceptions used when attempting to start a process """

class BadCommand(ProcessException):
    """ Indicates the command could not be parsed properly. """

class NotExecutable(ProcessException):
    """ Indicates that the filespec cannot be executed because its path
    resolves to a file which is not executable, or which is a directory. """

class NotFound(ProcessException):
    """ Indicates that the filespec cannot be executed because it could not
    be found """

class NoPermission(ProcessException):
    """ Indicates that the file cannot be executed because the supervisor
    process does not possess the appropriate UNIX filesystem permission
    to execute the file. """


########NEW FILE########
__FILENAME__ = pidproxy
#!/usr/bin/env python

""" An executable which proxies for a subprocess; upon a signal, it sends that
signal to the process identified by a pidfile. """

import os
import sys
import signal
import time

class PidProxy:
    pid = None
    def __init__(self, args):
        self.setsignals()
        try:
            self.pidfile, cmdargs = args[1], args[2:]
            self.command = os.path.abspath(cmdargs[0])
            self.cmdargs = cmdargs
        except (ValueError, IndexError):
            self.usage()
            sys.exit(1)

    def go(self):
        self.pid = os.spawnv(os.P_NOWAIT, self.command, self.cmdargs)
        while 1:
            time.sleep(5)
            try:
                pid, sts = os.waitpid(-1, os.WNOHANG)
            except OSError:
                pid, sts = None, None
            if pid:
                break

    def usage(self):
        print("pidproxy.py <pidfile name> <command> [<cmdarg1> ...]")

    def setsignals(self):
        signal.signal(signal.SIGTERM, self.passtochild)
        signal.signal(signal.SIGHUP, self.passtochild)
        signal.signal(signal.SIGINT, self.passtochild)
        signal.signal(signal.SIGUSR1, self.passtochild)
        signal.signal(signal.SIGUSR2, self.passtochild)
        signal.signal(signal.SIGQUIT, self.passtochild)
        signal.signal(signal.SIGCHLD, self.reap)

    def reap(self, sig, frame):
        # do nothing, we reap our child synchronously
        pass

    def passtochild(self, sig, frame):
        try:
            pid = int(open(self.pidfile, 'r').read().strip())
        except:
            print("Can't read child pidfile %s!" % self.pidfile)
            return
        os.kill(pid, sig)
        if sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
            sys.exit(0)

def main():
    pp = PidProxy(sys.argv)
    pp.go()

if __name__ == '__main__':
    main()
    
    
    

########NEW FILE########
__FILENAME__ = process
import os
import time
import errno
import shlex
import traceback
import signal

from supervisor.compat import maxint
from supervisor.compat import StringIO
from supervisor.compat import total_ordering

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.states import ProcessStates
from supervisor.states import SupervisorStates
from supervisor.states import getProcessStateDescription
from supervisor.states import STOPPED_STATES

from supervisor.options import decode_wait_status
from supervisor.options import signame
from supervisor.options import ProcessException, BadCommand

from supervisor.dispatchers import EventListenerStates

from supervisor import events

from supervisor.datatypes import RestartUnconditionally

from supervisor.socket_manager import SocketManager

class Subprocess(object):

    """A class to manage a subprocess."""

    # Initial state; overridden by instance variables

    pid = 0 # Subprocess pid; 0 when not running
    config = None # ProcessConfig instance
    state = None # process state code
    listener_state = None # listener state code (if we're an event listener)
    event = None # event currently being processed (if we're an event listener)
    laststart = 0 # Last time the subprocess was started; 0 if never
    laststop = 0  # Last time the subprocess was stopped; 0 if never
    delay = 0 # If nonzero, delay starting or killing until this time
    administrative_stop = 0 # true if the process has been stopped by an admin
    system_stop = 0 # true if the process has been stopped by the system
    killing = 0 # flag determining whether we are trying to kill this proc
    backoff = 0 # backoff counter (to startretries)
    dispatchers = None # asyncore output dispatchers (keyed by fd)
    pipes = None # map of channel name to file descriptor #
    exitstatus = None # status attached to dead process by finish()
    spawnerr = None # error message attached by spawn() if any
    group = None # ProcessGroup instance if process is in the group

    def __init__(self, config):
        """Constructor.

        Argument is a ProcessConfig instance.
        """
        self.config = config
        self.dispatchers = {}
        self.pipes = {}
        self.state = ProcessStates.STOPPED

    def removelogs(self):
        for dispatcher in self.dispatchers.values():
            if hasattr(dispatcher, 'removelogs'):
                dispatcher.removelogs()

    def reopenlogs(self):
        for dispatcher in self.dispatchers.values():
            if hasattr(dispatcher, 'reopenlogs'):
                dispatcher.reopenlogs()

    def drain(self):
        for dispatcher in self.dispatchers.values():
            # note that we *must* call readable() for every
            # dispatcher, as it may have side effects for a given
            # dispatcher (eg. call handle_listener_state_change for
            # event listener processes)
            if dispatcher.readable():
                dispatcher.handle_read_event()
            if dispatcher.writable():
                dispatcher.handle_write_event()

    def write(self, chars):
        if not self.pid or self.killing:
            raise OSError(errno.EPIPE, "Process already closed")

        stdin_fd = self.pipes['stdin']
        if stdin_fd is None:
            raise OSError(errno.EPIPE, "Process has no stdin channel")

        dispatcher = self.dispatchers[stdin_fd]
        if dispatcher.closed:
            raise OSError(errno.EPIPE, "Process' stdin channel is closed")

        dispatcher.input_buffer += chars
        dispatcher.flush() # this must raise EPIPE if the pipe is closed

    def get_execv_args(self):
        """Internal: turn a program name into a file name, using $PATH,
        make sure it exists / is executable, raising a ProcessException
        if not """
        try:
            commandargs = shlex.split(self.config.command)
        except ValueError as e:
            raise BadCommand("can't parse command %r: %s" % \
                (self.config.command, str(e)))

        if commandargs:
            program = commandargs[0]
        else:
            raise BadCommand("command is empty")

        if "/" in program:
            filename = program
            try:
                st = self.config.options.stat(filename)
            except OSError:
                st = None

        else:
            path = self.config.options.get_path()
            found = None
            st = None
            for dir in path:
                found = os.path.join(dir, program)
                try:
                    st = self.config.options.stat(found)
                except OSError:
                    pass
                else:
                    break
            if st is None:
                filename = program
            else:
                filename = found

        # check_execv_args will raise a ProcessException if the execv
        # args are bogus, we break it out into a separate options
        # method call here only to service unit tests
        self.config.options.check_execv_args(filename, commandargs, st)

        return filename, commandargs

    event_map = {
        ProcessStates.BACKOFF: events.ProcessStateBackoffEvent,
        ProcessStates.FATAL:   events.ProcessStateFatalEvent,
        ProcessStates.UNKNOWN: events.ProcessStateUnknownEvent,
        ProcessStates.STOPPED: events.ProcessStateStoppedEvent,
        ProcessStates.EXITED:  events.ProcessStateExitedEvent,
        ProcessStates.RUNNING: events.ProcessStateRunningEvent,
        ProcessStates.STARTING: events.ProcessStateStartingEvent,
        ProcessStates.STOPPING: events.ProcessStateStoppingEvent,
        }

    def change_state(self, new_state, expected=True):
        old_state = self.state
        if new_state is old_state:
            # exists for unit tests
            return False

        event_class = self.event_map.get(new_state)
        if event_class is not None:
            event = event_class(self, old_state, expected)
            events.notify(event)

        if new_state == ProcessStates.BACKOFF:
            now = time.time()
            self.backoff += 1
            self.delay = now + self.backoff

        self.state = new_state

    def _assertInState(self, *states):
        if self.state not in states:
            current_state = getProcessStateDescription(self.state)
            allowable_states = ' '.join(map(getProcessStateDescription, states))
            raise AssertionError('Assertion failed for %s: %s not in %s' %  (
                self.config.name, current_state, allowable_states))

    def record_spawnerr(self, msg):
        self.spawnerr = msg
        self.config.options.logger.info("spawnerr: %s" % msg)

    def spawn(self):
        """Start the subprocess.  It must not be running already.

        Return the process id.  If the fork() call fails, return None.
        """
        options = self.config.options

        if self.pid:
            msg = 'process %r already running' % self.config.name
            options.logger.warn(msg)
            return

        self.killing = 0
        self.spawnerr = None
        self.exitstatus = None
        self.system_stop = 0
        self.administrative_stop = 0

        self.laststart = time.time()

        self._assertInState(ProcessStates.EXITED, ProcessStates.FATAL,
                            ProcessStates.BACKOFF, ProcessStates.STOPPED)

        self.change_state(ProcessStates.STARTING)

        try:
            filename, argv = self.get_execv_args()
        except ProcessException as what:
            self.record_spawnerr(what.args[0])
            self._assertInState(ProcessStates.STARTING)
            self.change_state(ProcessStates.BACKOFF)
            return

        try:
            self.dispatchers, self.pipes = self.config.make_dispatchers(self)
        except OSError as why:
            code = why.args[0]
            if code == errno.EMFILE:
                # too many file descriptors open
                msg = 'too many open files to spawn %r' % self.config.name
            else:
                msg = 'unknown error: %s' % errno.errorcode.get(code, code)
            self.record_spawnerr(msg)
            self._assertInState(ProcessStates.STARTING)
            self.change_state(ProcessStates.BACKOFF)
            return

        try:
            pid = options.fork()
        except OSError as why:
            code = why.args[0]
            if code == errno.EAGAIN:
                # process table full
                msg  = ('Too many processes in process table to spawn %r' %
                        self.config.name)
            else:
                msg = 'unknown error: %s' % errno.errorcode.get(code, code)

            self.record_spawnerr(msg)
            self._assertInState(ProcessStates.STARTING)
            self.change_state(ProcessStates.BACKOFF)
            options.close_parent_pipes(self.pipes)
            options.close_child_pipes(self.pipes)
            return

        if pid != 0:
            return self._spawn_as_parent(pid)

        else:
            return self._spawn_as_child(filename, argv)

    def _spawn_as_parent(self, pid):
        # Parent
        self.pid = pid
        options = self.config.options
        options.close_child_pipes(self.pipes)
        options.logger.info('spawned: %r with pid %s' % (self.config.name, pid))
        self.spawnerr = None
        self.delay = time.time() + self.config.startsecs
        options.pidhistory[pid] = self
        return pid

    def _prepare_child_fds(self):
        options = self.config.options
        options.dup2(self.pipes['child_stdin'], 0)
        options.dup2(self.pipes['child_stdout'], 1)
        if self.config.redirect_stderr:
            options.dup2(self.pipes['child_stdout'], 2)
        else:
            options.dup2(self.pipes['child_stderr'], 2)
        for i in range(3, options.minfds):
            options.close_fd(i)

    def _spawn_as_child(self, filename, argv):
        options = self.config.options
        try:
            # prevent child from receiving signals sent to the
            # parent by calling os.setpgrp to create a new process
            # group for the child; this prevents, for instance,
            # the case of child processes being sent a SIGINT when
            # running supervisor in foreground mode and Ctrl-C in
            # the terminal window running supervisord is pressed.
            # Presumably it also prevents HUP, etc received by
            # supervisord from being sent to children.
            options.setpgrp()

            self._prepare_child_fds()
            # sending to fd 2 will put this output in the stderr log

            # set user
            setuid_msg = self.set_uid()
            if setuid_msg:
                uid = self.config.uid
                msg = "couldn't setuid to %s: %s\n" % (uid, setuid_msg)
                options.write(2, "supervisor: " + msg)
                return # finally clause will exit the child process

            # set environment
            env = os.environ.copy()
            env['SUPERVISOR_ENABLED'] = '1'
            serverurl = self.config.serverurl
            if serverurl is None: # unset
                serverurl = self.config.options.serverurl # might still be None
            if serverurl:
                env['SUPERVISOR_SERVER_URL'] = serverurl
            env['SUPERVISOR_PROCESS_NAME'] = self.config.name
            if self.group:
                env['SUPERVISOR_GROUP_NAME'] = self.group.config.name
            if self.config.environment is not None:
                env.update(self.config.environment)
            # change directory
            cwd = self.config.directory
            try:
                if cwd is not None:
                    options.chdir(cwd)
            except OSError as why:
                code = errno.errorcode.get(why.args[0], why.args[0])
                msg = "couldn't chdir to %s: %s\n" % (cwd, code)
                options.write(2, "supervisor: " + msg)
                return # finally clause will exit the child process

            # set umask, then execve
            try:
                if self.config.umask is not None:
                    options.setumask(self.config.umask)
                options.execve(filename, argv, env)
            except OSError as why:
                code = errno.errorcode.get(why.args[0], why.args[0])
                msg = "couldn't exec %s: %s\n" % (argv[0], code)
                options.write(2, "supervisor: " + msg)
            except:
                (file, fun, line), t,v,tbinfo = asyncore.compact_traceback()
                error = '%s, %s: file: %s line: %s' % (t, v, file, line)
                msg = "couldn't exec %s: %s\n" % (filename, error)
                options.write(2, "supervisor: " + msg)

            # this point should only be reached if execve failed.
            # the finally clause will exit the child process.

        finally:
            options.write(2, "supervisor: child process was not spawned\n")
            options._exit(127) # exit process with code for spawn failure

    def stop(self):
        """ Administrative stop """
        self.administrative_stop = 1
        return self.kill(self.config.stopsignal)

    def give_up(self):
        self.delay = 0
        self.backoff = 0
        self.system_stop = 1
        self._assertInState(ProcessStates.BACKOFF)
        self.change_state(ProcessStates.FATAL)

    def kill(self, sig):
        """Send a signal to the subprocess.  This may or may not kill it.

        Return None if the signal was sent, or an error message string
        if an error occurred or if the subprocess is not running.
        """
        now = time.time()
        options = self.config.options

        # Properly stop processes in BACKOFF state.
        if self.state == ProcessStates.BACKOFF:
            msg = ("Attempted to kill %s, which is in BACKOFF state." %
                   (self.config.name))
            options.logger.debug(msg)
            self.change_state(ProcessStates.STOPPED)
            return None

        if not self.pid:
            msg = ("attempted to kill %s with sig %s but it wasn't running" %
                   (self.config.name, signame(sig)))
            options.logger.debug(msg)
            return msg

        #If we're in the stopping state, then we've already sent the stop
        #signal and this is the kill signal
        if self.state == ProcessStates.STOPPING:
            killasgroup = self.config.killasgroup
        else:
            killasgroup = self.config.stopasgroup

        as_group = ""
        if killasgroup:
            as_group = "process group "

        options.logger.debug('killing %s (pid %s) %swith signal %s'
                             % (self.config.name,
                                self.pid,
                                as_group,
                                signame(sig))
                             )

        # RUNNING/STARTING/STOPPING -> STOPPING
        self.killing = 1
        self.delay = now + self.config.stopwaitsecs
        # we will already be in the STOPPING state if we're doing a
        # SIGKILL as a result of overrunning stopwaitsecs
        self._assertInState(ProcessStates.RUNNING,ProcessStates.STARTING,
                            ProcessStates.STOPPING)
        self.change_state(ProcessStates.STOPPING)

        pid = self.pid
        if killasgroup:
            # send to the whole process group instead
            pid = -self.pid

        try:
            options.kill(pid, sig)
        except:
            io = StringIO()
            traceback.print_exc(file=io)
            tb = io.getvalue()
            msg = 'unknown problem killing %s (%s):%s' % (self.config.name,
                                                          self.pid, tb)
            options.logger.critical(msg)
            self.change_state(ProcessStates.UNKNOWN)
            self.pid = 0
            self.killing = 0
            self.delay = 0
            return msg

        return None

    def finish(self, pid, sts):
        """ The process was reaped and we need to report and manage its state
        """
        self.drain()

        es, msg = decode_wait_status(sts)

        now = time.time()
        self.laststop = now
        processname = self.config.name

        tooquickly = now - self.laststart < self.config.startsecs
        exit_expected = es in self.config.exitcodes

        if self.killing:
            # likely the result of a stop request
            # implies STOPPING -> STOPPED
            self.killing = 0
            self.delay = 0
            self.exitstatus = es

            msg = "stopped: %s (%s)" % (processname, msg)
            self._assertInState(ProcessStates.STOPPING)
            self.change_state(ProcessStates.STOPPED)

        elif tooquickly:
            # the program did not stay up long enough to make it to RUNNING
            # implies STARTING -> BACKOFF
            self.exitstatus = None
            self.spawnerr = 'Exited too quickly (process log may have details)'
            msg = "exited: %s (%s)" % (processname, msg + "; not expected")
            self._assertInState(ProcessStates.STARTING)
            self.change_state(ProcessStates.BACKOFF)

        else:
            # this finish was not the result of a stop request, the
            # program was in the RUNNING state but exited implies
            # RUNNING -> EXITED
            self.delay = 0
            self.backoff = 0
            self.exitstatus = es

            if self.state == ProcessStates.STARTING:
                # XXX I don't know under which circumstances this
                # happens, but in the wild, there is a transition that
                # subverts the RUNNING state (directly from STARTING
                # to EXITED), so we perform the correct transition
                # here.
                self.change_state(ProcessStates.RUNNING)

            self._assertInState(ProcessStates.RUNNING)

            if exit_expected:
                # expected exit code
                msg = "exited: %s (%s)" % (processname, msg + "; expected")
                self.change_state(ProcessStates.EXITED, expected=True)
            else:
                # unexpected exit code
                self.spawnerr = 'Bad exit code %s' % es
                msg = "exited: %s (%s)" % (processname, msg + "; not expected")
                self.change_state(ProcessStates.EXITED, expected=False)

        self.config.options.logger.info(msg)

        self.pid = 0
        self.config.options.close_parent_pipes(self.pipes)
        self.pipes = {}
        self.dispatchers = {}

        # if we died before we processed the current event (only happens
        # if we're an event listener), notify the event system that this
        # event was rejected so it can be processed again.
        if self.event is not None:
            # Note: this should only be true if we were in the BUSY
            # state when finish() was called.
            events.notify(events.EventRejectedEvent(self, self.event))
            self.event = None

    def set_uid(self):
        if self.config.uid is None:
            return
        msg = self.config.options.dropPrivileges(self.config.uid)
        return msg

    def __lt__(self, other):
        return self.config.priority < other.config.priority

    def __eq__(self, other):
        # sort by priority
        return self.config.priority == other.config.priority

    def __repr__(self):
        return '<Subprocess at %s with name %s in state %s>' % (
            id(self),
            self.config.name,
            getProcessStateDescription(self.get_state()))

    def get_state(self):
        return self.state

    def transition(self):
        now = time.time()
        state = self.state

        logger = self.config.options.logger

        if self.config.options.mood > SupervisorStates.RESTARTING:
            # dont start any processes if supervisor is shutting down
            if state == ProcessStates.EXITED:
                if self.config.autorestart:
                    if self.config.autorestart is RestartUnconditionally:
                        # EXITED -> STARTING
                        self.spawn()
                    else: # autorestart is RestartWhenExitUnexpected
                        if self.exitstatus not in self.config.exitcodes:
                            # EXITED -> STARTING
                            self.spawn()
            elif state == ProcessStates.STOPPED and not self.laststart:
                if self.config.autostart:
                    # STOPPED -> STARTING
                    self.spawn()
            elif state == ProcessStates.BACKOFF:
                if self.backoff <= self.config.startretries:
                    if now > self.delay:
                        # BACKOFF -> STARTING
                        self.spawn()

        if state == ProcessStates.STARTING:
            if now - self.laststart > self.config.startsecs:
                # STARTING -> RUNNING if the proc has started
                # successfully and it has stayed up for at least
                # proc.config.startsecs,
                self.delay = 0
                self.backoff = 0
                self._assertInState(ProcessStates.STARTING)
                self.change_state(ProcessStates.RUNNING)
                msg = (
                    'entered RUNNING state, process has stayed up for '
                    '> than %s seconds (startsecs)' % self.config.startsecs)
                logger.info('success: %s %s' % (self.config.name, msg))

        if state == ProcessStates.BACKOFF:
            if self.backoff > self.config.startretries:
                # BACKOFF -> FATAL if the proc has exceeded its number
                # of retries
                self.give_up()
                msg = ('entered FATAL state, too many start retries too '
                       'quickly')
                logger.info('gave up: %s %s' % (self.config.name, msg))

        elif state == ProcessStates.STOPPING:
            time_left = self.delay - now
            if time_left <= 0:
                # kill processes which are taking too long to stop with a final
                # sigkill.  if this doesn't kill it, the process will be stuck
                # in the STOPPING state forever.
                self.config.options.logger.warn(
                    'killing %r (%s) with SIGKILL' % (self.config.name,
                                                      self.pid))
                self.kill(signal.SIGKILL)

Subprocess = total_ordering(Subprocess)

class FastCGISubprocess(Subprocess):
    """Extends Subprocess class to handle FastCGI subprocesses"""

    def __init__(self, config):
        Subprocess.__init__(self, config)
        self.fcgi_sock = None

    def before_spawn(self):
        """
        The FastCGI socket needs to be created by the parent before we fork
        """
        if self.group is None:
            raise NotImplementedError('No group set for FastCGISubprocess')
        if not hasattr(self.group, 'socket_manager'):
            raise NotImplementedError('No SocketManager set for '
                                      '%s:%s' % (self.group, dir(self.group)))
        self.fcgi_sock = self.group.socket_manager.get_socket()

    def spawn(self):
        """
        Overrides Subprocess.spawn() so we can hook in before it happens
        """
        self.before_spawn()
        pid = Subprocess.spawn(self)
        if pid is None:
            #Remove object reference to decrement the reference count on error
            self.fcgi_sock = None
        return pid

    def after_finish(self):
        """
        Releases reference to FastCGI socket when process is reaped
        """
        #Remove object reference to decrement the reference count
        self.fcgi_sock = None

    def finish(self, pid, sts):
        """
        Overrides Subprocess.finish() so we can hook in after it happens
        """
        retval = Subprocess.finish(self, pid, sts)
        self.after_finish()
        return retval

    def _prepare_child_fds(self):
        """
        Overrides Subprocess._prepare_child_fds()
        The FastCGI socket needs to be set to file descriptor 0 in the child
        """
        sock_fd = self.fcgi_sock.fileno()

        options = self.config.options
        options.dup2(sock_fd, 0)
        options.dup2(self.pipes['child_stdout'], 1)
        if self.config.redirect_stderr:
            options.dup2(self.pipes['child_stdout'], 2)
        else:
            options.dup2(self.pipes['child_stderr'], 2)
        for i in range(3, options.minfds):
            options.close_fd(i)

class ProcessGroupBase(object):
    def __init__(self, config):
        self.config = config
        self.processes = {}
        for pconfig in self.config.process_configs:
            self.processes[pconfig.name] = pconfig.make_process(self)


    def __lt__(self, other):
        return self.config.priority < other.config.priority

    def __eq__(self, other):
        return self.config.priority == other.config.priority

    def __repr__(self):
        return '<%s instance at %s named %s>' % (self.__class__, id(self),
                                                 self.config.name)

    def removelogs(self):
        for process in self.processes.values():
            process.removelogs()

    def reopenlogs(self):
        for process in self.processes.values():
            process.reopenlogs()

    def stop_all(self):
        processes = list(self.processes.values())
        processes.sort()
        processes.reverse() # stop in desc priority order

        for proc in processes:
            state = proc.get_state()
            if state == ProcessStates.RUNNING:
                # RUNNING -> STOPPING
                proc.stop()
            elif state == ProcessStates.STARTING:
                # STARTING -> STOPPING
                proc.stop()
            elif state == ProcessStates.BACKOFF:
                # BACKOFF -> FATAL
                proc.give_up()

    def get_unstopped_processes(self):
        """ Processes which aren't in a state that is considered 'stopped' """
        return [ x for x in self.processes.values() if x.get_state() not in
                 STOPPED_STATES ]

    def get_dispatchers(self):
        dispatchers = {}
        for process in self.processes.values():
            dispatchers.update(process.dispatchers)
        return dispatchers

ProcessGroupBase = total_ordering(ProcessGroupBase)

class ProcessGroup(ProcessGroupBase):
    def transition(self):
        for proc in self.processes.values():
            proc.transition()

class FastCGIProcessGroup(ProcessGroup):

    def __init__(self, config, **kwargs):
        ProcessGroup.__init__(self, config)
        sockManagerKlass = kwargs.get('socketManager', SocketManager)
        self.socket_manager = sockManagerKlass(config.socket_config,
                                               logger=config.options.logger)
        # It's not required to call get_socket() here but we want
        # to fail early during start up if there is a config error
        try:
            self.socket_manager.get_socket()
        except Exception as e:
            raise ValueError('Could not create FastCGI socket %s: %s' % (self.socket_manager.config(), e))

class EventListenerPool(ProcessGroupBase):
    def __init__(self, config):
        ProcessGroupBase.__init__(self, config)
        self.event_buffer = []
        for event_type in self.config.pool_events:
            events.subscribe(event_type, self._acceptEvent)
        events.subscribe(events.EventRejectedEvent, self.handle_rejected)
        self.serial = -1
        self.last_dispatch = 0
        self.dispatch_throttle = 0 # in seconds: .00195 is an interesting one

    def handle_rejected(self, event):
        process = event.process
        procs = self.processes.values()
        if process in procs: # this is one of our processes
            # rebuffer the event
            self._acceptEvent(event.event, head=True)

    def transition(self):
        processes = self.processes.values()
        dispatch_capable = False
        for process in processes:
            process.transition()
            # this is redundant, we do it in _dispatchEvent too, but we
            # want to reduce function call overhead
            if process.state == ProcessStates.RUNNING:
                if process.listener_state == EventListenerStates.READY:
                    dispatch_capable = True
        if dispatch_capable:
            if self.dispatch_throttle:
                now = time.time()
                if now - self.last_dispatch < self.dispatch_throttle:
                    return
            self.dispatch()

    def dispatch(self):
        while self.event_buffer:
            # dispatch the oldest event
            event = self.event_buffer.pop(0)
            ok = self._dispatchEvent(event)
            if not ok:
                # if we can't dispatch an event, rebuffer it and stop trying
                # to process any further events in the buffer
                self._acceptEvent(event, head=True)
                break
        self.last_dispatch = time.time()

    def _acceptEvent(self, event, head=False):
        # events are required to be instances
        # this has a side effect to fail with an attribute error on 'old style' classes
        if not hasattr(event, 'serial'):
            event.serial = new_serial(GlobalSerial)
        if not hasattr(event, 'pool_serials'):
            event.pool_serials = {}
        if self.config.name not in event.pool_serials:
            event.pool_serials[self.config.name] = new_serial(self)
        else:
            self.config.options.logger.debug(
                'rebuffering event %s for pool %s (bufsize %s)' % (
                (event.serial, self.config.name, len(self.event_buffer))))

        if len(self.event_buffer) >= self.config.buffer_size:
            if self.event_buffer:
                # discard the oldest event
                discarded_event = self.event_buffer.pop(0)
                self.config.options.logger.error(
                    'pool %s event buffer overflowed, discarding event %s' % (
                    (self.config.name, discarded_event.serial)))
        if head:
            self.event_buffer.insert(0, event)
        else:
            self.event_buffer.append(event)

    def _dispatchEvent(self, event):
        pool_serial = event.pool_serials[self.config.name]

        for process in self.processes.values():
            if process.state != ProcessStates.RUNNING:
                continue
            if process.listener_state == EventListenerStates.READY:
                payload = str(event)
                try:
                    event_type = event.__class__
                    serial = event.serial
                    envelope = self._eventEnvelope(event_type, serial,
                                                   pool_serial, payload)
                    process.write(envelope)
                except OSError as why:
                    if why.args[0] != errno.EPIPE:
                        raise
                    continue

                process.listener_state = EventListenerStates.BUSY
                process.event = event
                self.config.options.logger.debug(
                    'event %s sent to listener %s' % (
                    event.serial, process.config.name))
                return True

        return False

    def _eventEnvelope(self, event_type, serial, pool_serial, payload):
        event_name = events.getEventNameByType(event_type)
        payload_len = len(payload)
        D = {
            'ver':'3.0',
            'sid':self.config.options.identifier,
            'serial':serial,
            'pool_name':self.config.name,
            'pool_serial':pool_serial,
            'event_name':event_name,
            'len':payload_len,
            'payload':payload,
             }
        return ('ver:%(ver)s server:%(sid)s serial:%(serial)s '
                'pool:%(pool_name)s poolserial:%(pool_serial)s '
                'eventname:%(event_name)s len:%(len)s\n%(payload)s' % D)

class GlobalSerial(object):
    def __init__(self):
        self.serial = -1

GlobalSerial = GlobalSerial() # singleton

def new_serial(inst):
    if inst.serial == maxint:
        inst.serial = -1
    inst.serial += 1
    return inst.serial




########NEW FILE########
__FILENAME__ = rpcinterface
import os
import time
import datetime
import errno

from supervisor.compat import as_string
from supervisor.compat import unicode
from supervisor.compat import basestring

from supervisor.options import readFile
from supervisor.options import tailFile
from supervisor.options import NotExecutable
from supervisor.options import NotFound
from supervisor.options import NoPermission
from supervisor.options import make_namespec
from supervisor.options import split_namespec
from supervisor.options import VERSION

from supervisor.events import notify
from supervisor.events import RemoteCommunicationEvent

from supervisor.http import NOT_DONE_YET
from supervisor.xmlrpc import Faults
from supervisor.xmlrpc import RPCError

from supervisor.states import SupervisorStates
from supervisor.states import getSupervisorStateDescription
from supervisor.states import ProcessStates
from supervisor.states import getProcessStateDescription
from supervisor.states import RUNNING_STATES

API_VERSION  = '3.0'

class SupervisorNamespaceRPCInterface:
    def __init__(self, supervisord):
        self.supervisord = supervisord

    def _update(self, text):
        self.update_text = text # for unit tests, mainly
        if isinstance(self.supervisord.options.mood, int) and self.supervisord.options.mood < SupervisorStates.RUNNING:
            raise RPCError(Faults.SHUTDOWN_STATE)

    # RPC API methods

    def getAPIVersion(self):
        """ Return the version of the RPC API used by supervisord

        @return string version version id
        """
        self._update('getAPIVersion')
        return API_VERSION

    getVersion = getAPIVersion # b/w compatibility with releases before 3.0

    def getSupervisorVersion(self):
        """ Return the version of the supervisor package in use by supervisord

        @return string version version id
        """
        self._update('getSupervisorVersion')
        return VERSION

    def getIdentification(self):
        """ Return identifying string of supervisord

        @return string identifier identifying string
        """
        self._update('getIdentification')
        return self.supervisord.options.identifier

    def getState(self):
        """ Return current state of supervisord as a struct

        @return struct A struct with keys int statecode, string statename
        """
        self._update('getState')

        state = self.supervisord.options.mood
        statename = getSupervisorStateDescription(state)
        data =  {
            'statecode':state,
            'statename':statename,
            }
        return data

    def getPID(self):
        """ Return the PID of supervisord

        @return int PID
        """
        self._update('getPID')
        return self.supervisord.options.get_pid()

    def readLog(self, offset, length):
        """ Read length bytes from the main log starting at offset

        @param int offset         offset to start reading from.
        @param int length         number of bytes to read from the log.
        @return string result     Bytes of log
        """
        self._update('readLog')

        logfile = self.supervisord.options.logfile

        if logfile is None or not os.path.exists(logfile):
            raise RPCError(Faults.NO_FILE, logfile)

        try:
            return as_string(readFile(logfile, int(offset), int(length)))
        except ValueError as inst:
            why = inst.args[0]
            raise RPCError(getattr(Faults, why))

    readMainLog = readLog # b/w compatibility with releases before 2.1

    def clearLog(self):
        """ Clear the main log.

        @return boolean result always returns True unless error
        """
        self._update('clearLog')

        logfile = self.supervisord.options.logfile
        if logfile is None or not self.supervisord.options.exists(logfile):
            raise RPCError(Faults.NO_FILE)

        # there is a race condition here, but ignore it.
        try:
            self.supervisord.options.remove(logfile)
        except (OSError, IOError):
            raise RPCError(Faults.FAILED)

        for handler in self.supervisord.options.logger.handlers:
            if hasattr(handler, 'reopen'):
                self.supervisord.options.logger.info('reopening log file')
                handler.reopen()
        return True

    def shutdown(self):
        """ Shut down the supervisor process

        @return boolean result always returns True unless error
        """
        self._update('shutdown')
        self.supervisord.options.mood = SupervisorStates.SHUTDOWN
        return True

    def restart(self):
        """ Restart the supervisor process

        @return boolean result  always return True unless error
        """
        self._update('restart')

        self.supervisord.options.mood = SupervisorStates.RESTARTING
        return True

    def reloadConfig(self):
        """
        Reload configuration

        @return boolean result  always return True unless error
        """
        self._update('reloadConfig')
        try:
            self.supervisord.options.process_config(do_usage=False)
        except ValueError as msg:
            raise RPCError(Faults.CANT_REREAD, msg)

        added, changed, removed = self.supervisord.diff_to_active()

        added = [group.name for group in added]
        changed = [group.name for group in changed]
        removed = [group.name for group in removed]
        return [[added, changed, removed]] # cannot return len > 1, apparently

    def addProcessGroup(self, name):
        """ Update the config for a running process from config file.

        @param string name         name of process group to add
        @return boolean result     true if successful
        """
        self._update('addProcessGroup')

        for config in self.supervisord.options.process_group_configs:
            if config.name == name:
                result = self.supervisord.add_process_group(config)
                if not result:
                    raise RPCError(Faults.ALREADY_ADDED, name)
                return True
        raise RPCError(Faults.BAD_NAME, name)

    def removeProcessGroup(self, name):
        """ Remove a stopped process from the active configuration.

        @param string name         name of process group to remove
        @return boolean result     Indicates whether the removal was successful
        """
        self._update('removeProcessGroup')
        if name not in self.supervisord.process_groups:
            raise RPCError(Faults.BAD_NAME, name)

        result = self.supervisord.remove_process_group(name)
        if not result:
            raise RPCError(Faults.STILL_RUNNING)
        return True

    def _getAllProcesses(self, lexical=False):
        # if lexical is true, return processes sorted in lexical order,
        # otherwise, sort in priority order
        all_processes = []

        if lexical:
            group_names = list(self.supervisord.process_groups.keys())
            group_names.sort()
            for group_name in group_names:
                group = self.supervisord.process_groups[group_name]
                process_names = list(group.processes.keys())
                process_names.sort()
                for process_name in process_names:
                    process = group.processes[process_name]
                    all_processes.append((group, process))
        else:
            groups = list(self.supervisord.process_groups.values())
            groups.sort() # asc by priority

            for group in groups:
                processes = list(group.processes.values())
                processes.sort() # asc by priority
                for process in processes:
                    all_processes.append((group, process))

        return all_processes

    def _getGroupAndProcess(self, name):
        # get process to start from name
        group_name, process_name = split_namespec(name)

        group = self.supervisord.process_groups.get(group_name)
        if group is None:
            raise RPCError(Faults.BAD_NAME, name)

        if process_name is None:
            return group, None

        process = group.processes.get(process_name)
        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        return group, process

    def startProcess(self, name, wait=True):
        """ Start a process

        @param string name Process name (or ``group:name``, or ``group:*``)
        @param boolean wait Wait for process to be fully started
        @return boolean result     Always true unless error

        """
        self._update('startProcess')
        group, process = self._getGroupAndProcess(name)
        if process is None:
            group_name, process_name = split_namespec(name)
            return self.startProcessGroup(group_name, wait)

        # test filespec, don't bother trying to spawn if we know it will
        # eventually fail
        try:
            filename, argv = process.get_execv_args()
        except NotFound as why:
            raise RPCError(Faults.NO_FILE, why.args[0])
        except (NotExecutable, NoPermission) as why:
            raise RPCError(Faults.NOT_EXECUTABLE, why.args[0])

        started = []

        startsecs = process.config.startsecs

        def startit():
            if not started:

                if process.get_state() in RUNNING_STATES:
                    raise RPCError(Faults.ALREADY_STARTED, name)

                process.spawn()

                if process.spawnerr:
                    raise RPCError(Faults.SPAWN_ERROR, name)

                # we use a list here to fake out lexical scoping;
                # using a direct assignment to 'started' in the
                # function appears to not work (symptom: 2nd or 3rd
                # call through, it forgets about 'started', claiming
                # it's undeclared).
                started.append(time.time())

            if not wait or not startsecs:
                return True

            t = time.time()
            runtime = (t - started[0])
            state = process.get_state()

            if state not in (ProcessStates.STARTING, ProcessStates.RUNNING):
                raise RPCError(Faults.ABNORMAL_TERMINATION, name)

            if runtime < startsecs:
                return NOT_DONE_YET

            if state == ProcessStates.RUNNING:
                return True

            raise RPCError(Faults.ABNORMAL_TERMINATION, name)

        startit.delay = 0.05
        startit.rpcinterface = self
        return startit # deferred

    def startProcessGroup(self, name, wait=True):
        """ Start all processes in the group named 'name'

        @param string name     The group name
        @param boolean wait    Wait for each process to be fully started
        @return array result   An array of process status info structs
        """
        self._update('startProcessGroup')

        group = self.supervisord.process_groups.get(name)

        if group is None:
            raise RPCError(Faults.BAD_NAME, name)

        processes = list(group.processes.values())
        processes.sort()
        processes = [ (group, process) for process in processes ]

        startall = make_allfunc(processes, isNotRunning, self.startProcess,
                                wait=wait)

        startall.delay = 0.05
        startall.rpcinterface = self
        return startall # deferred

    def startAllProcesses(self, wait=True):
        """ Start all processes listed in the configuration file

        @param boolean wait    Wait for each process to be fully started
        @return array result   An array of process status info structs
        """
        self._update('startAllProcesses')

        processes = self._getAllProcesses()
        startall = make_allfunc(processes, isNotRunning, self.startProcess,
                                wait=wait)

        startall.delay = 0.05
        startall.rpcinterface = self
        return startall # deferred

    def stopProcess(self, name, wait=True):
        """ Stop a process named by name

        @param string name  The name of the process to stop (or 'group:name')
        @param boolean wait        Wait for the process to be fully stopped
        @return boolean result     Always return True unless error
        """
        self._update('stopProcess')

        group, process = self._getGroupAndProcess(name)

        if process is None:
            group_name, process_name = split_namespec(name)
            return self.stopProcessGroup(group_name, wait)

        stopped = []
        called  = []

        def killit():
            if not called:
                if process.get_state() not in RUNNING_STATES:
                    raise RPCError(Faults.NOT_RUNNING)
                # use a mutable for lexical scoping; see startProcess
                called.append(1)

            if not stopped:
                msg = process.stop()
                if msg is not None:
                    raise RPCError(Faults.FAILED, msg)
                stopped.append(1)

                if wait:
                    return NOT_DONE_YET
                else:
                    return True

            if process.get_state() not in (ProcessStates.STOPPED,
                                           ProcessStates.EXITED):
                return NOT_DONE_YET
            else:
                return True

        killit.delay = 0.2
        killit.rpcinterface = self
        return killit # deferred

    def stopProcessGroup(self, name, wait=True):
        """ Stop all processes in the process group named 'name'

        @param string name     The group name
        @param boolean wait    Wait for each process to be fully stopped
        @return array result   An array of process status info structs
        """
        self._update('stopProcessGroup')

        group = self.supervisord.process_groups.get(name)

        if group is None:
            raise RPCError(Faults.BAD_NAME, name)

        processes = list(group.processes.values())
        processes.sort()
        processes = [ (group, process) for process in processes ]

        killall = make_allfunc(processes, isRunning, self.stopProcess,
                               wait=wait)

        killall.delay = 0.05
        killall.rpcinterface = self
        return killall # deferred

    def stopAllProcesses(self, wait=True):
        """ Stop all processes in the process list

        @param  boolean wait   Wait for each process to be fully stopped
        @return array result   An array of process status info structs
        """
        self._update('stopAllProcesses')

        processes = self._getAllProcesses()

        killall = make_allfunc(processes, isRunning, self.stopProcess,
                               wait=wait)

        killall.delay = 0.05
        killall.rpcinterface = self
        return killall # deferred

    def getAllConfigInfo(self):
        """ Get info about all available process configurations. Each struct
        represents a single process (i.e. groups get flattened).

        @return array result  An array of process config info structs
        """
        self._update('getAllConfigInfo')

        configinfo = []
        for gconfig in self.supervisord.options.process_group_configs:
            inuse = gconfig.name in self.supervisord.process_groups
            for pconfig in gconfig.process_configs:
                configinfo.append(
                    { 'name': pconfig.name,
                      'group': gconfig.name,
                      'inuse': inuse,
                      'autostart': pconfig.autostart,
                      'group_prio': gconfig.priority,
                      'process_prio': pconfig.priority })

        configinfo.sort(key=lambda r: r['name'])
        return configinfo

    def _interpretProcessInfo(self, info):
        state = info['state']

        if state == ProcessStates.RUNNING:
            start = info['start']
            now = info['now']
            start_dt = datetime.datetime(*time.gmtime(start)[:6])
            now_dt = datetime.datetime(*time.gmtime(now)[:6])
            uptime = now_dt - start_dt
            desc = 'pid %s, uptime %s' % (info['pid'], uptime)

        elif state in (ProcessStates.FATAL, ProcessStates.BACKOFF):
            desc = info['spawnerr']
            if not desc:
                desc = 'unknown error (try "tail %s")' % info['name']

        elif state in (ProcessStates.STOPPED, ProcessStates.EXITED):
            if info['start']:
                stop = info['stop']
                stop_dt = datetime.datetime(*time.localtime(stop)[:7])
                desc = stop_dt.strftime('%b %d %I:%M %p')
            else:
                desc = 'Not started'

        else:
            desc = ''

        return desc

    def getProcessInfo(self, name):
        """ Get info about a process named name

        @param string name The name of the process (or 'group:name')
        @return struct result     A structure containing data about the process
        """
        self._update('getProcessInfo')

        group, process = self._getGroupAndProcess(name)

        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        start = int(process.laststart)
        stop = int(process.laststop)
        now = int(time.time())

        state = process.get_state()
        spawnerr = process.spawnerr or ''
        exitstatus = process.exitstatus or 0
        stdout_logfile = process.config.stdout_logfile or ''
        stderr_logfile = process.config.stderr_logfile or ''

        info = {
            'name':process.config.name,
            'group':group.config.name,
            'start':start,
            'stop':stop,
            'now':now,
            'state':state,
            'statename':getProcessStateDescription(state),
            'spawnerr':spawnerr,
            'exitstatus':exitstatus,
            'logfile':stdout_logfile, # b/c alias
            'stdout_logfile':stdout_logfile,
            'stderr_logfile':stderr_logfile,
            'pid':process.pid,
            }

        description = self._interpretProcessInfo(info)
        info['description'] = description
        return info

    def getAllProcessInfo(self):
        """ Get info about all processes

        @return array result  An array of process status results
        """
        self._update('getAllProcessInfo')

        all_processes = self._getAllProcesses(lexical=True)

        output = []
        for group, process in all_processes:
            name = make_namespec(group.config.name, process.config.name)
            output.append(self.getProcessInfo(name))
        return output

    def _readProcessLog(self, name, offset, length, channel):
        group, process = self._getGroupAndProcess(name)

        logfile = getattr(process.config, '%s_logfile' % channel)

        if logfile is None or not os.path.exists(logfile):
            raise RPCError(Faults.NO_FILE, logfile)

        try:
            return as_string(readFile(logfile, int(offset), int(length)))
        except ValueError as inst:
            why = inst.args[0]
            raise RPCError(getattr(Faults, why))

    def readProcessStdoutLog(self, name, offset, length):
        """ Read length bytes from name's stdout log starting at offset

        @param string name        the name of the process (or 'group:name')
        @param int offset         offset to start reading from.
        @param int length         number of bytes to read from the log.
        @return string result     Bytes of log
        """
        self._update('readProcessStdoutLog')
        return self._readProcessLog(name, offset, length, 'stdout')

    readProcessLog = readProcessStdoutLog # b/c alias

    def readProcessStderrLog(self, name, offset, length):
        """ Read length bytes from name's stderr log starting at offset

        @param string name        the name of the process (or 'group:name')
        @param int offset         offset to start reading from.
        @param int length         number of bytes to read from the log.
        @return string result     Bytes of log
        """
        self._update('readProcessStderrLog')
        return self._readProcessLog(name, offset, length, 'stderr')

    def _tailProcessLog(self, name, offset, length, channel):
        group, process = self._getGroupAndProcess(name)

        logfile = getattr(process.config, '%s_logfile' % channel)

        if logfile is None or not os.path.exists(logfile):
            return ['', 0, False]

        return tailFile(logfile, int(offset), int(length))

    def tailProcessStdoutLog(self, name, offset, length):
        """
        Provides a more efficient way to tail the (stdout) log than
        readProcessStdoutLog().  Use readProcessStdoutLog() to read
        chunks and tailProcessStdoutLog() to tail.

        Requests (length) bytes from the (name)'s log, starting at
        (offset).  If the total log size is greater than (offset +
        length), the overflow flag is set and the (offset) is
        automatically increased to position the buffer at the end of
        the log.  If less than (length) bytes are available, the
        maximum number of available bytes will be returned.  (offset)
        returned is always the last offset in the log +1.

        @param string name         the name of the process (or 'group:name')
        @param int offset          offset to start reading from
        @param int length          maximum number of bytes to return
        @return array result       [string bytes, int offset, bool overflow]
        """
        self._update('tailProcessStdoutLog')
        return self._tailProcessLog(name, offset, length, 'stdout')

    tailProcessLog = tailProcessStdoutLog # b/c alias

    def tailProcessStderrLog(self, name, offset, length):
        """
        Provides a more efficient way to tail the (stderr) log than
        readProcessStderrLog().  Use readProcessStderrLog() to read
        chunks and tailProcessStderrLog() to tail.

        Requests (length) bytes from the (name)'s log, starting at
        (offset).  If the total log size is greater than (offset +
        length), the overflow flag is set and the (offset) is
        automatically increased to position the buffer at the end of
        the log.  If less than (length) bytes are available, the
        maximum number of available bytes will be returned.  (offset)
        returned is always the last offset in the log +1.

        @param string name         the name of the process (or 'group:name')
        @param int offset          offset to start reading from
        @param int length          maximum number of bytes to return
        @return array result       [string bytes, int offset, bool overflow]
        """
        self._update('tailProcessStderrLog')
        return self._tailProcessLog(name, offset, length, 'stderr')

    def clearProcessLogs(self, name):
        """ Clear the stdout and stderr logs for the named process and
        reopen them.

        @param string name   The name of the process (or 'group:name')
        @return boolean result      Always True unless error
        """
        self._update('clearProcessLogs')

        group, process = self._getGroupAndProcess(name)

        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        try:
            # implies a reopen
            process.removelogs()
        except (IOError, OSError):
            raise RPCError(Faults.FAILED, name)

        return True

    clearProcessLog = clearProcessLogs # b/c alias

    def clearAllProcessLogs(self):
        """ Clear all process log files

        @return array result   An array of process status info structs
        """
        self._update('clearAllProcessLogs')
        results  = []
        callbacks = []

        all_processes = self._getAllProcesses()

        for group, process in all_processes:
            callbacks.append((group, process, self.clearProcessLog))

        def clearall():
            if not callbacks:
                return results

            group, process, callback = callbacks.pop(0)
            name = make_namespec(group.config.name, process.config.name)
            try:
                callback(name)
            except RPCError as e:
                results.append(
                    {'name':process.config.name,
                     'group':group.config.name,
                     'status':e.code,
                     'description':e.text})
            else:
                results.append(
                    {'name':process.config.name,
                     'group':group.config.name,
                     'status':Faults.SUCCESS,
                     'description':'OK'}
                    )

            if callbacks:
                return NOT_DONE_YET

            return results

        clearall.delay = 0.05
        clearall.rpcinterface = self
        return clearall # deferred

    def sendProcessStdin(self, name, chars):
        """ Send a string of chars to the stdin of the process name.
        If non-7-bit data is sent (unicode), it is encoded to utf-8
        before being sent to the process' stdin.  If chars is not a
        string or is not unicode, raise INCORRECT_PARAMETERS.  If the
        process is not running, raise NOT_RUNNING.  If the process'
        stdin cannot accept input (e.g. it was closed by the child
        process), raise NO_FILE.

        @param string name        The process name to send to (or 'group:name')
        @param string chars       The character data to send to the process
        @return boolean result    Always return True unless error
        """
        self._update('sendProcessStdin')

        if isinstance(chars, unicode):
            chars = chars.encode('utf-8')

        if not isinstance(chars, basestring):
            raise RPCError(Faults.INCORRECT_PARAMETERS, chars)

        group, process = self._getGroupAndProcess(name)

        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        if not process.pid or process.killing:
            raise RPCError(Faults.NOT_RUNNING, name)

        try:
            process.write(chars)
        except OSError as why:
            if why.args[0] == errno.EPIPE:
                raise RPCError(Faults.NO_FILE, name)
            else:
                raise

        return True

    def sendRemoteCommEvent(self, type, data):
        """ Send an event that will be received by event listener
        subprocesses subscribing to the RemoteCommunicationEvent.

        @param  string  type  String for the "type" key in the event header
        @param  string  data  Data for the event body
        @return boolean       Always return True unless error
        """
        if isinstance(type, unicode):
            type = type.encode('utf-8')
        if isinstance(data, unicode):
            data = data.encode('utf-8')

        notify(
            RemoteCommunicationEvent(type, data)
        )

        return True

def make_allfunc(processes, predicate, func, **extra_kwargs):
    """ Return a closure representing a function that calls a
    function for every process, and returns a result """

    callbacks = []
    results = []

    def allfunc(processes=processes, predicate=predicate, func=func,
                extra_kwargs=extra_kwargs, callbacks=callbacks,
                results=results):
        if not callbacks:

            for group, process in processes:
                name = make_namespec(group.config.name, process.config.name)
                if predicate(process):
                    try:
                        callback = func(name, **extra_kwargs)
                        callbacks.append((group, process, callback))
                    except RPCError as e:
                        results.append({'name':process.config.name,
                                        'group':group.config.name,
                                        'status':e.code,
                                        'description':e.text})
                        continue

        if not callbacks:
            return results

        group, process, callback = callbacks.pop(0)

        try:
            value = callback()
        except RPCError as e:
            results.append(
                {'name':process.config.name,
                 'group':group.config.name,
                 'status':e.code,
                 'description':e.text})
            return NOT_DONE_YET

        if value is NOT_DONE_YET:
            # push it back into the queue; it will finish eventually
            callbacks.append((group, process, callback))
        else:
            results.append(
                {'name':process.config.name,
                 'group':group.config.name,
                 'status':Faults.SUCCESS,
                 'description':'OK'}
                )

        if callbacks:
            return NOT_DONE_YET

        return results

    # XXX the above implementation has a weakness inasmuch as the
    # first call into each individual process callback will always
    # return NOT_DONE_YET, so they need to be called twice.  The
    # symptom of this is that calling this method causes the
    # client to block for much longer than it actually requires to
    # kill all of the running processes.  After the first call to
    # the killit callback, the process is actually dead, but the
    # above killall method processes the callbacks one at a time
    # during the select loop, which, because there is no output
    # from child processes after e.g. stopAllProcesses is called,
    # is not busy, so hits the timeout for each callback.  I
    # attempted to make this better, but the only way to make it
    # better assumes totally synchronous reaping of child
    # processes, which requires infrastructure changes to
    # supervisord that are scary at the moment as it could take a
    # while to pin down all of the platform differences and might
    # require a C extension to the Python signal module to allow
    # the setting of ignore flags to signals.
    return allfunc

def isRunning(process):
    if process.get_state() in RUNNING_STATES:
        return True

def isNotRunning(process):
    return not isRunning(process)

# this is not used in code but referenced via an entry point in the conf file
def make_main_rpcinterface(supervisord):
    return SupervisorNamespaceRPCInterface(supervisord)


########NEW FILE########
__FILENAME__ = loop_eventgen
#!/usr/bin/env python

# A process which emits a process communications event on its stdout,
# and subsequently waits for a line to be sent back to its stdin by
# loop_listener.py.

import sys
import time
from supervisor import childutils

def main(max):
    start = time.time()
    report = open('/tmp/report', 'w')
    i = 0
    while 1:
        childutils.pcomm.stdout('the_data')
        sys.stdin.readline()
        report.write(str(i) + ' @ %s\n' % childutils.get_asctime())
        report.flush()
        i+=1
        if max and i >= max:
            end = time.time()
            report.write('%s per second\n' % (i / (end - start)))
            sys.exit(0)

if __name__ == '__main__':
    max = 0
    if len(sys.argv) > 1:
        max = int(sys.argv[1])
    main(max)



########NEW FILE########
__FILENAME__ = loop_listener
#!/usr/bin/env python -u

# An event listener that listens for process communications events
# from loop_eventgen.py and uses RPC to write data to the event
# generator's stdin.

import os
from supervisor import childutils

def main():
    rpcinterface = childutils.getRPCInterface(os.environ)
    while 1:
        headers, payload = childutils.listener.wait()
        if headers['eventname'].startswith('PROCESS_COMMUNICATION'):
            pheaders, pdata = childutils.eventdata(payload)
            pname = '%s:%s' % (pheaders['processname'], pheaders['groupname'])
            rpcinterface.supervisor.sendProcessStdin(pname, 'Got it yo\n')
        childutils.listener.ok()

if __name__ == '__main__':
    main()

########NEW FILE########
__FILENAME__ = sample_commevent
#!/usr/bin/env python

# An example process which emits a stdout process communication event every
# second (or every number of seconds specified as a single argument).

import sys
import time

def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()

def main(sleep):
    while 1:
        write_stdout('<!--XSUPERVISOR:BEGIN-->')
        write_stdout('the data')
        write_stdout('<!--XSUPERVISOR:END-->')
        time.sleep(sleep)

if __name__ == '__main__':
    if len(sys.argv) > 1:
        main(float(sys.argv[1]))
    else:
        main(1)


########NEW FILE########
__FILENAME__ = sample_eventlistener
#!/usr/bin/env python -u

# A sample long-running supervisor event listener which demonstrates
# how to accept event notifications from supervisor and how to respond
# properly.  This demonstration does *not* use the
# supervisor.childutils module, which wraps the specifics of
# communications in higher-level API functions.  If your listeners are
# implemented using Python, it is recommended that you use the
# childutils module API instead of modeling your scripts on the
# lower-level protocol example below.

import sys

def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()

def write_stderr(s):
    sys.stderr.write(s)
    sys.stderr.flush()

def main():
    while 1:
        write_stdout('READY\n') # transition from ACKNOWLEDGED to READY
        line = sys.stdin.readline()  # read header line from stdin 
        write_stderr(line) # print it out to stderr (testing only)
        headers = dict([ x.split(':') for x in line.split() ])
        data = sys.stdin.read(int(headers['len'])) # read the event payload
        write_stderr(data) # print the event payload to stderr (testing only)
        write_stdout('RESULT 2\nOK') # transition from BUSY to ACKNOWLEDGED
        #write_stdout('RESULT 4\nFAIL') # transition from BUSY TO ACKNOWLEDGED

if __name__ == '__main__':
    main()

########NEW FILE########
__FILENAME__ = sample_exiting_eventlistener
#!/usr/bin/env python

# A sample long-running supervisor event listener which demonstrates
# how to accept event notifications from supervisor and how to respond
# properly.  It is the same as the sample_eventlistener.py script
# except it exits after each request (presumably to be restarted by
# supervisor).  This demonstration does *not* use the
# supervisor.childutils module, which wraps the specifics of
# communications in higher-level API functions.  If your listeners are
# implemented using Python, it is recommended that you use the
# childutils module API instead of modeling your scripts on the
# lower-level protocol example below.

import sys

def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()

def write_stderr(s):
    sys.stderr.write(s)
    sys.stderr.flush()

def main():
    write_stdout('READY\n') # transition from ACKNOWLEDGED to READY
    line = sys.stdin.readline()  # read a line from stdin from supervisord
    write_stderr(line) # print it out to stderr (testing only)
    headers = dict([ x.split(':') for x in line.split() ])
    data = sys.stdin.read(int(headers['len'])) # read the event payload
    write_stderr(data) # print the event payload to stderr (testing only)
    write_stdout('RESULT 2\nOK') # transition from READY to ACKNOWLEDGED
    # exit, if the eventlistener process config has autorestart=true,
    # it will be restarted by supervisord.

if __name__ == '__main__':
    main()
    

########NEW FILE########
__FILENAME__ = socket_manager
import supervisor.medusa.text_socket as socket

class Proxy:
    """ Class for wrapping a shared resource object and getting
        notified when it's deleted
    """
    
    def __init__(self, object, **kwargs):
        self.object = object
        self.on_delete = kwargs.get('on_delete', None)

    def __del__(self):
        if self.on_delete:
            self.on_delete()
    
    def __getattr__(self, name):
        return getattr(self.object, name)
        
    def _get(self):
        return self.object
        
class ReferenceCounter:
    """ Class for tracking references to a shared resource
    """
    
    def __init__(self, **kwargs):
        self.on_non_zero = kwargs['on_non_zero']
        self.on_zero = kwargs['on_zero']
        self.ref_count = 0
    
    def get_count(self):
        return self.ref_count
    
    def increment(self):
        if self.ref_count == 0:
            self.on_non_zero()
        self.ref_count += 1
        
    def decrement(self):
        if self.ref_count <= 0:
            raise Exception('Illegal operation: cannot decrement below zero')
        self.ref_count -= 1
        if self.ref_count == 0:
            self.on_zero()

class SocketManager:
    """ Class for managing sockets in servers that create/bind/listen
        before forking multiple child processes to accept() 
        Sockets are managed at the process group level and referenced counted
        at the process level b/c that's really the only place to hook in
    """
    
    def __init__(self, socket_config, **kwargs):
        self.logger = kwargs.get('logger', None)
        self.socket = None
        self.prepared = False
        self.socket_config = socket_config
        self.ref_ctr = ReferenceCounter(on_zero=self._close, on_non_zero=self._prepare_socket)
        
    def __repr__(self):
        return '<%s at %s for %s>' % (self.__class__,
                                      id(self),
                                      self.socket_config.url)

    def config(self):
        return self.socket_config
        
    def is_prepared(self):
        return self.prepared

    def get_socket(self):
        self.ref_ctr.increment()
        self._require_prepared()
        return Proxy(self.socket, on_delete=self.ref_ctr.decrement)
        
    def get_socket_ref_count(self):
        self._require_prepared()
        return self.ref_ctr.get_count()
        
    def _require_prepared(self):
        if not self.prepared:
            raise Exception('Socket has not been prepared')
    
    def _prepare_socket(self):
        if not self.prepared:
            if self.logger:
                self.logger.info('Creating socket %s' % self.socket_config)
            self.socket = self.socket_config.create_and_bind()
            self.socket.listen(socket.SOMAXCONN)
            self.prepared = True

    def _close(self):
        self._require_prepared()
        if self.logger:
            self.logger.info('Closing socket %s' % self.socket_config)
        self.socket.close()
        self.prepared = False

########NEW FILE########
__FILENAME__ = states
# This module must not depend on any other non-stdlib module to prevent
# circular import problems.

class ProcessStates:
    STOPPED = 0
    STARTING = 10
    RUNNING = 20
    BACKOFF = 30
    STOPPING = 40
    EXITED = 100
    FATAL = 200
    UNKNOWN = 1000

STOPPED_STATES = (ProcessStates.STOPPED,
                  ProcessStates.EXITED,
                  ProcessStates.FATAL,
                  ProcessStates.UNKNOWN)

RUNNING_STATES = (ProcessStates.RUNNING,
                  ProcessStates.BACKOFF,
                  ProcessStates.STARTING)



def getProcessStateDescription(code):
    for statename in ProcessStates.__dict__:
        if getattr(ProcessStates, statename) == code:
            return statename

class SupervisorStates:
    FATAL = 2
    RUNNING = 1
    RESTARTING = 0
    SHUTDOWN = -1

def getSupervisorStateDescription(code):
    for statename in SupervisorStates.__dict__:
        if getattr(SupervisorStates, statename) == code:
            return statename


class EventListenerStates:
    READY = 10 # the process ready to be sent an event from supervisor
    BUSY = 20 # event listener is processing an event sent to it by supervisor
    ACKNOWLEDGED = 30 # the event listener processed an event
    UNKNOWN = 40 # the event listener is in an unknown state

def getEventListenerStateDescription(code):
    for statename in EventListenerStates.__dict__:
        if getattr(EventListenerStates, statename) == code:
            return statename


########NEW FILE########
__FILENAME__ = supervisorctl
#!/usr/bin/env python -u

"""supervisorctl -- control applications run by supervisord from the cmd line.

Usage: %s [options] [action [arguments]]

Options:
-c/--configuration -- configuration file path (default /etc/supervisord.conf)
-h/--help -- print usage message and exit
-i/--interactive -- start an interactive shell after executing commands
-s/--serverurl URL -- URL on which supervisord server is listening
     (default "http://localhost:9001").
-u/--username -- username to use for authentication with server
-p/--password -- password to use for authentication with server
-r/--history-file -- keep a readline history (if readline is available)

action [arguments] -- see below

Actions are commands like "tail" or "stop".  If -i is specified or 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.
"""

import cmd
import sys
import getpass

import supervisor.medusa.text_socket as socket
import errno
import threading

from supervisor.compat import xmlrpclib
from supervisor.compat import urlparse
from supervisor.compat import unicode

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.options import ClientOptions
from supervisor.options import make_namespec
from supervisor.options import split_namespec
from supervisor import xmlrpc
from supervisor import states

class fgthread(threading.Thread):
    """ A subclass of threading.Thread, with a kill() method.
    To be used for foreground output/error streaming.
    http://mail.python.org/pipermail/python-list/2004-May/260937.html
    """

    def __init__(self, program, ctl):
        threading.Thread.__init__(self)
        import supervisor.http_client as http_client
        self.killed = False
        self.program = program
        self.ctl = ctl
        self.listener = http_client.Listener()
        self.output_handler = http_client.HTTPHandler(self.listener,
                                                      self.ctl.options.username,
                                                      self.ctl.options.password)
        self.error_handler = http_client.HTTPHandler(self.listener,
                                                     self.ctl.options.username,
                                                     self.ctl.options.password)

    def start(self):
        # Start the thread
        self.__run_backup = self.run
        self.run = self.__run
        threading.Thread.start(self)

    def run(self):
        self.output_handler.get(self.ctl.options.serverurl,
                                '/logtail/%s/stdout'%self.program)
        self.error_handler.get(self.ctl.options.serverurl,
                               '/logtail/%s/stderr'%self.program)
        asyncore.loop()

    def __run(self):
        # Hacked run function, which installs the trace
        sys.settrace(self.globaltrace)
        self.__run_backup()
        self.run = self.__run_backup

    def globaltrace(self, frame, why, arg):
        if why == 'call':
            return self.localtrace
        else:
            return None

    def localtrace(self, frame, why, arg):
        if self.killed:
            if why == 'line':
                raise SystemExit()
        return self.localtrace

    def kill(self):
        self.output_handler.close()
        self.error_handler.close()
        self.killed = True

class Controller(cmd.Cmd):

    def __init__(self, options, completekey='tab', stdin=None,
                 stdout=None):
        self.options = options
        self.prompt = self.options.prompt + '> '
        self.options.plugins = []
        self.vocab = ['help']
        self._complete_info = None
        cmd.Cmd.__init__(self, completekey, stdin, stdout)
        for name, factory, kwargs in self.options.plugin_factories:
            plugin = factory(self, **kwargs)
            for a in dir(plugin):
                if a.startswith('do_') and callable(getattr(plugin, a)):
                    self.vocab.append(a[3:])
            self.options.plugins.append(plugin)
            plugin.name = name

    def emptyline(self):
        # We don't want a blank line to repeat the last command.
        return

    def onecmd(self, line):
        """ Override the onecmd method to:
          - catch and print all exceptions
          - allow for composite commands in interactive mode (foo; bar)
          - call 'do_foo' on plugins rather than ourself
        """
        origline = line
        lines = line.split(';') # don't filter(None, line.split), as we pop
        line = lines.pop(0)
        # stuffing the remainder into cmdqueue will cause cmdloop to
        # call us again for each command.
        self.cmdqueue.extend(lines)
        cmd, arg, line = self.parseline(line)
        if not line:
            return self.emptyline()
        if cmd is None:
            return self.default(line)
        self._complete_info = None
        self.lastcmd = line
        if cmd == '':
            return self.default(line)
        else:
            do_func = self._get_do_func(cmd)
            if do_func is None:
                return self.default(line)
            try:
                try:
                    return do_func(arg)
                except xmlrpclib.ProtocolError as e:
                    if e.errcode == 401:
                        if self.options.interactive:
                            self.output('Server requires authentication')
                            username = raw_input('Username:')
                            password = getpass.getpass(prompt='Password:')
                            self.output('')
                            self.options.username = username
                            self.options.password = password
                            return self.onecmd(origline)
                        else:
                            self.options.usage('Server requires authentication')
                    else:
                        raise
                do_func(arg)
            except SystemExit:
                raise
            except Exception:
                (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
                error = 'error: %s, %s: file: %s line: %s' % (t, v, file, line)
                self.output(error)
                if not self.options.interactive:
                    sys.exit(2)

    def _get_do_func(self, cmd):
        func_name = 'do_' + cmd
        func = getattr(self, func_name, None)
        if not func:
            for plugin in self.options.plugins:
                func = getattr(plugin, func_name, None)
                if func is not None:
                    break
        return func

    def output(self, stuff):
        if stuff is not None:
            if isinstance(stuff, unicode):
                stuff = stuff.encode('utf-8')
            self.stdout.write(stuff + '\n')

    def get_supervisor(self):
        return self.get_server_proxy('supervisor')

    def get_server_proxy(self, namespace=None):
        proxy = self.options.getServerProxy()
        if namespace is None:
            return proxy
        else:
            return getattr(proxy, namespace)

    def upcheck(self):
        try:
            supervisor = self.get_supervisor()
            api = supervisor.getVersion() # deprecated
            from supervisor import rpcinterface
            if api != rpcinterface.API_VERSION:
                self.output(
                    'Sorry, this version of supervisorctl expects to '
                    'talk to a server with API version %s, but the '
                    'remote version is %s.' % (rpcinterface.API_VERSION, api))
                return False
        except xmlrpclib.Fault as e:
            if e.faultCode == xmlrpc.Faults.UNKNOWN_METHOD:
                self.output(
                    'Sorry, supervisord responded but did not recognize '
                    'the supervisor namespace commands that supervisorctl '
                    'uses to control it.  Please check that the '
                    '[rpcinterface:supervisor] section is enabled in the '
                    'configuration file (see sample.conf).')
                return False
            raise
        except socket.error as why:
            if why.args[0] == errno.ECONNREFUSED:
                self.output('%s refused connection' % self.options.serverurl)
                return False
            elif why.args[0] == errno.ENOENT:
                self.output('%s no such file' % self.options.serverurl)
                return False
            raise
        return True

    def complete(self, text, state, line=None):
        """Completer function that Cmd will register with readline using
        readline.set_completer().  This function will be called by readline
        as complete(text, state) where text is a fragment to complete and
        state is an integer (0..n).  Each call returns a string with a new
        completion.  When no more are available, None is returned."""
        if line is None: # line is only set in tests
            import readline
            line = readline.get_line_buffer()

        # take the last phrase from a line like "stop foo; start bar"
        phrase = line.split(';')[-1]

        matches = []
        # blank phrase completes to action list
        if not phrase.strip():
            matches = self._complete_actions(text)
        else:
            words = phrase.split()
            action = words[0]
            # incomplete action completes to action list
            if len(words) == 1 and not phrase.endswith(' '):
                matches = self._complete_actions(text)
            # actions that accept an action name
            elif action in ('help'):
                matches = self._complete_actions(text)
            # actions that accept a group name
            elif action in ('add', 'remove', 'update'):
                matches = self._complete_groups(text)
            # actions that accept a process name
            elif action in ('clear', 'fg', 'pid', 'restart', 'start',
                            'stop', 'status', 'tail'):
                matches = self._complete_processes(text)
        if len(matches) > state:
            return matches[state]

    def _complete_actions(self, text):
        """Build a completion list of action names matching text"""
        return [ a + ' ' for a in self.vocab if a.startswith(text)]

    def _complete_groups(self, text):
        """Build a completion list of group names matching text"""
        groups = []
        for info in self._get_complete_info():
            if info['group'] not in groups:
                groups.append(info['group'])
        return [ g + ' ' for g in groups if g.startswith(text) ]

    def _complete_processes(self, text):
        """Build a completion list of process names matching text"""
        processes = []
        for info in self._get_complete_info():
            if ':' in text or info['name'] != info['group']:
                processes.append('%s:%s' % (info['group'], info['name']))
                if '%s:*' % info['group'] not in processes:
                    processes.append('%s:*' % info['group'])
            else:
                processes.append(info['name'])
        return [ p + ' ' for p in processes if p.startswith(text) ]

    def _get_complete_info(self):
        """Get all process info used for completion.  We cache this between
        commands to reduce XML-RPC calls because readline may call
        complete() many times if the user hits tab only once."""
        if self._complete_info is None:
            self._complete_info = self.get_supervisor().getAllProcessInfo()
        return self._complete_info

    def do_help(self, arg):
        if arg.strip() == 'help':
            self.help_help()
        else:
            for plugin in self.options.plugins:
                plugin.do_help(arg)

    def help_help(self):
        self.output("help\t\tPrint a list of available actions")
        self.output("help <action>\tPrint help for <action>")

    def do_EOF(self, arg):
        self.output('')
        return 1

    def help_EOF(self):
        self.output("To quit, type ^D or use the quit command")

def get_names(inst):
    names = []
    classes = [inst.__class__]
    while classes:
        aclass = classes.pop(0)
        if aclass.__bases__:
            classes = classes + list(aclass.__bases__)
        names = names + dir(aclass)
    return names

class ControllerPluginBase:
    name = 'unnamed'

    def __init__(self, controller):
        self.ctl = controller

    def _doc_header(self):
        return "%s commands (type help <topic>):" % self.name
    doc_header = property(_doc_header)

    def do_help(self, arg):
        if arg:
            # XXX check arg syntax
            try:
                func = getattr(self, 'help_' + arg)
            except AttributeError:
                try:
                    doc = getattr(self, 'do_' + arg).__doc__
                    if doc:
                        self.ctl.output(doc)
                        return
                except AttributeError:
                    pass
                self.ctl.output(self.ctl.nohelp % (arg,))
                return
            func()
        else:
            names = get_names(self)
            cmds_doc = []
            cmds_undoc = []
            help = {}
            for name in names:
                if name[:5] == 'help_':
                    help[name[5:]]=1
            names.sort()
            # There can be duplicates if routines overridden
            prevname = ''
            for name in names:
                if name[:3] == 'do_':
                    if name == prevname:
                        continue
                    prevname = name
                    cmd=name[3:]
                    if cmd in help:
                        cmds_doc.append(cmd)
                        del help[cmd]
                    elif getattr(self, name).__doc__:
                        cmds_doc.append(cmd)
                    else:
                        cmds_undoc.append(cmd)
            self.ctl.output('')
            self.ctl.print_topics(self.doc_header, cmds_doc, 15, 80)

class DefaultControllerPlugin(ControllerPluginBase):
    name = 'default'
    listener = None # for unit tests
    def _tailf(self, path):
        self.ctl.output('==> Press Ctrl-C to exit <==')

        username = self.ctl.options.username
        password = self.ctl.options.password
        handler = None
        try:
            # Python's urllib2 (at least as of Python 2.4.2) isn't up
            # to this task; it doesn't actually implement a proper
            # HTTP/1.1 client that deals with chunked responses (it
            # always sends a Connection: close header).  We use a
            # homegrown client based on asyncore instead.  This makes
            # me sad.
            import supervisor.http_client as http_client
            if self.listener is None:
                listener = http_client.Listener()
            else:
                listener = self.listener # for unit tests
            handler = http_client.HTTPHandler(listener, username, password)
            handler.get(self.ctl.options.serverurl, path)
            asyncore.loop()
        except KeyboardInterrupt:
            if handler:
                handler.close()
            self.ctl.output('')
            return

    def do_tail(self, arg):
        if not self.ctl.upcheck():
            return

        args = arg.split()

        if len(args) < 1:
            self.ctl.output('Error: too few arguments')
            self.help_tail()
            return

        elif len(args) > 3:
            self.ctl.output('Error: too many arguments')
            self.help_tail()
            return

        modifier = None

        if args[0].startswith('-'):
            modifier = args.pop(0)

        if len(args) == 1:
            name = args[-1]
            channel = 'stdout'
        else:
            if args:
                name = args[0]
                channel = args[-1].lower()
                if channel not in ('stderr', 'stdout'):
                    self.ctl.output('Error: bad channel %r' % channel)
                    return
            else:
                self.ctl.output('Error: tail requires process name')
                return

        bytes = 1600

        if modifier is not None:
            what = modifier[1:]
            if what == 'f':
                bytes = None
            else:
                try:
                    bytes = int(what)
                except:
                    self.ctl.output('Error: bad argument %s' % modifier)
                    return

        supervisor = self.ctl.get_supervisor()

        if bytes is None:
            return self._tailf('/logtail/%s/%s' % (name, channel))

        else:
            try:
                if channel is 'stdout':
                    output = supervisor.readProcessStdoutLog(name,
                                                             -bytes, 0)
                else: # if channel is 'stderr'
                    output = supervisor.readProcessStderrLog(name,
                                                             -bytes, 0)
            except xmlrpclib.Fault as e:
                template = '%s: ERROR (%s)'
                if e.faultCode == xmlrpc.Faults.NO_FILE:
                    self.ctl.output(template % (name, 'no log file'))
                elif e.faultCode == xmlrpc.Faults.FAILED:
                    self.ctl.output(template % (name,
                                             'unknown error reading log'))
                elif e.faultCode == xmlrpc.Faults.BAD_NAME:
                    self.ctl.output(template % (name,
                                             'no such process name'))
                else:
                    raise
            else:
                self.ctl.output(output)

    def help_tail(self):
        self.ctl.output(
            "tail [-f] <name> [stdout|stderr] (default stdout)\n"
            "Ex:\n"
            "tail -f <name>\t\tContinuous tail of named process stdout\n"
            "\t\t\tCtrl-C to exit.\n"
            "tail -100 <name>\tlast 100 *bytes* of process stdout\n"
            "tail <name> stderr\tlast 1600 *bytes* of process stderr"
            )

    def do_maintail(self, arg):
        if not self.ctl.upcheck():
            return

        args = arg.split()

        if len(args) > 1:
            self.ctl.output('Error: too many arguments')
            self.help_maintail()
            return

        elif len(args) == 1:
            if args[0].startswith('-'):
                what = args[0][1:]
                if what == 'f':
                    path = '/mainlogtail'
                    return self._tailf(path)
                try:
                    what = int(what)
                except:
                    self.ctl.output('Error: bad argument %s' % args[0])
                    return
                else:
                    bytes = what
            else:
                self.ctl.output('Error: bad argument %s' % args[0])
                return

        else:
            bytes = 1600

        supervisor = self.ctl.get_supervisor()

        try:
            output = supervisor.readLog(-bytes, 0)
        except xmlrpclib.Fault as e:
            template = '%s: ERROR (%s)'
            if e.faultCode == xmlrpc.Faults.NO_FILE:
                self.ctl.output(template % ('supervisord', 'no log file'))
            elif e.faultCode == xmlrpc.Faults.FAILED:
                self.ctl.output(template % ('supervisord',
                                         'unknown error reading log'))
            else:
                raise
        else:
            self.ctl.output(output)

    def help_maintail(self):
        self.ctl.output(
            "maintail -f \tContinuous tail of supervisor main log file"
            " (Ctrl-C to exit)\n"
            "maintail -100\tlast 100 *bytes* of supervisord main log file\n"
            "maintail\tlast 1600 *bytes* of supervisor main log file\n"
            )

    def do_quit(self, arg):
        sys.exit(0)

    def help_quit(self):
        self.ctl.output("quit\tExit the supervisor shell.")

    do_exit = do_quit

    def help_exit(self):
        self.ctl.output("exit\tExit the supervisor shell.")

    def _show_statuses(self, process_infos):
        namespecs, maxlen = [], 30
        for i, info in enumerate(process_infos):
            namespecs.append(make_namespec(info['group'], info['name']))
            if len(namespecs[i]) > maxlen:
                maxlen = len(namespecs[i])

        template = '%(namespec)-' + str(maxlen+3) + 's%(state)-10s%(desc)s'
        for i, info in enumerate(process_infos):
            line = template % {'namespec': namespecs[i],
                               'state': info['statename'],
                               'desc': info['description']}
            self.ctl.output(line)

    def do_status(self, arg):
        if not self.ctl.upcheck():
            return

        supervisor = self.ctl.get_supervisor()
        all_infos = supervisor.getAllProcessInfo()

        names = arg.split()
        if not names or "all" in names:
            matching_infos = all_infos
        else:
            matching_infos = []

            for name in names:
                bad_name = True
                group_name, process_name = split_namespec(name)

                for info in all_infos:
                    matched = info['group'] == group_name
                    if process_name is not None:
                        matched = matched and info['name'] == process_name

                    if matched:
                        bad_name = False
                        matching_infos.append(info)

                if bad_name:
                    if process_name is None:
                        msg = "%s: ERROR (no such group)" % group_name
                    else:
                        msg = "%s: ERROR (no such process)" % name
                    self.ctl.output(msg)
        self._show_statuses(matching_infos)

    def help_status(self):
        self.ctl.output("status <name>\t\tGet status for a single process")
        self.ctl.output("status <gname>:*\tGet status for all "
                        "processes in a group")
        self.ctl.output("status <name> <name>\tGet status for multiple named "
                        "processes")
        self.ctl.output("status\t\t\tGet all process status info")

    def do_pid(self, arg):
        supervisor = self.ctl.get_supervisor()
        if not self.ctl.upcheck():
            return
        names = arg.split()
        if not names:
            pid = supervisor.getPID()
            self.ctl.output(str(pid))
        elif 'all' in names:
            for info in supervisor.getAllProcessInfo():
                self.ctl.output(str(info['pid']))
        else:
            for name in names:
                try:
                    info = supervisor.getProcessInfo(name)
                except xmlrpclib.Fault as e:
                    if e.faultCode == xmlrpc.Faults.BAD_NAME:
                        self.ctl.output('No such process %s' % name)
                    else:
                        raise
                else:
                    self.ctl.output(str(info['pid']))

    def help_pid(self):
        self.ctl.output("pid\t\t\tGet the PID of supervisord.")
        self.ctl.output("pid <name>\t\tGet the PID of a single "
            "child process by name.")
        self.ctl.output("pid all\t\t\tGet the PID of every child "
            "process, one per line.")

    def _startresult(self, result):
        name = make_namespec(result['group'], result['name'])
        code = result['status']
        template = '%s: ERROR (%s)'
        if code == xmlrpc.Faults.BAD_NAME:
            return template % (name, 'no such process')
        elif code == xmlrpc.Faults.NO_FILE:
            return template % (name, 'no such file')
        elif code == xmlrpc.Faults.NOT_EXECUTABLE:
            return template % (name, 'file is not executable')
        elif code == xmlrpc.Faults.ALREADY_STARTED:
            return template % (name, 'already started')
        elif code == xmlrpc.Faults.SPAWN_ERROR:
            return template % (name, 'spawn error')
        elif code == xmlrpc.Faults.ABNORMAL_TERMINATION:
            return template % (name, 'abnormal termination')
        elif code == xmlrpc.Faults.SUCCESS:
            return '%s: started' % name
        # assertion
        raise ValueError('Unknown result code %s for %s' % (code, name))

    def do_start(self, arg):
        if not self.ctl.upcheck():
            return

        names = arg.split()
        supervisor = self.ctl.get_supervisor()

        if not names:
            self.ctl.output("Error: start requires a process name")
            self.help_start()
            return

        if 'all' in names:
            results = supervisor.startAllProcesses()
            for result in results:
                result = self._startresult(result)
                self.ctl.output(result)

        else:
            for name in names:
                group_name, process_name = split_namespec(name)
                if process_name is None:
                    try:
                        results = supervisor.startProcessGroup(group_name)
                        for result in results:
                            result = self._startresult(result)
                            self.ctl.output(result)
                    except xmlrpclib.Fault as e:
                        if e.faultCode == xmlrpc.Faults.BAD_NAME:
                            error = "%s: ERROR (no such group)" % group_name
                            self.ctl.output(error)
                        else:
                            raise
                else:
                    try:
                        result = supervisor.startProcess(name)
                    except xmlrpclib.Fault as e:
                        error = self._startresult({'status': e.faultCode,
                                                   'name': process_name,
                                                   'group': group_name,
                                                   'description': e.faultString})
                        self.ctl.output(error)
                    else:
                        name = make_namespec(group_name, process_name)
                        self.ctl.output('%s: started' % name)

    def help_start(self):
        self.ctl.output("start <name>\t\tStart a process")
        self.ctl.output("start <gname>:*\t\tStart all processes in a group")
        self.ctl.output(
            "start <name> <name>\tStart multiple processes or groups")
        self.ctl.output("start all\t\tStart all processes")

    def _stopresult(self, result):
        name = make_namespec(result['group'], result['name'])
        code = result['status']
        fault_string = result['description']
        template = '%s: ERROR (%s)'
        if code == xmlrpc.Faults.BAD_NAME:
            return template % (name, 'no such process')
        elif code == xmlrpc.Faults.NOT_RUNNING:
            return template % (name, 'not running')
        elif code == xmlrpc.Faults.SUCCESS:
            return '%s: stopped' % name
        elif code == xmlrpc.Faults.FAILED:
            return fault_string
        # assertion
        raise ValueError('Unknown result code %s for %s' % (code, name))

    def do_stop(self, arg):
        if not self.ctl.upcheck():
            return

        names = arg.split()
        supervisor = self.ctl.get_supervisor()

        if not names:
            self.ctl.output('Error: stop requires a process name')
            self.help_stop()
            return

        if 'all' in names:
            results = supervisor.stopAllProcesses()
            for result in results:
                result = self._stopresult(result)
                self.ctl.output(result)

        else:
            for name in names:
                group_name, process_name = split_namespec(name)
                if process_name is None:
                    try:
                        results = supervisor.stopProcessGroup(group_name)
                        for result in results:
                            result = self._stopresult(result)
                            self.ctl.output(result)
                    except xmlrpclib.Fault as e:
                        if e.faultCode == xmlrpc.Faults.BAD_NAME:
                            error = "%s: ERROR (no such group)" % group_name
                            self.ctl.output(error)
                        else:
                            raise
                else:
                    try:
                        supervisor.stopProcess(name)
                    except xmlrpclib.Fault as e:
                        error = self._stopresult({'status': e.faultCode,
                                                  'name': process_name,
                                                  'group': group_name,
                                                  'description':e.faultString})
                        self.ctl.output(error)
                    else:
                        name = make_namespec(group_name, process_name)
                        self.ctl.output('%s: stopped' % name)

    def help_stop(self):
        self.ctl.output("stop <name>\t\tStop a process")
        self.ctl.output("stop <gname>:*\t\tStop all processes in a group")
        self.ctl.output("stop <name> <name>\tStop multiple processes or groups")
        self.ctl.output("stop all\t\tStop all processes")

    def do_restart(self, arg):
        if not self.ctl.upcheck():
            return

        names = arg.split()

        if not names:
            self.ctl.output('Error: restart requires a process name')
            self.help_restart()
            return

        self.do_stop(arg)
        self.do_start(arg)

    def help_restart(self):
        self.ctl.output("restart <name>\t\tRestart a process")
        self.ctl.output("restart <gname>:*\tRestart all processes in a group")
        self.ctl.output("restart <name> <name>\tRestart multiple processes or "
                     "groups")
        self.ctl.output("restart all\t\tRestart all processes")
        self.ctl.output("Note: restart does not reread config files. For that,"
                        " see reread and update.")

    def do_shutdown(self, arg):
        if self.ctl.options.interactive:
            yesno = raw_input('Really shut the remote supervisord process '
                              'down y/N? ')
            really = yesno.lower().startswith('y')
        else:
            really = 1

        if really:
            supervisor = self.ctl.get_supervisor()
            try:
                supervisor.shutdown()
            except xmlrpclib.Fault as e:
                if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                    self.ctl.output('ERROR: already shutting down')
                else:
                    raise
            except socket.error as e:
                if e.args[0] == errno.ECONNREFUSED:
                    msg = 'ERROR: %s refused connection (already shut down?)'
                    self.ctl.output(msg % self.ctl.options.serverurl)
                elif e.args[0] == errno.ENOENT:
                    msg = 'ERROR: %s no such file (already shut down?)'
                    self.ctl.output(msg % self.ctl.options.serverurl)
                else:
                    raise
            else:
                self.ctl.output('Shut down')

    def help_shutdown(self):
        self.ctl.output("shutdown \tShut the remote supervisord down.")

    def do_reload(self, arg):
        if self.ctl.options.interactive:
            yesno = raw_input('Really restart the remote supervisord process '
                              'y/N? ')
            really = yesno.lower().startswith('y')
        else:
            really = 1
        if really:
            supervisor = self.ctl.get_supervisor()
            try:
                supervisor.restart()
            except xmlrpclib.Fault as e:
                if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                    self.ctl.output('ERROR: already shutting down')
                else:
                    raise
            else:
                self.ctl.output('Restarted supervisord')

    def help_reload(self):
        self.ctl.output("reload \t\tRestart the remote supervisord.")

    def _formatChanges(self, added_changed_dropped_tuple):
        added, changed, dropped = added_changed_dropped_tuple
        changedict = {}
        for n, t in [(added, 'available'),
                     (changed, 'changed'),
                     (dropped, 'disappeared')]:
            changedict.update(dict(zip(n, [t] * len(n))))

        if changedict:
            names = list(changedict.keys())
            names.sort()
            for name in names:
                self.ctl.output("%s: %s" % (name, changedict[name]))
        else:
            self.ctl.output("No config updates to processes")

    def _formatConfigInfo(self, configinfo):
        name = make_namespec(configinfo['group'], configinfo['name'])
        formatted = { 'name': name }
        if configinfo['inuse']:
            formatted['inuse'] = 'in use'
        else:
            formatted['inuse'] = 'avail'
        if configinfo['autostart']:
            formatted['autostart'] = 'auto'
        else:
            formatted['autostart'] = 'manual'
        formatted['priority'] = "%s:%s" % (configinfo['group_prio'],
                                           configinfo['process_prio'])

        template = '%(name)-32s %(inuse)-9s %(autostart)-9s %(priority)s'
        return template % formatted

    def do_avail(self, arg):
        supervisor = self.ctl.get_supervisor()
        try:
            configinfo = supervisor.getAllConfigInfo()
        except xmlrpclib.Fault as e:
            if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                self.ctl.output('ERROR: supervisor shutting down')
            else:
                raise
        else:
            for pinfo in configinfo:
                self.ctl.output(self._formatConfigInfo(pinfo))

    def help_avail(self):
        self.ctl.output("avail\t\t\tDisplay all configured processes")

    def do_reread(self, arg):
        supervisor = self.ctl.get_supervisor()
        try:
            result = supervisor.reloadConfig()
        except xmlrpclib.Fault as e:
            if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                self.ctl.output('ERROR: supervisor shutting down')
            elif e.faultCode == xmlrpc.Faults.CANT_REREAD:
                self.ctl.output('ERROR: %s' % e.faultString)
            else:
                raise
        else:
            self._formatChanges(result[0])

    def help_reread(self):
        self.ctl.output("reread \t\t\tReload the daemon's configuration files")

    def do_add(self, arg):
        names = arg.split()

        supervisor = self.ctl.get_supervisor()
        for name in names:
            try:
                supervisor.addProcessGroup(name)
            except xmlrpclib.Fault as e:
                if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                    self.ctl.output('ERROR: shutting down')
                elif e.faultCode == xmlrpc.Faults.ALREADY_ADDED:
                    self.ctl.output('ERROR: process group already active')
                elif e.faultCode == xmlrpc.Faults.BAD_NAME:
                    self.ctl.output(
                        "ERROR: no such process/group: %s" % name)
                else:
                    raise
            else:
                self.ctl.output("%s: added process group" % name)

    def help_add(self):
        self.ctl.output("add <name> [...]\tActivates any updates in config "
                        "for process/group")

    def do_remove(self, arg):
        names = arg.split()

        supervisor = self.ctl.get_supervisor()
        for name in names:
            try:
                supervisor.removeProcessGroup(name)
            except xmlrpclib.Fault as e:
                if e.faultCode == xmlrpc.Faults.STILL_RUNNING:
                    self.ctl.output('ERROR: process/group still running: %s'
                                    % name)
                elif e.faultCode == xmlrpc.Faults.BAD_NAME:
                    self.ctl.output(
                        "ERROR: no such process/group: %s" % name)
                else:
                    raise
            else:
                self.ctl.output("%s: removed process group" % name)

    def help_remove(self):
        self.ctl.output("remove <name> [...]\tRemoves process/group from "
                        "active config")

    def do_update(self, arg):
        def log(name, message):
            self.ctl.output("%s: %s" % (name, message))

        supervisor = self.ctl.get_supervisor()
        try:
            result = supervisor.reloadConfig()
        except xmlrpclib.Fault as e:
            if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                self.ctl.output('ERROR: already shutting down')
                return
            else:
                raise

        added, changed, removed = result[0]
        valid_gnames = set(arg.split())

        # If all is specified treat it as if nothing was specified.
        if "all" in valid_gnames:
            valid_gnames = set()

        # If any gnames are specified we need to verify that they are
        # valid in order to print a useful error message.
        if valid_gnames:
            groups = set()
            for info in supervisor.getAllProcessInfo():
                groups.add(info['group'])
            # New gnames would not currently exist in this set so
            # add those as well.
            groups.update(added)

            for gname in valid_gnames:
                if gname not in groups:
                    self.ctl.output('ERROR: no such group: %s' % gname)

        for gname in removed:
            if valid_gnames and gname not in valid_gnames:
                continue
            results = supervisor.stopProcessGroup(gname)
            log(gname, "stopped")

            fails = [res for res in results
                     if res['status'] == xmlrpc.Faults.FAILED]
            if fails:
                log(gname, "has problems; not removing")
                continue
            supervisor.removeProcessGroup(gname)
            log(gname, "removed process group")

        for gname in changed:
            if valid_gnames and gname not in valid_gnames:
                continue
            supervisor.stopProcessGroup(gname)
            log(gname, "stopped")

            supervisor.removeProcessGroup(gname)
            supervisor.addProcessGroup(gname)
            log(gname, "updated process group")

        for gname in added:
            if valid_gnames and gname not in valid_gnames:
                continue
            supervisor.addProcessGroup(gname)
            log(gname, "added process group")

    def help_update(self):
        self.ctl.output("update\t\t\tReload config and add/remove as necessary")
        self.ctl.output("update all\t\tReload config and add/remove as necessary")
        self.ctl.output("update <gname> [...]\tUpdate specific groups")

    def _clearresult(self, result):
        name = make_namespec(result['group'], result['name'])
        code = result['status']
        template = '%s: ERROR (%s)'
        if code == xmlrpc.Faults.BAD_NAME:
            return template % (name, 'no such process')
        elif code == xmlrpc.Faults.FAILED:
            return template % (name, 'failed')
        elif code == xmlrpc.Faults.SUCCESS:
            return '%s: cleared' % name
        raise ValueError('Unknown result code %s for %s' % (code, name))

    def do_clear(self, arg):
        if not self.ctl.upcheck():
            return

        names = arg.split()

        if not names:
            self.ctl.output('Error: clear requires a process name')
            self.help_clear()
            return

        supervisor = self.ctl.get_supervisor()

        if 'all' in names:
            results = supervisor.clearAllProcessLogs()
            for result in results:
                result = self._clearresult(result)
                self.ctl.output(result)
        else:
            for name in names:
                group_name, process_name = split_namespec(name)
                try:
                    supervisor.clearProcessLogs(name)
                except xmlrpclib.Fault as e:
                    error = self._clearresult({'status': e.faultCode,
                                               'name': process_name,
                                               'group': group_name,
                                               'description': e.faultString})
                    self.ctl.output(error)
                else:
                    name = make_namespec(group_name, process_name)
                    self.ctl.output('%s: cleared' % name)

    def help_clear(self):
        self.ctl.output("clear <name>\t\tClear a process' log files.")
        self.ctl.output(
            "clear <name> <name>\tClear multiple process' log files")
        self.ctl.output("clear all\t\tClear all process' log files")

    def do_open(self, arg):
        url = arg.strip()
        parts = urlparse.urlparse(url)
        if parts[0] not in ('unix', 'http'):
            self.ctl.output('ERROR: url must be http:// or unix://')
            return
        self.ctl.options.serverurl = url
        self.do_status('')

    def help_open(self):
        self.ctl.output("open <url>\tConnect to a remote supervisord process.")
        self.ctl.output("\t\t(for UNIX domain socket, use unix:///socket/path)")

    def do_version(self, arg):
        if not self.ctl.upcheck():
            return
        supervisor = self.ctl.get_supervisor()
        self.ctl.output(supervisor.getSupervisorVersion())

    def help_version(self):
        self.ctl.output(
            "version\t\t\tShow the version of the remote supervisord "
            "process")

    def do_fg(self,args=None):
        if not self.ctl.upcheck():
            return
        if not args:
            self.ctl.output('Error: no process name supplied')
            self.help_fg()
            return
        args = args.split()
        if len(args) > 1:
            self.ctl.output('Error: too many process names supplied')
            return
        program = args[0]
        supervisor = self.ctl.get_supervisor()
        try:
            info = supervisor.getProcessInfo(program)
        except xmlrpclib.Fault as msg:
            if msg.faultCode == xmlrpc.Faults.BAD_NAME:
                self.ctl.output('Error: bad process name supplied')
                return
            # for any other fault
            self.ctl.output(str(msg))
            return
        if not info['state'] == states.ProcessStates.RUNNING:
            self.ctl.output('Error: process not running')
            return
        # everything good; continue
        a = None
        try:
            a = fgthread(program,self.ctl)
            # this thread takes care of
            # the output/error messages
            a.start()
            while True:
                # this takes care of the user input
                inp = raw_input() + '\n'
                try:
                    supervisor.sendProcessStdin(program, inp)
                except xmlrpclib.Fault as msg:
                    if msg.faultCode == xmlrpc.Faults.NOT_RUNNING:
                        self.ctl.output('Process got killed')
                        self.ctl.output('Exiting foreground')
                        a.kill()
                        return
                info = supervisor.getProcessInfo(program)
                if not info['state'] == states.ProcessStates.RUNNING:
                    self.ctl.output('Process got killed')
                    self.ctl.output('Exiting foreground')
                    a.kill()
                    return
                continue
        except (KeyboardInterrupt, EOFError):
            if a:
                a.kill()
            self.ctl.output('Exiting foreground')
        return

    def help_fg(self,args=None):
        self.ctl.output('fg <process>\tConnect to a process in foreground mode')
        self.ctl.output('Press Ctrl+C to exit foreground')

def main(args=None, options=None):
    if options is None:
        options = ClientOptions()
    options.realize(args, doc=__doc__)
    c = Controller(options)
    if options.args:
        c.onecmd(" ".join(options.args))
    if options.interactive:
        try:
            import readline
            delims = readline.get_completer_delims()
            delims = delims.replace(':', '') # "group:process" as one word
            delims = delims.replace('*', '') # "group:*" as one word
            delims = delims.replace('-', '') # names with "-" as one word
            readline.set_completer_delims(delims)

            if options.history_file:
                try:
                    readline.read_history_file(options.history_file)
                except IOError:
                    pass
                def save():
                    try:
                        readline.write_history_file(options.history_file)
                    except IOError:
                        pass

                import atexit
                atexit.register(save)
        except ImportError:
            pass
        try:
            c.cmdqueue.append('status')
            c.cmdloop()
        except KeyboardInterrupt:
            c.output('')
            pass

if __name__ == "__main__":
    main()

########NEW FILE########
__FILENAME__ = supervisord
#!/usr/bin/env python

"""supervisord -- run a set of applications as daemons.

Usage: %s [options]

Options:
-c/--configuration FILENAME -- configuration file
-n/--nodaemon -- run in the foreground (same as 'nodaemon true' in config file)
-h/--help -- print this usage message and exit
-v/--version -- print supervisord version number and exit
-u/--user USER -- run supervisord as this user (or numeric uid)
-m/--umask UMASK -- use this umask for daemon subprocess (default is 022)
-d/--directory DIRECTORY -- directory to chdir to when daemonized
-l/--logfile FILENAME -- use FILENAME as logfile path
-y/--logfile_maxbytes BYTES -- use BYTES to limit the max size of logfile
-z/--logfile_backups NUM -- number of backups to keep when max bytes reached
-e/--loglevel LEVEL -- use LEVEL as log level (debug,info,warn,error,critical)
-j/--pidfile FILENAME -- write a pid file for the daemon process to FILENAME
-i/--identifier STR -- identifier used for this instance of supervisord
-q/--childlogdir DIRECTORY -- the log directory for child process logs
-k/--nocleanup --  prevent the process from performing cleanup (removal of
                   old automatic child log files) at startup.
-a/--minfds NUM -- the minimum number of file descriptors for start success
-t/--strip_ansi -- strip ansi escape codes from process output
--minprocs NUM  -- the minimum number of processes available for start success
--profile_options OPTIONS -- run supervisord under profiler and output
                             results based on OPTIONS, which  is a comma-sep'd
                             list of 'cumulative', 'calls', and/or 'callers',
                             e.g. 'cumulative,callers')
"""

import os
import time
import errno
import select
import signal

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.options import ServerOptions
from supervisor.options import signame
from supervisor import events
from supervisor.states import SupervisorStates
from supervisor.states import getProcessStateDescription

class Supervisor:
    stopping = False # set after we detect that we are handling a stop request
    lastshutdownreport = 0 # throttle for delayed process error reports at stop
    process_groups = None # map of process group name to process group object
    stop_groups = None # list used for priority ordered shutdown

    def __init__(self, options):
        self.options = options
        self.process_groups = {}
        self.ticks = {}

    def main(self):
        if not self.options.first:
            # prevent crash on libdispatch-based systems, at least for the
            # first request
            self.options.cleanup_fds()
        info_messages = []
        critical_messages = []
        warn_messages = []
        setuid_msg = self.options.set_uid()
        if setuid_msg:
            critical_messages.append(setuid_msg)
        if self.options.first:
            rlimit_messages = self.options.set_rlimits()
            info_messages.extend(rlimit_messages)
        warn_messages.extend(self.options.parse_warnings)

        # this sets the options.logger object
        # delay logger instantiation until after setuid
        self.options.make_logger(critical_messages, warn_messages,
                                 info_messages)

        if not self.options.nocleanup:
            # clean up old automatic logs
            self.options.clear_autochildlogdir()

        self.run()

    def run(self):
        self.process_groups = {} # clear
        self.stop_groups = None # clear
        events.clear()
        try:
            for config in self.options.process_group_configs:
                self.add_process_group(config)
            self.options.process_environment()
            self.options.openhttpservers(self)
            self.options.setsignals()
            if (not self.options.nodaemon) and self.options.first:
                self.options.daemonize()
            # writing pid file needs to come *after* daemonizing or pid
            # will be wrong
            self.options.write_pidfile()
            self.runforever()
        finally:
            self.options.cleanup()

    def diff_to_active(self, new=None):
        if not new:
            new = self.options.process_group_configs
        cur = [group.config for group in self.process_groups.values()]

        curdict = dict(zip([cfg.name for cfg in cur], cur))
        newdict = dict(zip([cfg.name for cfg in new], new))

        added   = [cand for cand in new if cand.name not in curdict]
        removed = [cand for cand in cur if cand.name not in newdict]

        changed = [cand for cand in new
                   if cand != curdict.get(cand.name, cand)]

        return added, changed, removed

    def add_process_group(self, config):
        name = config.name
        if name not in self.process_groups:
            config.after_setuid()
            self.process_groups[name] = config.make_group()
            events.notify(events.ProcessGroupAddedEvent(name))
            return True
        return False

    def remove_process_group(self, name):
        if self.process_groups[name].get_unstopped_processes():
            return False
        del self.process_groups[name]
        events.notify(events.ProcessGroupRemovedEvent(name))
        return True

    def get_process_map(self):
        process_map = {}
        for group in self.process_groups.values():
            process_map.update(group.get_dispatchers())
        return process_map

    def shutdown_report(self):
        unstopped = []

        for group in self.process_groups.values():
            unstopped.extend(group.get_unstopped_processes())

        if unstopped:
            # throttle 'waiting for x to die' reports
            now = time.time()
            if now > (self.lastshutdownreport + 3): # every 3 secs
                names = [ p.config.name for p in unstopped ]
                namestr = ', '.join(names)
                self.options.logger.info('waiting for %s to die' % namestr)
                self.lastshutdownreport = now
                for proc in unstopped:
                    state = getProcessStateDescription(proc.get_state())
                    self.options.logger.blather(
                        '%s state: %s' % (proc.config.name, state))
        return unstopped

    def ordered_stop_groups_phase_1(self):
        if self.stop_groups:
            # stop the last group (the one with the "highest" priority)
            self.stop_groups[-1].stop_all()

    def ordered_stop_groups_phase_2(self):
        # after phase 1 we've transitioned and reaped, let's see if we
        # can remove the group we stopped from the stop_groups queue.
        if self.stop_groups:
            # pop the last group (the one with the "highest" priority)
            group = self.stop_groups.pop()
            if group.get_unstopped_processes():
                # if any processes in the group aren't yet in a
                # stopped state, we're not yet done shutting this
                # group down, so push it back on to the end of the
                # stop group queue
                self.stop_groups.append(group)

    def runforever(self):
        events.notify(events.SupervisorRunningEvent())
        timeout = 1 # this cannot be fewer than the smallest TickEvent (5)

        socket_map = self.options.get_socket_map()

        while 1:
            combined_map = {}
            combined_map.update(socket_map)
            combined_map.update(self.get_process_map())

            pgroups = list(self.process_groups.values())
            pgroups.sort()

            if self.options.mood < SupervisorStates.RUNNING:
                if not self.stopping:
                    # first time, set the stopping flag, do a
                    # notification and set stop_groups
                    self.stopping = True
                    self.stop_groups = pgroups[:]
                    events.notify(events.SupervisorStoppingEvent())

                self.ordered_stop_groups_phase_1()

                if not self.shutdown_report():
                    # if there are no unstopped processes (we're done
                    # killing everything), it's OK to swtop or reload
                    raise asyncore.ExitNow

            r, w, x = [], [], []

            for fd, dispatcher in combined_map.items():
                if dispatcher.readable():
                    r.append(fd)
                if dispatcher.writable():
                    w.append(fd)

            try:
                r, w, x = self.options.select(r, w, x, timeout)
            except select.error as err:
                r = w = x = []
                if err.args[0] == errno.EINTR:
                    self.options.logger.blather('EINTR encountered in select')
                else:
                    raise

            for fd in r:
                if fd in combined_map:
                    try:
                        dispatcher = combined_map[fd]
                        self.options.logger.blather(
                            'read event caused by %(dispatcher)s',
                            dispatcher=dispatcher)
                        dispatcher.handle_read_event()
                    except asyncore.ExitNow:
                        raise
                    except:
                        combined_map[fd].handle_error()

            for fd in w:
                if fd in combined_map:
                    try:
                        dispatcher = combined_map[fd]
                        self.options.logger.blather(
                            'write event caused by %(dispatcher)s',
                            dispatcher=dispatcher)
                        dispatcher.handle_write_event()
                    except asyncore.ExitNow:
                        raise
                    except:
                        combined_map[fd].handle_error()

            [ group.transition() for group  in pgroups ]

            self.reap()
            self.handle_signal()
            self.tick()

            if self.options.mood < SupervisorStates.RUNNING:
                self.ordered_stop_groups_phase_2()

            if self.options.test:
                break

    def tick(self, now=None):
        """ Send one or more 'tick' events when the timeslice related to
        the period for the event type rolls over """
        if now is None:
            # now won't be None in unit tests
            now = time.time()
        for event in events.TICK_EVENTS:
            period = event.period
            last_tick = self.ticks.get(period)
            if last_tick is None:
                # we just started up
                last_tick = self.ticks[period] = timeslice(period, now)
            this_tick = timeslice(period, now)
            if this_tick != last_tick:
                self.ticks[period] = this_tick
                events.notify(event(this_tick, self))

    def reap(self, once=False):
        pid, sts = self.options.waitpid()
        if pid:
            process = self.options.pidhistory.get(pid, None)
            if process is None:
                self.options.logger.critical('reaped unknown pid %s' % pid)
            else:
                process.finish(pid, sts)
                del self.options.pidhistory[pid]
            if not once:
                self.reap() # keep reaping until no more kids to reap

    def handle_signal(self):
        sig = self.options.get_signal()
        if sig:
            if sig in (signal.SIGTERM, signal.SIGINT, signal.SIGQUIT):
                self.options.logger.warn(
                    'received %s indicating exit request' % signame(sig))
                self.options.mood = SupervisorStates.SHUTDOWN
            elif sig == signal.SIGHUP:
                self.options.logger.warn(
                    'received %s indicating restart request' % signame(sig))
                self.options.mood = SupervisorStates.RESTARTING
            elif sig == signal.SIGCHLD:
                self.options.logger.debug(
                    'received %s indicating a child quit' % signame(sig))
            elif sig == signal.SIGUSR2:
                self.options.logger.info(
                    'received %s indicating log reopen request' % signame(sig))
                self.options.reopenlogs()
                for group in self.process_groups.values():
                    group.reopenlogs()
            else:
                self.options.logger.blather(
                    'received %s indicating nothing' % signame(sig))

    def get_state(self):
        return self.options.mood

def timeslice(period, when):
    return int(when - (when % period))

# profile entry point
def profile(cmd, globals, locals, sort_order, callers):
    try:
        import cProfile as profile
    except ImportError:
        import profile
    import pstats
    import tempfile
    fd, fn = tempfile.mkstemp()
    try:
        profile.runctx(cmd, globals, locals, fn)
        stats = pstats.Stats(fn)
        stats.strip_dirs()
        # calls,time,cumulative and cumulative,calls,time are useful
        stats.sort_stats(*sort_order or ('cumulative', 'calls', 'time'))
        if callers:
            stats.print_callers(.3)
        else:
            stats.print_stats(.3)
    finally:
        os.remove(fn)


# Main program
def main(args=None, test=False):
    assert os.name == "posix", "This code makes Unix-specific assumptions"
    # if we hup, restart by making a new Supervisor()
    first = True
    while 1:
        options = ServerOptions()
        options.realize(args, doc=__doc__)
        options.first = first
        options.test = test
        if options.profile_options:
            sort_order, callers = options.profile_options
            profile('go(options)', globals(), locals(), sort_order, callers)
        else:
            go(options)
        if test or (options.mood < SupervisorStates.RESTARTING):
            break
        options.close_httpservers()
        options.close_logger()
        first = False

def go(options):
    d = Supervisor(options)
    try:
        d.main()
    except asyncore.ExitNow:
        pass

if __name__ == "__main__":
    main()

########NEW FILE########
__FILENAME__ = base
_NOW = 1151365354
_TIMEFORMAT = '%b %d %I:%M %p'

from supervisor.compat import total_ordering
from supervisor.compat import Fault
from supervisor.compat import as_string

# mock is imported here for py2/3 compat.  we only declare mock as a dependency
# via tests_require so it is not available on all supervisor installs.  the
# modules imported in supervisor.compat must always be available.

try: # pragma: no cover
    from unittest.mock import Mock, patch, sentinel
except ImportError: # pragma: no cover
    from mock import Mock, patch, sentinel

try: # pragma: no cover
    import unittest.mock as mock
except ImportError: # pragma: no cover
    import mock

class DummyOptions:

    make_pipes_error = None
    fork_error = None
    execv_error = None
    kill_error = None
    minfds = 5
    loglevel = 20

    def __init__(self):
        self.identifier = 'supervisor'
        self.childlogdir = '/tmp'
        self.uid = 999
        self.logger = self.getLogger()
        self.backofflimit = 10
        self.logfile = '/tmp/logfile'
        self.nocleanup = False
        self.strip_ansi = False
        self.pidhistory = {}
        self.process_group_configs = []
        self.nodaemon = False
        self.socket_map = {}
        self.mood = 1
        self.mustreopen = False
        self.realizeargs = None
        self.fds_cleaned_up = False
        self.rlimit_set = False
        self.setuid_called = False
        self.httpservers_opened = False
        self.signals_set = False
        self.daemonized = False
        self.make_logger_messages = None
        self.autochildlogdir_cleared = False
        self.cleaned_up = False
        self.pidfile_written = False
        self.directory = None
        self.waitpid_return = None, None
        self.kills = {}
        self._signal = None
        self.parent_pipes_closed = None
        self.child_pipes_closed = None
        self.forkpid = 0
        self.pgrp_set = None
        self.duped = {}
        self.written = {}
        self.fds_closed = []
        self._exitcode = None
        self.execve_called = False
        self.execv_args = None
        self.setuid_msg = None
        self.privsdropped = None
        self.logs_reopened = False
        self.environment_processed = False
        self.select_result = [], [], []
        self.select_error = None
        self.write_accept = None
        self.write_error = None
        self.tempfile_name = '/foo/bar'
        self.remove_error = None
        self.removed = []
        self.existing = []
        self.openreturn = None
        self.readfd_result = ''
        self.parse_warnings = []
        self.serverurl = 'http://localhost:9001'
        self.changed_directory = False
        self.chdir_error = None
        self.umaskset = None

    def getLogger(self, *args, **kw):
        logger = DummyLogger()
        logger.handlers = [DummyLogger()]
        logger.args = args, kw
        return logger

    def realize(self, args, **kw):
        self.realizeargs = args
        self.realizekw = kw

    def process_config(self, do_usage=True):
        pass

    def cleanup_fds(self):
        self.fds_cleaned_up = True

    def set_rlimits(self):
        self.rlimits_set = True
        return ['rlimits_set']

    def set_uid(self):
        self.setuid_called = True
        return 'setuid_called'

    def openhttpservers(self, supervisord):
        self.httpservers_opened = True

    def daemonize(self):
        self.daemonized = True

    def setsignals(self):
        self.signals_set = True

    def get_signal(self):
        return self._signal

    def get_socket_map(self):
        return self.socket_map

    def make_logger(self, critical_msgs, warn_msgs, info_msgs):
        self.make_logger_messages = critical_msgs, warn_msgs, info_msgs

    def clear_autochildlogdir(self):
        self.autochildlogdir_cleared = True

    def get_autochildlog_name(self, *ignored):
        return self.tempfile_name

    def cleanup(self):
        self.cleaned_up = True

    def write_pidfile(self):
        self.pidfile_written = True

    def waitpid(self):
        return self.waitpid_return

    def kill(self, pid, sig):
        if self.kill_error:
            raise OSError(self.kill_error)
        self.kills[pid] = sig

    def stat(self, filename):
        import os
        return os.stat(filename)

    def get_path(self):
        return ["/bin", "/usr/bin", "/usr/local/bin"]

    def get_pid(self):
        import os
        return os.getpid()

    def check_execv_args(self, filename, argv, st):
        if filename == '/bad/filename':
            from supervisor.options import NotFound
            raise NotFound('bad filename')

    def make_pipes(self, stderr=True):
        if self.make_pipes_error:
            raise OSError(self.make_pipes_error)
        pipes = {'child_stdin': 3, 'stdin': 4, 'stdout': 5, 'child_stdout': 6}
        if stderr:
            pipes['stderr'], pipes['child_stderr'] = (7, 8)
        else:
            pipes['stderr'], pipes['child_stderr'] = None, None
        return pipes

    def write(self, fd, chars):
        if self.write_error:
            raise OSError(self.write_error)
        if self.write_accept:
            chars = chars[self.write_accept]
        data = self.written.setdefault(fd, '')
        data += chars
        self.written[fd] = data
        return len(chars)

    def fork(self):
        if self.fork_error:
            raise OSError(self.fork_error)
        return self.forkpid

    def close_fd(self, fd):
        self.fds_closed.append(fd)

    def close_parent_pipes(self, pipes):
        self.parent_pipes_closed = pipes

    def close_child_pipes(self, pipes):
        self.child_pipes_closed = pipes

    def setpgrp(self):
        self.pgrp_set = True

    def dup2(self, frm, to):
        self.duped[frm] = to

    def _exit(self, code):
        self._exitcode = code

    def execve(self, filename, argv, environment):
        self.execve_called = True
        if self.execv_error:
            if self.execv_error == 1:
                raise OSError(self.execv_error)
            else:
                raise RuntimeError(self.execv_error)
        self.execv_args = (filename, argv)
        self.execv_environment = environment

    def dropPrivileges(self, uid):
        if self.setuid_msg:
            return self.setuid_msg
        self.privsdropped = uid

    def readfd(self, fd):
        return self.readfd_result

    def reopenlogs(self):
        self.logs_reopened = True

    def process_environment(self):
        self.environment_processed = True

    def mktempfile(self, prefix, suffix, dir):
        return self.tempfile_name

    def select(self, r, w, x, timeout):
        import select
        if self.select_error:
            raise select.error(self.select_error)
        return self.select_result

    def remove(self, path):
        import os
        if self.remove_error:
            raise os.error(self.remove_error)
        self.removed.append(path)

    def exists(self, path):
        if path in self.existing:
            return True
        return False

    def open(self, name, mode='r'):
        if self.openreturn:
            return self.openreturn
        return open(name, mode)

    def chdir(self, dir):
        if self.chdir_error:
            raise OSError(self.chdir_error)
        self.changed_directory = True

    def setumask(self, mask):
        self.umaskset = mask

class DummyLogger:
    level = None

    def __init__(self):
        self.reopened = False
        self.removed = False
        self.closed = False
        self.data = []

    def info(self, msg, **kw):
        if kw:
            msg = msg % kw
        self.data.append(msg)
    warn = debug = critical = trace = error = blather = info

    def log(self, level, msg, **kw):
        if kw:
            msg = msg % kw
        self.data.append(msg)

    def addHandler(self, handler):
        pass

    def reopen(self):
        self.reopened = True
    def close(self):
        self.closed = True
    def remove(self):
        self.removed = True
    def flush(self):
        self.flushed = True
    def getvalue(self):
        return ''.join(self.data)

class DummySupervisor:
    def __init__(self, options=None, state=None, process_groups=None):
        if options is None:
            self.options = DummyOptions()
        else:
            self.options = options
        if state is None:
            from supervisor.supervisord import SupervisorStates
            self.options.mood = SupervisorStates.RUNNING
        else:
            self.options.mood = state
        if process_groups is None:
            self.process_groups = {}
        else:
            self.process_groups = process_groups

    def get_state(self):
        return self.options.mood

class DummySocket:
    bind_called = False
    bind_addr = None
    listen_called = False
    listen_backlog = None
    close_called = False

    def __init__(self, fd):
        self.fd = fd

    def fileno(self):
        return self.fd

    def bind(self, addr):
        self.bind_called = True
        self.bind_addr = addr

    def listen(self, backlog):
        self.listen_called = True
        self.listen_backlog = backlog

    def close(self):
        self.close_called = True

    def __str__(self):
        return 'dummy socket'

class DummySocketConfig:
    def __init__(self, fd):
        self.fd = fd
        self.url = 'unix:///sock'

    def addr(self):
        return 'dummy addr'

    def __eq__(self, other):
        return self.fd == other.fd

    def __ne__(self, other):
        return not self.__eq__(other)

    def create_and_bind(self):
        return DummySocket(self.fd)

class DummySocketManager:
    def __init__(self, config, **kwargs):
        self._config = config

    def config(self):
        return self._config

    def get_socket(self):
        return DummySocket(self._config.fd)

class DummyProcess(object):
    # Initial state; overridden by instance variables
    pid = 0 # Subprocess pid; 0 when not running
    laststart = 0 # Last time the subprocess was started; 0 if never
    laststop = 0  # Last time the subprocess was stopped; 0 if never
    delay = 0 # If nonzero, delay starting or killing until this time
    administrative_stop = 0 # true if the process has been stopped by an admin
    system_stop = 0 # true if the process has been stopped by the system
    killing = 0 # flag determining whether we are trying to kill this proc
    backoff = 0 # backoff counter (to backofflimit)
    waitstatus = None
    exitstatus = None
    pipes = None
    rpipes = None
    dispatchers = None
    stdout_logged = ''
    stderr_logged = ''
    spawnerr = None
    stdout_buffer = '' # buffer of characters from child stdout output to log
    stderr_buffer = '' # buffer of characters from child stderr output to log
    stdin_buffer = '' # buffer of characters to send to child process' stdin
    listener_state = None
    group = None

    def __init__(self, config, state=None):
        self.config = config
        self.logsremoved = False
        self.stop_called = False
        self.backoff_secs = None
        self.spawned = False
        if state is None:
            from supervisor.process import ProcessStates
            state = ProcessStates.RUNNING
        self.state = state
        self.error_at_clear = False
        self.killed_with = None
        self.drained = False
        self.stdout_buffer = ''
        self.stderr_buffer = ''
        self.stdout_logged = ''
        self.stderr_logged = ''
        self.stdin_buffer = ''
        self.pipes = {}
        self.rpipes = {}
        self.dispatchers = {}
        self.finished = None
        self.logs_reopened = False
        self.execv_arg_exception = None
        self.input_fd_drained = None
        self.output_fd_drained = None
        self.transitioned = False
        self.write_error = None

    def reopenlogs(self):
        self.logs_reopened = True

    def removelogs(self):
        if self.error_at_clear:
            raise IOError('whatever')
        self.logsremoved = True

    def get_state(self):
        return self.state

    def stop(self):
        self.stop_called = True
        self.killing = False
        from supervisor.process import ProcessStates
        self.state = ProcessStates.STOPPED

    def kill(self, signal):
        self.killed_with = signal

    def spawn(self):
        self.spawned = True
        from supervisor.process import ProcessStates
        self.state = ProcessStates.RUNNING

    def drain(self):
        self.drained = True

    def readable_fds(self):
        return []

    def record_output(self):
        self.stdout_logged += self.stdout_buffer
        self.stdout_buffer = ''

        self.stderr_logged += self.stderr_buffer
        self.stderr_buffer = ''

    def finish(self, pid, sts):
        self.finished = pid, sts

    def give_up(self):
        from supervisor.process import ProcessStates
        self.state = ProcessStates.FATAL

    def get_execv_args(self):
        if self.execv_arg_exception:
            raise self.execv_arg_exception('whatever')
        import shlex
        commandargs = shlex.split(self.config.command)
        program = commandargs[0]
        return program, commandargs

    def drain_output_fd(self, fd):
        self.output_fd_drained = fd

    def drain_input_fd(self, fd):
        self.input_fd_drained = fd

    def write(self, chars):
        if self.write_error:
            raise OSError(self.write_error)
        self.stdin_buffer += chars

    def transition(self):
        self.transitioned = True

    def __eq__(self, other):
        return self.config.priority == other.config.priority

    def __lt__(self, other):
        return self.config.priority < other.config.priority

DummyProcess = total_ordering(DummyProcess)

class DummyPConfig:
    def __init__(self, options, name, command, directory=None, umask=None,
                 priority=999, autostart=True,
                 autorestart=True, startsecs=10, startretries=999,
                 uid=None, stdout_logfile=None, stdout_capture_maxbytes=0,
                 stdout_events_enabled=False,
                 stdout_logfile_backups=0, stdout_logfile_maxbytes=0,
                 stderr_logfile=None, stderr_capture_maxbytes=0,
                 stderr_events_enabled=False,
                 stderr_logfile_backups=0, stderr_logfile_maxbytes=0,
                 redirect_stderr=False,
                 stopsignal=None, stopwaitsecs=10, stopasgroup=False, killasgroup=False,
                 exitcodes=(0,2), environment=None, serverurl=None):
        self.options = options
        self.name = name
        self.command = command
        self.priority = priority
        self.autostart = autostart
        self.autorestart = autorestart
        self.startsecs = startsecs
        self.startretries = startretries
        self.uid = uid
        self.stdout_logfile = stdout_logfile
        self.stdout_capture_maxbytes = stdout_capture_maxbytes
        self.stdout_events_enabled = stdout_events_enabled
        self.stdout_logfile_backups = stdout_logfile_backups
        self.stdout_logfile_maxbytes = stdout_logfile_maxbytes
        self.stderr_logfile = stderr_logfile
        self.stderr_capture_maxbytes = stderr_capture_maxbytes
        self.stderr_events_enabled = stderr_events_enabled
        self.stderr_logfile_backups = stderr_logfile_backups
        self.stderr_logfile_maxbytes = stderr_logfile_maxbytes
        self.redirect_stderr = redirect_stderr
        if stopsignal is None:
            import signal
            stopsignal = signal.SIGTERM
        self.stopsignal = stopsignal
        self.stopwaitsecs = stopwaitsecs
        self.stopasgroup = stopasgroup
        self.killasgroup = killasgroup
        self.exitcodes = exitcodes
        self.environment = environment
        self.directory = directory
        self.umask = umask
        self.autochildlogs_created = False
        self.serverurl = serverurl

    def create_autochildlogs(self):
        self.autochildlogs_created = True

    def make_process(self, group=None):
        process = DummyProcess(self)
        process.group = group
        return process

    def make_dispatchers(self, proc):
        use_stderr = not self.redirect_stderr
        pipes = self.options.make_pipes(use_stderr)
        stdout_fd,stderr_fd,stdin_fd = (pipes['stdout'],pipes['stderr'],
                                        pipes['stdin'])
        dispatchers = {}
        if stdout_fd is not None:
            dispatchers[stdout_fd] = DummyDispatcher(readable=True)
        if stderr_fd is not None:
            dispatchers[stderr_fd] = DummyDispatcher(readable=True)
        if stdin_fd is not None:
            dispatchers[stdin_fd] = DummyDispatcher(writable=True)
        return dispatchers, pipes

def makeExecutable(file, substitutions=None):
    import os
    import sys
    import tempfile

    if substitutions is None:
        substitutions = {}
    data = open(file).read()
    last = os.path.split(file)[1]

    substitutions['PYTHON'] = sys.executable
    for key in substitutions.keys():
        data = data.replace('<<%s>>' % key.upper(), substitutions[key])

    tmpnam = tempfile.mktemp(prefix=last)
    f = open(tmpnam, 'w')
    f.write(data)
    f.close()
    os.chmod(tmpnam, 493) # 0755 on Py2, 0o755 on Py3
    return tmpnam

def makeSpew(unkillable=False):
    import os
    here = os.path.dirname(__file__)
    if not unkillable:
        return makeExecutable(os.path.join(here, 'fixtures/spew.py'))
    return makeExecutable(os.path.join(here, 'fixtures/unkillable_spew.py'))

class DummyMedusaServerLogger:
    def __init__(self):
        self.logged = []
    def log(self, category, msg):
        self.logged.append((category, msg))

class DummyMedusaServer:
    def __init__(self):
        self.logger = DummyMedusaServerLogger()

class DummyMedusaChannel:
    def __init__(self):
        self.server = DummyMedusaServer()
        self.producer = None

    def push_with_producer(self, producer):
        self.producer = producer

    def close_when_done(self):
        pass

    def set_terminator(self, terminator):
        pass

class DummyRequest(dict):
    command = 'GET'
    _error = None
    _done = False
    version = '1.0'
    def __init__(self, path, params, query, fragment, env=None):
        self.args = path, params, query, fragment
        self.producers = []
        self.headers = {}
        self.header = []
        self.outgoing = []
        self.channel = DummyMedusaChannel()
        if env is None:
            self.env = {}
        else:
            self.env = env

    def split_uri(self):
        return self.args

    def error(self, code):
        self._error = code

    def push(self, producer):
        self.producers.append(producer)

    def __setitem__(self, header, value):
        self.headers[header] = value

    def has_key(self, header):
        return header in self.headers

    def __contains__(self, item):
        return item in self.headers

    def done(self):
        self._done = True

    def build_reply_header(self):
        return ''

    def log(self, *arg, **kw):
        pass

    def cgi_environment(self):
        return self.env

    def get_server_url(self):
        return 'http://example.com'


class DummyRPCInterfaceFactory:
    def __init__(self, supervisord, **config):
        self.supervisord = supervisord
        self.config = config

class DummyRPCServer:
    def __init__(self):
        self.supervisor = DummySupervisorRPCNamespace()
        self.system = DummySystemRPCNamespace()

class DummySystemRPCNamespace:
    pass

class DummySupervisorRPCNamespace:
    _restartable = True
    _restarted = False
    _shutdown = False
    _readlog_error = False


    from supervisor.process import ProcessStates
    all_process_info = [
        {
        'name':'foo',
        'group':'foo',
        'pid':11,
        'state':ProcessStates.RUNNING,
        'statename':'RUNNING',
        'start':_NOW - 100,
        'stop':0,
        'spawnerr':'',
        'now':_NOW,
        'description':'foo description',
        },
        {
        'name':'bar',
        'group':'bar',
        'pid':12,
        'state':ProcessStates.FATAL,
        'statename':'FATAL',
        'start':_NOW - 100,
        'stop':_NOW - 50,
        'spawnerr':'screwed',
        'now':_NOW,
        'description':'bar description',
        },
        {
        'name':'baz_01',
        'group':'baz',
        'pid':13,
        'state':ProcessStates.STOPPED,
        'statename':'STOPPED',
        'start':_NOW - 100,
        'stop':_NOW - 25,
        'spawnerr':'',
        'now':_NOW,
        'description':'baz description',
        },
        ]

    def getAPIVersion(self):
        return '3.0'

    getVersion = getAPIVersion # deprecated

    def getPID(self):
        return 42

    def readProcessStdoutLog(self, name, offset, length):
        from supervisor import xmlrpc
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        elif name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, 'FAILED')
        elif name == 'NO_FILE':
            raise Fault(xmlrpc.Faults.NO_FILE, 'NO_FILE')
        a = 'output line\n' * 10
        return a[offset:]

    readProcessLog = readProcessStdoutLog
    readProcessStderrLog = readProcessStdoutLog

    def getAllProcessInfo(self):
        return self.all_process_info

    def getProcessInfo(self, name):
        from supervisor import xmlrpc
        for i in self.all_process_info:
            if i['name']==name:
                info=i
                return info
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        if name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, 'FAILED')
        if name == 'NO_FILE':
            raise Fault(xmlrpc.Faults.NO_FILE, 'NO_FILE')

    def startProcess(self, name):
        from supervisor import xmlrpc
        if name == 'BAD_NAME:BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME:BAD_NAME')
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        if name == 'NO_FILE':
            raise Fault(xmlrpc.Faults.NO_FILE, 'NO_FILE')
        if name == 'NOT_EXECUTABLE':
            raise Fault(xmlrpc.Faults.NOT_EXECUTABLE, 'NOT_EXECUTABLE')
        if name == 'ALREADY_STARTED':
            raise Fault(xmlrpc.Faults.ALREADY_STARTED, 'ALREADY_STARTED')
        if name == 'SPAWN_ERROR':
            raise Fault(xmlrpc.Faults.SPAWN_ERROR, 'SPAWN_ERROR')
        if name == 'ABNORMAL_TERMINATION':
            raise Fault(xmlrpc.Faults.ABNORMAL_TERMINATION,
                        'ABNORMAL_TERMINATION')
        return True

    def startProcessGroup(self, name):
        from supervisor import xmlrpc
        from supervisor.compat import Fault
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        return [
            {'name':'foo_00', 'group':'foo',
             'status': xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo_01', 'group':'foo',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            ]

    def startAllProcesses(self):
        from supervisor import xmlrpc
        return [
            {'name':'foo', 'group':'foo',
             'status': xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo2', 'group':'foo2',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'failed', 'group':'failed_group',
             'status':xmlrpc.Faults.SPAWN_ERROR,
             'description':'SPAWN_ERROR'}
            ]

    def stopProcessGroup(self, name):
        from supervisor import xmlrpc
        from supervisor.compat import Fault
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        return [
            {'name':'foo_00', 'group':'foo',
             'status': xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo_01', 'group':'foo',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            ]

    def stopProcess(self, name):
        from supervisor import xmlrpc
        if name == 'BAD_NAME:BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME:BAD_NAME')
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        if name == 'NOT_RUNNING':
            raise Fault(xmlrpc.Faults.NOT_RUNNING, 'NOT_RUNNING')
        if name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, 'FAILED')

        return True

    def stopAllProcesses(self):
        from supervisor import xmlrpc
        return [
            {'name':'foo','group':'foo',
             'status': xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo2', 'group':'foo2',
             'status':xmlrpc.Faults.SUCCESS,'description': 'OK'},
            {'name':'failed', 'group':'failed_group',
             'status':xmlrpc.Faults.BAD_NAME,
             'description':'FAILED'}
            ]

    def restart(self):
        if self._restartable:
            self._restarted = True
            return
        from supervisor import xmlrpc
        raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')

    def shutdown(self):
        if self._restartable:
            self._shutdown = True
            return
        from supervisor import xmlrpc
        raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')

    def reloadConfig(self):
        return [[['added'], ['changed'], ['removed']]]

    def addProcessGroup(self, name):
        from supervisor import xmlrpc
        if name == 'ALREADY_ADDED':
            raise Fault(xmlrpc.Faults.ALREADY_ADDED, '')
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, '')
        if name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, '')
        if name == 'SHUTDOWN_STATE':
            raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
        if hasattr(self, 'processes'):
            self.processes.append(name)
        else:
            self.processes = [name]

    def removeProcessGroup(self, name):
        from supervisor import xmlrpc
        if name == 'STILL_RUNNING':
            raise Fault(xmlrpc.Faults.STILL_RUNNING, '')
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, '')
        if name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, '')
        self.processes.remove(name)

    def clearProcessStdoutLog(self, name):
        from supervisor import xmlrpc
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        return True

    clearProcessLog = clearProcessStdoutLog
    clearProcessStderrLog = clearProcessStdoutLog
    clearProcessLogs = clearProcessStdoutLog

    def clearAllProcessLogs(self):
        from supervisor import xmlrpc
        return [
            {'name':'foo', 'group':'foo',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo2', 'group':'foo2',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'failed', 'group':'failed_group',
             'status':xmlrpc.Faults.FAILED,
             'description':'FAILED'}
            ]

    def raiseError(self):
        raise ValueError('error')

    def getSupervisorVersion(self):
        return '3000'

    def readLog(self, whence, offset):
        if self._readlog_error:
            raise Fault(self._readlog_error, '')
        return 'mainlogdata'

class DummyPGroupConfig:
    def __init__(self, options, name='whatever', priority=999, pconfigs=None):
        self.options = options
        self.name = name
        self.priority = priority
        if pconfigs is None:
            pconfigs = []
        self.process_configs = pconfigs
        self.after_setuid_called = False
        self.pool_events = []
        self.buffer_size = 10

    def after_setuid(self):
        self.after_setuid_called = True

    def make_group(self):
        return DummyProcessGroup(self)

    def __repr__(self):
        return '<%s instance at %s named %s>' % (self.__class__, id(self),
                                                 self.name)

class DummyFCGIGroupConfig(DummyPGroupConfig):
    def __init__(self, options, name='whatever', priority=999, pconfigs=None, socket_config=DummySocketConfig(1)):
        DummyPGroupConfig.__init__(self, options, name, priority, pconfigs)
        self.socket_config = socket_config

class DummyProcessGroup(object):
    def __init__(self, config):
        self.config = config
        self.transitioned = False
        self.all_stopped = False
        self.dispatchers = {}
        self.unstopped_processes = []

    def transition(self):
        self.transitioned = True

    def stop_all(self):
        self.all_stopped = True

    def get_unstopped_processes(self):
        return self.unstopped_processes

    def get_dispatchers(self):
        return self.dispatchers

    def __lt__(self, other):
        return self.config.priority < other.config.priority

    def __eq__(self, other):
        return self.config.priority == other.config.priority

DummyProcessGroup = total_ordering(DummyProcessGroup)

class DummyFCGIProcessGroup(DummyProcessGroup):

    def __init__(self, config):
        DummyProcessGroup.__init__(self, config)
        self.socket_manager = DummySocketManager(config.socket_config)

class PopulatedDummySupervisor(DummySupervisor):
    def __init__(self, options, group_name, *pconfigs):
        DummySupervisor.__init__(self, options)
        self.process_groups = {}
        processes = {}
        self.group_name = group_name
        gconfig = DummyPGroupConfig(options, group_name, pconfigs=pconfigs)
        pgroup = DummyProcessGroup(gconfig)
        self.process_groups[group_name] = pgroup
        for pconfig in pconfigs:
            process = DummyProcess(pconfig)
            processes[pconfig.name] = process
        pgroup.processes = processes

    def set_procattr(self, process_name, attr_name, val, group_name=None):
        if group_name is None:
            group_name = self.group_name
        process = self.process_groups[group_name].processes[process_name]
        setattr(process, attr_name, val)

class DummyDispatcher:
    write_event_handled = False
    read_event_handled = False
    error_handled = False
    logs_reopened = False
    logs_removed = False
    closed = False
    flush_error = None
    flushed = False
    def __init__(self, readable=False, writable=False, error=False):
        self._readable = readable
        self._writable = writable
        self._error = error
        self.input_buffer = ''
        if readable:
            # only readable dispatchers should have these methods
            def reopenlogs():
                self.logs_reopened = True
            self.reopenlogs = reopenlogs
            def removelogs():
                self.logs_removed = True
            self.removelogs = removelogs

    def readable(self):
        return self._readable
    def writable(self):
        return self._writable
    def handle_write_event(self):
        if self._error:
            raise self._error
        self.write_event_handled = True
    def handle_read_event(self):
        if self._error:
            raise self._error
        self.read_event_handled = True
    def handle_error(self):
        self.error_handled = True
    def close(self):
        self.closed = True
    def flush(self):
        if self.flush_error:
            raise OSError(self.flush_error)
        self.flushed = True

class DummyStream:
    def __init__(self, error=None, fileno=20):
        self.error = error
        self.closed = False
        self.flushed = False
        self.written = ''
        self._fileno = fileno
    def close(self):
        if self.error:
            raise self.error
        self.closed = True
    def flush(self):
        if self.error:
            raise self.error
        self.flushed = True
    def write(self, msg):
        if self.error:
            error = self.error
            self.error = None
            raise error
        self.written += as_string(msg)
    def seek(self, num, whence=0):
        pass
    def tell(self):
        return len(self.written)
    def fileno(self):
        return self._fileno

class DummyEvent:
    def __init__(self, serial='abc'):
        if serial is not None:
            self.serial = serial

    def __str__(self):
        return 'dummy event'

def dummy_handler(event, result):
    pass

def rejecting_handler(event, result):
    from supervisor.dispatchers import RejectEvent
    raise RejectEvent(result)

def exception_handler(event, result):
    raise ValueError(result)

def lstrip(s):
    strings = [x.strip() for x in s.split('\n')]
    return '\n'.join(strings)

########NEW FILE########
__FILENAME__ = fakeos
from os import *
from os import _exit
import os

class FakeOS:
    def __init__(self):
        self.orig_uid = os.getuid()
        self.orig_gid = os.getgid()

    def setgroups(*args):
        return

    def getuid():
        return 0

    def setuid(arg):
        self.uid = arg
        self.setuid_called = 1

    def setgid(arg):
        self.gid = arg
        self.setgid_called = 1

    def clear():
        self.uid = orig_uid
        self.gid = orig_gid
        self.setuid_called = 0
        self.setgid_called = 0

fake = FakeOS()

setgroups = fake.setgroups
getuid = fake.getuid
setuid = fake.setuid
setgid = fake.setgid
clear = fake.clear

########NEW FILE########
__FILENAME__ = spew
#!<<PYTHON>>
import time

counter = 0

while 1:
   time.sleep(0.01)
   print("more spewage %s" % counter)
   counter += 1
   

########NEW FILE########
__FILENAME__ = unkillable_spew
#!<<PYTHON>>
import time
import signal
signal.signal(signal.SIGTERM, signal.SIG_IGN)

counter = 0

while 1:
   time.sleep(0.01)
   print("more spewage %s" % counter)
   counter += 1
   

########NEW FILE########
__FILENAME__ = test_childutils
import sys
import time
import unittest
from supervisor.compat import StringIO

class ChildUtilsTests(unittest.TestCase):
    def test_getRPCInterface(self):
        from supervisor.childutils import getRPCInterface
        rpc = getRPCInterface({'SUPERVISOR_SERVER_URL':'http://localhost:9001'})
        # we can't really test this thing; its a magic object
        self.assertTrue(rpc is not None)

    def test_getRPCTransport_no_uname_pass(self):
        from supervisor.childutils import getRPCTransport
        t = getRPCTransport({'SUPERVISOR_SERVER_URL':'http://localhost:9001'})
        self.assertEqual(t.username, '')
        self.assertEqual(t.password, '')
        self.assertEqual(t.serverurl, 'http://localhost:9001')

    def test_getRPCTransport_with_uname_pass(self):
        from supervisor.childutils import getRPCTransport
        env = {'SUPERVISOR_SERVER_URL':'http://localhost:9001',
               'SUPERVISOR_USERNAME':'chrism',
               'SUPERVISOR_PASSWORD':'abc123'}
        t = getRPCTransport(env)
        self.assertEqual(t.username, 'chrism')
        self.assertEqual(t.password, 'abc123')
        self.assertEqual(t.serverurl, 'http://localhost:9001')

    def test_get_headers(self):
        from supervisor.childutils import get_headers
        line = 'a:1 b:2'
        result = get_headers(line)
        self.assertEqual(result, {'a':'1', 'b':'2'})

    def test_eventdata(self):
        from supervisor.childutils import eventdata
        payload = 'a:1 b:2\nthedata\n'
        headers, data = eventdata(payload)
        self.assertEqual(headers, {'a':'1', 'b':'2'})
        self.assertEqual(data, 'thedata\n')

    def test_get_asctime(self):
        from supervisor.childutils import get_asctime
        timestamp = time.mktime((2009, 1, 18, 22, 14, 7, 0, 0, -1))
        result = get_asctime(timestamp)
        self.assertEqual(result, '2009-01-18 22:14:07,000')

class TestProcessCommunicationsProtocol(unittest.TestCase):
    def test_send(self):
        from supervisor.childutils import pcomm
        stdout = StringIO()
        pcomm.send('hello', stdout)
        from supervisor.events import ProcessCommunicationEvent
        begin = ProcessCommunicationEvent.BEGIN_TOKEN
        end = ProcessCommunicationEvent.END_TOKEN
        self.assertEqual(stdout.getvalue(), '%s%s%s' % (begin, 'hello', end))

    def test_stdout(self):
        from supervisor.childutils import pcomm
        old = sys.stdout
        try:
            io = sys.stdout = StringIO()
            pcomm.stdout('hello')
            from supervisor.events import ProcessCommunicationEvent
            begin = ProcessCommunicationEvent.BEGIN_TOKEN
            end = ProcessCommunicationEvent.END_TOKEN
            self.assertEqual(io.getvalue(), '%s%s%s' % (begin, 'hello', end))
        finally:
            sys.stdout = old

    def test_stderr(self):
        from supervisor.childutils import pcomm
        old = sys.stderr
        try:
            io = sys.stderr = StringIO()
            pcomm.stderr('hello')
            from supervisor.events import ProcessCommunicationEvent
            begin = ProcessCommunicationEvent.BEGIN_TOKEN
            end = ProcessCommunicationEvent.END_TOKEN
            self.assertEqual(io.getvalue(), '%s%s%s' % (begin, 'hello', end))
        finally:
            sys.stderr = old

class TestEventListenerProtocol(unittest.TestCase):
    def test_wait(self):
        from supervisor.childutils import listener
        class Dummy:
            def readline(self):
                return 'len:5'
            def read(self, *ignored):
                return 'hello'
        stdin = Dummy()
        stdout = StringIO()
        headers, payload = listener.wait(stdin, stdout)
        self.assertEqual(headers, {'len':'5'})
        self.assertEqual(payload, 'hello')
        self.assertEqual(stdout.getvalue(), 'READY\n')

    def test_token(self):
        from supervisor.childutils import listener
        from supervisor.dispatchers import PEventListenerDispatcher
        token = PEventListenerDispatcher.READY_FOR_EVENTS_TOKEN
        stdout = StringIO()
        listener.ready(stdout)
        self.assertEqual(stdout.getvalue(), token)

    def test_ok(self):
        from supervisor.childutils import listener
        from supervisor.dispatchers import PEventListenerDispatcher
        begin = PEventListenerDispatcher.RESULT_TOKEN_START
        stdout = StringIO()
        listener.ok(stdout)
        self.assertEqual(stdout.getvalue(), begin + '2\nOK')

    def test_fail(self):
        from supervisor.childutils import listener
        from supervisor.dispatchers import PEventListenerDispatcher
        begin = PEventListenerDispatcher.RESULT_TOKEN_START
        stdout = StringIO()
        listener.fail(stdout)
        self.assertEqual(stdout.getvalue(), begin + '4\nFAIL')

    def test_send(self):
        from supervisor.childutils import listener
        from supervisor.dispatchers import PEventListenerDispatcher
        begin = PEventListenerDispatcher.RESULT_TOKEN_START
        stdout = StringIO()
        msg = 'the body data ya fool\n'
        listener.send(msg, stdout)
        expected = '%s%s\n%s' % (begin, len(msg), msg)
        self.assertEqual(stdout.getvalue(), expected)


def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_confecho
"""Test suite for supervisor.confecho"""

import sys
import unittest
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO
from supervisor import confecho

class TopLevelFunctionTests(unittest.TestCase):
    def test_main_writes_data_out_that_looks_like_a_config_file(self):
        sio = StringIO()
        confecho.main(out=sio)

        output = sio.getvalue()
        self.assertTrue("[supervisord]" in output)


def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_datatypes
"""Test suite for supervisor.datatypes"""

import os
import signal
import tempfile
import unittest

from supervisor.tests.base import Mock, patch, sentinel
from supervisor.compat import maxint

from supervisor import datatypes
import supervisor.medusa.text_socket as socket

class ProcessOrGroupName(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.process_or_group_name(arg)

    def test_strips_surrounding_whitespace(self):
        name = " foo\t"
        self.assertEqual(self._callFUT(name), "foo")

    def test_disallows_inner_spaces(self):
        name = "foo bar"
        self.assertRaises(ValueError, self._callFUT, name)

    def test_disallows_colons(self):
        name = "foo:bar"
        self.assertRaises(ValueError, self._callFUT, name)

class IntegerTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.integer(arg)

    def test_converts_numeric(self):
        self.assertEqual(self._callFUT('1'), 1)

    def test_converts_numeric_overflowing_int(self):
        self.assertEqual(self._callFUT(str(maxint+1)), maxint+1)

    def test_raises_for_non_numeric(self):
        self.assertRaises(ValueError, self._callFUT, 'abc')

class BooleanTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.boolean(arg)

    def test_returns_true_for_truthy_values(self):
        for s in datatypes.TRUTHY_STRINGS:
            self.assertEqual(self._callFUT(s), True)

    def test_returns_true_for_upper_truthy_values(self):
        for s in map(str.upper, datatypes.TRUTHY_STRINGS):
            self.assertEqual(self._callFUT(s), True)

    def test_returns_false_for_falsy_values(self):
        for s in datatypes.FALSY_STRINGS:
            self.assertEqual(self._callFUT(s), False)

    def test_returns_false_for_upper_falsy_values(self):
        for s in map(str.upper, datatypes.FALSY_STRINGS):
            self.assertEqual(self._callFUT(s), False)

    def test_braises_value_error_for_bad_value(self):
        self.assertRaises(ValueError,
                          self._callFUT, 'not-a-value')

class ListOfStringsTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.list_of_strings(arg)

    def test_returns_empty_list_for_empty_string(self):
        self.assertEqual(self._callFUT(''), [])

    def test_returns_list_of_strings_by_comma_split(self):
        self.assertEqual(self._callFUT('foo,bar'), ['foo', 'bar'])

    def test_returns_strings_with_whitespace_stripped(self):
        self.assertEqual(self._callFUT(' foo , bar '), ['foo', 'bar'])

    def test_raises_value_error_when_comma_split_fails(self):
        self.assertRaises(ValueError,
                          self._callFUT, 42)

class ListOfIntsTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.list_of_ints(arg)

    def test_returns_empty_list_for_empty_string(self):
        self.assertEqual(self._callFUT(''), [])

    def test_returns_list_of_ints_by_comma_split(self):
        self.assertEqual(self._callFUT('1,42'), [1,42])

    def test_returns_ints_even_if_whitespace_in_string(self):
        self.assertEqual(self._callFUT(' 1 , 42 '), [1,42])

    def test_raises_value_error_when_comma_split_fails(self):
        self.assertRaises(ValueError,
                          self._callFUT, 42)

    def test_raises_value_error_when_one_value_is_bad(self):
        self.assertRaises(ValueError,
                          self._callFUT, '1, bad, 42')

class ListOfExitcodesTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.list_of_exitcodes(arg)

    def test_returns_list_of_ints_from_csv(self):
        self.assertEqual(self._callFUT('1,2,3'), [1,2,3])

    def test_returns_list_of_ints_from_one(self):
        self.assertEqual(self._callFUT('1'), [1])

    def test_raises_for_invalid_exitcode_values(self):
        self.assertRaises(ValueError, self._callFUT, 'a,b,c')
        self.assertRaises(ValueError, self._callFUT, '1024')
        self.assertRaises(ValueError, self._callFUT, '-1,1')

class DictOfKeyValuePairsTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.dict_of_key_value_pairs(arg)

    def test_returns_empty_dict_for_empty_str(self):
        actual = self._callFUT('')
        self.assertEqual({}, actual)

    def test_returns_dict_from_single_pair_str(self):
        actual = self._callFUT('foo=bar')
        expected = {'foo': 'bar'}
        self.assertEqual(actual, expected)

    def test_returns_dict_from_multi_pair_str(self):
        actual = self._callFUT('foo=bar,baz=qux')
        expected = {'foo': 'bar', 'baz': 'qux'}
        self.assertEqual(actual, expected)

    def test_returns_dict_even_if_whitespace(self):
        actual = self._callFUT(' foo = bar , baz = qux ')
        expected = {'foo': 'bar', 'baz': 'qux'}
        self.assertEqual(actual, expected)

    def test_returns_dict_even_if_newlines(self):
        actual = self._callFUT('foo\n=\nbar\n,\nbaz\n=\nqux')
        expected = {'foo': 'bar', 'baz': 'qux'}
        self.assertEqual(actual, expected)

    def test_handles_commas_inside_apostrophes(self):
        actual = self._callFUT("foo='bar,baz',baz='q,ux'")
        expected = {'foo': 'bar,baz', 'baz': 'q,ux'}
        self.assertEqual(actual, expected)

    def test_handles_commas_inside_quotes(self):
        actual = self._callFUT('foo="bar,baz",baz="q,ux"')
        expected = {'foo': 'bar,baz', 'baz': 'q,ux'}
        self.assertEqual(actual, expected)

    def test_handles_newlines_inside_quotes(self):
        actual = datatypes.dict_of_key_value_pairs('foo="a\nb\nc"')
        expected = {'foo': 'a\nb\nc'}
        self.assertEqual(actual, expected)

    def test_handles_quotes_inside_quotes(self):
        actual = datatypes.dict_of_key_value_pairs('foo="\'\\""')
        expected = {'foo': '\'"'}
        self.assertEqual(actual, expected)

    def test_handles_empty_inside_quotes(self):
        actual = datatypes.dict_of_key_value_pairs('foo=""')
        expected = {'foo': ''}
        self.assertEqual(actual, expected)

    def test_handles_unquoted_non_alphanum(self):
        actual = self._callFUT(
            'HOME=/home/auser,FOO=/.foo+(1.2)-_/,'
            'SUPERVISOR_SERVER_URL=http://127.0.0.1:9001')
        expected = {'HOME': '/home/auser', 'FOO': '/.foo+(1.2)-_/',
                    'SUPERVISOR_SERVER_URL': 'http://127.0.0.1:9001'}
        self.assertEqual(actual, expected)

    def test_allows_trailing_comma(self):
        actual = self._callFUT('foo=bar,')
        expected = {'foo': 'bar'}
        self.assertEqual(actual, expected)

    def test_raises_value_error_on_too_short(self):
        self.assertRaises(ValueError,
                          self._callFUT, 'foo')
        self.assertRaises(ValueError,
                          self._callFUT, 'foo=')
        self.assertRaises(ValueError,
                          self._callFUT, 'foo=bar,baz')
        self.assertRaises(ValueError,
                          self._callFUT, 'foo=bar,baz=')

    def test_raises_when_comma_is_missing(self):
        kvp = 'KEY1=no-comma KEY2=ends-with-comma,'
        self.assertRaises(ValueError,
                          self._callFUT, kvp)

class LogfileNameTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.logfile_name(arg)

    def test_returns_none_for_none_values(self):
        for thing in datatypes.LOGFILE_NONES:
            actual = self._callFUT(thing)
            self.assertEqual(actual, None)

    def test_returns_none_for_uppered_none_values(self):
        for thing in datatypes.LOGFILE_NONES:
            if hasattr(thing, 'upper'):
                thing = thing.upper()
            actual = self._callFUT(thing)
            self.assertEqual(actual, None)

    def test_returns_automatic_for_auto_values(self):
        for thing in datatypes.LOGFILE_AUTOS:
            actual = self._callFUT(thing)
            self.assertEqual(actual, datatypes.Automatic)

    def test_returns_automatic_for_uppered_auto_values(self):
        for thing in datatypes.LOGFILE_AUTOS:
            if hasattr(thing, 'upper'):
                thing = thing.upper()
            actual = self._callFUT(thing)
            self.assertEqual(actual, datatypes.Automatic)

    def test_returns_existing_dirpath_for_other_values(self):
        func = datatypes.existing_dirpath
        datatypes.existing_dirpath = lambda path: path
        try:
            path = '/path/to/logfile/With/Case/Preserved'
            actual = self._callFUT(path)
            self.assertEqual(actual, path)
        finally:
            datatypes.existing_dirpath = func

class RangeCheckedConversionTests(unittest.TestCase):
    def _getTargetClass(self):
        return datatypes.RangeCheckedConversion

    def _makeOne(self, conversion, rmin=None, rmax=None):
        return self._getTargetClass()(conversion, rmin, rmax)

    def test_below_lower_bound(self):
        conversion = self._makeOne(lambda *arg: -1, 0)
        self.assertRaises(ValueError, conversion, None)

    def test_above_upper_lower_bound(self):
        conversion = self._makeOne(lambda *arg: 1, 0, 0)
        self.assertRaises(ValueError, conversion, None)

    def test_passes(self):
        conversion = self._makeOne(lambda *arg: 0, 0, 0)
        self.assertEqual(conversion(0), 0)


class NameToGidTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.name_to_gid(arg)

    @patch("grp.getgrnam", Mock(return_value=[0,0,42]))
    def test_gets_gid_from_group_name(self):
        gid = self._callFUT("foo")
        self.assertEqual(gid, 42)

    @patch("grp.getgrgid", Mock(return_value=[0,0,42]))
    def test_gets_gid_from_group_id(self):
        gid = self._callFUT("42")
        self.assertEqual(gid, 42)

    @patch("grp.getgrnam", Mock(side_effect=KeyError("bad group name")))
    def test_raises_for_bad_group_name(self):
        self.assertRaises(ValueError, self._callFUT, "foo")

    @patch("grp.getgrgid", Mock(side_effect=KeyError("bad group id")))
    def test_raises_for_bad_group_id(self):
        self.assertRaises(ValueError, self._callFUT, "42")

class NameToUidTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.name_to_uid(arg)

    @patch("pwd.getpwnam", Mock(return_value=[0,0,42]))
    def test_gets_uid_from_username(self):
        uid = self._callFUT("foo")
        self.assertEqual(uid, 42)

    @patch("pwd.getpwuid", Mock(return_value=[0,0,42]))
    def test_gets_uid_from_user_id(self):
        uid = self._callFUT("42")
        self.assertEqual(uid, 42)

    @patch("pwd.getpwnam", Mock(side_effect=KeyError("bad username")))
    def test_raises_for_bad_username(self):
        self.assertRaises(ValueError, self._callFUT, "foo")

    @patch("pwd.getpwuid", Mock(side_effect=KeyError("bad user id")))
    def test_raises_for_bad_user_id(self):
        self.assertRaises(ValueError, self._callFUT, "42")

class OctalTypeTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.octal_type(arg)

    def test_success(self):
        self.assertEqual(self._callFUT('10'), 8)

    def test_raises_for_non_numeric(self):
        try:
            self._callFUT('bad')
            self.fail()
        except ValueError as e:
            expected = 'bad can not be converted to an octal type'
            self.assertEqual(e.args[0], expected)

    def test_raises_for_unconvertable_numeric(self):
        try:
            self._callFUT('1.2')
            self.fail()
        except ValueError as e:
            expected = '1.2 can not be converted to an octal type'
            self.assertEqual(e.args[0], expected)

class ExistingDirectoryTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.existing_directory(arg)

    def test_dir_exists(self):
        path = os.path.dirname(__file__)
        self.assertEqual(path, self._callFUT(path))

    def test_dir_does_not_exist(self):
        path = os.path.join(os.path.dirname(__file__), 'nonexistant')
        try:
            self._callFUT(path)
            self.fail()
        except ValueError as e:
            expected = "%s is not an existing directory" % path
            self.assertEqual(e.args[0], expected)

    def test_not_a_directory(self):
        path = __file__
        try:
            self._callFUT(path)
            self.fail()
        except ValueError as e:
            expected = "%s is not an existing directory" % path
            self.assertEqual(e.args[0], expected)

    def test_expands_home(self):
        home = os.path.expanduser('~')
        if os.path.exists(home):
            path = self._callFUT('~')
            self.assertEqual(home, path)

    def test_expands_here(self):
        datatypes.here = os.path.dirname(__file__)
        try:
            self.assertEqual(self._callFUT('%(here)s'), datatypes.here)
        finally:
            datatypes.here = None

class ExistingDirpathTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.existing_dirpath(arg)

    def test_returns_existing_dirpath(self):
        self.assertEqual(self._callFUT(__file__), __file__)

    def test_returns_dirpath_if_relative(self):
        self.assertEqual(self._callFUT('foo'), 'foo')

    def test_raises_if_dir_does_not_exist(self):
        path = os.path.join(os.path.dirname(__file__), 'nonexistant', 'foo')
        try:
            self._callFUT(path)
            self.fail()
        except ValueError as e:
            expected = ('The directory named as part of the path %s '
                        'does not exist.' % path)
            self.assertEqual(e.args[0], expected)

    def test_raises_if_exists_but_not_a_dir(self):
        path = os.path.join(os.path.dirname(__file__),
                            os.path.basename(__file__), 'foo')
        try:
            self._callFUT(path)
            self.fail()
        except ValueError as e:
            expected = ('The directory named as part of the path %s '
                        'does not exist.' % path)
            self.assertEqual(e.args[0], expected)

    def test_expands_home(self):
        home = os.path.expanduser('~')
        if os.path.exists(home):
            path = self._callFUT('~/foo')
            self.assertEqual(os.path.join(home, 'foo'), path)

    def test_expands_here(self):
        datatypes.here = os.path.dirname(__file__)
        try:
            expected = os.path.join(datatypes.here, 'foo')
            self.assertEqual(self._callFUT('%(here)s/foo'), expected)
        finally:
            datatypes.here = None

class LoggingLevelTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.logging_level(arg)

    def test_returns_level_from_name_case_insensitive(self):
        from supervisor.loggers import LevelsByName
        self.assertEqual(self._callFUT("wArN"), LevelsByName.WARN)

    def test_raises_for_bad_level_name(self):
        self.assertRaises(ValueError,
                          self._callFUT, "foo")

class UrlTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.url(arg)

    def test_accepts_urlparse_recognized_scheme_with_netloc(self):
        good_url = 'http://localhost:9001'
        self.assertEqual(self._callFUT(good_url), good_url)

    def test_rejects_urlparse_recognized_scheme_but_no_netloc(self):
        bad_url = 'http://'
        self.assertRaises(ValueError, self._callFUT, bad_url)

    def test_accepts_unix_scheme_with_path(self):
        good_url = "unix://somepath"
        self.assertEqual(good_url, self._callFUT(good_url))

    def test_rejects_unix_scheme_with_no_slashes_or_path(self):
        bad_url = "unix:"
        self.assertRaises(ValueError, self._callFUT, bad_url)

    def test_rejects_unix_scheme_with_slashes_but_no_path(self):
        bad_url = "unix://"
        self.assertRaises(ValueError, self._callFUT, bad_url)

class InetStreamSocketConfigTests(unittest.TestCase):
    def _getTargetClass(self):
        return datatypes.InetStreamSocketConfig

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_url(self):
        conf = self._makeOne('127.0.0.1', 8675)
        self.assertEqual(conf.url, 'tcp://127.0.0.1:8675')

    def test___str__(self):
        cfg = self._makeOne('localhost', 65531)
        self.assertEqual(str(cfg), 'tcp://localhost:65531')

    def test_repr(self):
        conf = self._makeOne('127.0.0.1', 8675)
        s = repr(conf)
        self.assertTrue('supervisor.datatypes.InetStreamSocketConfig' in s)
        self.assertTrue(s.endswith('for tcp://127.0.0.1:8675>'), s)

    def test_addr(self):
        conf = self._makeOne('127.0.0.1', 8675)
        addr = conf.addr()
        self.assertEqual(addr, ('127.0.0.1', 8675))

    def test_port_as_string(self):
        conf = self._makeOne('localhost', '5001')
        addr = conf.addr()
        self.assertEqual(addr, ('localhost', 5001))

    def test_create_and_bind(self):
        conf = self._makeOne('127.0.0.1', 8675)
        sock = conf.create_and_bind()
        reuse = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
        self.assertTrue(reuse)
        self.assertEqual(conf.addr(), sock.getsockname()) #verifies that bind was called
        sock.close()

    def test_same_urls_are_equal(self):
        conf1 = self._makeOne('localhost', 5001)
        conf2 = self._makeOne('localhost', 5001)
        self.assertTrue(conf1 == conf2)
        self.assertFalse(conf1 != conf2)

    def test_diff_urls_are_not_equal(self):
        conf1 = self._makeOne('localhost', 5001)
        conf2 = self._makeOne('localhost', 5002)
        self.assertTrue(conf1 != conf2)
        self.assertFalse(conf1 == conf2)

    def test_diff_objs_are_not_equal(self):
        conf1 = self._makeOne('localhost', 5001)
        conf2 = 'blah'
        self.assertTrue(conf1 != conf2)
        self.assertFalse(conf1 == conf2)

class UnixStreamSocketConfigTests(unittest.TestCase):
    def _getTargetClass(self):
        return datatypes.UnixStreamSocketConfig

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_url(self):
        conf = self._makeOne('/tmp/foo.sock')
        self.assertEqual(conf.url, 'unix:///tmp/foo.sock')

    def test___str__(self):
        cfg = self._makeOne('foo/bar')
        self.assertEqual(str(cfg), 'unix://foo/bar')

    def test_repr(self):
        conf = self._makeOne('/tmp/foo.sock')
        s = repr(conf)
        self.assertTrue('supervisor.datatypes.UnixStreamSocketConfig' in s)
        self.assertTrue(s.endswith('for unix:///tmp/foo.sock>'), s)

    def test_get_addr(self):
        conf = self._makeOne('/tmp/foo.sock')
        addr = conf.addr()
        self.assertEqual(addr, '/tmp/foo.sock')

    def test_create_and_bind(self):
        (tf_fd, tf_name) = tempfile.mkstemp()
        owner = (sentinel.uid, sentinel.gid)
        mode = sentinel.mode
        conf = self._makeOne(tf_name, owner=owner, mode=mode)

        # Patch os.chmod and os.chown functions with mocks
        # objects so that the test does not depend on
        # any specific system users or permissions
        chown_mock = Mock()
        chmod_mock = Mock()
        @patch('os.chown', chown_mock)
        @patch('os.chmod', chmod_mock)
        def call_create_and_bind(conf):
            return conf.create_and_bind()

        sock = call_create_and_bind(conf)
        self.assertTrue(os.path.exists(tf_name))
        # verifies that bind was called
        self.assertEqual(conf.addr(), sock.getsockname())
        sock.close()
        self.assertTrue(os.path.exists(tf_name))
        os.unlink(tf_name)
        # Verify that os.chown was called with correct args
        self.assertEqual(1, chown_mock.call_count)
        path_arg = chown_mock.call_args[0][0]
        uid_arg = chown_mock.call_args[0][1]
        gid_arg = chown_mock.call_args[0][2]
        self.assertEqual(tf_name, path_arg)
        self.assertEqual(owner[0], uid_arg)
        self.assertEqual(owner[1], gid_arg)
        # Verify that os.chmod was called with correct args
        self.assertEqual(1, chmod_mock.call_count)
        path_arg = chmod_mock.call_args[0][0]
        mode_arg = chmod_mock.call_args[0][1]
        self.assertEqual(tf_name, path_arg)
        self.assertEqual(mode, mode_arg)

    def test_create_and_bind_when_chown_fails(self):
        (tf_fd, tf_name) = tempfile.mkstemp()
        owner = (sentinel.uid, sentinel.gid)
        mode = sentinel.mode
        conf = self._makeOne(tf_name, owner=owner, mode=mode)

        @patch('os.chown', Mock(side_effect=OSError("msg")))
        @patch('os.chmod', Mock())
        def call_create_and_bind(conf):
            return conf.create_and_bind()

        try:
            call_create_and_bind(conf)
            self.fail()
        except ValueError as e:
            expected = "Could not change ownership of socket file: msg"
            self.assertEqual(e.args[0], expected)
            self.assertFalse(os.path.exists(tf_name))

    def test_create_and_bind_when_chmod_fails(self):
        (tf_fd, tf_name) = tempfile.mkstemp()
        owner = (sentinel.uid, sentinel.gid)
        mode = sentinel.mode
        conf = self._makeOne(tf_name, owner=owner, mode=mode)

        @patch('os.chown', Mock())
        @patch('os.chmod', Mock(side_effect=OSError("msg")))
        def call_create_and_bind(conf):
            return conf.create_and_bind()

        try:
            call_create_and_bind(conf)
            self.fail()
        except ValueError as e:
            expected = "Could not change permissions of socket file: msg"
            self.assertEqual(e.args[0], expected)
            self.assertFalse(os.path.exists(tf_name))

    def test_same_paths_are_equal(self):
        conf1 = self._makeOne('/tmp/foo.sock')
        conf2 = self._makeOne('/tmp/foo.sock')
        self.assertTrue(conf1 == conf2)
        self.assertFalse(conf1 != conf2)

    def test_diff_paths_are_not_equal(self):
        conf1 = self._makeOne('/tmp/foo.sock')
        conf2 = self._makeOne('/tmp/bar.sock')
        self.assertTrue(conf1 != conf2)
        self.assertFalse(conf1 == conf2)

    def test_diff_objs_are_not_equal(self):
        conf1 = self._makeOne('/tmp/foo.sock')
        conf2 = 'blah'
        self.assertTrue(conf1 != conf2)
        self.assertFalse(conf1 == conf2)

class InetAddressTests(unittest.TestCase):
    def _callFUT(self, s):
        return datatypes.inet_address(s)

    def test_no_port_number(self):
        self.assertRaises(ValueError, self._callFUT, 'a:')

    def test_bad_port_number(self):
        self.assertRaises(ValueError, self._callFUT, 'a')

    def test_default_host(self):
        host, port = self._callFUT('*:8080')
        self.assertEqual(host, '')
        self.assertEqual(port, 8080)

    def test_boring(self):
        host, port = self._callFUT('localhost:80')
        self.assertEqual(host, 'localhost')
        self.assertEqual(port, 80)

class SocketAddressTests(unittest.TestCase):
    def _getTargetClass(self):
        return datatypes.SocketAddress

    def _makeOne(self, s):
        return self._getTargetClass()(s)

    def test_unix_socket(self):
        addr = self._makeOne('/foo/bar')
        self.assertEqual(addr.family, socket.AF_UNIX)
        self.assertEqual(addr.address, '/foo/bar')

    def test_inet_socket(self):
        addr = self._makeOne('localhost:8080')
        self.assertEqual(addr.family, socket.AF_INET)
        self.assertEqual(addr.address, ('localhost', 8080))

class ColonSeparatedUserGroupTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.colon_separated_user_group(arg)

    def test_ok_username(self):
        self.assertEqual(self._callFUT('root')[0], 0)

    def test_missinguser_username(self):
        self.assertRaises(ValueError,
                          self._callFUT, 'godihopethisuserdoesntexist')

    def test_missinguser_username_and_groupname(self):
        self.assertRaises(ValueError,
                          self._callFUT, 'godihopethisuserdoesntexist:foo')

    def test_separated_user_group_returns_both(self):
        name_to_uid = Mock(return_value=12)
        name_to_gid = Mock(return_value=34)

        @patch("supervisor.datatypes.name_to_uid", name_to_uid)
        @patch("supervisor.datatypes.name_to_gid", name_to_gid)
        def colon_separated(value):
            return self._callFUT(value)

        uid, gid = colon_separated("foo:bar")
        name_to_uid.assert_called_with("foo")
        self.assertEqual(12, uid)
        name_to_gid.assert_called_with("bar")
        self.assertEqual(34, gid)

    def test_separated_user_group_returns_user_only(self):
        name_to_uid = Mock(return_value=42)

        @patch("supervisor.datatypes.name_to_uid", name_to_uid)
        def colon_separated(value):
            return self._callFUT(value)

        uid, gid = colon_separated("foo")
        name_to_uid.assert_called_with("foo")
        self.assertEqual(42, uid)
        self.assertEqual(-1, gid)

class SignalNumberTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.signal_number(arg)

    def test_converts_number(self):
        self.assertEqual(self._callFUT(signal.SIGTERM), signal.SIGTERM)

    def test_converts_name(self):
        self.assertEqual(self._callFUT(' term '), signal.SIGTERM)

    def test_converts_signame(self):
        self.assertEqual(self._callFUT('SIGTERM'), signal.SIGTERM)

    def test_raises_for_bad_number(self):
        try:
            self._callFUT('12345678')
            self.fail()
        except ValueError as e:
            expected = "value 12345678 is not a valid signal number"
            self.assertEqual(e.args[0], expected)

    def test_raises_for_bad_name(self):
        try:
            self._callFUT('BADSIG')
            self.fail()
        except ValueError as e:
            expected = "value BADSIG is not a valid signal name"
            self.assertEqual(e.args[0], expected)

class AutoRestartTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.auto_restart(arg)

    def test_converts_truthy(self):
        for s in datatypes.TRUTHY_STRINGS:
            result = self._callFUT(s)
            self.assertEqual(result, datatypes.RestartUnconditionally)

    def test_converts_falsy(self):
        for s in datatypes.FALSY_STRINGS:
            self.assertFalse(self._callFUT(s))

    def test_converts_unexpected(self):
        for s in ('unexpected', 'UNEXPECTED'):
            result = self._callFUT(s)
            self.assertEqual(result, datatypes.RestartWhenExitUnexpected)

    def test_raises_for_bad_value(self):
        try:
            self._callFUT('bad')
            self.fail()
        except ValueError as e:
            self.assertEqual(e.args[0], "invalid 'autorestart' value 'bad'")

class ProfileOptionsTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.profile_options(arg)

    def test_empty(self):
        sort_options, callers = self._callFUT('')
        self.assertEqual([], sort_options)
        self.assertFalse(callers)

    def test_without_callers(self):
        sort_options, callers = self._callFUT('CUMULATIVE,calls')
        self.assertEqual(['cumulative', 'calls'], sort_options)
        self.assertFalse(callers)

    def test_with_callers(self):
        sort_options, callers = self._callFUT('cumulative, callers')
        self.assertEqual(['cumulative'], sort_options)
        self.assertTrue(callers)

########NEW FILE########
__FILENAME__ = test_dispatchers
import unittest
import os
import sys

from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyLogger
from supervisor.tests.base import DummyEvent
from supervisor import read_file

class PDispatcherTests(unittest.TestCase):
    def setUp(self):
        from supervisor.events import clear
        clear()

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.dispatchers import PDispatcher
        return PDispatcher

    def _makeOne(self, process=None, channel='stdout', fd=0):
        return self._getTargetClass()(process, channel, fd)
    
    def test_readable(self):
        inst = self._makeOne()
        self.assertRaises(NotImplementedError, inst.readable)
        
    def test_writable(self):
        inst = self._makeOne()
        self.assertRaises(NotImplementedError, inst.writable)

    def test_flush(self):
        inst = self._makeOne()
        self.assertEqual(inst.flush(), None)
        
class POutputDispatcherTests(unittest.TestCase):
    def setUp(self):
        from supervisor.events import clear
        clear()

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.dispatchers import POutputDispatcher
        return POutputDispatcher

    def _makeOne(self, process, channel='stdout'):
        from supervisor import events
        events = {'stdout': events.ProcessCommunicationStdoutEvent,
                  'stderr': events.ProcessCommunicationStderrEvent}
        # dispatcher derives its channel from event class
        return self._getTargetClass()(process, events[channel], 0)

    def test_writable(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.writable(), False)

    def test_readable_open(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.closed = False
        self.assertEqual(dispatcher.readable(), True)

    def test_readable_closed(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.closed = True
        self.assertEqual(dispatcher.readable(), False)

    def test_handle_write_event(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertRaises(NotImplementedError, dispatcher.handle_write_event)

    def test_handle_read_event(self):
        options = DummyOptions()
        options.readfd_result = 'abc'
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_capture_maxbytes=100)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(dispatcher.output_buffer, 'abc')

    def test_handle_read_event_no_data_closes(self):
        options = DummyOptions()
        options.readfd_result = ''
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_capture_maxbytes=100)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertFalse(dispatcher.closed)
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(dispatcher.output_buffer, '')
        self.assertTrue(dispatcher.closed)

    def test_handle_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        try:
            raise ValueError('foo')
        except:
            dispatcher.handle_error()
        result = options.logger.data[0]
        self.assertTrue(result.startswith(
            'uncaptured python exception, closing channel'),result)

    def test_toggle_capturemode_sends_event(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo',
                              stdout_capture_maxbytes=500)
        process = DummyProcess(config)
        process.pid = 4000
        dispatcher = self._makeOne(process)
        dispatcher.capturemode = True
        dispatcher.capturelog.getvalue = lambda: 'hallooo'
        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_COMMUNICATION, doit)
        dispatcher.toggle_capturemode()
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.process, process)
        self.assertEqual(event.pid, 4000)
        self.assertEqual(event.data, 'hallooo')

    def test_removelogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.removelogs()
        self.assertEqual(dispatcher.mainlog.handlers[0].reopened, True)
        self.assertEqual(dispatcher.mainlog.handlers[0].removed, True)
        self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)
        self.assertEqual(dispatcher.childlog.handlers[0].removed, True)

    def test_reopenlogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.reopenlogs()
        self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)
        self.assertEqual(dispatcher.mainlog.handlers[0].reopened, True)

    def test_record_output_log_non_capturemode(self):
        # stdout/stderr goes to the process log and the main log,
        # in non-capturemode, the data length doesn't matter
        options = DummyOptions()
        from supervisor import loggers
        options.loglevel = loggers.LevelsByName.TRAC
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.output_buffer = 'a'
        dispatcher.record_output()
        self.assertEqual(dispatcher.childlog.data, ['a'])
        self.assertEqual(options.logger.data[0],
             "'process1' stdout output:\na")
        self.assertEqual(dispatcher.output_buffer, '')

    def test_record_output_emits_stdout_event_when_enabled(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_events_enabled=True)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process, 'stdout')
        dispatcher.output_buffer = 'hello from stdout'

        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_LOG_STDOUT, doit)
        dispatcher.record_output()

        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.process, process)
        self.assertEqual(event.data, 'hello from stdout')

    def test_record_output_does_not_emit_stdout_event_when_disabled(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_events_enabled=False)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process, 'stdout')
        dispatcher.output_buffer = 'hello from stdout'

        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_LOG_STDOUT, doit)
        dispatcher.record_output()

        self.assertEqual(len(L), 0)

    def test_record_output_emits_stderr_event_when_enabled(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stderr_events_enabled=True)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process, 'stderr')
        dispatcher.output_buffer = 'hello from stderr'

        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_LOG_STDERR, doit)
        dispatcher.record_output()

        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.process, process)
        self.assertEqual(event.data, 'hello from stderr')

    def test_record_output_does_not_emit_stderr_event_when_disabled(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stderr_events_enabled=False)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process, 'stderr')
        dispatcher.output_buffer = 'hello from stderr'

        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_LOG_STDERR, doit)
        dispatcher.record_output()

        self.assertEqual(len(L), 0)

    def test_record_output_capturemode_string_longer_than_token(self):
        # stdout/stderr goes to the process log and the main log,
        # in capturemode, the length of the data needs to be longer
        # than the capture token to make it out.
        options = DummyOptions()
        from supervisor import loggers
        options.loglevel = loggers.LevelsByName.TRAC
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo',
                              stdout_capture_maxbytes=100)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.output_buffer = 'stdout string longer than a token'
        dispatcher.record_output()
        self.assertEqual(dispatcher.childlog.data,
                         ['stdout string longer than a token'])
        self.assertEqual(options.logger.data[0],
             "'process1' stdout output:\nstdout string longer than a token")

    def test_record_output_capturemode_string_not_longer_than_token(self):
        # stdout/stderr goes to the process log and the main log,
        # in capturemode, the length of the data needs to be longer
        # than the capture token to make it out.
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo',
                              stdout_capture_maxbytes=100)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.output_buffer = 'a'
        dispatcher.record_output()
        self.assertEqual(dispatcher.childlog.data, [])
        self.assertEqual(dispatcher.output_buffer, 'a')

    def test_stdout_capturemode_single_buffer(self):
        # mike reported that comm events that took place within a single
        # output buffer were broken 8/20/2007
        from supervisor.events import ProcessCommunicationEvent
        from supervisor.events import subscribe
        events = []
        def doit(event):
            events.append(event)
        subscribe(ProcessCommunicationEvent, doit)
        BEGIN_TOKEN = ProcessCommunicationEvent.BEGIN_TOKEN
        END_TOKEN = ProcessCommunicationEvent.END_TOKEN
        data = BEGIN_TOKEN + 'hello' + END_TOKEN
        options = DummyOptions()
        from supervisor.loggers import getLogger
        options.getLogger = getLogger # actually use real logger
        logfile = '/tmp/log'
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile=logfile,
                              stdout_capture_maxbytes=1000)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)

        try:
            dispatcher.output_buffer = data
            dispatcher.record_output()
            self.assertEqual(read_file(logfile), '')
            self.assertEqual(dispatcher.output_buffer, '')
            self.assertEqual(len(events), 1)

            event = events[0]
            from supervisor.events import ProcessCommunicationStdoutEvent
            self.assertEqual(event.__class__, ProcessCommunicationStdoutEvent)
            self.assertEqual(event.process, process)
            self.assertEqual(event.channel, 'stdout')
            self.assertEqual(event.data, 'hello')

        finally:
            try:
                dispatcher.capturelog.close()
                dispatcher.childlog.close()
                os.remove(logfile)
            except (OSError, IOError):
                pass

    def test_stdout_capturemode_multiple_buffers(self):
        from supervisor.events import ProcessCommunicationEvent
        from supervisor.events import subscribe
        events = []
        def doit(event):
            events.append(event)
        subscribe(ProcessCommunicationEvent, doit)
        import string
        # ascii_letters for python 3
        letters = getattr(string, "letters", string.ascii_letters)
        digits = string.digits * 4
        BEGIN_TOKEN = ProcessCommunicationEvent.BEGIN_TOKEN
        END_TOKEN = ProcessCommunicationEvent.END_TOKEN
        data = (letters +  BEGIN_TOKEN + digits + END_TOKEN + letters)

        # boundaries that split tokens
        broken = data.split(':')
        first = broken[0] + ':'
        second = broken[1] + ':'
        third = broken[2]

        options = DummyOptions()
        from supervisor.loggers import getLogger
        options.getLogger = getLogger # actually use real logger
        logfile = '/tmp/log'
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile=logfile,
                              stdout_capture_maxbytes=10000)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        try:
            dispatcher.output_buffer = first
            dispatcher.record_output()
            [ x.flush() for x in dispatcher.childlog.handlers]
            self.assertEqual(read_file(logfile), letters)
            self.assertEqual(dispatcher.output_buffer, first[len(letters):])
            self.assertEqual(len(events), 0)

            dispatcher.output_buffer += second
            dispatcher.record_output()
            self.assertEqual(len(events), 0)
            [ x.flush() for x in dispatcher.childlog.handlers]
            self.assertEqual(read_file(logfile), letters)
            self.assertEqual(dispatcher.output_buffer, first[len(letters):])
            self.assertEqual(len(events), 0)

            dispatcher.output_buffer += third
            dispatcher.record_output()
            [ x.flush() for x in dispatcher.childlog.handlers]
            self.assertEqual(read_file(logfile), letters *2)
            self.assertEqual(len(events), 1)
            event = events[0]
            from supervisor.events import ProcessCommunicationStdoutEvent
            self.assertEqual(event.__class__, ProcessCommunicationStdoutEvent)
            self.assertEqual(event.process, process)
            self.assertEqual(event.channel, 'stdout')
            self.assertEqual(event.data, digits)

        finally:
            try:
                dispatcher.capturelog.close()
                dispatcher.childlog.close()
                os.remove(logfile)
            except (OSError, IOError):
                pass

    def test_strip_ansi(self):
        options = DummyOptions()
        options.strip_ansi = True
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        ansi = '\x1b[34mHello world... this is longer than a token!\x1b[0m'
        noansi = 'Hello world... this is longer than a token!'

        dispatcher.output_buffer = ansi
        dispatcher.record_output()
        self.assertEqual(len(dispatcher.childlog.data), 1)
        self.assertEqual(dispatcher.childlog.data[0], noansi)

        options.strip_ansi = False

        dispatcher.output_buffer = ansi
        dispatcher.record_output()
        self.assertEqual(len(dispatcher.childlog.data), 2)
        self.assertEqual(dispatcher.childlog.data[1], ansi)

    def test_ctor_nologfiles(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.capturelog, None)
        self.assertEqual(dispatcher.mainlog, None)
        self.assertEqual(dispatcher.childlog, None)

    def test_ctor_logfile_only(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.capturelog, None)
        self.assertEqual(dispatcher.mainlog.__class__, DummyLogger)
        self.assertEqual(dispatcher.childlog, dispatcher.mainlog)

    def test_ctor_capturelog_only(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_capture_maxbytes=300)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.capturelog.__class__,DummyLogger)
        self.assertEqual(dispatcher.mainlog, None)
        self.assertEqual(dispatcher.childlog, None)

    def test_ctor_nologs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.capturelog, None)
        self.assertEqual(dispatcher.mainlog, None)
        self.assertEqual(dispatcher.childlog, None)

    def test_repr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        drepr = repr(dispatcher)
        self.assertTrue('POutputDispatcher' in drepr)
        self.assertNotEqual(
            drepr.find('supervisor.tests.base.DummyProcess'),
            -1)
        self.assertTrue(drepr.endswith('(stdout)>'), drepr)

    def test_close(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.close()
        self.assertEqual(dispatcher.closed, True)
        dispatcher.close() # make sure we don't error if we try to close twice
        self.assertEqual(dispatcher.closed, True)


    def test_syslog_logfile_deprecated(self):
        import warnings
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        config.stdout_logfile = 'syslog'
        process = DummyProcess(config)
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            self._makeOne(process)
            self.assertEqual(len(w), 1)
        
        


class PInputDispatcherTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.dispatchers import PInputDispatcher
        return PInputDispatcher

    def _makeOne(self, process):
        channel = 'stdin'
        return self._getTargetClass()(process, channel, 0)

    def test_writable_open_nodata(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'a'
        dispatcher.closed = False
        self.assertEqual(dispatcher.writable(), True)

    def test_writable_open_withdata(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = ''
        dispatcher.closed = False
        self.assertEqual(dispatcher.writable(), False)

    def test_writable_closed_nodata(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'a'
        dispatcher.closed = True
        self.assertEqual(dispatcher.writable(), False)

    def test_writable_closed_withdata(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = ''
        dispatcher.closed = True
        self.assertEqual(dispatcher.writable(), False)

    def test_readable(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.readable(), False)

    def test_handle_write_event(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'halloooo'
        self.assertEqual(dispatcher.handle_write_event(), None)
        self.assertEqual(options.written[0], 'halloooo')

    def test_handle_write_event_nodata(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.input_buffer, '')
        dispatcher.handle_write_event
        self.assertEqual(dispatcher.input_buffer, '')
        self.assertEqual(options.written, {})

    def test_handle_write_event_epipe_raised(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'halloooo'
        import errno
        options.write_error = errno.EPIPE
        dispatcher.handle_write_event()
        self.assertEqual(dispatcher.input_buffer, '')
        self.assertTrue(options.logger.data[0].startswith(
            'fd 0 closed, stopped monitoring'))
        self.assertTrue(options.logger.data[0].endswith('(stdin)>'))

    def test_handle_write_event_uncaught_raised(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'halloooo'
        import errno
        options.write_error = errno.EBADF
        self.assertRaises(OSError, dispatcher.handle_write_event)

    def test_handle_write_event_over_os_limit(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        options.write_accept = 1
        dispatcher.input_buffer = 'a' * 50
        dispatcher.handle_write_event()
        self.assertEqual(len(dispatcher.input_buffer), 49)
        self.assertEqual(options.written[0], 'a')

    def test_handle_read_event(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        self.assertRaises(NotImplementedError, dispatcher.handle_read_event)

    def test_handle_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        try:
            raise ValueError('foo')
        except:
            dispatcher.handle_error()
        result = options.logger.data[0]
        self.assertTrue(result.startswith(
            'uncaptured python exception, closing channel'),result)

    def test_repr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        drepr = repr(dispatcher)
        self.assertTrue('PInputDispatcher' in drepr)
        self.assertNotEqual(
            drepr.find('supervisor.tests.base.DummyProcess'),
            -1)
        self.assertTrue(drepr.endswith('(stdin)>'), drepr)

    def test_close(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.close()
        self.assertEqual(dispatcher.closed, True)
        dispatcher.close() # make sure we don't error if we try to close twice
        self.assertEqual(dispatcher.closed, True)

class PEventListenerDispatcherTests(unittest.TestCase):
    def setUp(self):
        from supervisor.events import clear
        clear()

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.dispatchers import PEventListenerDispatcher
        return PEventListenerDispatcher

    def _makeOne(self, process):
        channel = 'stdout'
        return self._getTargetClass()(process, channel, 0)

    def test_writable(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.writable(), False)

    def test_readable_open(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.closed = False
        self.assertEqual(dispatcher.readable(), True)

    def test_readable_closed(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.closed = True
        self.assertEqual(dispatcher.readable(), False)

    def test_handle_write_event(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertRaises(NotImplementedError, dispatcher.handle_write_event)

    def test_handle_read_event_calls_handle_listener_state_change(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher = self._makeOne(process)
        options.readfd_result = dispatcher.READY_FOR_EVENTS_TOKEN
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(process.listener_state, EventListenerStates.READY)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(len(dispatcher.childlog.data), 1)
        self.assertEqual(dispatcher.childlog.data[0],
                         dispatcher.READY_FOR_EVENTS_TOKEN)

    def test_handle_read_event_nodata(self):
        options = DummyOptions()
        options.readfd_result = ''
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        from supervisor.dispatchers import EventListenerStates
        self.assertEqual(dispatcher.process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)

    def test_handle_read_event_logging_nologs(self):
        options = DummyOptions()
        options.readfd_result = 'supercalifragilisticexpialidocious'
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        # just make sure there are no errors if a child logger doesnt
        # exist
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(dispatcher.childlog, None)

    def test_handle_read_event_logging_childlog(self):
        options = DummyOptions()
        options.readfd_result = 'supercalifragilisticexpialidocious'
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(len(dispatcher.childlog.data), 1)
        self.assertEqual(dispatcher.childlog.data[0],
                         'supercalifragilisticexpialidocious')

    def test_handle_listener_state_change_from_unknown(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.UNKNOWN
        dispatcher.state_buffer = 'whatever'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data, [])
        self.assertEqual(process.listener_state, EventListenerStates.UNKNOWN)

    def test_handle_listener_state_change_acknowledged_to_ready(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher.state_buffer = 'READY\n'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: ACKNOWLEDGED -> READY')
        self.assertEqual(process.listener_state, EventListenerStates.READY)

    def test_handle_listener_state_change_acknowledged_gobbles(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher.state_buffer = 'READY\ngarbage\n'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: ACKNOWLEDGED -> READY')
        self.assertEqual(options.logger.data[1],
                         'process1: READY -> UNKNOWN')
        self.assertEqual(process.listener_state, EventListenerStates.UNKNOWN)

    def test_handle_listener_state_change_acknowledged_to_insufficient(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher.state_buffer = 'RE'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, 'RE')
        self.assertEqual(options.logger.data, [])
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)

    def test_handle_listener_state_change_acknowledged_to_unknown(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher.state_buffer = 'bogus data yo'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: ACKNOWLEDGED -> UNKNOWN')
        self.assertEqual(process.listener_state, EventListenerStates.UNKNOWN)

    def test_handle_listener_state_change_ready_to_unknown(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.READY
        dispatcher.state_buffer = 'bogus data yo'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: READY -> UNKNOWN')
        self.assertEqual(process.listener_state, EventListenerStates.UNKNOWN)

    def test_handle_listener_state_change_busy_to_insufficient(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        dispatcher.state_buffer = 'bogus data yo'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, 'bogus data yo')
        self.assertEqual(process.listener_state, EventListenerStates.BUSY)

    def test_handle_listener_state_change_busy_to_acknowledged_procd(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        from supervisor.dispatchers import default_handler
        process.group.config.result_handler = default_handler
        dispatcher.state_buffer = 'RESULT 2\nOKabc'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, 'abc')
        self.assertEqual(options.logger.data[0],
                         'process1: BUSY -> ACKNOWLEDGED (processed)')
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)

    def test_handle_listener_state_change_busy_to_acknowledged_rejected(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        from supervisor.dispatchers import default_handler
        process.group.config.result_handler = default_handler
        dispatcher.state_buffer = 'RESULT 4\nFAILabc'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, 'abc')
        self.assertEqual(options.logger.data[0],
                         'process1: BUSY -> ACKNOWLEDGED (rejected)')
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)

    def test_handle_listener_state_change_busy_to_unknown(self):
        from supervisor.events import EventRejectedEvent
        from supervisor.events import subscribe
        events = []
        def doit(event):
            events.append(event)
        subscribe(EventRejectedEvent, doit)
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        current_event = DummyEvent()
        process.event = current_event
        dispatcher.state_buffer = 'bogus data\n'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                'process1: BUSY -> UNKNOWN (bad result line \'bogus data\')')
        self.assertEqual(process.listener_state,
                         EventListenerStates.UNKNOWN)
        self.assertEqual(events[0].process, process)
        self.assertEqual(events[0].event, current_event)

    def test_handle_listener_state_busy_gobbles(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        from supervisor.dispatchers import default_handler
        process.group.config.result_handler = default_handler
        dispatcher.state_buffer = 'RESULT 2\nOKbogus data\n'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: BUSY -> ACKNOWLEDGED (processed)')
        self.assertEqual(options.logger.data[1],
                         'process1: ACKNOWLEDGED -> UNKNOWN')
        self.assertEqual(process.listener_state,
                         EventListenerStates.UNKNOWN)

    def test_handle_result_accept(self):
        from supervisor.events import subscribe
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        subscribe(events.EventRejectedEvent, doit)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        def handle(event, result):
            pass
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        process.group.config.result_handler = handle
        process.listener_state = EventListenerStates.BUSY
        dispatcher.handle_result('foo')
        self.assertEqual(len(L), 0)
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)
        result = options.logger.data[0]
        self.assertTrue(result.endswith('BUSY -> ACKNOWLEDGED (processed)'))

    def test_handle_result_rejectevent(self):
        from supervisor.events import subscribe
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        subscribe(events.EventRejectedEvent, doit)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        def rejected(event, result):
            from supervisor.dispatchers import RejectEvent
            raise RejectEvent(result)
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        process.group.config.result_handler = rejected
        process.listener_state = EventListenerStates.BUSY
        dispatcher.handle_result('foo')
        self.assertEqual(len(L), 1)
        self.assertEqual(L[0].__class__, events.EventRejectedEvent)
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)
        result = options.logger.data[0]
        self.assertTrue(result.endswith('BUSY -> ACKNOWLEDGED (rejected)'))

    def test_handle_result_exception(self):
        from supervisor.events import subscribe
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        subscribe(events.EventRejectedEvent, doit)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        def exception(event, result):
            raise ValueError
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        process.group.config.result_handler = exception
        process.group.result_handler = exception
        process.listener_state = EventListenerStates.BUSY
        dispatcher.handle_result('foo')
        self.assertEqual(len(L), 1)
        self.assertEqual(L[0].__class__, events.EventRejectedEvent)
        self.assertEqual(process.listener_state,
                         EventListenerStates.UNKNOWN)
        result = options.logger.data[0]
        self.assertTrue(result.endswith('BUSY -> UNKNOWN'))

    def test_handle_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        try:
            raise ValueError('foo')
        except:
            dispatcher.handle_error()
        result = options.logger.data[0]
        self.assertTrue(result.startswith(
            'uncaptured python exception, closing channel'),result)

    def test_removelogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.removelogs()
        self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)
        self.assertEqual(dispatcher.childlog.handlers[0].removed, True)

    def test_reopenlogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.reopenlogs()
        self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)

    def test_strip_ansi(self):
        options = DummyOptions()
        options.strip_ansi = True
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        ansi = '\x1b[34mHello world... this is longer than a token!\x1b[0m'
        noansi = 'Hello world... this is longer than a token!'

        options.readfd_result = ansi
        dispatcher.handle_read_event()
        self.assertEqual(len(dispatcher.childlog.data), 1)
        self.assertEqual(dispatcher.childlog.data[0], noansi)

        options.strip_ansi = False

        options.readfd_result = ansi
        dispatcher.handle_read_event()
        self.assertEqual(len(dispatcher.childlog.data), 2)
        self.assertEqual(dispatcher.childlog.data[1], ansi)

    def test_ctor_nologfiles(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.childlog, None)

    def test_ctor_logfile_only(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.childlog.__class__, DummyLogger)

    def test_repr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        drepr = repr(dispatcher)
        self.assertTrue('PEventListenerDispatcher' in drepr)
        self.assertNotEqual(
            drepr.find('supervisor.tests.base.DummyProcess'),
            -1)
        self.assertTrue(drepr.endswith('(stdout)>'), drepr)

    def test_close(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.close()
        self.assertEqual(dispatcher.closed, True)
        dispatcher.close() # make sure we don't error if we try to close twice
        self.assertEqual(dispatcher.closed, True)


class stripEscapeTests(unittest.TestCase):
    def _callFUT(self, s):
        from supervisor.dispatchers import stripEscapes
        return stripEscapes(s)

    def test_zero_length_string(self):
        self.assertEqual(self._callFUT(''), '')

    def test_ansi(self):
        ansi = '\x1b[34mHello world... this is longer than a token!\x1b[0m'
        noansi = 'Hello world... this is longer than a token!'
        self.assertEqual(self._callFUT(ansi), noansi)

    def test_noansi(self):
        noansi = 'Hello world... this is longer than a token!'
        self.assertEqual(self._callFUT(noansi), noansi)

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_events
import sys
import unittest

from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyEvent

class EventSubscriptionNotificationTests(unittest.TestCase):
    def setUp(self):
        from supervisor import events
        events.callbacks[:] = []

    def tearDown(self):
        from supervisor import events
        events.callbacks[:] = []

    def test_subscribe(self):
        from supervisor import events
        events.subscribe(None, None)
        self.assertEqual(events.callbacks, [(None, None)])

    def test_clear(self):
        from supervisor import events
        events.callbacks[:] = [(None, None)]
        events.clear()
        self.assertEqual(events.callbacks, [])

    def test_notify_true(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        class DummyEvent:
            pass
        events.callbacks[:] = [(DummyEvent, callback)]
        events.notify(DummyEvent())
        self.assertEqual(L, [1])

    def test_notify_false(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        class DummyEvent:
            pass
        class AnotherEvent:
            pass
        events.callbacks[:] = [(AnotherEvent, callback)]
        events.notify(DummyEvent())
        self.assertEqual(L, [])

    def test_notify_via_subclass(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        class DummyEvent:
            pass
        class ASubclassEvent(DummyEvent):
            pass
        events.callbacks[:] = [(DummyEvent, callback)]
        events.notify(ASubclassEvent())
        self.assertEqual(L, [1])


class TestEventTypes(unittest.TestCase):
    def test_ProcessLogEvent_attributes(self):
        from supervisor.events import ProcessLogEvent
        inst = ProcessLogEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)

    def test_ProcessLogEvent_inheritence(self):
        from supervisor.events import ProcessLogEvent
        from supervisor.events import Event
        self.assertTrue(
            issubclass(ProcessLogEvent, Event)
        )

    def test_ProcessLogStdoutEvent_attributes(self):
        from supervisor.events import ProcessLogStdoutEvent
        inst = ProcessLogStdoutEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)
        self.assertEqual(inst.channel, 'stdout')

    def test_ProcessLogStdoutEvent_inheritence(self):
        from supervisor.events import ProcessLogStdoutEvent
        from supervisor.events import ProcessLogEvent
        self.assertTrue(
            issubclass(ProcessLogStdoutEvent, ProcessLogEvent)
        )

    def test_ProcessLogStderrEvent_attributes(self):
        from supervisor.events import ProcessLogStderrEvent
        inst = ProcessLogStderrEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)
        self.assertEqual(inst.channel, 'stderr')

    def test_ProcessLogStderrEvent_inheritence(self):
        from supervisor.events import ProcessLogStderrEvent
        from supervisor.events import ProcessLogEvent
        self.assertTrue(
            issubclass(ProcessLogStderrEvent, ProcessLogEvent)
        )

    def test_ProcessCommunicationEvent_attributes(self):
        from supervisor.events import ProcessCommunicationEvent
        inst = ProcessCommunicationEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)

    def test_ProcessCommunicationEvent_inheritence(self):
        from supervisor.events import ProcessCommunicationEvent
        from supervisor.events import Event
        self.assertTrue(
            issubclass(ProcessCommunicationEvent, Event)
        )

    def test_ProcessCommunicationStdoutEvent_attributes(self):
        from supervisor.events import ProcessCommunicationStdoutEvent
        inst = ProcessCommunicationStdoutEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)
        self.assertEqual(inst.channel, 'stdout')

    def test_ProcessCommunicationStdoutEvent_inheritence(self):
        from supervisor.events import ProcessCommunicationStdoutEvent
        from supervisor.events import ProcessCommunicationEvent
        self.assertTrue(
            issubclass(ProcessCommunicationStdoutEvent,
                       ProcessCommunicationEvent)
        )

    def test_ProcessCommunicationStderrEvent_attributes(self):
        from supervisor.events import ProcessCommunicationStderrEvent
        inst = ProcessCommunicationStderrEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)
        self.assertEqual(inst.channel, 'stderr')

    def test_ProcessCommunicationStderrEvent_inheritence(self):
        from supervisor.events import ProcessCommunicationStderrEvent
        from supervisor.events import ProcessCommunicationEvent
        self.assertTrue(
            issubclass(ProcessCommunicationStderrEvent,
                       ProcessCommunicationEvent)
        )

    def test_RemoteCommunicationEvent_attributes(self):
        from supervisor.events import RemoteCommunicationEvent
        inst = RemoteCommunicationEvent(1, 2)
        self.assertEqual(inst.type, 1)
        self.assertEqual(inst.data, 2)

    def test_RemoteCommunicationEvent_inheritence(self):
        from supervisor.events import RemoteCommunicationEvent
        from supervisor.events import Event
        self.assertTrue(
            issubclass(RemoteCommunicationEvent, Event)
        )

    def test_EventRejectedEvent_attributes(self):
        from supervisor.events import EventRejectedEvent
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process = DummyProcess(pconfig1)
        rejected_event = DummyEvent()
        event = EventRejectedEvent(process, rejected_event)
        self.assertEqual(event.process, process)
        self.assertEqual(event.event, rejected_event)

    def test_EventRejectedEvent_does_not_inherit_from_event(self):
        from supervisor.events import EventRejectedEvent
        from supervisor.events import Event
        self.assertFalse(
            issubclass(EventRejectedEvent, Event)
        )

    def test_all_SupervisorStateChangeEvents(self):
        from supervisor import events
        for klass in (
            events.SupervisorStateChangeEvent,
            events.SupervisorRunningEvent,
            events.SupervisorStoppingEvent
            ):
            self._test_one_SupervisorStateChangeEvent(klass)

    def _test_one_SupervisorStateChangeEvent(self, klass):
        from supervisor.events import SupervisorStateChangeEvent
        self.assertTrue(issubclass(klass, SupervisorStateChangeEvent))

    def test_all_ProcessStateEvents(self):
        from supervisor import events
        for klass in (
            events.ProcessStateEvent,
            events.ProcessStateStoppedEvent,
            events.ProcessStateExitedEvent,
            events.ProcessStateFatalEvent,
            events.ProcessStateBackoffEvent,
            events.ProcessStateRunningEvent,
            events.ProcessStateUnknownEvent,
            events.ProcessStateStoppingEvent,
            events.ProcessStateStartingEvent,
            ):
            self._test_one_ProcessStateEvent(klass)

    def _test_one_ProcessStateEvent(self, klass):
        from supervisor.states import ProcessStates
        from supervisor.events import ProcessStateEvent
        self.assertTrue(issubclass(klass, ProcessStateEvent))
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process = DummyProcess(pconfig1)
        inst = klass(process, ProcessStates.STARTING)
        self.assertEqual(inst.process, process)
        self.assertEqual(inst.from_state, ProcessStates.STARTING)
        self.assertEqual(inst.expected, True)

    def test_all_TickEvents(self):
        from supervisor import events
        for klass in (
           events.TickEvent,
           events.Tick5Event,
           events.Tick60Event,
           events.Tick3600Event
           ):
           self._test_one_TickEvent(klass)

    def _test_one_TickEvent(self, klass):
        from supervisor.events import TickEvent
        self.assertTrue(issubclass(klass, TickEvent))

        inst = klass(1, 2)
        self.assertEqual(inst.when, 1)
        self.assertEqual(inst.supervisord, 2)

    def test_ProcessGroupAddedEvent_attributes(self):
        from supervisor.events import ProcessGroupAddedEvent
        inst = ProcessGroupAddedEvent('myprocess')
        self.assertEqual(inst.group, 'myprocess')

    def test_ProcessGroupRemovedEvent_attributes(self):
        from supervisor.events import ProcessGroupRemovedEvent
        inst = ProcessGroupRemovedEvent('myprocess')
        self.assertEqual(inst.group, 'myprocess')

class TestSerializations(unittest.TestCase):
    def _deserialize(self, serialization):
        data = serialization.split('\n')
        headerdata = data[0]
        payload = ''
        headers = {}
        if len(data) > 1:
            payload = data[1]
        if headerdata:
            try:
                headers = dict( [ x.split(':',1) for x in
                                  headerdata.split()] )
            except ValueError:
                raise AssertionError('headerdata %r could not be deserialized' %
                                     headerdata)
        return headers, payload

    def test_plog_stdout_event(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        from supervisor.events import ProcessLogStdoutEvent
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        event = ProcessLogStdoutEvent(process1, 1, 'yo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['processname'], 'process1', headers)
        self.assertEqual(headers['groupname'], 'process1', headers)
        self.assertEqual(headers['pid'], '1', headers)
        self.assertEqual(payload, 'yo')

    def test_plog_stderr_event(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        from supervisor.events import ProcessLogStderrEvent
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        event = ProcessLogStderrEvent(process1, 1, 'yo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['processname'], 'process1', headers)
        self.assertEqual(headers['groupname'], 'process1', headers)
        self.assertEqual(headers['pid'], '1', headers)
        self.assertEqual(payload, 'yo')

    def test_pcomm_stdout_event(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        from supervisor.events import ProcessCommunicationStdoutEvent
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        event = ProcessCommunicationStdoutEvent(process1, 1, 'yo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['processname'], 'process1', headers)
        self.assertEqual(headers['groupname'], 'process1', headers)
        self.assertEqual(headers['pid'], '1', headers)
        self.assertEqual(payload, 'yo')

    def test_pcomm_stderr_event(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        from supervisor.events import ProcessCommunicationStderrEvent
        event = ProcessCommunicationStderrEvent(process1, 1, 'yo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['processname'], 'process1', headers)
        self.assertEqual(headers['groupname'], 'process1', headers)
        self.assertEqual(headers['pid'], '1', headers)
        self.assertEqual(payload, 'yo')

    def test_remote_comm_event(self):
        from supervisor.events import RemoteCommunicationEvent
        event = RemoteCommunicationEvent('foo', 'bar')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['type'], 'foo', headers)
        self.assertEqual(payload, 'bar')

    def test_process_group_added_event(self):
        from supervisor.events import ProcessGroupAddedEvent
        event = ProcessGroupAddedEvent('foo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['groupname'], 'foo')
        self.assertEqual(payload, '')

    def test_process_group_removed_event(self):
        from supervisor.events import ProcessGroupRemovedEvent
        event = ProcessGroupRemovedEvent('foo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['groupname'], 'foo')
        self.assertEqual(payload, '')

    def test_process_state_events_without_extra_values(self):
        from supervisor.states import ProcessStates
        from supervisor import events
        for klass in (
            events.ProcessStateFatalEvent,
            events.ProcessStateUnknownEvent,
            ):
            options = DummyOptions()
            pconfig1 = DummyPConfig(options, 'process1', 'process1',
                                    '/bin/process1')
            class DummyGroup:
                config = pconfig1
            process1 = DummyProcess(pconfig1)
            process1.group = DummyGroup
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(len(headers), 3)
            self.assertEqual(headers['processname'], 'process1')
            self.assertEqual(headers['groupname'], 'process1')
            self.assertEqual(headers['from_state'], 'STARTING')
            self.assertEqual(payload, '')

    def test_process_state_events_with_pid(self):
        from supervisor.states import ProcessStates
        from supervisor import events
        for klass in (
            events.ProcessStateRunningEvent,
            events.ProcessStateStoppedEvent,
            events.ProcessStateStoppingEvent,
            ):
            options = DummyOptions()
            pconfig1 = DummyPConfig(options, 'process1', 'process1',
                                    '/bin/process1')
            class DummyGroup:
                config = pconfig1
            process1 = DummyProcess(pconfig1)
            process1.group = DummyGroup
            process1.pid = 1
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(len(headers), 4)
            self.assertEqual(headers['processname'], 'process1')
            self.assertEqual(headers['groupname'], 'process1')
            self.assertEqual(headers['from_state'], 'STARTING')
            self.assertEqual(headers['pid'], '1')
            self.assertEqual(payload, '')

    def test_process_state_events_starting_and_backoff(self):
        from supervisor.states import ProcessStates
        from supervisor import events
        for klass in (
            events.ProcessStateStartingEvent,
            events.ProcessStateBackoffEvent,
            ):
            options = DummyOptions()
            pconfig1 = DummyPConfig(options, 'process1', 'process1',
                                    '/bin/process1')
            class DummyGroup:
                config = pconfig1
            process1 = DummyProcess(pconfig1)
            process1.group = DummyGroup
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(len(headers), 4)
            self.assertEqual(headers['processname'], 'process1')
            self.assertEqual(headers['groupname'], 'process1')
            self.assertEqual(headers['from_state'], 'STARTING')
            self.assertEqual(headers['tries'], '0')
            self.assertEqual(payload, '')
            process1.backoff = 1
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(headers['tries'], '1')
            process1.backoff = 2
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(headers['tries'], '2')

    def test_process_state_exited_event_expected(self):
        from supervisor import events
        from supervisor.states import ProcessStates
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        process1.pid = 1
        event = events.ProcessStateExitedEvent(process1,
                                               ProcessStates.STARTING,
                                               expected=True)
        headers, payload = self._deserialize(str(event))
        self.assertEqual(len(headers), 5)
        self.assertEqual(headers['processname'], 'process1')
        self.assertEqual(headers['groupname'], 'process1')
        self.assertEqual(headers['pid'], '1')
        self.assertEqual(headers['from_state'], 'STARTING')
        self.assertEqual(headers['expected'], '1')
        self.assertEqual(payload, '')

    def test_process_state_exited_event_unexpected(self):
        from supervisor import events
        from supervisor.states import ProcessStates
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        process1.pid = 1
        event = events.ProcessStateExitedEvent(process1,
                                               ProcessStates.STARTING,
                                               expected=False)
        headers, payload = self._deserialize(str(event))
        self.assertEqual(len(headers), 5)
        self.assertEqual(headers['processname'], 'process1')
        self.assertEqual(headers['groupname'], 'process1')
        self.assertEqual(headers['pid'], '1')
        self.assertEqual(headers['from_state'], 'STARTING')
        self.assertEqual(headers['expected'], '0')
        self.assertEqual(payload, '')

    def test_supervisor_sc_event(self):
        from supervisor import events
        event = events.SupervisorRunningEvent()
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers, {})
        self.assertEqual(payload, '')

    def test_tick_events(self):
        from supervisor import events
        for klass in (
            events.Tick5Event,
            events.Tick60Event,
            events.Tick3600Event,
            ):
            event = klass(1, 2)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(headers, {'when':'1'})
            self.assertEqual(payload, '')

class TestUtilityFunctions(unittest.TestCase):
    def test_getEventNameByType(self):
        from supervisor import events
        for name, value in events.EventTypes.__dict__.items():
            self.assertEqual(events.getEventNameByType(value), name)

    def test_register(self):
        from supervisor import events
        self.assertFalse(hasattr(events.EventTypes, 'FOO'))
        class FooEvent(events.Event):
            pass
        try:
            events.register('FOO', FooEvent)
            self.assertTrue(events.EventTypes.FOO is FooEvent)
        finally:
            del events.EventTypes.FOO

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_http
import base64
import os
import stat
import sys
import supervisor.medusa.text_socket as socket
import tempfile
import unittest

from supervisor.compat import as_bytes
from supervisor.compat import as_string
from supervisor.compat import sha1

from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import PopulatedDummySupervisor
from supervisor.tests.base import DummyRPCInterfaceFactory
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyRequest

from supervisor.http import NOT_DONE_YET

class HandlerTests:
    def _makeOne(self, supervisord):
        return self._getTargetClass()(supervisord)

    def test_match(self):
        class FakeRequest:
            def __init__(self, uri):
                self.uri = uri
        supervisor = DummySupervisor()
        handler = self._makeOne(supervisor)
        self.assertEqual(handler.match(FakeRequest(handler.path)), True)

class LogtailHandlerTests(HandlerTests, unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import logtail_handler
        return logtail_handler

    def test_handle_request_stdout_logfile_none(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                               stdout_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        handler = self._makeOne(supervisord)
        request = DummyRequest('/logtail/process1', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, 410)

    def test_handle_request_stdout_logfile_missing(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', 'it/is/missing')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        handler = self._makeOne(supervisord)
        request = DummyRequest('/logtail/foo', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, 410)

    def test_handle_request(self):
        f = tempfile.NamedTemporaryFile()
        t = f.name
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', stdout_logfile=t)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        handler = self._makeOne(supervisord)
        request = DummyRequest('/logtail/foo', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, None)
        from supervisor.medusa import http_date
        self.assertEqual(request.headers['Last-Modified'],
                         http_date.build_http_date(os.stat(t)[stat.ST_MTIME]))
        self.assertEqual(request.headers['Content-Type'], 'text/plain')
        self.assertEqual(len(request.producers), 1)
        self.assertEqual(request._done, True)

class MainLogTailHandlerTests(HandlerTests, unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import mainlogtail_handler
        return mainlogtail_handler

    def test_handle_request_stdout_logfile_none(self):
        supervisor = DummySupervisor()
        handler = self._makeOne(supervisor)
        request = DummyRequest('/mainlogtail', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, 410)

    def test_handle_request_stdout_logfile_missing(self):
        supervisor = DummySupervisor()
        supervisor.options.logfile = '/not/there'
        request = DummyRequest('/mainlogtail', None, None, None)
        handler = self._makeOne(supervisor)
        handler.handle_request(request)
        self.assertEqual(request._error, 410)

    def test_handle_request(self):
        supervisor = DummySupervisor()
        f = tempfile.NamedTemporaryFile()
        t = f.name
        supervisor.options.logfile = t
        handler = self._makeOne(supervisor)
        request = DummyRequest('/mainlogtail', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, None)
        from supervisor.medusa import http_date
        self.assertEqual(request.headers['Last-Modified'],
                         http_date.build_http_date(os.stat(t)[stat.ST_MTIME]))
        self.assertEqual(request.headers['Content-Type'], 'text/plain')
        self.assertEqual(len(request.producers), 1)
        self.assertEqual(request._done, True)


class TailFProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import tail_f_producer
        return tail_f_producer

    def _makeOne(self, request, filename, head):
        return self._getTargetClass()(request, filename, head)

    def test_handle_more(self):
        request = DummyRequest('/logtail/foo', None, None, None)
        from supervisor import http
        f = tempfile.NamedTemporaryFile()
        f.write(as_bytes('a' * 80))
        f.flush()
        t = f.name
        producer = self._makeOne(request, t, 80)
        result = producer.more()
        self.assertEqual(result, as_bytes('a' * 80))
        f.write(as_bytes('w' * 100))
        f.flush()
        result = producer.more()
        self.assertEqual(result, as_bytes('w' * 100))
        result = producer.more()
        self.assertEqual(result, http.NOT_DONE_YET)
        f.truncate(0)
        f.flush()
        result = producer.more()
        self.assertEqual(result, '==> File truncated <==\n')

class DeferringChunkedProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_chunked_producer
        return deferring_chunked_producer

    def _makeOne(self, producer, footers=None):
        return self._getTargetClass()(producer, footers)

    def test_more_not_done_yet(self):
        wrapped = DummyProducer(NOT_DONE_YET)
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_string(self):
        wrapped = DummyProducer('hello')
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), '5\r\nhello\r\n')

    def test_more_nodata(self):
        wrapped = DummyProducer()
        producer = self._makeOne(wrapped, footers=['a', 'b'])
        self.assertEqual(producer.more(), '0\r\na\r\nb\r\n\r\n')

    def test_more_nodata_footers(self):
        wrapped = DummyProducer('')
        producer = self._makeOne(wrapped, footers=['a', 'b'])
        self.assertEqual(producer.more(), '0\r\na\r\nb\r\n\r\n')

    def test_more_nodata_nofooters(self):
        wrapped = DummyProducer('')
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), '0\r\n\r\n')

    def test_more_noproducer(self):
        producer = self._makeOne(None)
        self.assertEqual(producer.more(), '')
        
class DeferringCompositeProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_composite_producer
        return deferring_composite_producer

    def _makeOne(self, producers):
        return self._getTargetClass()(producers)

    def test_more_not_done_yet(self):
        wrapped = DummyProducer(NOT_DONE_YET)
        producer = self._makeOne([wrapped])
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_string(self):
        wrapped1 = DummyProducer('hello')
        wrapped2 = DummyProducer('goodbye')
        producer = self._makeOne([wrapped1, wrapped2])
        self.assertEqual(producer.more(), 'hello')
        self.assertEqual(producer.more(), 'goodbye')
        self.assertEqual(producer.more(), '')

    def test_more_nodata(self):
        wrapped = DummyProducer()
        producer = self._makeOne([wrapped])
        self.assertEqual(producer.more(), '')

class DeferringGlobbingProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_globbing_producer
        return deferring_globbing_producer

    def _makeOne(self, producer, buffer_size=1<<16):
        return self._getTargetClass()(producer, buffer_size)

    def test_more_not_done_yet(self):
        wrapped = DummyProducer(NOT_DONE_YET)
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_string(self):
        wrapped = DummyProducer('hello', 'there', 'guy')
        producer = self._makeOne(wrapped, buffer_size=1)
        self.assertEqual(producer.more(), 'hello')

        wrapped = DummyProducer('hello', 'there', 'guy')
        producer = self._makeOne(wrapped, buffer_size=50)
        self.assertEqual(producer.more(), 'hellothereguy')

    def test_more_nodata(self):
        wrapped = DummyProducer()
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), '')

class DeferringHookedProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_hooked_producer
        return deferring_hooked_producer

    def _makeOne(self, producer, function):
        return self._getTargetClass()(producer, function)

    def test_more_not_done_yet(self):
        wrapped = DummyProducer(NOT_DONE_YET)
        producer = self._makeOne(wrapped, None)
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_string(self):
        wrapped = DummyProducer('hello')
        L = []
        def callback(bytes):
            L.append(bytes)
        producer = self._makeOne(wrapped, callback)
        self.assertEqual(producer.more(), 'hello')
        self.assertEqual(L, [])
        producer.more()
        self.assertEqual(L, [5])

    def test_more_nodata(self):
        wrapped = DummyProducer()
        L = []
        def callback(bytes):
            L.append(bytes)
        producer = self._makeOne(wrapped, callback)
        self.assertEqual(producer.more(), '')
        self.assertEqual(L, [0])

    def test_more_noproducer(self):
        producer = self._makeOne(None, None)
        self.assertEqual(producer.more(), '')

class Test_deferring_http_request(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_http_request
        return deferring_http_request

    def _makeOne(
        self,
        channel=None,
        req='GET / HTTP/1.0',
        command='GET',
        uri='/',
        version='1.0',
        header=(),
        ):
        return self._getTargetClass()(
            channel, req, command, uri, version, header
            )

    def _makeChannel(self):
        class Channel:
            closed = False
            def close_when_done(self):
                self.closed = True
            def push_with_producer(self, producer):
                self.producer = producer
        return Channel()
    
    def test_done_http_10_nokeepalive(self):
        channel = self._makeChannel()
        inst = self._makeOne(channel=channel, version='1.0')
        inst.done()
        self.assertTrue(channel.closed)

    def test_done_http_10_keepalive_no_content_length(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.0',
            header=['Connection: Keep-Alive'],
            )
        
        inst.done()
        self.assertTrue(channel.closed)
        
    def test_done_http_10_keepalive_and_content_length(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.0',
            header=['Connection: Keep-Alive'],
            )
        inst.reply_headers['Content-Length'] = 1
        inst.done()
        self.assertEqual(inst['Connection'], 'Keep-Alive')
        self.assertFalse(channel.closed)

    def test_done_http_11_connection_close(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            header=['Connection: close']
            )
        inst.done()
        self.assertTrue(channel.closed)

    def test_done_http_11_unknown_transfer_encoding(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            )
        inst.reply_headers['Transfer-Encoding'] = 'notchunked'
        inst.done()
        self.assertTrue(channel.closed)

    def test_done_http_11_chunked_transfer_encoding(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            )
        inst.reply_headers['Transfer-Encoding'] = 'chunked'
        inst.done()
        self.assertFalse(channel.closed)

    def test_done_http_11_use_chunked(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            )
        inst.use_chunked = True
        inst.done()
        self.assertTrue('Transfer-Encoding' in inst)
        self.assertFalse(channel.closed)

    def test_done_http_11_wo_content_length_no_te_no_use_chunked_close(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            )
        inst.use_chunked = False
        inst.done()
        self.assertTrue(channel.closed)

    def test_done_http_09(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version=None,
            )
        inst.done()
        self.assertTrue(channel.closed)
        
class EncryptedDictionaryAuthorizedTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import encrypted_dictionary_authorizer
        return encrypted_dictionary_authorizer

    def _makeOne(self, dict):
        return self._getTargetClass()(dict)

    def test_authorize_baduser(self):
        authorizer = self._makeOne({})
        self.assertFalse(authorizer.authorize(('foo', 'bar')))

    def test_authorize_gooduser_badpassword(self):
        authorizer = self._makeOne({'foo':'password'})
        self.assertFalse(authorizer.authorize(('foo', 'bar')))

    def test_authorize_gooduser_goodpassword(self):
        authorizer = self._makeOne({'foo':'password'})
        self.assertTrue(authorizer.authorize(('foo', 'password')))

    def test_authorize_gooduser_goodpassword_with_colon(self):
        authorizer = self._makeOne({'foo':'pass:word'})
        self.assertTrue(authorizer.authorize(('foo', 'pass:word')))

    def test_authorize_gooduser_badpassword_sha(self):
        password = '{SHA}' + sha1(as_bytes('password')).hexdigest()
        authorizer = self._makeOne({'foo':password})
        self.assertFalse(authorizer.authorize(('foo', 'bar')))

    def test_authorize_gooduser_goodpassword_sha(self):
        password = '{SHA}' + sha1(as_bytes('password')).hexdigest()
        authorizer = self._makeOne({'foo':password})
        self.assertTrue(authorizer.authorize(('foo', 'password')))

class SupervisorAuthHandlerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import supervisor_auth_handler
        return supervisor_auth_handler

    def _makeOne(self, dict, handler):
        return self._getTargetClass()(dict, handler)

    def test_ctor(self):
        handler = self._makeOne({'a':1}, None)
        from supervisor.http import encrypted_dictionary_authorizer
        self.assertEqual(handler.authorizer.__class__,
                         encrypted_dictionary_authorizer)

    def test_handle_request_authorizes_good_credentials(self):
        request = DummyRequest('/logtail/process1', None, None, None)
        encoded = base64.b64encode(as_bytes("user:password"))
        request.header = ["Authorization: Basic %s" % as_string(encoded)]
        handler = DummyHandler()
        auth_handler = self._makeOne({'user':'password'}, handler)
        auth_handler.handle_request(request)
        self.assertTrue(handler.handled_request)

    def test_handle_request_authorizes_good_password_with_colon(self):
        request = DummyRequest('/logtail/process1', None, None, None)
        # password contains colon
        encoded = base64.b64encode(as_bytes("user:pass:word"))
        request.header = ["Authorization: Basic %s" % as_string(encoded)]
        handler = DummyHandler()
        auth_handler = self._makeOne({'user':'pass:word'}, handler)
        auth_handler.handle_request(request)
        self.assertTrue(handler.handled_request)

    def test_handle_request_does_not_authorize_bad_credentials(self):
        request = DummyRequest('/logtail/process1', None, None, None)
        encoded = base64.b64encode(as_bytes("wrong:wrong"))
        request.header = ["Authorization: Basic %s" % as_string(encoded)]
        handler = DummyHandler()
        auth_handler = self._makeOne({'user':'password'}, handler)
        auth_handler.handle_request(request)
        self.assertFalse(handler.handled_request)


class TopLevelFunctionTests(unittest.TestCase):
    def _make_http_servers(self, sconfigs):
        options = DummyOptions()
        options.server_configs = sconfigs
        options.rpcinterface_factories = [('dummy',DummyRPCInterfaceFactory,{})]
        supervisord = DummySupervisor()
        from supervisor.http import make_http_servers
        servers = make_http_servers(options, supervisord)
        try:
            for config, s in servers:
                s.close()
                socketfile = config.get('file')
                if socketfile is not None:
                    os.unlink(socketfile)
        finally:
            from asyncore import socket_map
            socket_map.clear()
        return servers

    def test_make_http_servers_noauth(self):
        socketfile = tempfile.mktemp()
        inet = {'family':socket.AF_INET, 'host':'localhost', 'port':17735,
                'username':None, 'password':None, 'section':'inet_http_server'}
        unix = {'family':socket.AF_UNIX, 'file':socketfile, 'chmod':448, # 0700 in Py2, 0o700 in Py3
                'chown':(-1, -1), 'username':None, 'password':None,
                'section':'unix_http_server'}
        servers = self._make_http_servers([inet, unix])
        self.assertEqual(len(servers), 2)

        inetdata = servers[0]
        self.assertEqual(inetdata[0], inet)
        server = inetdata[1]
        idents = [
            'Supervisor XML-RPC Handler',
            'Logtail HTTP Request Handler',
            'Main Logtail HTTP Request Handler',
            'Supervisor Web UI HTTP Request Handler',
            'Default HTTP Request Handler'
            ]
        self.assertEqual([x.IDENT for x in server.handlers], idents)

        unixdata = servers[1]
        self.assertEqual(unixdata[0], unix)
        server = unixdata[1]
        self.assertEqual([x.IDENT for x in server.handlers], idents)

    def test_make_http_servers_withauth(self):
        socketfile = tempfile.mktemp()
        inet = {'family':socket.AF_INET, 'host':'localhost', 'port':17736,
                'username':'username', 'password':'password',
                'section':'inet_http_server'}
        unix = {'family':socket.AF_UNIX, 'file':socketfile, 'chmod':448, # 0700 in Py2, 0o700 in Py3
                'chown':(-1, -1), 'username':'username', 'password':'password',
                'section':'unix_http_server'}
        servers = self._make_http_servers([inet, unix])
        self.assertEqual(len(servers), 2)
        from supervisor.http import supervisor_auth_handler
        for config, server in servers:
            for handler in server.handlers:
                self.assertTrue(isinstance(handler, supervisor_auth_handler),
                                handler)

class DummyHandler:
    def __init__(self):
        self.handled_request = False

    def handle_request(self, request):
        self.handled_request = True

class DummyProducer:
    def __init__(self, *data):
        self.data = list(data)

    def more(self):
        if self.data:
            return self.data.pop(0)
        else:
            return ''

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_http_client
import socket
import sys
import unittest

class ListenerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http_client import Listener
        return Listener

    def _makeOne(self):
        return self._getTargetClass()()

    def _makeFakeStdout(self):
        class Stdout(object):
            def __init__(self):
                self.things = []
                self.flushed = False
            def write(self, thing):
                self.things.append(thing)
            def flush(self):
                self.flushed = True
        stdout = Stdout()
        return stdout

    def test_status(self):
        inst = self._makeOne()
        self.assertEqual(inst.status(None, None), None)
        
    def test_error(self):
        inst = self._makeOne()
        try:
            old_stdout = sys.stdout
            stdout = self._makeFakeStdout()
            sys.stdout = stdout
            self.assertEqual(inst.error('url', 'error'), None)
            self.assertEqual(stdout.things, ['url error\n'])
        finally:
            sys.stdout = old_stdout
        
    def test_response_header(self):
        inst = self._makeOne()
        self.assertEqual(inst.response_header(None, None, None), None)

    def test_done(self):
        inst = self._makeOne()
        self.assertEqual(inst.done(None), None)

    def test_feed(self):
        inst = self._makeOne()
        try:
            old_stdout = sys.stdout
            stdout = self._makeFakeStdout()
            sys.stdout = stdout
            inst.feed('url', 'data')
            self.assertEqual(stdout.things, ['data'])
        finally:
            sys.stdout = old_stdout

    def test_close(self):
        inst = self._makeOne()
        self.assertEqual(inst.close(None), None)

class HTTPHandlerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http_client import HTTPHandler
        return HTTPHandler

    def _makeOne(self, listener=None, username='', password=None):
        if listener is None:
            listener = self._makeListener()
        socket_map = {}
        return self._getTargetClass()(
            listener,
            username,
            password,
            map=socket_map,
            )

    def _makeListener(self):
        listener = DummyListener()
        return listener

    def test_get_url_not_None(self):
        inst = self._makeOne()
        inst.url = 'abc'
        self.assertRaises(AssertionError, inst.get, 'abc')

    def test_get_bad_scheme(self):
        inst = self._makeOne()
        self.assertRaises(
            NotImplementedError,
            inst.get,
            'nothttp://localhost',
            '/abc'
            )
        
    def test_get_implied_port_80(self):
        inst = self._makeOne()
        sockets = []
        connects = []
        inst.create_socket = lambda *arg: sockets.append(arg)
        inst.connect = lambda tup: connects.append(tup)
        inst.get('http://localhost', '/abc/def')
        self.assertEqual(inst.port, 80)
        self.assertEqual(sockets, [(socket.AF_INET, socket.SOCK_STREAM)])
        self.assertEqual(connects, [('localhost', 80)])

    def test_get_explicit_port(self):
        inst = self._makeOne()
        sockets = []
        connects = []
        inst.create_socket = lambda *arg: sockets.append(arg)
        inst.connect = lambda tup: connects.append(tup)
        inst.get('http://localhost:8080', '/abc/def')
        self.assertEqual(inst.port, 8080)
        self.assertEqual(sockets, [(socket.AF_INET, socket.SOCK_STREAM)])
        self.assertEqual(connects, [('localhost', 8080)])

    def test_get_explicit_unix_domain_socket(self):
        inst = self._makeOne()
        sockets = []
        connects = []
        inst.create_socket = lambda *arg: sockets.append(arg)
        inst.connect = lambda tup: connects.append(tup)
        inst.get('unix:///a/b/c', '')
        self.assertEqual(sockets, [(socket.AF_UNIX, socket.SOCK_STREAM)])
        self.assertEqual(connects, ['/a/b/c'])

    def test_close(self):
        inst = self._makeOne()
        dels = []
        inst.del_channel = lambda: dels.append(True)
        inst.socket = DummySocket()
        inst.close()
        self.assertEqual(inst.listener.closed, None)
        self.assertEqual(inst.connected, 0)
        self.assertEqual(dels, [True])
        self.assertTrue(inst.socket.closed)
        self.assertEqual(inst.url, 'CLOSED')

    def test_header(self):
        from supervisor.http_client import CRLF
        inst = self._makeOne()
        pushes = []
        inst.push = lambda val: pushes.append(val)
        inst.header('name', 'val')
        self.assertEqual(pushes, ['name: val', CRLF])

    def test_handle_error_already_handled(self):
        inst = self._makeOne()
        inst.error_handled = True
        self.assertEqual(inst.handle_error(), None)
        
    def test_handle_error(self):
        inst = self._makeOne()
        closed = []
        inst.close = lambda: closed.append(True)
        inst.url = 'foo'
        self.assertEqual(inst.handle_error(), None)
        self.assertEqual(inst.listener.error_url, 'foo')
        self.assertEqual(
            inst.listener.error_msg,
            'Cannot connect, error: None (None)',
            )
        self.assertEqual(closed, [True])
        self.assertTrue(inst.error_handled)

    def test_handle_connect_no_password(self):
        inst = self._makeOne()
        pushed = []
        inst.push = lambda val: pushed.append(val)
        inst.path = '/'
        inst.host = 'localhost'
        inst.handle_connect()
        self.assertTrue(inst.connected)
        self.assertEqual(
            pushed,
            ['GET / HTTP/1.1',
             '\r\n',
             'Host: localhost',
             '\r\n',
             'Accept-Encoding: chunked',
             '\r\n',
             'Accept: */*',
             '\r\n',
             'User-agent: Supervisor HTTP Client',
             '\r\n',
             '\r\n',
             '\r\n']
            )

    def test_handle_connect_with_password(self):
        inst = self._makeOne()
        pushed = []
        inst.push = lambda val: pushed.append(val)
        inst.path = '/'
        inst.host = 'localhost'
        inst.password = 'password'
        inst.username = 'username'
        inst.handle_connect()
        self.assertTrue(inst.connected)
        self.assertEqual(
            pushed,
             ['GET / HTTP/1.1',
              '\r\n',
              'Host: localhost',
              '\r\n',
              'Accept-Encoding: chunked',
              '\r\n',
              'Accept: */*',
              '\r\n',
              'User-agent: Supervisor HTTP Client',
              '\r\n',
              'Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=',
              '\r\n',
              '\r\n',
              '\r\n'],
            )

    def test_feed(self):
        inst = self._makeOne()
        inst.feed('data')
        self.assertEqual(inst.listener.fed_data, ['data'])

    def test_collect_incoming_data_part_is_body(self):
        inst = self._makeOne()
        inst.part = inst.body
        inst.buffer = 'abc'
        inst.collect_incoming_data('foo')
        self.assertEqual(inst.listener.fed_data, ['abcfoo'])
        self.assertEqual(inst.buffer, '')

    def test_collect_incoming_data_part_is_not_body(self):
        inst = self._makeOne()
        inst.part = None
        inst.buffer = 'abc'
        inst.collect_incoming_data('foo')
        self.assertEqual(inst.listener.fed_data, [])
        self.assertEqual(inst.buffer, 'abcfoo')

    def test_found_terminator(self):
        inst = self._makeOne()
        parted = []
        inst.part = lambda: parted.append(True)
        inst.buffer = None
        inst.found_terminator()
        self.assertEqual(parted, [True])
        self.assertEqual(inst.buffer, '')

    def test_ignore(self):
        inst = self._makeOne()
        inst.buffer = None
        inst.ignore()
        self.assertEqual(inst.buffer, '')

    def test_status_line_not_startswith_http(self):
        inst = self._makeOne()
        inst.buffer = 'NOTHTTP/1.0 200 OK'
        self.assertRaises(ValueError, inst.status_line)

    def test_status_line_200(self):
        inst = self._makeOne()
        inst.buffer = 'HTTP/1.0 200 OK'
        version, status, reason = inst.status_line()
        self.assertEqual(version, 'HTTP/1.0')
        self.assertEqual(status, 200)
        self.assertEqual(reason, 'OK')
        self.assertEqual(inst.part, inst.headers)

    def test_status_line_not_200(self):
        inst = self._makeOne()
        inst.buffer = 'HTTP/1.0 201 OK'
        closed = []
        inst.close = lambda: closed.append(True)
        version, status, reason = inst.status_line()
        self.assertEqual(version, 'HTTP/1.0')
        self.assertEqual(status, 201)
        self.assertEqual(reason, 'OK')
        self.assertEqual(inst.part, inst.ignore)
        self.assertEqual(
            inst.listener.error_msg,
            'Cannot read, status code 201'
            )
        self.assertEqual(closed, [True])

    def test_headers_empty_line_nonchunked(self):
        inst = self._makeOne()
        inst.buffer = ''
        inst.encoding = 'not chunked'
        inst.length = 3
        terms = []
        inst.set_terminator = lambda L: terms.append(L)
        inst.headers()
        self.assertEqual(inst.part, inst.body)
        self.assertEqual(terms, [3])
        
    def test_headers_empty_line_chunked(self):
        inst = self._makeOne()
        inst.buffer = ''
        inst.encoding = 'chunked'
        inst.headers()
        self.assertEqual(inst.part, inst.chunked_size)

    def test_headers_nonempty_line_no_name_no_value(self):
        inst = self._makeOne()
        inst.buffer = ':'
        self.assertEqual(inst.headers(), None)

    def test_headers_nonempty_line_transfer_encoding(self):
        inst = self._makeOne()
        inst.buffer = 'Transfer-Encoding: chunked'
        responses = []
        inst.response_header = lambda n, v: responses.append((n, v))
        inst.headers()
        self.assertEqual(inst.encoding, 'chunked')
        self.assertEqual(responses, [('transfer-encoding', 'chunked')])

    def test_headers_nonempty_line_content_length(self):
        inst = self._makeOne()
        inst.buffer = 'Content-Length: 3'
        responses = []
        inst.response_header = lambda n, v: responses.append((n, v))
        inst.headers()
        self.assertEqual(inst.length, 3)
        self.assertEqual(responses, [('content-length', '3')])

    def test_headers_nonempty_line_arbitrary(self):
        inst = self._makeOne()
        inst.buffer = 'X-Test: abc'
        responses = []
        inst.response_header = lambda n, v: responses.append((n, v))
        inst.headers()
        self.assertEqual(responses, [('x-test', 'abc')])

    def test_response_header(self):
        inst = self._makeOne()
        inst.response_header('a', 'b')
        self.assertEqual(inst.listener.response_header_name, 'a')
        self.assertEqual(inst.listener.response_header_value, 'b')

    def test_body(self):
        inst = self._makeOne()
        closed = []
        inst.close = lambda: closed.append(True)
        inst.body()
        self.assertEqual(closed, [True])
        self.assertTrue(inst.listener.done)

    def test_done(self):
        inst = self._makeOne()
        inst.done()
        self.assertTrue(inst.listener.done)

    def test_chunked_size_empty_line(self):
        inst = self._makeOne()
        inst.buffer = ''
        inst.length = 1
        self.assertEqual(inst.chunked_size(), None)
        self.assertEqual(inst.length, 1)
        
    def test_chunked_size_zero_size(self):
        inst = self._makeOne()
        inst.buffer = '0'
        inst.length = 1
        self.assertEqual(inst.chunked_size(), None)
        self.assertEqual(inst.length, 1)
        self.assertEqual(inst.part, inst.trailer)

    def test_chunked_size_nonzero_size(self):
        inst = self._makeOne()
        inst.buffer = '10'
        inst.length = 1
        terms = []
        inst.set_terminator = lambda sz: terms.append(sz)
        self.assertEqual(inst.chunked_size(), None)
        self.assertEqual(inst.part, inst.chunked_body)
        self.assertEqual(inst.length, 17)
        self.assertEqual(terms, [16])

    def test_chunked_body(self):
        from supervisor.http_client import CRLF
        inst = self._makeOne()
        inst.buffer = 'buffer'
        terms = []
        lines = []
        inst.set_terminator = lambda v: terms.append(v)
        inst.feed = lambda v: lines.append(v)
        inst.chunked_body()
        self.assertEqual(terms, [CRLF])
        self.assertEqual(lines, ['buffer'])
        self.assertEqual(inst.part, inst.chunked_size)

    def test_trailer_line_not_crlf(self):
        inst = self._makeOne()
        inst.buffer = ''
        self.assertEqual(inst.trailer(), None)

    def test_trailer_line_crlf(self):
        from supervisor.http_client import CRLF
        inst = self._makeOne()
        inst.buffer = CRLF
        dones = []
        closes = []
        inst.done = lambda: dones.append(True)
        inst.close = lambda: closes.append(True)
        self.assertEqual(inst.trailer(), None)
        self.assertEqual(dones, [True])
        self.assertEqual(closes, [True])
        
class DummyListener(object):
    closed = None
    error_url = None
    error_msg = None
    done = False
    def __init__(self):
        self.fed_data = []
        
    def close(self, url):
        self.closed = url

    def error(self, url, msg):
        self.error_url = url
        self.error_msg = msg

    def feed(self, url, data):
        self.fed_data.append(data)

    def status(self, url, int):
        self.status_url = url
        self.status_int = int

    def response_header(self, url, name, value):
        self.response_header_name = name
        self.response_header_value = value

    def done(self, url):
        self.done = True

class DummySocket(object):
    closed = False
    def close(self):
        self.closed = True

########NEW FILE########
__FILENAME__ = test_loggers
# -*- coding: utf-8 -*-
import errno
import sys
import unittest
import tempfile
import shutil
import os
import syslog

from supervisor.compat import PY3
from supervisor.compat import as_string

from supervisor.tests.base import mock
from supervisor.tests.base import DummyStream
from supervisor import read_file

class LevelTests(unittest.TestCase):
    def test_LOG_LEVELS_BY_NUM_doesnt_include_builtins(self):
        from supervisor import loggers
        for level_name in loggers.LOG_LEVELS_BY_NUM.values():
            self.assertFalse(level_name.startswith('_'))

class HandlerTests:
    def setUp(self):
        self.basedir = tempfile.mkdtemp()
        self.filename = os.path.join(self.basedir, 'thelog')

    def tearDown(self):
        try:
            shutil.rmtree(self.basedir)
        except OSError:
            pass

    def _makeOne(self, *arg, **kw):
        klass = self._getTargetClass()
        return klass(*arg, **kw)

    def _makeLogRecord(self, msg):
        from supervisor import loggers
        record = loggers.LogRecord(
            level=loggers.LevelsByName.INFO,
            msg=msg,
            exc_info=None
            )
        return record

class BareHandlerTests(HandlerTests, unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.loggers import Handler
        return Handler

    def test_flush_stream_flush_raises_IOError_EPIPE(self):
        stream = DummyStream(error=IOError(errno.EPIPE))
        inst = self._makeOne(stream=stream)
        self.assertEqual(inst.flush(), None) # does not raise

    def test_flush_stream_flush_raises_IOError_not_EPIPE(self):
        stream = DummyStream(error=IOError(errno.EALREADY))
        inst = self._makeOne(stream=stream)
        self.assertRaises(IOError, inst.flush) # non-EPIPE IOError raises

    def test_close_already_closed(self):
        stream = DummyStream()
        inst = self._makeOne(stream=stream)
        inst.closed = True
        self.assertEqual(inst.close(), None)

    def test_close_stream_fileno_above_3(self):
        stream = DummyStream(fileno=50)
        inst = self._makeOne(stream=stream)
        self.assertEqual(inst.close(), None)
        self.assertTrue(inst.closed)
        self.assertTrue(inst.stream.closed)

    def test_close_stream_fileno_below_3(self):
        stream = DummyStream(fileno=0)
        inst = self._makeOne(stream=stream)
        self.assertEqual(inst.close(), None)
        self.assertFalse(inst.closed)
        self.assertFalse(inst.stream.closed)

    def test_emit_gardenpath(self):
        stream = DummyStream()
        inst = self._makeOne(stream=stream)
        record = self._makeLogRecord('foo')
        inst.emit(record)
        self.assertEqual(stream.flushed, True)
        self.assertEqual(stream.written, 'foo')

    def test_emit_unicode_error(self):
        stream = DummyStream(error=UnicodeError)
        inst = self._makeOne(stream=stream)
        record = self._makeLogRecord('foo')
        inst.emit(record)
        self.assertEqual(stream.flushed, True)
        self.assertEqual(stream.written, 'foo')

    def test_emit_other_error(self):
        stream = DummyStream(error=TypeError)
        inst = self._makeOne(stream=stream)
        handled = []
        inst.handleError = lambda: handled.append(True)
        record = self._makeLogRecord('foo')
        inst.emit(record)
        self.assertEqual(stream.flushed, False)
        self.assertEqual(stream.written, '')

class FileHandlerTests(HandlerTests, unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.loggers import FileHandler
        return FileHandler

    def test_ctor(self):
        handler = self._makeOne(self.filename)
        self.assertTrue(os.path.exists(self.filename), self.filename)
        self.assertEqual(handler.mode, 'a')
        self.assertEqual(handler.baseFilename, self.filename)
        self.assertEqual(handler.stream.name, self.filename)
        handler.close()

    def test_close(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        handler.stream = DummyStream()
        handler.close()
        self.assertEqual(handler.stream.closed, True)

    def test_close_raises(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        handler.stream = DummyStream(OSError)
        self.assertRaises(OSError, handler.close)
        self.assertEqual(handler.stream.closed, False)

    def test_reopen(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        stream = DummyStream()
        handler.stream = stream
        handler.reopen()
        self.assertEqual(stream.closed, True)
        self.assertEqual(handler.stream.name, self.filename)
        handler.close()

    def test_reopen_raises(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        stream = DummyStream()
        handler.stream = stream
        handler.baseFilename = os.path.join(self.basedir, 'notthere', 'a.log')
        self.assertRaises(IOError, handler.reopen)
        self.assertEqual(stream.closed, True)

    def test_remove_exists(self):
        handler = self._makeOne(self.filename)
        self.assertTrue(os.path.exists(self.filename), self.filename)
        handler.remove()
        self.assertFalse(os.path.exists(self.filename), self.filename)

    def test_remove_doesntexist(self):
        handler = self._makeOne(self.filename)
        os.remove(self.filename)
        self.assertFalse(os.path.exists(self.filename), self.filename)
        handler.remove() # should not raise
        self.assertFalse(os.path.exists(self.filename), self.filename)

    def test_remove_raises(self):
        handler = self._makeOne(self.filename)
        os.remove(self.filename)
        os.mkdir(self.filename)
        self.assertTrue(os.path.exists(self.filename), self.filename)
        self.assertRaises(OSError, handler.remove)

    def test_emit_ascii_noerror(self):
        handler = self._makeOne(self.filename)
        record = self._makeLogRecord('hello!')
        handler.emit(record)
        handler.close()
        self.assertEqual(read_file(self.filename,'r'), 'hello!')

#    def test_emit_unicode_noerror(self):
#        handler = self._makeOne(self.filename)
#        record = self._makeLogRecord(u'fi\xed')
#        handler.emit(record)
#        content = open(self.filename, 'r').read()
#        self.assertEqual(content, 'fi\xc3\xad')

    def test_emit_error(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        handler.stream = DummyStream(error=OSError)
        record = self._makeLogRecord('hello!')
        try:
            old_stderr = sys.stderr
            dummy_stderr = DummyStream()
            sys.stderr = dummy_stderr
            handler.emit(record)
        finally:
            sys.stderr = old_stderr

        self.assertTrue(dummy_stderr.written.endswith('OSError\n'),
                        dummy_stderr.written)

class RotatingFileHandlerTests(FileHandlerTests):

    def _getTargetClass(self):
        from supervisor.loggers import RotatingFileHandler
        return RotatingFileHandler

    def test_ctor(self):
        handler = self._makeOne(self.filename)
        self.assertEqual(handler.mode, 'a')
        self.assertEqual(handler.maxBytes, 512*1024*1024)
        self.assertEqual(handler.backupCount, 10)

    def test_emit_does_rollover(self):
        handler = self._makeOne(self.filename, maxBytes=10, backupCount=2)
        record = self._makeLogRecord('a' * 4)

        handler.emit(record) # 4 bytes
        self.assertFalse(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 8 bytes
        self.assertFalse(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 12 bytes, do rollover
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 16 bytes
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 20 bytes
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 24 bytes, do rollover
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertTrue(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 28 bytes
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertTrue(os.path.exists(self.filename + '.2'))

        self.assertEqual(read_file(self.filename), 'a' * 4)
        one = read_file(self.filename+'.1')
        self.assertEqual(one, 'a'*12)
        two = read_file(self.filename+'.2')
        self.assertEqual(two, 'a'*12)

    def test_current_logfile_removed(self):
        handler = self._makeOne(self.filename, maxBytes=6, backupCount=1)
        record = self._makeLogRecord('a' * 4)

        handler.emit(record) # 4 bytes
        self.assertTrue(os.path.exists(self.filename))
        self.assertFalse(os.path.exists(self.filename + '.1'))

        # Someone removes the active log file! :-(
        os.unlink(self.filename)
        self.assertFalse(os.path.exists(self.filename))

        handler.emit(record) # 8 bytes, do rollover
        self.assertTrue(os.path.exists(self.filename))
        self.assertFalse(os.path.exists(self.filename + '.1'))

    def test_removeAndRename_destination_does_not_exist(self):
        inst = self._makeOne(self.filename)
        renames = []
        removes = []
        inst._remove = lambda v: removes.append(v)
        inst._exists = lambda v: False
        inst._rename = lambda s, t: renames.append((s, t))
        inst.removeAndRename('foo', 'bar')
        self.assertEqual(renames, [('foo', 'bar')])
        self.assertEqual(removes, [])

    def test_removeAndRename_destination_exists(self):
        inst = self._makeOne(self.filename)
        renames = []
        removes = []
        inst._remove = lambda v: removes.append(v)
        inst._exists = lambda v: True
        inst._rename = lambda s, t: renames.append((s, t))
        inst.removeAndRename('foo', 'bar')
        self.assertEqual(renames, [('foo', 'bar')])
        self.assertEqual(removes, ['bar'])

    def test_removeAndRename_remove_raises_ENOENT(self):
        def remove(fn):
            raise OSError(errno.ENOENT)
        inst = self._makeOne(self.filename)
        renames = []
        inst._remove = remove
        inst._exists = lambda v: True
        inst._rename = lambda s, t: renames.append((s, t))
        inst.removeAndRename('foo', 'bar')
        self.assertEqual(renames, [('foo', 'bar')])

    def test_removeAndRename_remove_raises_other_than_ENOENT(self):
        def remove(fn):
            raise OSError(errno.EAGAIN)
        inst = self._makeOne(self.filename)
        inst._remove = remove
        inst._exists = lambda v: True
        self.assertRaises(OSError, inst.removeAndRename, 'foo', 'bar')

    def test_removeAndRename_rename_raises_ENOENT(self):
        def rename(s, d):
            raise OSError(errno.ENOENT)
        inst = self._makeOne(self.filename)
        inst._rename = rename
        inst._exists = lambda v: False
        self.assertEqual(inst.removeAndRename('foo', 'bar'), None)

    def test_removeAndRename_rename_raises_other_than_ENOENT(self):
        def rename(s, d):
            raise OSError(errno.EAGAIN)
        inst = self._makeOne(self.filename)
        inst._rename = rename
        inst._exists = lambda v: False
        self.assertRaises(OSError, inst.removeAndRename, 'foo', 'bar')

    def test_doRollover_maxbytes_lte_zero(self):
        inst = self._makeOne(self.filename)
        inst.maxBytes = 0
        self.assertEqual(inst.doRollover(), None)


class BoundIOTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.loggers import BoundIO
        return BoundIO

    def _makeOne(self, maxbytes, buf=''):
        klass = self._getTargetClass()
        return klass(maxbytes, buf)

    def test_write_overflow(self):
        io = self._makeOne(1, 'a')
        io.write('b')
        self.assertEqual(io.buf, 'b')

    def test_getvalue(self):
        io = self._makeOne(1, 'a')
        self.assertEqual(io.getvalue(), 'a')

    def test_clear(self):
        io = self._makeOne(1, 'a')
        io.clear()
        self.assertEqual(io.buf, '')

    def test_close(self):
        io = self._makeOne(1, 'a')
        io.close()
        self.assertEqual(io.buf, '')

class LoggerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.loggers import Logger
        return Logger

    def _makeOne(self, level=None, handlers=None):
        klass = self._getTargetClass()
        return klass(level, handlers)

    def test_blather(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.BLAT)
        logger = self._makeOne(LevelsByName.BLAT, (handler,))
        logger.blather('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.TRAC
        logger.blather('hello')
        self.assertEqual(len(handler.records), 1)

    def test_trace(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.TRAC)
        logger = self._makeOne(LevelsByName.TRAC, (handler,))
        logger.trace('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.DEBG
        logger.trace('hello')
        self.assertEqual(len(handler.records), 1)

    def test_debug(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.DEBG)
        logger = self._makeOne(LevelsByName.DEBG, (handler,))
        logger.debug('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.INFO
        logger.debug('hello')
        self.assertEqual(len(handler.records), 1)

    def test_info(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.INFO)
        logger = self._makeOne(LevelsByName.INFO, (handler,))
        logger.info('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.WARN
        logger.info('hello')
        self.assertEqual(len(handler.records), 1)

    def test_warn(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.WARN)
        logger = self._makeOne(LevelsByName.WARN, (handler,))
        logger.warn('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.ERRO
        logger.warn('hello')
        self.assertEqual(len(handler.records), 1)

    def test_error(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.ERRO)
        logger = self._makeOne(LevelsByName.ERRO, (handler,))
        logger.error('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.CRIT
        logger.error('hello')
        self.assertEqual(len(handler.records), 1)

    def test_critical(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.CRIT)
        logger = self._makeOne(LevelsByName.CRIT, (handler,))
        logger.critical('hello')
        self.assertEqual(len(handler.records), 1)

    def test_close(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.CRIT)
        logger = self._makeOne(LevelsByName.CRIT, (handler,))
        logger.close()
        self.assertEqual(handler.closed, True)

    def test_getvalue(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.CRIT)
        logger = self._makeOne(LevelsByName.CRIT, (handler,))
        self.assertRaises(NotImplementedError, logger.getvalue)


class MockSysLog(mock.Mock):
    def __call__(self, *args, **kwargs):
        message = args[-1]
        if sys.version_info < (3, 0) and isinstance(message, unicode):
            # Python 2.x raises a UnicodeEncodeError when attempting to
            #  transmit unicode characters that don't encode in the
            #  default encoding.
            message.encode()
        super(MockSysLog, self).__call__(*args, **kwargs)

class SyslogHandlerTests(HandlerTests, unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def _getTargetClass(self):
        return __import__('supervisor.loggers').loggers.SyslogHandler

    def _makeOne(self):
        return self._getTargetClass()()

    def test_emit_record_asdict_raises(self):
        class Record(object):
            def asdict(self):
                raise TypeError
        record = Record()
        handler = self._makeOne()
        handled = []
        handler.handleError = lambda: handled.append(True)
        handler.emit(record)
        self.assertEqual(handled, [True])


    @mock.patch('syslog.syslog', MockSysLog())
    def test_emit_ascii_noerror(self):
        handler = self._makeOne()
        record = self._makeLogRecord('hello!')
        handler.emit(record)
        syslog.syslog.assert_called_with('hello!')

    @mock.patch('syslog.syslog', MockSysLog())
    def test_close(self):
        handler = self._makeOne()
        handler.close()  # no-op for syslog

    @mock.patch('syslog.syslog', MockSysLog())
    def test_reopen(self):
        handler = self._makeOne()
        handler.reopen()  # no-op for syslog

    if PY3:
        @mock.patch('syslog.syslog', MockSysLog())
        def test_emit_unicode_noerror(self):
            handler = self._makeOne()
            record = self._makeLogRecord('fií')
            handler.emit(record)
            syslog.syslog.assert_called_with('fií')
        def test_emit_unicode_witherror(self):
            handler = self._makeOne()
            called = []
            def fake_syslog(msg):
                if not called:
                    called.append(msg)
                    raise UnicodeError
            handler._syslog = fake_syslog
            record = self._makeLogRecord('fií')
            handler.emit(record)
            self.assertEqual(called, ['fií'])
    else:
        @mock.patch('syslog.syslog', MockSysLog())
        def test_emit_unicode_noerror(self):
            handler = self._makeOne()
            inp = as_string('fií')
            record = self._makeLogRecord(inp)
            handler.emit(record)
            syslog.syslog.assert_called_with('fi\xc3\xad')
        def test_emit_unicode_witherror(self):
            handler = self._makeOne()
            called = []
            def fake_syslog(msg):
                if not called:
                    called.append(msg)
                    raise UnicodeError
            handler._syslog = fake_syslog
            record = self._makeLogRecord(as_string('fií'))
            handler.emit(record)
            self.assertEqual(called, [as_string('fi\xc3\xad')])

class DummyHandler:
    close = False
    def __init__(self, level):
        self.level = level
        self.records = []
    def emit(self, record):
        self.records.append(record)
    def close(self):
        self.closed = True

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_options
"""Test suite for supervisor.options"""

import logging
import os
import sys
import tempfile
import supervisor.medusa.text_socket as socket
import unittest
import signal
import shutil
import errno

from supervisor import read_file
from supervisor.compat import StringIO
from supervisor.compat import as_bytes

from supervisor.tests.base import Mock, sentinel, patch
from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import DummyLogger
from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummySocketConfig
from supervisor.tests.base import lstrip


def missing_but_potential_file():
    """Quick and dirty way of coming up with a decent filename that might exist"""
    tempf = tempfile.NamedTemporaryFile()
    fname = tempf.name
    tempf.close()
    return fname

class DummyExitException(Exception):
    def __init__(self, exitcode):
        self.exitcode = exitcode

def dummy_exit():
    """Dummy out exit so we don't actually exit"""
    def dummy_exit(_exitcode=0):
        raise DummyExitException(exitcode=_exitcode)
    return dummy_exit

def _getTempFile(name):
    prefix = 'supervisor.{0}.'.format(name)
    return tempfile.NamedTemporaryFile(prefix=prefix)

logger = logging.getLogger(__name__)


class OptionTests(unittest.TestCase):

    def _getTargetClass(self):
        from supervisor.options import Options
        return Options

    def _makeOptions(self, read_error=False):
        Options = self._getTargetClass()
        from supervisor.datatypes import integer

        class MyOptions(Options):
            master = {
                'other': 41 }
            def __init__(self, read_error=read_error):
                self.read_error = read_error
                Options.__init__(self)
                class Foo(object): pass
                self.configroot = Foo()

            def read_config(self, fp):
                if self.read_error:
                    raise ValueError(self.read_error)
                # Pretend we read it from file:
                self.configroot.__dict__.update(self.default_map)
                self.configroot.__dict__.update(self.master)

        options = MyOptions()
        options.configfile = StringIO()
        options.add(name='anoption', confname='anoption',
                    short='o', long='option', default='default')
        options.add(name='other', confname='other', env='OTHER',
                    short='p:', long='other=', handler=integer)
        return options

    def test_add_flag_not_None_handler_not_None(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(ValueError, inst.add, flag=True, handler=True)

    def test_add_flag_not_None_long_false_short_false(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            flag=True,
            long=False,
            short=False,
            )

    def test_add_flag_not_None_short_endswith_colon(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            flag=True,
            long=False,
            short=":",
            )

    def test_add_flag_not_None_long_endswith_equal(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            flag=True,
            long='=',
            short=False,
            )

    def test_add_inconsistent_short_long_options(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            long='=',
            short='abc',
            )

    def test_add_short_option_startswith_dash(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            long=False,
            short='-abc',
            )

    def test_add_short_option_too_long(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            long=False,
            short='abc',
            )

    def test_add_duplicate_short_option_key(self):
        cls = self._getTargetClass()
        inst = cls()
        inst.options_map = {'-a':True}
        self.assertRaises(
            ValueError,
            inst.add,
            long=False,
            short='a',
            )

    def test_add_long_option_startswith_dash(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            long='-abc',
            short=False,
            )

    def test_add_duplicate_long_option_key(self):
        cls = self._getTargetClass()
        inst = cls()
        inst.options_map = {'--abc':True}
        self.assertRaises(
            ValueError,
            inst.add,
            long='abc',
            short=False,
            )

    def test_searchpaths(self):
        options = self._makeOptions()
        self.assertEqual(len(options.searchpaths), 5)
        self.assertTrue('supervisord.conf' in options.searchpaths)
        self.assertTrue('etc/supervisord.conf' in options.searchpaths)
        self.assertTrue('/etc/supervisord.conf' in options.searchpaths)

    def test_options_and_args_order(self):
        # Only config file exists
        options = self._makeOptions()
        options.realize([])
        self.assertEqual(options.anoption, 'default')
        self.assertEqual(options.other, 41)

        # Env should trump config
        options = self._makeOptions()
        os.environ['OTHER'] = '42'
        options.realize([])
        self.assertEqual(options.other, 42)

        # Opt should trump both env (still set) and config
        options = self._makeOptions()
        options.realize(['-p', '43'])
        self.assertEqual(options.other, 43)
        del os.environ['OTHER']

    def test_config_reload(self):
        options = self._makeOptions()
        options.realize([])
        self.assertEqual(options.other, 41)
        options.master['other'] = 42
        options.process_config()
        self.assertEqual(options.other, 42)

    def test_config_reload_do_usage_false(self):
        options = self._makeOptions(read_error='error')
        self.assertRaises(ValueError, options.process_config,
                          False)

    def test_config_reload_do_usage_true(self):
        options = self._makeOptions(read_error='error')
        L = []
        def exit(num):
            L.append(num)
        options.stderr = options.stdout = StringIO()
        options.exit = exit
        options.configroot.anoption = 1
        options.configroot.other = 1
        options.process_config(True)
        self.assertEqual(L, [2])

    def test__set(self):
        from supervisor.options import Options
        options = Options()
        options._set('foo', 'bar', 0)
        self.assertEqual(options.foo, 'bar')
        self.assertEqual(options.attr_priorities['foo'], 0)
        options._set('foo', 'baz', 1)
        self.assertEqual(options.foo, 'baz')
        self.assertEqual(options.attr_priorities['foo'], 1)
        options._set('foo', 'gazonk', 0)
        self.assertEqual(options.foo, 'baz')
        self.assertEqual(options.attr_priorities['foo'], 1)
        options._set('foo', 'gazonk', 1)
        self.assertEqual(options.foo, 'gazonk')

    def test_missing_default_config(self):
        options = self._makeOptions()
        options.searchpaths = [missing_but_potential_file()]
        options.exit = dummy_exit()
        options.stderr = StringIO()
        try:
            options.default_configfile()
        except DummyExitException as e:
            self.assertEqual(e.exitcode, 2)
        else:
            self.fail("expected exception")
        self.assertTrue(options.stderr.getvalue().startswith("Error: No config file found at default paths"))

    def test_default_config(self):
        options = self._makeOptions()
        tempf = tempfile.NamedTemporaryFile()
        options.searchpaths = [tempf.name]
        config = options.default_configfile()
        self.assertEqual(config, tempf.name)
        tempf.close()

    def test_help(self):
        options = self._makeOptions()
        options.exit = dummy_exit()
        options.stdout = StringIO()
        options.progname = 'test_help'
        options.doc = 'A sample docstring for %s'
        try:
            options.help('Argument ignored?')
        except DummyExitException:
            self.assertEqual(options.stdout.getvalue(), 'A sample docstring for test_help\n')
        else:
            self.fail('help() did not try to exit.')

class ClientOptionsTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import ClientOptions
        return ClientOptions

    def _makeOne(self):
        return self._getTargetClass()()

    def test_no_config_file(self):
        """Making sure config file is not required."""
        instance = self._makeOne()

        # No default config file search in case they would exist
        self.assertTrue(len(instance.searchpaths) > 0)
        instance.searchpaths = []

        instance.exit = dummy_exit()

        instance.realize(args=['-s', 'http://localhost:9001', '-u', 'chris',
                               '-p', '123'])

        self.assertEqual(instance.interactive, 1)
        self.assertEqual(instance.serverurl, 'http://localhost:9001')
        self.assertEqual(instance.username, 'chris')
        self.assertEqual(instance.password, '123')

    def test_options(self):
        tempdir = tempfile.gettempdir()
        s = lstrip("""[supervisorctl]
        serverurl=http://localhost:9001
        username=chris
        password=123
        prompt=mysupervisor
        history_file=%s/sc_history
        """ % tempdir)

        fp = StringIO(s)
        instance = self._makeOne()
        instance.configfile = fp
        instance.realize(args=[])
        self.assertEqual(instance.interactive, True)
        history_file = os.path.join(tempdir, 'sc_history')
        self.assertEqual(instance.history_file, history_file)
        options = instance.configroot.supervisorctl
        self.assertEqual(options.prompt, 'mysupervisor')
        self.assertEqual(options.serverurl, 'http://localhost:9001')
        self.assertEqual(options.username, 'chris')
        self.assertEqual(options.password, '123')
        self.assertEqual(options.history_file, history_file)

    def test_unreadable_config_file(self):
        fname = missing_but_potential_file()
        self.assertFalse(os.path.exists(fname))

        instance = self._makeOne()
        instance.stderr = StringIO()

        instance.exit = dummy_exit()
        try:
            instance.realize(args=['-c', fname])
        except DummyExitException as e:
            self.assertEqual(e.exitcode, 2)
        else:
            self.fail("expected exception")

        try:
            instance.read_config(fname)
        except ValueError as e:
            self.assertTrue("could not find config file" in str(e))
        else:
            self.fail("expected exception")

        tempf = tempfile.NamedTemporaryFile()
        os.chmod(tempf.name, 0) # Removing read perms
        try:
            instance.read_config(tempf.name)
        except ValueError as e:
            self.assertTrue("could not read config file" in str(e))
        else:
            self.fail("expected exception")
        tempf.close()

    def test_options_unixsocket_cli(self):
        fp = StringIO('[supervisorctl]')
        instance = self._makeOne()
        instance.configfile = fp
        instance.realize(args=['--serverurl', 'unix:///dev/null'])
        self.assertEqual(instance.serverurl, 'unix:///dev/null')

class ServerOptionsTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import ServerOptions
        return ServerOptions

    def _makeOne(self):
        return self._getTargetClass()()

    def test_version(self):
        from supervisor.options import VERSION
        options = self._makeOne()
        options.stdout = StringIO()
        self.assertRaises(SystemExit, options.version, None)
        self.assertEqual(options.stdout.getvalue(), VERSION + '\n')

    def test_options(self):
        s = lstrip("""[inet_http_server]
        port=127.0.0.1:8999
        username=chrism
        password=foo

        [supervisord]
        directory=%(tempdir)s
        backofflimit=10
        user=root
        umask=022
        logfile=supervisord.log
        logfile_maxbytes=1000MB
        logfile_backups=5
        loglevel=error
        pidfile=supervisord.pid
        nodaemon=true
        identifier=fleeb
        childlogdir=%(tempdir)s
        nocleanup=true
        minfds=2048
        minprocs=300
        environment=FAKE_ENV_VAR=/some/path

        [program:cat1]
        command=/bin/cat
        priority=1
        autostart=true
        user=root
        stdout_logfile=/tmp/cat.log
        stopsignal=KILL
        stopwaitsecs=5
        startsecs=5
        startretries=10
        directory=/tmp
        umask=002

        [program:cat2]
        priority=2
        command=/bin/cat
        autostart=true
        autorestart=false
        stdout_logfile_maxbytes = 1024
        stdout_logfile_backups = 2
        stdout_logfile = /tmp/cat2.log

        [program:cat3]
        priority=3
        process_name = replaced
        command=/bin/cat
        autorestart=true
        exitcodes=0,1,127
        stopasgroup=true
        killasgroup=true

        [program:cat4]
        priority=4
        process_name = fleeb_%%(process_num)s
        numprocs = 2
        command = /bin/cat
        autorestart=unexpected

        [program:cat5]
        priority=5
        process_name = foo_%%(process_num)02d
        numprocs = 2
        numprocs_start = 1
        command = /bin/cat
        directory = /some/path/foo_%%(process_num)02d
        """ % {'tempdir':tempfile.gettempdir()})

        from supervisor import datatypes

        fp = StringIO(s)
        instance = self._makeOne()
        instance.configfile = fp
        instance.realize(args=[])
        options = instance.configroot.supervisord
        self.assertEqual(options.directory, tempfile.gettempdir())
        self.assertEqual(options.umask, 18) # 022 in Py2, 0o22 in Py3
        self.assertEqual(options.logfile, 'supervisord.log')
        self.assertEqual(options.logfile_maxbytes, 1000 * 1024 * 1024)
        self.assertEqual(options.logfile_backups, 5)
        self.assertEqual(options.loglevel, 40)
        self.assertEqual(options.pidfile, 'supervisord.pid')
        self.assertEqual(options.nodaemon, True)
        self.assertEqual(options.identifier, 'fleeb')
        self.assertEqual(options.childlogdir, tempfile.gettempdir())
        self.assertEqual(len(options.server_configs), 1)
        self.assertEqual(options.server_configs[0]['family'], socket.AF_INET)
        self.assertEqual(options.server_configs[0]['host'], '127.0.0.1')
        self.assertEqual(options.server_configs[0]['port'], 8999)
        self.assertEqual(options.server_configs[0]['username'], 'chrism')
        self.assertEqual(options.server_configs[0]['password'], 'foo')
        self.assertEqual(options.nocleanup, True)
        self.assertEqual(options.minfds, 2048)
        self.assertEqual(options.minprocs, 300)
        self.assertEqual(options.nocleanup, True)
        self.assertEqual(len(options.process_group_configs), 5)
        self.assertEqual(options.environment, dict(FAKE_ENV_VAR='/some/path'))

        cat1 = options.process_group_configs[0]
        self.assertEqual(cat1.name, 'cat1')
        self.assertEqual(cat1.priority, 1)
        self.assertEqual(len(cat1.process_configs), 1)

        proc1 = cat1.process_configs[0]
        self.assertEqual(proc1.name, 'cat1')
        self.assertEqual(proc1.command, '/bin/cat')
        self.assertEqual(proc1.priority, 1)
        self.assertEqual(proc1.autostart, True)
        self.assertEqual(proc1.autorestart, datatypes.RestartWhenExitUnexpected)
        self.assertEqual(proc1.startsecs, 5)
        self.assertEqual(proc1.startretries, 10)
        self.assertEqual(proc1.uid, 0)
        self.assertEqual(proc1.stdout_logfile, '/tmp/cat.log')
        self.assertEqual(proc1.stopsignal, signal.SIGKILL)
        self.assertEqual(proc1.stopwaitsecs, 5)
        self.assertEqual(proc1.stopasgroup, False)
        self.assertEqual(proc1.killasgroup, False)
        self.assertEqual(proc1.stdout_logfile_maxbytes,
                         datatypes.byte_size('50MB'))
        self.assertEqual(proc1.stdout_logfile_backups, 10)
        self.assertEqual(proc1.exitcodes, [0,2])
        self.assertEqual(proc1.directory, '/tmp')
        self.assertEqual(proc1.umask, 2)
        self.assertEqual(proc1.environment, dict(FAKE_ENV_VAR='/some/path'))

        cat2 = options.process_group_configs[1]
        self.assertEqual(cat2.name, 'cat2')
        self.assertEqual(cat2.priority, 2)
        self.assertEqual(len(cat2.process_configs), 1)

        proc2 = cat2.process_configs[0]
        self.assertEqual(proc2.name, 'cat2')
        self.assertEqual(proc2.command, '/bin/cat')
        self.assertEqual(proc2.priority, 2)
        self.assertEqual(proc2.autostart, True)
        self.assertEqual(proc2.autorestart, False)
        self.assertEqual(proc2.uid, None)
        self.assertEqual(proc2.stdout_logfile, '/tmp/cat2.log')
        self.assertEqual(proc2.stopsignal, signal.SIGTERM)
        self.assertEqual(proc2.stopasgroup, False)
        self.assertEqual(proc2.killasgroup, False)
        self.assertEqual(proc2.stdout_logfile_maxbytes, 1024)
        self.assertEqual(proc2.stdout_logfile_backups, 2)
        self.assertEqual(proc2.exitcodes, [0,2])
        self.assertEqual(proc2.directory, None)

        cat3 = options.process_group_configs[2]
        self.assertEqual(cat3.name, 'cat3')
        self.assertEqual(cat3.priority, 3)
        self.assertEqual(len(cat3.process_configs), 1)

        proc3 = cat3.process_configs[0]
        self.assertEqual(proc3.name, 'replaced')
        self.assertEqual(proc3.command, '/bin/cat')
        self.assertEqual(proc3.priority, 3)
        self.assertEqual(proc3.autostart, True)
        self.assertEqual(proc3.autorestart, datatypes.RestartUnconditionally)
        self.assertEqual(proc3.uid, None)
        self.assertEqual(proc3.stdout_logfile, datatypes.Automatic)
        self.assertEqual(proc3.stdout_logfile_maxbytes,
                         datatypes.byte_size('50MB'))
        self.assertEqual(proc3.stdout_logfile_backups, 10)
        self.assertEqual(proc3.exitcodes, [0,1,127])
        self.assertEqual(proc3.stopsignal, signal.SIGTERM)
        self.assertEqual(proc3.stopasgroup, True)
        self.assertEqual(proc3.killasgroup, True)

        cat4 = options.process_group_configs[3]
        self.assertEqual(cat4.name, 'cat4')
        self.assertEqual(cat4.priority, 4)
        self.assertEqual(len(cat4.process_configs), 2)

        proc4_a = cat4.process_configs[0]
        self.assertEqual(proc4_a.name, 'fleeb_0')
        self.assertEqual(proc4_a.command, '/bin/cat')
        self.assertEqual(proc4_a.priority, 4)
        self.assertEqual(proc4_a.autostart, True)
        self.assertEqual(proc4_a.autorestart,
                         datatypes.RestartWhenExitUnexpected)
        self.assertEqual(proc4_a.uid, None)
        self.assertEqual(proc4_a.stdout_logfile, datatypes.Automatic)
        self.assertEqual(proc4_a.stdout_logfile_maxbytes,
                         datatypes.byte_size('50MB'))
        self.assertEqual(proc4_a.stdout_logfile_backups, 10)
        self.assertEqual(proc4_a.exitcodes, [0,2])
        self.assertEqual(proc4_a.stopsignal, signal.SIGTERM)
        self.assertEqual(proc4_a.stopasgroup, False)
        self.assertEqual(proc4_a.killasgroup, False)
        self.assertEqual(proc4_a.directory, None)

        proc4_b = cat4.process_configs[1]
        self.assertEqual(proc4_b.name, 'fleeb_1')
        self.assertEqual(proc4_b.command, '/bin/cat')
        self.assertEqual(proc4_b.priority, 4)
        self.assertEqual(proc4_b.autostart, True)
        self.assertEqual(proc4_b.autorestart,
                         datatypes.RestartWhenExitUnexpected)
        self.assertEqual(proc4_b.uid, None)
        self.assertEqual(proc4_b.stdout_logfile, datatypes.Automatic)
        self.assertEqual(proc4_b.stdout_logfile_maxbytes,
                         datatypes.byte_size('50MB'))
        self.assertEqual(proc4_b.stdout_logfile_backups, 10)
        self.assertEqual(proc4_b.exitcodes, [0,2])
        self.assertEqual(proc4_b.stopsignal, signal.SIGTERM)
        self.assertEqual(proc4_b.stopasgroup, False)
        self.assertEqual(proc4_b.killasgroup, False)
        self.assertEqual(proc4_b.directory, None)

        cat5 = options.process_group_configs[4]
        self.assertEqual(cat5.name, 'cat5')
        self.assertEqual(cat5.priority, 5)
        self.assertEqual(len(cat5.process_configs), 2)

        proc5_a = cat5.process_configs[0]
        self.assertEqual(proc5_a.name, 'foo_01')
        self.assertEqual(proc5_a.directory, '/some/path/foo_01')

        proc5_b = cat5.process_configs[1]
        self.assertEqual(proc5_b.name, 'foo_02')
        self.assertEqual(proc5_b.directory, '/some/path/foo_02')

        here = os.path.abspath(os.getcwd())
        self.assertEqual(instance.uid, 0)
        self.assertEqual(instance.gid, 0)
        self.assertEqual(instance.directory, tempfile.gettempdir())
        self.assertEqual(instance.umask, 18) # 022 in Py2, 0o22 in Py3
        self.assertEqual(instance.logfile, os.path.join(here,'supervisord.log'))
        self.assertEqual(instance.logfile_maxbytes, 1000 * 1024 * 1024)
        self.assertEqual(instance.logfile_backups, 5)
        self.assertEqual(instance.loglevel, 40)
        self.assertEqual(instance.pidfile, os.path.join(here,'supervisord.pid'))
        self.assertEqual(instance.nodaemon, True)
        self.assertEqual(instance.passwdfile, None)
        self.assertEqual(instance.identifier, 'fleeb')
        self.assertEqual(instance.childlogdir, tempfile.gettempdir())

        self.assertEqual(len(instance.server_configs), 1)
        self.assertEqual(instance.server_configs[0]['family'], socket.AF_INET)
        self.assertEqual(instance.server_configs[0]['host'], '127.0.0.1')
        self.assertEqual(instance.server_configs[0]['port'], 8999)
        self.assertEqual(instance.server_configs[0]['username'], 'chrism')
        self.assertEqual(instance.server_configs[0]['password'], 'foo')

        self.assertEqual(instance.nocleanup, True)
        self.assertEqual(instance.minfds, 2048)
        self.assertEqual(instance.minprocs, 300)

    def test_no_config_file_exits(self):
        instance = self._makeOne()

        # No default config file search in case they would exist
        self.assertTrue(len(instance.searchpaths) > 0)
        instance.searchpaths = []

        instance.exit = dummy_exit()

        # Making sure we capture stdout and stderr
        instance.stderr = StringIO()

        try:
            instance.realize()
        except DummyExitException as e:
            # Caught expected exception
            import traceback
            self.assertEqual(
                e.exitcode, 2,
                "Wrong exitcode for: %s" % traceback.format_exc()
                )
        else:
            self.fail("Did not get a DummyExitException.")

    def test_reload(self):
        text = lstrip("""\
        [supervisord]
        user=root

        [program:one]
        command = /bin/cat

        [program:two]
        command = /bin/dog

        [program:four]
        command = /bin/sheep

        [group:thegroup]
        programs = one,two
        """)

        instance = self._makeOne()
        instance.configfile = StringIO(text)
        instance.realize(args=[])

        section = instance.configroot.supervisord

        self.assertEqual(len(section.process_group_configs), 2)

        cat = section.process_group_configs[0]
        self.assertEqual(len(cat.process_configs), 1)

        cat = section.process_group_configs[1]
        self.assertEqual(len(cat.process_configs), 2)
        self.assertTrue(section.process_group_configs is
                        instance.process_group_configs)

        text = lstrip("""\
        [supervisord]
        user=root

        [program:one]
        command = /bin/cat

        [program:three]
        command = /bin/pig

        [group:thegroup]
        programs = three
        """)
        instance.configfile = StringIO(text)
        instance.process_config()

        section = instance.configroot.supervisord

        self.assertEqual(len(section.process_group_configs), 2)

        cat = section.process_group_configs[0]
        self.assertEqual(len(cat.process_configs), 1)
        proc = cat.process_configs[0]
        self.assertEqual(proc.name, 'one')
        self.assertEqual(proc.command, '/bin/cat')
        self.assertTrue(section.process_group_configs is
                        instance.process_group_configs)

        cat = section.process_group_configs[1]
        self.assertEqual(len(cat.process_configs), 1)
        proc = cat.process_configs[0]
        self.assertEqual(proc.name, 'three')
        self.assertEqual(proc.command, '/bin/pig')

    def test_reload_clears_parse_warnings(self):
        instance = self._makeOne()
        old_warning = "Warning from a prior config read"
        instance.parse_warnings = [old_warning]

        text = lstrip("""\
        [supervisord]
        user=root

        [program:cat]
        command = /bin/cat
        """)
        instance.configfile = StringIO(text)
        instance.realize(args=[])
        self.assertFalse(old_warning in instance.parse_warnings)

    def test_unreadable_config_file(self):
        # Quick and dirty way of coming up with a decent filename
        tempf = tempfile.NamedTemporaryFile()
        fname = tempf.name
        tempf.close()
        self.assertFalse(os.path.exists(fname))

        instance = self._makeOne()
        instance.stderr = StringIO()

        instance.exit = dummy_exit()
        try:
            instance.realize(args=['-c', fname])
        except DummyExitException as e:
            self.assertEqual(e.exitcode, 2)
        else:
            self.fail("expected exception")

        try:
            instance.read_config(fname)
        except ValueError as e:
            self.assertTrue("could not find config file" in str(e))
        else:
            self.fail("expected exception")

        tempf = tempfile.NamedTemporaryFile()
        os.chmod(tempf.name, 0) # Removing read perms
        try:
            instance.read_config(tempf.name)
        except ValueError as e:
            self.assertTrue("could not read config file" in str(e))
        else:
            self.fail("expected exception")
        tempf.close()

    def test_readFile_failed(self):
        from supervisor.options import readFile
        try:
            readFile('/notthere', 0, 10)
        except ValueError as inst:
            self.assertEqual(inst.args[0], 'FAILED')
        else:
            raise AssertionError("Didn't raise")

    def test_include_with_no_matching_files_logs_warning(self):
        instance = self._makeOne()
        text = lstrip("""\
        [supervisord]
        user=root

        [include]
        files=nonexistant/*
        """)
        instance.configfile = StringIO(text)
        instance.realize(args=[])
        self.assertEqual(instance.parse_warnings,
                         ['No file matches via include "./nonexistant/*"'])

    def test_get_pid(self):
        instance = self._makeOne()
        self.assertEqual(os.getpid(), instance.get_pid())

    def test_get_signal_delegates_to_signal_receiver(self):
        instance = self._makeOne()
        instance.signal_receiver.receive(signal.SIGTERM, None)
        instance.signal_receiver.receive(signal.SIGCHLD, None)
        self.assertEqual(instance.get_signal(), signal.SIGTERM)
        self.assertEqual(instance.get_signal(), signal.SIGCHLD)
        self.assertEqual(instance.get_signal(), None)

    def test_check_execv_args_cant_find_command(self):
        instance = self._makeOne()
        from supervisor.options import NotFound
        self.assertRaises(NotFound, instance.check_execv_args,
                          '/not/there', None, None)

    def test_check_execv_args_notexecutable(self):
        instance = self._makeOne()
        from supervisor.options import NotExecutable
        self.assertRaises(NotExecutable,
                          instance.check_execv_args, '/etc/passwd',
                          ['etc/passwd'], os.stat('/etc/passwd'))

    def test_check_execv_args_isdir(self):
        instance = self._makeOne()
        from supervisor.options import NotExecutable
        self.assertRaises(NotExecutable,
                          instance.check_execv_args, '/',
                          ['/'], os.stat('/'))

    def test_cleanup_afunix_unlink(self):
        fn = tempfile.mktemp()
        f = open(fn, 'w')
        f.write('foo')
        f.close()
        instance = self._makeOne()
        class Port:
            family = socket.AF_UNIX
            address = fn
        class Server:
            pass
        instance.httpservers = [({'family':socket.AF_UNIX, 'file':fn},
                                 Server())]
        instance.pidfile = ''
        instance.cleanup()
        self.assertFalse(os.path.exists(fn))

    def test_cleanup_afunix_nounlink(self):
        fn = tempfile.mktemp()
        try:
            f = open(fn, 'w')
            f.write('foo')
            f.close()
            instance = self._makeOne()
            class Port:
                family = socket.AF_UNIX
                address = fn
            class Server:
                pass
            instance.httpservers = [({'family':socket.AF_UNIX, 'file':fn},
                                     Server())]
            instance.pidfile = ''
            instance.unlink_socketfiles = False
            instance.cleanup()
            self.assertTrue(os.path.exists(fn))
        finally:
            try:
                os.unlink(fn)
            except OSError:
                pass

    def test_close_httpservers(self):
        instance = self._makeOne()
        class Server:
            closed = False
            def close(self):
                self.closed = True
        server = Server()
        instance.httpservers = [({}, server)]
        instance.close_httpservers()
        self.assertEqual(server.closed, True)

    def test_close_logger(self):
        instance = self._makeOne()
        logger = DummyLogger()
        instance.logger = logger
        instance.close_logger()
        self.assertEqual(logger.closed, True)

    def test_write_pidfile_ok(self):
        fn = tempfile.mktemp()
        try:
            instance = self._makeOne()
            instance.logger = DummyLogger()
            instance.pidfile = fn
            instance.write_pidfile()
            self.assertTrue(os.path.exists(fn))
            pid = int(read_file(fn)[:-1])
            self.assertEqual(pid, os.getpid())
            msg = instance.logger.data[0]
            self.assertTrue(msg.startswith('supervisord started with pid'))
        finally:
            try:
                os.unlink(fn)
            except OSError:
                pass

    def test_write_pidfile_fail(self):
        fn = '/cannot/possibly/exist'
        instance = self._makeOne()
        instance.logger = DummyLogger()
        instance.pidfile = fn
        instance.write_pidfile()
        msg = instance.logger.data[0]
        self.assertTrue(msg.startswith('could not write pidfile'))

    def test_close_fd(self):
        instance = self._makeOne()
        innie, outie = os.pipe()
        os.read(innie, 0) # we can read it while its open
        os.write(outie, as_bytes('foo')) # we can write to it while its open
        instance.close_fd(innie)
        self.assertRaises(OSError, os.read, innie, 0)
        instance.close_fd(outie)
        self.assertRaises(OSError, os.write, outie, as_bytes('foo'))

    def test_processes_from_section(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        priority = 1
        autostart = false
        autorestart = false
        startsecs = 100
        startretries = 100
        user = root
        stdout_logfile = NONE
        stdout_logfile_backups = 1
        stdout_logfile_maxbytes = 100MB
        stdout_events_enabled = true
        stopsignal = KILL
        stopwaitsecs = 100
        killasgroup = true
        exitcodes = 1,4
        redirect_stderr = false
        environment = KEY1=val1,KEY2=val2,KEY3=%(process_num)s
        numprocs = 2
        process_name = %(group_name)s_%(program_name)s_%(process_num)02d
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
        self.assertEqual(len(pconfigs), 2)
        pconfig = pconfigs[0]
        self.assertEqual(pconfig.name, 'bar_foo_00')
        self.assertEqual(pconfig.command, '/bin/cat')
        self.assertEqual(pconfig.autostart, False)
        self.assertEqual(pconfig.autorestart, False)
        self.assertEqual(pconfig.startsecs, 100)
        self.assertEqual(pconfig.startretries, 100)
        self.assertEqual(pconfig.uid, 0)
        self.assertEqual(pconfig.stdout_logfile, None)
        self.assertEqual(pconfig.stdout_capture_maxbytes, 0)
        self.assertEqual(pconfig.stdout_logfile_maxbytes, 104857600)
        self.assertEqual(pconfig.stdout_events_enabled, True)
        self.assertEqual(pconfig.stopsignal, signal.SIGKILL)
        self.assertEqual(pconfig.stopasgroup, False)
        self.assertEqual(pconfig.killasgroup, True)
        self.assertEqual(pconfig.stopwaitsecs, 100)
        self.assertEqual(pconfig.exitcodes, [1,4])
        self.assertEqual(pconfig.redirect_stderr, False)
        self.assertEqual(pconfig.environment,
                         {'KEY1':'val1', 'KEY2':'val2', 'KEY3':'0'})

    def test_processes_from_section_host_node_name_expansion(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/foo --host=%(host_node_name)s
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
        import platform
        expected = "/bin/foo --host=" + platform.node()
        self.assertEqual(pconfigs[0].command, expected)

    def test_processes_from_section_environment_variables_expansion(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/foo --path='%(ENV_PATH)s'
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
        expected = "/bin/foo --path='%s'" % os.environ['PATH']
        self.assertEqual(pconfigs[0].command, expected)

    def test_processes_from_section_bad_program_name_spaces(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:spaces are bad]
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:spaces are bad', None)

    def test_processes_from_section_bad_program_name_colons(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:colons:are:bad]
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:colons:are:bad', None)

    def test_processes_from_section_no_procnum_in_processname(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        numprocs = 2
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_no_command(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        numprocs = 2
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_missing_replacement_in_process_name(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = %(not_there)s
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_bad_expression_in_process_name(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = %(program_name)
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_bad_chars_in_process_name(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = colons:are:bad
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_stopasgroup_implies_killasgroup(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = %(program_name)s
        stopasgroup = true
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
        self.assertEqual(len(pconfigs), 1)
        pconfig = pconfigs[0]
        self.assertEqual(pconfig.stopasgroup, True)
        self.assertEqual(pconfig.killasgroup, True)

    def test_processes_from_section_killasgroup_mismatch_w_stopasgroup(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = %(program_name)s
        stopasgroup = true
        killasgroup = false
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_autolog_without_rollover(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/foo
        stdout_logfile = AUTO
        stdout_logfile_maxbytes = 0
        stderr_logfile = AUTO
        stderr_logfile_maxbytes = 0
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        instance.logger = DummyLogger()
        config.read_string(text)
        instance.processes_from_section(config, 'program:foo', None)
        self.assertEqual(instance.parse_warnings[0],
             'For [program:foo], AUTO logging used for stdout_logfile '
             'without rollover, set maxbytes > 0 to avoid filling up '
              'filesystem unintentionally')
        self.assertEqual(instance.parse_warnings[1],
             'For [program:foo], AUTO logging used for stderr_logfile '
             'without rollover, set maxbytes > 0 to avoid filling up '
              'filesystem unintentionally')

    def test_homogeneous_process_groups_from_parser(self):
        text = lstrip("""\
        [program:many]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/cat
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 1)
        gconfig = gconfigs[0]
        self.assertEqual(gconfig.name, 'many')
        self.assertEqual(gconfig.priority, 1)
        self.assertEqual(len(gconfig.process_configs), 2)

    def test_event_listener_pools_from_parser(self):
        text = lstrip("""\
        [eventlistener:dog]
        events=PROCESS_COMMUNICATION
        process_name = %(program_name)s_%(process_num)s
        command = /bin/dog
        numprocs = 2
        priority = 1

        [eventlistener:cat]
        events=PROCESS_COMMUNICATION
        process_name = %(program_name)s_%(process_num)s
        command = /bin/cat
        numprocs = 3

        [eventlistener:biz]
        events=PROCESS_COMMUNICATION
        process_name = %(program_name)s_%(process_num)s
        command = /bin/biz
        numprocs = 2
        """)
        from supervisor.options import UnhosedConfigParser
        from supervisor.dispatchers import default_handler
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 3)

        gconfig1 = gconfigs[0]
        self.assertEqual(gconfig1.name, 'biz')
        self.assertEqual(gconfig1.result_handler, default_handler)
        self.assertEqual(len(gconfig1.process_configs), 2)

        gconfig1 = gconfigs[1]
        self.assertEqual(gconfig1.name, 'cat')
        self.assertEqual(gconfig1.priority, -1)
        self.assertEqual(gconfig1.result_handler, default_handler)
        self.assertEqual(len(gconfig1.process_configs), 3)

        gconfig1 = gconfigs[2]
        self.assertEqual(gconfig1.name, 'dog')
        self.assertEqual(gconfig1.priority, 1)
        self.assertEqual(gconfig1.result_handler, default_handler)
        self.assertEqual(len(gconfig1.process_configs), 2)

    def test_event_listener_pool_with_event_results_handler(self):
        text = lstrip("""\
        [eventlistener:dog]
        events=PROCESS_COMMUNICATION
        command = /bin/dog
        result_handler = supervisor.tests.base:dummy_handler
        """)
        from supervisor.options import UnhosedConfigParser
        from supervisor.tests.base import dummy_handler
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 1)

        gconfig1 = gconfigs[0]
        self.assertEqual(gconfig1.result_handler, dummy_handler)

    def test_event_listener_pool_noeventsline(self):
        text = lstrip("""\
        [eventlistener:dog]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/dog
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_event_listener_pool_unknown_eventtype(self):
        text = lstrip("""\
        [eventlistener:dog]
        events=PROCESS_COMMUNICATION,THIS_EVENT_TYPE_DOESNT_EXIST
        process_name = %(program_name)s_%(process_num)s
        command = /bin/dog
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_programs_from_parser(self):
        from supervisor.options import FastCGIGroupConfig
        from supervisor.options import FastCGIProcessConfig
        text = lstrip("""\
        [fcgi-program:foo]
        socket = unix:///tmp/%(program_name)s.sock
        socket_owner = testuser:testgroup
        socket_mode = 0666
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1

        [fcgi-program:bar]
        socket = unix:///tmp/%(program_name)s.sock
        process_name = %(program_name)s_%(process_num)s
        command = /bin/bar
        user = testuser
        numprocs = 3

        [fcgi-program:flub]
        socket = unix:///tmp/%(program_name)s.sock
        command = /bin/flub

        [fcgi-program:cub]
        socket = tcp://localhost:6000
        command = /bin/cub
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()

        #Patch pwd and grp module functions to give us sentinel
        #uid/gid values so that the test does not depend on
        #any specific system users
        pwd_mock = Mock()
        pwd_mock.return_value = (None, None, sentinel.uid, sentinel.gid)
        grp_mock = Mock()
        grp_mock.return_value = (None, None, sentinel.gid)
        @patch('pwd.getpwuid', pwd_mock)
        @patch('pwd.getpwnam', pwd_mock)
        @patch('grp.getgrnam', grp_mock)
        def get_process_groups(instance, config):
            return instance.process_groups_from_parser(config)

        gconfigs = get_process_groups(instance, config)

        exp_owner = (sentinel.uid, sentinel.gid)

        self.assertEqual(len(gconfigs), 4)

        gconf_foo = gconfigs[0]
        self.assertEqual(gconf_foo.__class__, FastCGIGroupConfig)
        self.assertEqual(gconf_foo.name, 'foo')
        self.assertEqual(gconf_foo.priority, 1)
        self.assertEqual(gconf_foo.socket_config.url,
                                'unix:///tmp/foo.sock')
        self.assertEqual(exp_owner, gconf_foo.socket_config.get_owner())
        self.assertEqual(438, gconf_foo.socket_config.get_mode()) # 0666 in Py2, 0o666 in Py3
        self.assertEqual(len(gconf_foo.process_configs), 2)
        pconfig_foo = gconf_foo.process_configs[0]
        self.assertEqual(pconfig_foo.__class__, FastCGIProcessConfig)

        gconf_bar = gconfigs[1]
        self.assertEqual(gconf_bar.name, 'bar')
        self.assertEqual(gconf_bar.priority, 999)
        self.assertEqual(gconf_bar.socket_config.url,
                         'unix:///tmp/bar.sock')
        self.assertEqual(exp_owner, gconf_bar.socket_config.get_owner())
        self.assertEqual(448, gconf_bar.socket_config.get_mode()) # 0700 in Py2, 0o700 in Py3
        self.assertEqual(len(gconf_bar.process_configs), 3)

        gconf_cub = gconfigs[2]
        self.assertEqual(gconf_cub.name, 'cub')
        self.assertEqual(gconf_cub.socket_config.url,
                         'tcp://localhost:6000')
        self.assertEqual(len(gconf_cub.process_configs), 1)

        gconf_flub = gconfigs[3]
        self.assertEqual(gconf_flub.name, 'flub')
        self.assertEqual(gconf_flub.socket_config.url,
                         'unix:///tmp/flub.sock')
        self.assertEqual(None, gconf_flub.socket_config.get_owner())
        self.assertEqual(448, gconf_flub.socket_config.get_mode()) # 0700 in Py2, 0o700 in Py3
        self.assertEqual(len(gconf_flub.process_configs), 1)



    def test_fcgi_program_no_socket(self):
        text = lstrip("""\
        [fcgi-program:foo]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_unknown_socket_protocol(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=junk://blah
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_rel_unix_sock_path(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=unix://relative/path
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_bad_tcp_sock_format(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=tcp://missingport
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_bad_expansion_proc_num(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=unix:///tmp/%(process_num)s.sock
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_socket_owner_set_for_tcp(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=tcp://localhost:8000
        socket_owner=nobody:nobody
        command = /bin/foo
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_socket_mode_set_for_tcp(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket = tcp://localhost:8000
        socket_mode = 0777
        command = /bin/foo
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_bad_socket_owner(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket = unix:///tmp/foo.sock
        socket_owner = sometotaljunkuserthatshouldnobethere
        command = /bin/foo
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_bad_socket_mode(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket = unix:///tmp/foo.sock
        socket_mode = junk
        command = /bin/foo
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_heterogeneous_process_groups_from_parser(self):
        text = lstrip("""\
        [program:one]
        command = /bin/cat

        [program:two]
        command = /bin/cat

        [group:thegroup]
        programs = one,two
        priority = 5
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 1)
        gconfig = gconfigs[0]
        self.assertEqual(gconfig.name, 'thegroup')
        self.assertEqual(gconfig.priority, 5)
        self.assertEqual(len(gconfig.process_configs), 2)

    def test_mixed_process_groups_from_parser1(self):
        text = lstrip("""\
        [program:one]
        command = /bin/cat

        [program:two]
        command = /bin/cat

        [program:many]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/cat
        numprocs = 2
        priority = 1

        [group:thegroup]
        programs = one,two
        priority = 5
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 2)

        manyconfig = gconfigs[0]
        self.assertEqual(manyconfig.name, 'many')
        self.assertEqual(manyconfig.priority, 1)
        self.assertEqual(len(manyconfig.process_configs), 2)

        gconfig = gconfigs[1]
        self.assertEqual(gconfig.name, 'thegroup')
        self.assertEqual(gconfig.priority, 5)
        self.assertEqual(len(gconfig.process_configs), 2)

    def test_mixed_process_groups_from_parser2(self):
        text = lstrip("""\
        [program:one]
        command = /bin/cat

        [program:two]
        command = /bin/cat

        [program:many]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/cat
        numprocs = 2
        priority = 1

        [group:thegroup]
        programs = one,two, many
        priority = 5
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 1)

        gconfig = gconfigs[0]
        self.assertEqual(gconfig.name, 'thegroup')
        self.assertEqual(gconfig.priority, 5)
        self.assertEqual(len(gconfig.process_configs), 4)

    def test_unknown_program_in_heterogeneous_group(self):
        text = lstrip("""\
        [program:one]
        command = /bin/cat

        [group:foo]
        programs = notthere
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError, instance.process_groups_from_parser,
                          config)

    def test_rpcinterfaces_from_parser(self):
        text = lstrip("""\
        [rpcinterface:dummy]
        supervisor.rpcinterface_factory = %s
        foo = bar
        """ % __name__)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        factories = instance.get_plugins(config,
                                         'supervisor.rpcinterface_factory',
                                         'rpcinterface:')
        self.assertEqual(len(factories), 1)
        factory = factories[0]
        self.assertEqual(factory[0], 'dummy')
        self.assertEqual(factory[1], sys.modules[__name__])
        self.assertEqual(factory[2], {'foo':'bar'})

    def test_rpcinterfaces_from_parser_factory_missing(self):
        text = lstrip("""\
        [rpcinterface:dummy]
        # note: no supervisor.rpcinterface_factory here
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        try:
            instance.get_plugins(config,
                                 'supervisor.rpcinterface_factory',
                                 'rpcinterface:')
            self.fail('nothing raised')
        except ValueError as exc:
            self.assertEqual(exc.args[0], 'section [rpcinterface:dummy] '
                'does not specify a supervisor.rpcinterface_factory')

    def test_rpcinterfaces_from_parser_factory_not_importable(self):
        text = lstrip("""\
        [rpcinterface:dummy]
        supervisor.rpcinterface_factory = nonexistant
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        try:
            instance.get_plugins(config,
                                 'supervisor.rpcinterface_factory',
                                 'rpcinterface:')
            self.fail('nothing raised')
        except ValueError as exc:
            self.assertEqual(exc.args[0], 'nonexistant cannot be resolved '
                'within [rpcinterface:dummy]')

    def test_clear_autochildlogdir(self):
        dn = tempfile.mkdtemp()
        try:
            instance = self._makeOne()
            instance.childlogdir = dn
            sid = 'supervisor'
            instance.identifier = sid
            logfn = instance.get_autochildlog_name('foo', sid,'stdout')
            first = logfn + '.1'
            second = logfn + '.2'
            f1 = open(first, 'w')
            f2 = open(second, 'w')
            instance.clear_autochildlogdir()
            self.assertFalse(os.path.exists(logfn))
            self.assertFalse(os.path.exists(first))
            self.assertFalse(os.path.exists(second))
            f1.close()
            f2.close()
        finally:
            shutil.rmtree(dn)

    def test_clear_autochildlog_oserror(self):
        instance = self._makeOne()
        instance.childlogdir = '/tmp/this/cant/possibly/existjjjj'
        instance.logger = DummyLogger()
        instance.clear_autochildlogdir()
        self.assertEqual(instance.logger.data, ['Could not clear childlog dir'])

    def test_openhttpservers_reports_friendly_usage_when_eaddrinuse(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def raise_eaddrinuse(supervisord):
            raise socket.error(errno.EADDRINUSE)
        instance.make_http_servers = raise_eaddrinuse

        recorder = []
        def record_usage(message):
            recorder.append(message)
        instance.usage = record_usage

        instance.openhttpservers(supervisord)
        self.assertEqual(len(recorder), 1)
        expected = 'Another program is already listening'
        self.assertTrue(recorder[0].startswith(expected))

    def test_openhttpservers_reports_socket_error_with_errno(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def make_http_servers(supervisord):
            raise socket.error(errno.EPERM)
        instance.make_http_servers = make_http_servers

        recorder = []
        def record_usage(message):
            recorder.append(message)
        instance.usage = record_usage

        instance.openhttpservers(supervisord)
        self.assertEqual(len(recorder), 1)
        expected = ('Cannot open an HTTP server: socket.error '
                    'reported errno.EPERM (%d)' % errno.EPERM)
        self.assertEqual(recorder[0], expected)

    def test_openhttpservers_reports_other_socket_errors(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def make_http_servers(supervisord):
            raise socket.error('uh oh')
        instance.make_http_servers = make_http_servers

        recorder = []
        def record_usage(message):
            recorder.append(message)
        instance.usage = record_usage

        instance.openhttpservers(supervisord)
        self.assertEqual(len(recorder), 1)
        expected = ('Cannot open an HTTP server: socket.error '
                    'reported uh oh')
        self.assertEqual(recorder[0], expected)

    def test_openhttpservers_reports_value_errors(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def make_http_servers(supervisord):
            raise ValueError('not prefixed with help')
        instance.make_http_servers = make_http_servers

        recorder = []
        def record_usage(message):
            recorder.append(message)
        instance.usage = record_usage

        instance.openhttpservers(supervisord)
        self.assertEqual(len(recorder), 1)
        expected = 'not prefixed with help'
        self.assertEqual(recorder[0], expected)

    def test_openhttpservers_does_not_catch_other_exception_types(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def make_http_servers(supervisord):
            raise OverflowError
        instance.make_http_servers = make_http_servers

        # this scenario probably means a bug in supervisor.  we dump
        # all the gory details on the poor user for troubleshooting
        self.assertRaises(OverflowError,
                          instance.openhttpservers, supervisord)

    def test_dropPrivileges_user_none(self):
        instance = self._makeOne()
        msg = instance.dropPrivileges(None)
        self.assertEqual(msg, "No user specified to setuid to!")

    @patch('pwd.getpwuid', Mock(return_value=["foo", None, 12, 34]))
    @patch('os.getuid', Mock(return_value=12))
    def test_dropPrivileges_nonroot_same_user(self):
        instance = self._makeOne()
        msg = instance.dropPrivileges(os.getuid())
        self.assertEqual(msg, None) # no error if same user

    @patch('pwd.getpwuid', Mock(return_value=["foo", None, 55, 34]))
    @patch('os.getuid', Mock(return_value=12))
    def test_dropPrivileges_nonroot_different_user(self):
        instance = self._makeOne()
        msg = instance.dropPrivileges(42)
        self.assertEqual(msg, "Can't drop privilege as nonroot user")

class TestProcessConfig(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import ProcessConfig
        return ProcessConfig

    def _makeOne(self, *arg, **kw):
        defaults = {}
        for name in ('name', 'command', 'directory', 'umask',
                     'priority', 'autostart', 'autorestart',
                     'startsecs', 'startretries', 'uid',
                     'stdout_logfile', 'stdout_capture_maxbytes',
                     'stdout_events_enabled', 'stdout_syslog',
                     'stderr_logfile', 'stderr_capture_maxbytes',
                     'stderr_events_enabled', 'stderr_syslog',
                     'stopsignal', 'stopwaitsecs', 'stopasgroup',
                     'killasgroup', 'exitcodes', 'redirect_stderr',
                     'environment'):
            defaults[name] = name
        for name in ('stdout_logfile_backups', 'stdout_logfile_maxbytes',
                     'stderr_logfile_backups', 'stderr_logfile_maxbytes'):
            defaults[name] = 10
        defaults.update(kw)
        return self._getTargetClass()(*arg, **defaults)

    def test_create_autochildlogs(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        from supervisor.datatypes import Automatic
        instance.stdout_logfile = Automatic
        instance.stderr_logfile = Automatic
        instance.create_autochildlogs()
        self.assertEqual(instance.stdout_logfile, options.tempfile_name)
        self.assertEqual(instance.stderr_logfile, options.tempfile_name)

    def test_make_process(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        process = instance.make_process()
        from supervisor.process import Subprocess
        self.assertEqual(process.__class__, Subprocess)
        self.assertEqual(process.group, None)

    def test_make_process_with_group(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        process = instance.make_process('abc')
        from supervisor.process import Subprocess
        self.assertEqual(process.__class__, Subprocess)
        self.assertEqual(process.group, 'abc')

    def test_make_dispatchers_stderr_not_redirected(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        with _getTempFile('stderr_logfile') as stdout_logfile:
            with _getTempFile('stderr_logfile') as stderr_logfile:
                instance.stdout_logfile = stdout_logfile.name
                instance.stderr_logfile = stderr_logfile.name
                logger.debug('instance.stdout_logfile = %r',
                             instance.stdout_logfile)
                logger.debug('instance.stderr_logfile = %r',
                             instance.stderr_logfile)
                instance.redirect_stderr = False
                process1 = DummyProcess(instance)
                dispatchers, pipes = instance.make_dispatchers(process1)
                self.assertEqual(dispatchers[5].channel, 'stdout')
                from supervisor.events import ProcessCommunicationStdoutEvent
                self.assertEqual(dispatchers[5].event_type,
                                 ProcessCommunicationStdoutEvent)
                self.assertEqual(pipes['stdout'], 5)
                self.assertEqual(dispatchers[7].channel, 'stderr')
                from supervisor.events import ProcessCommunicationStderrEvent
                self.assertEqual(dispatchers[7].event_type,
                                 ProcessCommunicationStderrEvent)
                self.assertEqual(pipes['stderr'], 7)

    def test_make_dispatchers_stderr_redirected(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        with _getTempFile('stderr_logfile') as stdout_logfile:
            instance.stdout_logfile = stdout_logfile.name
            logger.debug('instance.stdout_logfile = %r',
                         instance.stdout_logfile)
            process1 = DummyProcess(instance)
            dispatchers, pipes = instance.make_dispatchers(process1)
            self.assertEqual(dispatchers[5].channel, 'stdout')
            self.assertEqual(pipes['stdout'], 5)
            self.assertEqual(pipes['stderr'], None)

class FastCGIProcessConfigTest(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import FastCGIProcessConfig
        return FastCGIProcessConfig

    def _makeOne(self, *arg, **kw):
        defaults = {}
        for name in ('name', 'command', 'directory', 'umask',
                     'priority', 'autostart', 'autorestart',
                     'startsecs', 'startretries', 'uid',
                     'stdout_logfile', 'stdout_capture_maxbytes',
                     'stdout_events_enabled', 'stdout_syslog',
                     'stderr_logfile', 'stderr_capture_maxbytes',
                     'stderr_events_enabled', 'stderr_syslog',
                     'stopsignal', 'stopwaitsecs', 'stopasgroup',
                     'killasgroup', 'exitcodes', 'redirect_stderr',
                     'environment'):
            defaults[name] = name
        for name in ('stdout_logfile_backups', 'stdout_logfile_maxbytes',
                     'stderr_logfile_backups', 'stderr_logfile_maxbytes'):
            defaults[name] = 10
        defaults.update(kw)
        return self._getTargetClass()(*arg, **defaults)

    def test_make_process(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        self.assertRaises(NotImplementedError, instance.make_process)

    def test_make_process_with_group(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        process = instance.make_process('abc')
        from supervisor.process import FastCGISubprocess
        self.assertEqual(process.__class__, FastCGISubprocess)
        self.assertEqual(process.group, 'abc')

    def test_make_dispatchers(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        with _getTempFile('stderr_logfile') as stdout_logfile:
            with _getTempFile('stderr_logfile') as stderr_logfile:
                instance.stdout_logfile = stdout_logfile.name
                instance.stderr_logfile = stderr_logfile.name
                logger.debug('instance.stdout_logfile = %r',
                             instance.stdout_logfile)
                logger.debug('instance.stderr_logfile = %r',
                             instance.stderr_logfile)
                instance.redirect_stderr = False
                process1 = DummyProcess(instance)
                dispatchers, pipes = instance.make_dispatchers(process1)
                self.assertEqual(dispatchers[4].channel, 'stdin')
                self.assertEqual(dispatchers[4].closed, True)
                self.assertEqual(dispatchers[5].channel, 'stdout')
                from supervisor.events import ProcessCommunicationStdoutEvent
                self.assertEqual(dispatchers[5].event_type,
                                 ProcessCommunicationStdoutEvent)
                self.assertEqual(pipes['stdout'], 5)
                self.assertEqual(dispatchers[7].channel, 'stderr')
                from supervisor.events import ProcessCommunicationStderrEvent
                self.assertEqual(dispatchers[7].event_type,
                                 ProcessCommunicationStderrEvent)
                self.assertEqual(pipes['stderr'], 7)

class ProcessGroupConfigTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import ProcessGroupConfig
        return ProcessGroupConfig

    def _makeOne(self, options, name, priority, pconfigs):
        return self._getTargetClass()(options, name, priority, pconfigs)

    def test_ctor(self):
        options = DummyOptions()
        instance = self._makeOne(options, 'whatever', 999, [])
        self.assertEqual(instance.options, options)
        self.assertEqual(instance.name, 'whatever')
        self.assertEqual(instance.priority, 999)
        self.assertEqual(instance.process_configs, [])

    def test_after_setuid(self):
        options = DummyOptions()
        pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
        instance = self._makeOne(options, 'whatever', 999, pconfigs)
        instance.after_setuid()
        self.assertEqual(pconfigs[0].autochildlogs_created, True)

    def test_make_group(self):
        options = DummyOptions()
        pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
        instance = self._makeOne(options, 'whatever', 999, pconfigs)
        group = instance.make_group()
        from supervisor.process import ProcessGroup
        self.assertEqual(group.__class__, ProcessGroup)

class FastCGIGroupConfigTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import FastCGIGroupConfig
        return FastCGIGroupConfig

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_ctor(self):
        options = DummyOptions()
        sock_config = DummySocketConfig(6)
        instance = self._makeOne(options, 'whatever', 999, [], sock_config)
        self.assertEqual(instance.options, options)
        self.assertEqual(instance.name, 'whatever')
        self.assertEqual(instance.priority, 999)
        self.assertEqual(instance.process_configs, [])
        self.assertEqual(instance.socket_config, sock_config)

    def test_same_sockets_are_equal(self):
        options = DummyOptions()
        sock_config1 = DummySocketConfig(6)
        instance1 = self._makeOne(options, 'whatever', 999, [], sock_config1)

        sock_config2 = DummySocketConfig(6)
        instance2 = self._makeOne(options, 'whatever', 999, [], sock_config2)

        self.assertTrue(instance1 == instance2)
        self.assertFalse(instance1 != instance2)

    def test_diff_sockets_are_not_equal(self):
        options = DummyOptions()
        sock_config1 = DummySocketConfig(6)
        instance1 = self._makeOne(options, 'whatever', 999, [], sock_config1)

        sock_config2 = DummySocketConfig(7)
        instance2 = self._makeOne(options, 'whatever', 999, [], sock_config2)

        self.assertTrue(instance1 != instance2)
        self.assertFalse(instance1 == instance2)

class SignalReceiverTests(unittest.TestCase):
    def test_returns_None_initially(self):
        from supervisor.options import SignalReceiver
        sr = SignalReceiver()
        self.assertEqual(sr.get_signal(), None)

    def test_returns_signals_in_order_received(self):
        from supervisor.options import SignalReceiver
        sr = SignalReceiver()
        sr.receive(signal.SIGTERM, 'frame')
        sr.receive(signal.SIGCHLD, 'frame')
        self.assertEqual(sr.get_signal(), signal.SIGTERM)
        self.assertEqual(sr.get_signal(), signal.SIGCHLD)
        self.assertEqual(sr.get_signal(), None)

    def test_does_not_queue_duplicate_signals(self):
        from supervisor.options import SignalReceiver
        sr = SignalReceiver()
        sr.receive(signal.SIGTERM, 'frame')
        sr.receive(signal.SIGTERM, 'frame')
        self.assertEqual(sr.get_signal(), signal.SIGTERM)
        self.assertEqual(sr.get_signal(), None)

    def test_queues_again_after_being_emptied(self):
        from supervisor.options import SignalReceiver
        sr = SignalReceiver()
        sr.receive(signal.SIGTERM, 'frame')
        self.assertEqual(sr.get_signal(), signal.SIGTERM)
        self.assertEqual(sr.get_signal(), None)
        sr.receive(signal.SIGCHLD, 'frame')
        self.assertEqual(sr.get_signal(), signal.SIGCHLD)
        self.assertEqual(sr.get_signal(), None)

class UtilFunctionsTests(unittest.TestCase):
    def test_make_namespec(self):
        from supervisor.options import make_namespec
        self.assertEqual(make_namespec('group', 'process'), 'group:process')
        self.assertEqual(make_namespec('process', 'process'), 'process')

    def test_split_namespec(self):
        from supervisor.options import split_namespec
        s = split_namespec
        self.assertEqual(s('process:group'), ('process', 'group'))
        self.assertEqual(s('process'), ('process', 'process'))
        self.assertEqual(s('group:'), ('group', None))
        self.assertEqual(s('group:*'), ('group', None))

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_process
import errno
import os
import signal
import sys
import time
import unittest

from supervisor.compat import as_bytes
from supervisor.compat import maxint

from supervisor.tests.base import Mock, patch, sentinel
from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyPGroupConfig
from supervisor.tests.base import DummyDispatcher
from supervisor.tests.base import DummyEvent
from supervisor.tests.base import DummyFCGIGroupConfig
from supervisor.tests.base import DummySocketConfig
from supervisor.tests.base import DummyProcessGroup
from supervisor.tests.base import DummyFCGIProcessGroup

from supervisor.process import Subprocess
from supervisor.options import BadCommand

class SubprocessTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.process import Subprocess
        return Subprocess

    def _makeOne(self, *arg, **kw):
        return self._getTargetClass()(*arg, **kw)

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def test_getProcessStateDescription(self):
        from supervisor.states import ProcessStates
        from supervisor.process import getProcessStateDescription
        for statename, code in ProcessStates.__dict__.items():
            if isinstance(code, int):
                self.assertEqual(getProcessStateDescription(code), statename)

    def test_ctor(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'cat', 'bin/cat',
                              stdout_logfile='/tmp/temp123.log',
                              stderr_logfile='/tmp/temp456.log')
        instance = self._makeOne(config)
        self.assertEqual(instance.config, config)
        self.assertEqual(instance.config.options, options)
        self.assertEqual(instance.laststart, 0)
        self.assertEqual(instance.pid, 0)
        self.assertEqual(instance.laststart, 0)
        self.assertEqual(instance.laststop, 0)
        self.assertEqual(instance.delay, 0)
        self.assertEqual(instance.administrative_stop, 0)
        self.assertEqual(instance.killing, 0)
        self.assertEqual(instance.backoff, 0)
        self.assertEqual(instance.pipes, {})
        self.assertEqual(instance.dispatchers, {})
        self.assertEqual(instance.spawnerr, None)

    def test_repr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'cat', 'bin/cat')
        instance = self._makeOne(config)
        s = repr(instance)
        self.assertTrue(s.startswith('<Subprocess at'))
        self.assertTrue(s.endswith('with name cat in state STOPPED>'))

    def test_reopenlogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.dispatchers = {0:DummyDispatcher(readable=True),
                                1:DummyDispatcher(writable=True)}
        instance.reopenlogs()
        self.assertEqual(instance.dispatchers[0].logs_reopened, True)
        self.assertEqual(instance.dispatchers[1].logs_reopened, False)

    def test_removelogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.dispatchers = {0:DummyDispatcher(readable=True),
                                1:DummyDispatcher(writable=True)}
        instance.removelogs()
        self.assertEqual(instance.dispatchers[0].logs_removed, True)
        self.assertEqual(instance.dispatchers[1].logs_removed, False)

    def test_drain(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test',
                              stdout_logfile='/tmp/foo',
                              stderr_logfile='/tmp/bar')
        instance = self._makeOne(config)
        instance.dispatchers = {0:DummyDispatcher(readable=True),
                                1:DummyDispatcher(writable=True)}
        instance.drain()
        self.assertTrue(instance.dispatchers[0].read_event_handled)
        self.assertTrue(instance.dispatchers[1].write_event_handled)

    def test_get_execv_args_bad_command_extraquote(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'extraquote', 'extraquote"')
        instance = self._makeOne(config)
        self.assertRaises(BadCommand, instance.get_execv_args)

    def test_get_execv_args_bad_command_empty(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'empty', '')
        instance = self._makeOne(config)
        self.assertRaises(BadCommand, instance.get_execv_args)

    def test_get_execv_args_bad_command_whitespaceonly(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'whitespaceonly', ' \t ')
        instance = self._makeOne(config)
        self.assertRaises(BadCommand, instance.get_execv_args)

    def test_get_execv_args_abs_missing(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere')
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(args, ('/notthere', ['/notthere']))

    def test_get_execv_args_abs_withquotes_missing(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere "an argument"')
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(args, ('/notthere', ['/notthere', 'an argument']))

    def test_get_execv_args_rel_missing(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', 'notthere')
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(args, ('notthere', ['notthere']))

    def test_get_execv_args_rel_withquotes_missing(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', 'notthere "an argument"')
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(args, ('notthere', ['notthere', 'an argument']))

    def test_get_execv_args_abs(self):
        executable = '/bin/sh foo'
        options = DummyOptions()
        config = DummyPConfig(options, 'sh', executable)
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(len(args), 2)
        self.assertEqual(args[0], '/bin/sh')
        self.assertEqual(args[1], ['/bin/sh', 'foo'])

    def test_get_execv_args_rel(self):
        executable = 'sh foo'
        options = DummyOptions()
        config = DummyPConfig(options, 'sh', executable)
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(len(args), 2)
        self.assertEqual(args[0], '/bin/sh')
        self.assertEqual(args[1], ['sh', 'foo'])

    def test_record_spawnerr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.record_spawnerr('foo')
        self.assertEqual(instance.spawnerr, 'foo')
        self.assertEqual(options.logger.data[0], 'spawnerr: foo')

    def test_spawn_already_running(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'sh', '/bin/sh')
        instance = self._makeOne(config)
        instance.pid = True
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.RUNNING
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.logger.data[0], "process 'sh' already running")
        self.assertEqual(instance.state, ProcessStates.RUNNING)

    def test_spawn_fail_check_execv_args(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'bad', '/bad/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr, 'bad filename')
        self.assertEqual(options.logger.data[0], "spawnerr: bad filename")
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1 = L[0]
        event2 = L[1]
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_fail_make_pipes_emfile(self):
        options = DummyOptions()
        options.make_pipes_error = errno.EMFILE
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr,
                         "too many open files to spawn 'good'")
        self.assertEqual(options.logger.data[0],
                         "spawnerr: too many open files to spawn 'good'")
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_fail_make_pipes_other(self):
        options = DummyOptions()
        options.make_pipes_error = 1
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr, 'unknown error: EPERM')
        self.assertEqual(options.logger.data[0],
                         "spawnerr: unknown error: EPERM")
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_fork_fail_eagain(self):
        options = DummyOptions()
        options.fork_error = errno.EAGAIN
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr,
                         "Too many processes in process table to spawn 'good'")
        self.assertEqual(options.logger.data[0],
             "spawnerr: Too many processes in process table to spawn 'good'")
        self.assertEqual(len(options.parent_pipes_closed), 6)
        self.assertEqual(len(options.child_pipes_closed), 6)
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_fork_fail_other(self):
        options = DummyOptions()
        options.fork_error = 1
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr, 'unknown error: EPERM')
        self.assertEqual(options.logger.data[0],
                         "spawnerr: unknown error: EPERM")
        self.assertEqual(len(options.parent_pipes_closed), 6)
        self.assertEqual(len(options.child_pipes_closed), 6)
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_as_child_setuid_ok(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.privsdropped, 1)
        self.assertEqual(options.execv_args,
                         ('/good/filename', ['/good/filename']) )
        self.assertEqual(options.execve_called, True)
        # if the real execve() succeeds, the code that writes the
        # "was not spawned" message won't be reached.  this assertion
        # is to test that no other errors were written.
        self.assertEqual(options.written,
             {2: "supervisor: child process was not spawned\n"})

    def test_spawn_as_child_setuid_fail(self):
        options = DummyOptions()
        options.forkpid = 0
        options.setuid_msg = 'failure reason'
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.written,
             {2: "supervisor: couldn't setuid to 1: failure reason\n"
                 "supervisor: child process was not spawned\n"})
        self.assertEqual(options.privsdropped, None)
        self.assertEqual(options.execve_called, False)
        self.assertEqual(options._exitcode, 127)

    def test_spawn_as_child_cwd_ok(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename',
                              directory='/tmp')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.execv_args,
                         ('/good/filename', ['/good/filename']) )
        self.assertEqual(options.changed_directory, True)
        self.assertEqual(options.execve_called, True)
        # if the real execve() succeeds, the code that writes the
        # "was not spawned" message won't be reached.  this assertion
        # is to test that no other errors were written.
        self.assertEqual(options.written,
             {2: "supervisor: child process was not spawned\n"})

    def test_spawn_as_child_sets_umask(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', umask=2)
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.execv_args,
                         ('/good/filename', ['/good/filename']) )
        self.assertEqual(options.umaskset, 2)
        self.assertEqual(options.execve_called, True)
        # if the real execve() succeeds, the code that writes the
        # "was not spawned" message won't be reached.  this assertion
        # is to test that no other errors were written.
        self.assertEqual(options.written,
             {2: "supervisor: child process was not spawned\n"})

    def test_spawn_as_child_cwd_fail(self):
        options = DummyOptions()
        options.forkpid = 0
        options.chdir_error = 2
        config = DummyPConfig(options, 'good', '/good/filename',
                              directory='/tmp')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.execv_args, None)
        out = {2: "supervisor: couldn't chdir to /tmp: ENOENT\n"
                  "supervisor: child process was not spawned\n"}
        self.assertEqual(options.written, out)
        self.assertEqual(options._exitcode, 127)
        self.assertEqual(options.changed_directory, False)
        self.assertEqual(options.execve_called, False)

    def test_spawn_as_child_execv_fail_oserror(self):
        options = DummyOptions()
        options.forkpid = 0
        options.execv_error = 1
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        out = {2: "supervisor: couldn't exec /good/filename: EPERM\n"
                  "supervisor: child process was not spawned\n"}
        self.assertEqual(options.written, out)
        self.assertEqual(options.privsdropped, None)
        self.assertEqual(options._exitcode, 127)

    def test_spawn_as_child_execv_fail_runtime_error(self):
        options = DummyOptions()
        options.forkpid = 0
        options.execv_error = 2
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        msg = options.written[2] # dict, 2 is fd #
        head = "supervisor: couldn't exec /good/filename:"
        self.assertTrue(msg.startswith(head))
        self.assertTrue("RuntimeError" in msg)
        self.assertEqual(options.privsdropped, None)
        self.assertEqual(options._exitcode, 127)

    def test_spawn_as_child_uses_pconfig_environment(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'cat', '/bin/cat',
                              environment={'_TEST_':'1'})
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.execv_args, ('/bin/cat', ['/bin/cat']) )
        self.assertEqual(options.execv_environment['_TEST_'], '1')

    def test_spawn_as_child_environment_supervisor_envvars(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'cat', '/bin/cat')
        instance = self._makeOne(config)
        class Dummy:
            name = 'dummy'
        instance.group = Dummy()
        instance.group.config = Dummy()
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.execv_args, ('/bin/cat', ['/bin/cat']) )
        self.assertEqual(
            options.execv_environment['SUPERVISOR_ENABLED'], '1')
        self.assertEqual(
            options.execv_environment['SUPERVISOR_PROCESS_NAME'], 'cat')
        self.assertEqual(
            options.execv_environment['SUPERVISOR_GROUP_NAME'], 'dummy')
        self.assertEqual(
            options.execv_environment['SUPERVISOR_SERVER_URL'],
            'http://localhost:9001')

    def test_spawn_as_child_stderr_redirected(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        config.redirect_stderr = True
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 2)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.privsdropped, 1)
        self.assertEqual(options.execv_args,
                         ('/good/filename', ['/good/filename']) )
        self.assertEqual(options.execve_called, True)
        # if the real execve() succeeds, the code that writes the
        # "was not spawned" message won't be reached.  this assertion
        # is to test that no other errors were written.
        self.assertEqual(options.written,
             {2: "supervisor: child process was not spawned\n"})

    def test_spawn_as_parent(self):
        options = DummyOptions()
        options.forkpid = 10
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, 10)
        self.assertEqual(instance.dispatchers[4].__class__, DummyDispatcher)
        self.assertEqual(instance.dispatchers[5].__class__, DummyDispatcher)
        self.assertEqual(instance.dispatchers[7].__class__, DummyDispatcher)
        self.assertEqual(instance.pipes['stdin'], 4)
        self.assertEqual(instance.pipes['stdout'], 5)
        self.assertEqual(instance.pipes['stderr'], 7)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(len(options.child_pipes_closed), 6)
        self.assertEqual(options.logger.data[0], "spawned: 'good' with pid 10")
        self.assertEqual(instance.spawnerr, None)
        self.assertTrue(instance.delay)
        self.assertEqual(instance.config.options.pidhistory[10], instance)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.STARTING)

    def test_spawn_redirect_stderr(self):
        options = DummyOptions()
        options.forkpid = 10
        config = DummyPConfig(options, 'good', '/good/filename',
                              redirect_stderr=True)
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, 10)
        self.assertEqual(instance.dispatchers[4].__class__, DummyDispatcher)
        self.assertEqual(instance.dispatchers[5].__class__, DummyDispatcher)
        self.assertEqual(instance.pipes['stdin'], 4)
        self.assertEqual(instance.pipes['stdout'], 5)
        self.assertEqual(instance.pipes['stderr'], None)

    def test_write(self):
        executable = '/bin/cat'
        options = DummyOptions()
        config = DummyPConfig(options, 'output', executable)
        instance = self._makeOne(config)
        sent = 'a' * (1 << 13)
        self.assertRaises(OSError, instance.write, sent)
        options.forkpid = 1
        instance.spawn()
        instance.write(sent)
        stdin_fd = instance.pipes['stdin']
        self.assertEqual(sent, instance.dispatchers[stdin_fd].input_buffer)
        instance.killing = True
        self.assertRaises(OSError, instance.write, sent)

    def test_write_dispatcher_closed(self):
        executable = '/bin/cat'
        options = DummyOptions()
        config = DummyPConfig(options, 'output', executable)
        instance = self._makeOne(config)
        sent = 'a' * (1 << 13)
        self.assertRaises(OSError, instance.write, sent)
        options.forkpid = 1
        instance.spawn()
        stdin_fd = instance.pipes['stdin']
        instance.dispatchers[stdin_fd].close()
        self.assertRaises(OSError, instance.write, sent)

    def test_write_stdin_fd_none(self):
        executable = '/bin/cat'
        options = DummyOptions()
        config = DummyPConfig(options, 'output', executable)
        instance = self._makeOne(config)
        options.forkpid = 1
        instance.spawn()
        stdin_fd = instance.pipes['stdin']
        instance.dispatchers[stdin_fd].close()
        instance.pipes['stdin'] = None
        try:
            instance.write('foo')
            self.fail('nothing raised')
        except OSError as exc:
            self.assertEqual(exc.args[0], errno.EPIPE)
            self.assertEqual(exc.args[1], 'Process has no stdin channel')

    def test_write_dispatcher_flush_raises_epipe(self):
        executable = '/bin/cat'
        options = DummyOptions()
        config = DummyPConfig(options, 'output', executable)
        instance = self._makeOne(config)
        sent = 'a' * (1 << 13)
        self.assertRaises(OSError, instance.write, sent)
        options.forkpid = 1
        instance.spawn()
        stdin_fd = instance.pipes['stdin']
        instance.dispatchers[stdin_fd].flush_error = errno.EPIPE
        self.assertRaises(OSError, instance.write, sent)

    def _dont_test_spawn_and_kill(self):
        # this is a functional test
        from supervisor.tests.base import makeSpew
        try:
            called = 0
            def foo(*args):
                called = 1
            signal.signal(signal.SIGCHLD, foo)
            executable = makeSpew()
            options = DummyOptions()
            config = DummyPConfig(options, 'spew', executable)
            instance = self._makeOne(config)
            result = instance.spawn()
            msg = options.logger.data[0]
            self.assertTrue(msg.startswith("spawned: 'spew' with pid"))
            self.assertEqual(len(instance.pipes), 6)
            self.assertTrue(instance.pid)
            self.assertEqual(instance.pid, result)
            origpid = instance.pid
            while 1:
                try:
                    data = os.popen('ps').read()
                    break
                except IOError as why:
                    if why.args[0] != errno.EINTR:
                        raise
                        # try again ;-)
            time.sleep(0.1) # arbitrary, race condition possible
            self.assertTrue(data.find(as_bytes(repr(origpid))) != -1 )
            msg = instance.kill(signal.SIGTERM)
            time.sleep(0.1) # arbitrary, race condition possible
            self.assertEqual(msg, None)
            pid, sts = os.waitpid(-1, os.WNOHANG)
            data = os.popen('ps').read()
            self.assertEqual(data.find(as_bytes(repr(origpid))), -1) # dubious
        finally:
            try:
                os.remove(executable)
            except:
                pass
            signal.signal(signal.SIGCHLD, signal.SIG_DFL)

    def test_stop(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        dispatcher = DummyDispatcher(writable=True)
        instance.dispatchers = {'foo':dispatcher}
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.RUNNING
        instance.stop()
        self.assertEqual(instance.administrative_stop, 1)
        self.assertTrue(instance.delay)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGTERM')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[11], signal.SIGTERM)

    def test_stop_not_in_stoppable_state_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        dispatcher = DummyDispatcher(writable=True)
        instance.dispatchers = {'foo':dispatcher}
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.STOPPED
        try:
            instance.stop()
            self.fail('nothing raised')
        except AssertionError as exc:
            self.assertEqual(exc.args[0], 'Assertion failed for test: '
                'STOPPED not in RUNNING STARTING STOPPING')

    def test_give_up(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        instance.state = ProcessStates.BACKOFF
        instance.give_up()
        self.assertEqual(instance.system_stop, 1)
        self.assertFalse(instance.delay)
        self.assertFalse(instance.backoff)
        self.assertEqual(instance.state, ProcessStates.FATAL)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateFatalEvent)

    def test_kill_nopid(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0],
              'attempted to kill test with sig SIGTERM but it wasn\'t running')
        self.assertEqual(instance.killing, 0)

    def test_kill_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        options.kill_error = 1
        instance = self._makeOne(config)
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.ProcessStateEvent,
                         lambda x: L.append(x))
        instance.pid = 11
        instance.state = ProcessStates.RUNNING
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGTERM')
        self.assertTrue(options.logger.data[1].startswith(
            'unknown problem killing test'))
        self.assertEqual(instance.killing, 0)
        self.assertEqual(len(L), 2)
        event1 = L[0]
        event2 = L[1]
        self.assertEqual(event1.__class__, events.ProcessStateStoppingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateUnknownEvent)

    def test_kill_from_starting(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.ProcessStateEvent,lambda x: L.append(x))
        instance.state = ProcessStates.STARTING
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGTERM')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[11], signal.SIGTERM)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppingEvent)

    def test_kill_from_running(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        instance.state = ProcessStates.RUNNING
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGTERM')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[11], signal.SIGTERM)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppingEvent)

    def test_kill_from_stopping(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.Event,lambda x: L.append(x))
        instance.state = ProcessStates.STOPPING
        instance.kill(signal.SIGKILL)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGKILL')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[11], signal.SIGKILL)
        self.assertEqual(L, []) # no event because we didn't change state

    def test_kill_from_backoff(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.Event, L.append)
        instance.state = ProcessStates.BACKOFF
        instance.kill(signal.SIGKILL)
        self.assertEqual(options.logger.data[0],
                         'Attempted to kill test, which is in BACKOFF state.')
        self.assertEqual(instance.killing, 0)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppedEvent)

    def test_kill_from_stopping_w_killasgroup(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test', killasgroup=True)
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.Event,lambda x: L.append(x))
        instance.state = ProcessStates.STOPPING
        instance.kill(signal.SIGKILL)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) '
                         'process group with signal SIGKILL')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[-11], signal.SIGKILL)
        self.assertEqual(L, []) # no event because we didn't change state

    def test_stopasgroup(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test', stopasgroup=True)
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.Event,lambda x: L.append(x))
        instance.state = ProcessStates.RUNNING
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) '
                         'process group with signal SIGTERM')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[-11], signal.SIGTERM)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppingEvent)
        self.assertEqual(event.extra_values, [('pid', 11)])
        self.assertEqual(event.from_state, ProcessStates.RUNNING)

    def test_finish(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo')
        instance = self._makeOne(config)
        instance.waitstatus = (123, 1) # pid, waitstatus
        instance.config.options.pidhistory[123] = instance
        instance.killing = 1
        pipes = {'stdout':'','stderr':''}
        instance.pipes = pipes
        from supervisor.states import ProcessStates
        from supervisor import events
        instance.state = ProcessStates.STOPPING
        L = []
        events.subscribe(events.ProcessStateStoppedEvent, lambda x: L.append(x))
        instance.pid = 123
        instance.finish(123, 1)
        self.assertEqual(instance.killing, 0)
        self.assertEqual(instance.pid, 0)
        self.assertEqual(options.parent_pipes_closed, pipes)
        self.assertEqual(instance.pipes, {})
        self.assertEqual(instance.dispatchers, {})
        self.assertEqual(options.logger.data[0], 'stopped: notthere '
                         '(terminated by SIGHUP)')
        self.assertEqual(instance.exitstatus, -1)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppedEvent)
        self.assertEqual(event.extra_values, [('pid', 123)])
        self.assertEqual(event.from_state, ProcessStates.STOPPING)

    def test_finish_expected(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo')
        instance = self._makeOne(config)
        instance.config.options.pidhistory[123] = instance
        pipes = {'stdout':'','stderr':''}
        instance.pipes = pipes
        instance.config.exitcodes =[-1]
        from supervisor.states import ProcessStates
        from supervisor import events
        instance.state = ProcessStates.RUNNING
        L = []
        events.subscribe(events.ProcessStateExitedEvent, lambda x: L.append(x))
        instance.pid = 123
        instance.finish(123, 1)
        self.assertEqual(instance.killing, 0)
        self.assertEqual(instance.pid, 0)
        self.assertEqual(options.parent_pipes_closed, pipes)
        self.assertEqual(instance.pipes, {})
        self.assertEqual(instance.dispatchers, {})
        self.assertEqual(options.logger.data[0],
                         'exited: notthere (terminated by SIGHUP; expected)')
        self.assertEqual(instance.exitstatus, -1)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__,
                         events.ProcessStateExitedEvent)
        self.assertEqual(event.expected, True)
        self.assertEqual(event.extra_values, [('expected', True), ('pid', 123)])
        self.assertEqual(event.from_state, ProcessStates.RUNNING)

    def test_finish_tooquickly(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo', startsecs=10)
        instance = self._makeOne(config)
        instance.config.options.pidhistory[123] = instance
        pipes = {'stdout':'','stderr':''}
        instance.pipes = pipes
        instance.config.exitcodes =[-1]
        import time
        instance.laststart = time.time()
        from supervisor.states import ProcessStates
        from supervisor import events
        instance.state = ProcessStates.STARTING
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        instance.pid = 123
        instance.finish(123, 1)
        self.assertEqual(instance.killing, 0)
        self.assertEqual(instance.pid, 0)
        self.assertEqual(options.parent_pipes_closed, pipes)
        self.assertEqual(instance.pipes, {})
        self.assertEqual(instance.dispatchers, {})
        self.assertEqual(options.logger.data[0],
                      'exited: notthere (terminated by SIGHUP; not expected)')
        self.assertEqual(instance.exitstatus, None)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateBackoffEvent)
        self.assertEqual(event.from_state, ProcessStates.STARTING)

    def test_finish_with_current_event_sends_rejected(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        events.subscribe(events.EventRejectedEvent, lambda x: L.append(x))
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo', startsecs=10)
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.RUNNING
        event = DummyEvent()
        instance.event = event
        instance.finish(123, 1)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__,
                         events.ProcessStateExitedEvent)
        self.assertEqual(event2.__class__, events.EventRejectedEvent)
        self.assertEqual(event2.process, instance)
        self.assertEqual(event2.event, event)
        self.assertEqual(instance.event, None)

    def test_set_uid_no_uid(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.set_uid()
        self.assertEqual(options.privsdropped, None)

    def test_set_uid(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test', uid=1)
        instance = self._makeOne(config)
        msg = instance.set_uid()
        self.assertEqual(options.privsdropped, 1)
        self.assertEqual(msg, None)

    def test_cmp_bypriority(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo',
                              priority=1)
        instance = self._makeOne(config)

        config = DummyPConfig(options, 'notthere1', '/notthere',
                              stdout_logfile='/tmp/foo',
                              priority=2)
        instance1 = self._makeOne(config)

        config = DummyPConfig(options, 'notthere2', '/notthere',
                              stdout_logfile='/tmp/foo',
                              priority=3)
        instance2 = self._makeOne(config)

        L = [instance2, instance, instance1]
        L.sort()

        self.assertEqual(L, [instance, instance1, instance2])

    def test_transition_stopped_to_starting_supervisor_stopping(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.SHUTDOWN

        # this should not be spawned, as supervisor is shutting down
        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 0
        process.state = ProcessStates.STOPPED
        process.transition()
        self.assertEqual(process.state, ProcessStates.STOPPED)
        self.assertEqual(L, [])

    def test_transition_stopped_to_starting_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.RUNNING

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 0
        process.state = ProcessStates.STOPPED
        process.transition()
        self.assertEqual(process.state, ProcessStates.STARTING)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStartingEvent)

    def test_transition_exited_to_starting_supervisor_stopping(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.SHUTDOWN

        # this should not be spawned, as supervisor is shutting down
        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        from supervisor.datatypes import RestartUnconditionally
        pconfig.autorestart = RestartUnconditionally
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.system_stop = 1
        process.state = ProcessStates.EXITED
        process.transition()
        self.assertEqual(process.state, ProcessStates.EXITED)
        self.assertEqual(process.system_stop, 1)
        self.assertEqual(L, [])

    def test_transition_exited_to_starting_uncond_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        from supervisor.datatypes import RestartUnconditionally
        pconfig.autorestart = RestartUnconditionally
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.state = ProcessStates.EXITED
        process.transition()
        self.assertEqual(process.state, ProcessStates.STARTING)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStartingEvent)

    def test_transition_exited_to_starting_condit_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        from supervisor.datatypes import RestartWhenExitUnexpected
        pconfig.autorestart = RestartWhenExitUnexpected
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.state = ProcessStates.EXITED
        process.exitstatus = 'bogus'
        process.transition()
        self.assertEqual(process.state, ProcessStates.STARTING)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStartingEvent)

    def test_transition_exited_to_starting_condit_fls_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        from supervisor.datatypes import RestartWhenExitUnexpected
        pconfig.autorestart = RestartWhenExitUnexpected
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.state = ProcessStates.EXITED
        process.exitstatus = 0
        process.transition()
        self.assertEqual(process.state, ProcessStates.EXITED)
        self.assertEqual(L, [])

    def test_transition_backoff_to_starting_supervisor_stopping(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.SHUTDOWN

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.delay = 0
        process.backoff = 0
        process.state = ProcessStates.BACKOFF
        process.transition()
        self.assertEqual(process.state, ProcessStates.BACKOFF)
        self.assertEqual(L, [])

    def test_transition_backoff_to_starting_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.RUNNING

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.delay = 0
        process.backoff = 0
        process.state = ProcessStates.BACKOFF
        process.transition()
        self.assertEqual(process.state, ProcessStates.STARTING)
        self.assertEqual(len(L), 1)
        self.assertEqual(L[0].__class__, events.ProcessStateStartingEvent)

    def test_transition_backoff_to_starting_supervisor_running_notyet(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.RUNNING

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.delay = maxint
        process.backoff = 0
        process.state = ProcessStates.BACKOFF
        process.transition()
        self.assertEqual(process.state, ProcessStates.BACKOFF)
        self.assertEqual(L, [])

    def test_transition_starting_to_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates

        options = DummyOptions()

        # this should go from STARTING to RUNNING via transition()
        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.backoff = 1
        process.delay = 1
        process.system_stop = 0
        process.laststart = 1
        process.pid = 1
        process.stdout_buffer = 'abc'
        process.stderr_buffer = 'def'
        process.state = ProcessStates.STARTING
        process.transition()

        # this implies RUNNING
        self.assertEqual(process.backoff, 0)
        self.assertEqual(process.delay, 0)
        self.assertEqual(process.system_stop, 0)
        self.assertEqual(options.logger.data[0],
                         'success: process entered RUNNING state, process has '
                         'stayed up for > than 10 seconds (startsecs)')
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateRunningEvent)

    def test_transition_backoff_to_fatal(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        # this should go from BACKOFF to FATAL via transition()
        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.backoff = 10000
        process.delay = 1
        process.system_stop = 0
        process.stdout_buffer = 'abc'
        process.stderr_buffer = 'def'
        process.state = ProcessStates.BACKOFF

        process.transition()

        # this implies FATAL
        self.assertEqual(process.backoff, 0)
        self.assertEqual(process.delay, 0)
        self.assertEqual(process.system_stop, 1)
        self.assertEqual(options.logger.data[0],
                         'gave up: process entered FATAL state, too many start'
                         ' retries too quickly')
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateFatalEvent)

    def test_transition_stops_unkillable_notyet(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.delay = maxint
        process.state = ProcessStates.STOPPING

        process.transition()
        self.assertEqual(process.state, ProcessStates.STOPPING)
        self.assertEqual(L, [])

    def test_transition_stops_unkillable(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.delay = 0
        process.pid = 1
        process.killing = 0
        process.state = ProcessStates.STOPPING

        process.transition()
        self.assertEqual(process.killing, 1)
        self.assertNotEqual(process.delay, 0)
        self.assertEqual(process.state, ProcessStates.STOPPING)
        self.assertEqual(options.logger.data[0],
                         "killing 'process' (1) with SIGKILL")
        import signal
        self.assertEqual(options.kills[1], signal.SIGKILL)
        self.assertEqual(L, [])

    def test_change_state_doesnt_notify_if_no_state_change(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.state = 10
        self.assertEqual(instance.change_state(10), False)

    def test_change_state_sets_backoff_and_delay(self):
        from supervisor.states import ProcessStates
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.state = 10
        instance.change_state(ProcessStates.BACKOFF)
        self.assertEqual(instance.backoff, 1)
        self.assertTrue(instance.delay > 0)

class FastCGISubprocessTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.process import FastCGISubprocess
        return FastCGISubprocess

    def _makeOne(self, *arg, **kw):
        return self._getTargetClass()(*arg, **kw)

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def test_no_group(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        self.assertRaises(NotImplementedError, instance.spawn)

    def test_no_socket_manager(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.group = DummyProcessGroup(DummyPGroupConfig(options))
        self.assertRaises(NotImplementedError, instance.spawn)

    def test_prepare_child_fds(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        sock_config = DummySocketConfig(7)
        gconfig = DummyFCGIGroupConfig(options, 'whatever', 999, None,
                                       sock_config)
        instance.group = DummyFCGIProcessGroup(gconfig)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(options.duped[7], 0)
        self.assertEqual(options.duped[instance.pipes['child_stdout']], 1)
        self.assertEqual(options.duped[instance.pipes['child_stderr']], 2)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)

    def test_prepare_child_fds_stderr_redirected(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        config.redirect_stderr = True
        instance = self._makeOne(config)
        sock_config = DummySocketConfig(13)
        gconfig = DummyFCGIGroupConfig(options, 'whatever', 999, None,
                                       sock_config)
        instance.group = DummyFCGIProcessGroup(gconfig)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(len(options.duped), 2)
        self.assertEqual(options.duped[13], 0)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)

    def test_before_spawn_gets_socket_ref(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        sock_config = DummySocketConfig(7)
        gconfig = DummyFCGIGroupConfig(options, 'whatever', 999, None,
                                       sock_config)
        instance.group = DummyFCGIProcessGroup(gconfig)
        self.assertTrue(instance.fcgi_sock is None)
        instance.before_spawn()
        self.assertFalse(instance.fcgi_sock is None)

    def test_after_finish_removes_socket_ref(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.fcgi_sock = 'hello'
        instance.after_finish()
        self.assertTrue(instance.fcgi_sock is None)

    #Patch Subprocess.finish() method for this test to verify override
    @patch.object(Subprocess, 'finish', Mock(return_value=sentinel.finish_result))
    def test_finish_override(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.after_finish = Mock()
        result = instance.finish(sentinel.pid, sentinel.sts)
        self.assertEqual(sentinel.finish_result, result,
                        'FastCGISubprocess.finish() did not pass thru result')
        self.assertEqual(1, instance.after_finish.call_count,
                            'FastCGISubprocess.after_finish() not called once')
        finish_mock = Subprocess.finish
        self.assertEqual(1, finish_mock.call_count,
                            'Subprocess.finish() not called once')
        pid_arg = finish_mock.call_args[0][1]
        sts_arg = finish_mock.call_args[0][2]
        self.assertEqual(sentinel.pid, pid_arg,
                            'Subprocess.finish() pid arg was not passed')
        self.assertEqual(sentinel.sts, sts_arg,
                            'Subprocess.finish() sts arg was not passed')

    #Patch Subprocess.spawn() method for this test to verify override
    @patch.object(Subprocess, 'spawn', Mock(return_value=sentinel.ppid))
    def test_spawn_override_success(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.before_spawn = Mock()
        result = instance.spawn()
        self.assertEqual(sentinel.ppid, result,
                        'FastCGISubprocess.spawn() did not pass thru result')
        self.assertEqual(1, instance.before_spawn.call_count,
                            'FastCGISubprocess.before_spawn() not called once')
        spawn_mock = Subprocess.spawn
        self.assertEqual(1, spawn_mock.call_count,
                            'Subprocess.spawn() not called once')

    #Patch Subprocess.spawn() method for this test to verify error handling
    @patch.object(Subprocess, 'spawn', Mock(return_value=None))
    def test_spawn_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.before_spawn = Mock()
        instance.fcgi_sock = 'nuke me on error'
        result = instance.spawn()
        self.assertEqual(None, result,
                        'FastCGISubprocess.spawn() did return None on error')
        self.assertEqual(1, instance.before_spawn.call_count,
                            'FastCGISubprocess.before_spawn() not called once')
        self.assertEqual(None, instance.fcgi_sock,
                'FastCGISubprocess.spawn() did not remove sock ref on error')

class ProcessGroupBaseTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.process import ProcessGroupBase
        return ProcessGroupBase

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_get_unstopped_processes(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        group = self._makeOne(gconfig)
        group.processes = { 'process1': process1 }
        unstopped = group.get_unstopped_processes()
        self.assertEqual(unstopped, [process1])

    def test_stop_all(self):
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPED)

        pconfig2 = DummyPConfig(options, 'process2', 'process2','/bin/process2')
        process2 = DummyProcess(pconfig2, state=ProcessStates.RUNNING)

        pconfig3 = DummyPConfig(options, 'process3', 'process3','/bin/process3')
        process3 = DummyProcess(pconfig3, state=ProcessStates.STARTING)
        pconfig4 = DummyPConfig(options, 'process4', 'process4','/bin/process4')
        process4 = DummyProcess(pconfig4, state=ProcessStates.BACKOFF)
        process4.delay = 1000
        process4.backoff = 10
        gconfig = DummyPGroupConfig(
            options,
            pconfigs=[pconfig1, pconfig2, pconfig3, pconfig4])
        group = self._makeOne(gconfig)
        group.processes = {'process1': process1, 'process2': process2,
                           'process3':process3, 'process4':process4}

        group.stop_all()
        self.assertEqual(process1.stop_called, False)
        self.assertEqual(process2.stop_called, True)
        self.assertEqual(process3.stop_called, True)
        self.assertEqual(process4.stop_called, False)
        self.assertEqual(process4.state, ProcessStates.FATAL)

    def test_get_dispatchers(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        process1.dispatchers = {4:None}
        pconfig2 = DummyPConfig(options, 'process2', 'process2','/bin/process2')
        process2 = DummyProcess(pconfig2, state=ProcessStates.STOPPING)
        process2.dispatchers = {5:None}
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1, pconfig2])
        group = self._makeOne(gconfig)
        group.processes = { 'process1': process1, 'process2': process2 }
        result= group.get_dispatchers()
        self.assertEqual(result, {4:None, 5:None})

    def test_reopenlogs(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        group = self._makeOne(gconfig)
        group.processes = {'process1': process1}
        group.reopenlogs()
        self.assertEqual(process1.logs_reopened, True)

    def test_removelogs(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        group = self._makeOne(gconfig)
        group.processes = {'process1': process1}
        group.removelogs()
        self.assertEqual(process1.logsremoved, True)

    def test_cmp(self):
        options = DummyOptions()
        gconfig1 = DummyPGroupConfig(options)
        group1 = self._makeOne(gconfig1)

        gconfig2 = DummyPGroupConfig(options)
        group2 = self._makeOne(gconfig2)

        group1.config.priority = 5
        group2.config.priority = 1

        L = [group1, group2]
        L.sort()

        self.assertEqual(L, [group2, group1])

class ProcessGroupTests(ProcessGroupBaseTests):
    def _getTargetClass(self):
        from supervisor.process import ProcessGroup
        return ProcessGroup

    def test_repr(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        group = self._makeOne(gconfig)
        s = repr(group)
        self.assertTrue('supervisor.process.ProcessGroup' in s)
        self.assertTrue(s.endswith('named whatever>'), s)

    def test_transition(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        group = self._makeOne(gconfig)
        group.processes = {'process1': process1}
        group.transition()
        self.assertEqual(process1.transitioned, True)

class EventListenerPoolTests(ProcessGroupBaseTests):
    def setUp(self):
        from supervisor.events import clear
        clear()

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.process import EventListenerPool
        return EventListenerPool

    def test_ctor(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        class EventType:
            pass
        gconfig.pool_events = (EventType,)
        pool = self._makeOne(gconfig)
        from supervisor import events
        self.assertEqual(len(events.callbacks), 2)
        self.assertEqual(events.callbacks[0],
            (EventType, pool._acceptEvent))
        self.assertEqual(events.callbacks[1],
            (events.EventRejectedEvent, pool.handle_rejected))
        self.assertEqual(pool.serial, -1)

    def test__eventEnvelope(self):
        options = DummyOptions()
        options.identifier = 'thesupervisorname'
        gconfig = DummyPGroupConfig(options)
        gconfig.name = 'thepoolname'
        pool = self._makeOne(gconfig)
        from supervisor import events
        result = pool._eventEnvelope(
            events.EventTypes.PROCESS_COMMUNICATION_STDOUT, 80, 20, 'payload\n')
        header, payload = result.split('\n', 1)
        headers = header.split()
        self.assertEqual(headers[0], 'ver:3.0')
        self.assertEqual(headers[1], 'server:thesupervisorname')
        self.assertEqual(headers[2], 'serial:80')
        self.assertEqual(headers[3], 'pool:thepoolname')
        self.assertEqual(headers[4], 'poolserial:20')
        self.assertEqual(headers[5], 'eventname:PROCESS_COMMUNICATION_STDOUT')
        self.assertEqual(headers[6], 'len:8')
        self.assertEqual(payload, 'payload\n')

    def test_handle_rejected_no_overflow(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        pool.event_buffer = [None, None]
        class DummyEvent1:
            serial = 'abc'
        class DummyEvent2:
            process = process1
            event = DummyEvent1()
        dummyevent = DummyEvent2()
        dummyevent.serial = 1
        pool.handle_rejected(dummyevent)
        self.assertEqual(pool.event_buffer, [dummyevent.event, None, None])

    def test_handle_rejected_event_buffer_overflowed(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        gconfig.buffer_size = 3
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        class DummyEvent:
            def __init__(self, serial):
                self.serial = serial
        class DummyRejectedEvent:
            def __init__(self, serial):
                self.process = process1
                self.event = DummyEvent(serial)
        event_a = DummyEvent('a')
        event_b = DummyEvent('b')
        event_c = DummyEvent('c')
        rej_event = DummyRejectedEvent('rejected')
        pool.event_buffer = [event_a, event_b, event_c]
        pool.handle_rejected(rej_event)
        serials = [ x.serial for x in pool.event_buffer ]
        # we popped a, and we inserted the rejected event into the 1st pos
        self.assertEqual(serials, ['rejected', 'b', 'c'])
        self.assertEqual(pool.config.options.logger.data[0],
            'pool whatever event buffer overflowed, discarding event a')

    def test_dispatch_pipe_error(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        from supervisor.states import EventListenerStates
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        process1 = pool.processes['process1']
        process1.write_error = errno.EPIPE
        process1.listener_state = EventListenerStates.READY
        event = DummyEvent()
        pool._acceptEvent(event)
        pool.dispatch()
        self.assertEqual(process1.listener_state, EventListenerStates.READY)
        self.assertEqual(pool.event_buffer, [event])
        self.assertEqual(options.logger.data[0],
                         'rebuffering event abc for pool whatever (bufsize 0)')

    def test__acceptEvent_attaches_pool_serial_and_serial(self):
        from supervisor.process import GlobalSerial
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        process1 = pool.processes['process1']
        from supervisor.states import EventListenerStates
        process1.listener_state = EventListenerStates.READY
        event = DummyEvent(None)
        pool._acceptEvent(event)
        self.assertEqual(event.serial, GlobalSerial.serial)
        self.assertEqual(event.pool_serials['whatever'], pool.serial)

    def test_repr(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pool = self._makeOne(gconfig)
        s = repr(pool)
        self.assertTrue('supervisor.process.EventListenerPool' in s)
        self.assertTrue(s.endswith('named whatever>'))

    def test_transition_nobody_ready(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STARTING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        event = DummyEvent()
        event.serial = 'a'
        from supervisor.states import EventListenerStates
        process1.listener_state = EventListenerStates.BUSY
        pool._acceptEvent(event)
        pool.transition()
        self.assertEqual(process1.transitioned, True)
        self.assertEqual(pool.event_buffer, [event])

    def test_transition_event_proc_not_running(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STARTING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        event = DummyEvent()
        from supervisor.states import EventListenerStates
        event.serial = 1
        process1.listener_state = EventListenerStates.READY
        pool._acceptEvent(event)
        pool.transition()
        self.assertEqual(process1.transitioned, True)
        self.assertEqual(pool.event_buffer, [event])
        self.assertEqual(process1.stdin_buffer, '')
        self.assertEqual(process1.listener_state, EventListenerStates.READY)

    def test_transition_event_proc_running(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.RUNNING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        event = DummyEvent()
        from supervisor.states import EventListenerStates
        process1.listener_state = EventListenerStates.READY
        class DummyGroup:
            config = gconfig
        process1.group = DummyGroup
        pool._acceptEvent(event)
        pool.transition()
        self.assertEqual(process1.transitioned, True)
        self.assertEqual(pool.event_buffer, [])
        header, payload = process1.stdin_buffer.split('\n', 1)
        self.assertEqual(payload, 'dummy event', payload)
        self.assertEqual(process1.listener_state, EventListenerStates.BUSY)
        self.assertEqual(process1.event, event)

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_rpcinterfaces
# -*- coding: utf-8 -*-
import unittest
import sys
import os
import time
import errno

from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyPGroupConfig
from supervisor.tests.base import DummyProcessGroup
from supervisor.tests.base import PopulatedDummySupervisor
from supervisor.tests.base import _NOW
from supervisor.tests.base import _TIMEFORMAT

class TestBase(unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def _assertRPCError(self, code, callable, *args, **kw):
        from supervisor import xmlrpc
        try:
            callable(*args, **kw)
        except xmlrpc.RPCError as inst:
            self.assertEqual(inst.code, code)
        else:
            raise AssertionError("Didn't raise")

class MainXMLRPCInterfaceTests(TestBase):

    def _getTargetClass(self):
        from supervisor import xmlrpc
        return xmlrpc.RootRPCInterface

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_ctor(self):
        interface = self._makeOne([('supervisor', None)])
        self.assertEqual(interface.supervisor, None)

    def test_traverse(self):
        dummy = DummyRPCInterface()
        interface = self._makeOne([('dummy', dummy)])
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.UNKNOWN_METHOD,
                             xmlrpc.traverse, interface, 'notthere.hello', [])
        self._assertRPCError(xmlrpc.Faults.UNKNOWN_METHOD,
                             xmlrpc.traverse, interface,
                             'supervisor._readFile', [])
        self._assertRPCError(xmlrpc.Faults.INCORRECT_PARAMETERS,
                             xmlrpc.traverse, interface,
                             'dummy.hello', [1])
        self.assertEqual(xmlrpc.traverse(
            interface, 'dummy.hello', []), 'Hello!')

class SupervisorNamespaceXMLRPCInterfaceTests(TestBase):
    def _getTargetClass(self):
        from supervisor import rpcinterface
        return rpcinterface.SupervisorNamespaceRPCInterface

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_update(self):
        from supervisor import xmlrpc
        from supervisor.supervisord import SupervisorStates
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        interface._update('foo')
        self.assertEqual(interface.update_text, 'foo')
        supervisord.options.mood = SupervisorStates.SHUTDOWN
        self._assertRPCError(xmlrpc.Faults.SHUTDOWN_STATE, interface._update,
                             'foo')

    def test_getAPIVersion(self):
        from supervisor import rpcinterface
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        version = interface.getAPIVersion()
        self.assertEqual(version, rpcinterface.API_VERSION)
        self.assertEqual(interface.update_text, 'getAPIVersion')

    def test_getAPIVersion_aliased_to_deprecated_getVersion(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.getAPIVersion, interface.getVersion)

    def test_getSupervisorVersion(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        version = interface.getSupervisorVersion()
        from supervisor import options
        self.assertEqual(version, options.VERSION)
        self.assertEqual(interface.update_text, 'getSupervisorVersion')


    def test_getIdentification(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        identifier = interface.getIdentification()
        self.assertEqual(identifier, supervisord.options.identifier)
        self.assertEqual(interface.update_text, 'getIdentification')

    def test_getState(self):
        from supervisor.states import getSupervisorStateDescription
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        stateinfo = interface.getState()
        statecode = supervisord.options.mood
        statename = getSupervisorStateDescription(statecode)
        self.assertEqual(stateinfo['statecode'], statecode)
        self.assertEqual(stateinfo['statename'], statename)
        self.assertEqual(interface.update_text, 'getState')

    def test_getPID(self):
        options = DummyOptions()
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.getPID(), options.get_pid())
        self.assertEqual(interface.update_text, 'getPID')

    def test_readLog_aliased_to_deprecated_readMainLog(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.readMainLog, interface.readLog)

    def test_readLog_unreadable(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.NO_FILE, interface.readLog,
                             offset=0, length=1)

    def test_readLog_badargs(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        try:
            logfile = supervisord.options.logfile
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.close()
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readLog, offset=-1, length=1)
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readLog, offset=-1,
                                 length=-1)
        finally:
            os.remove(logfile)

    def test_readLog(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        logfile = supervisord.options.logfile
        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.write('y' * 2048)
            f.close()
            data = interface.readLog(offset=0, length=0)
            self.assertEqual(interface.update_text, 'readLog')
            self.assertEqual(data, ('x' * 2048) + ('y' * 2048))
            data = interface.readLog(offset=2048, length=0)
            self.assertEqual(data, 'y' * 2048)
            data = interface.readLog(offset=0, length=2048)
            self.assertEqual(data, 'x' * 2048)
            data = interface.readLog(offset=-4, length=0)
            self.assertEqual(data, 'y' * 4)
        finally:
            os.remove(logfile)

    def test_clearLog_unreadable(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.NO_FILE, interface.clearLog)

    def test_clearLog_unremoveable(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        options.existing = [options.logfile]
        options.remove_error = 1
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)
        self.assertRaises(xmlrpc.RPCError, interface.clearLog)
        self.assertEqual(interface.update_text, 'clearLog')

    def test_clearLog(self):
        options = DummyOptions()
        options.existing = [options.logfile]
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)
        result = interface.clearLog()
        self.assertEqual(interface.update_text, 'clearLog')
        self.assertEqual(result, True)
        self.assertEqual(options.removed[0], options.logfile)
        for handler in supervisord.options.logger.handlers:
            self.assertEqual(handler.reopened, True)

    def test_shutdown(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        value = interface.shutdown()
        self.assertEqual(value, True)
        self.assertEqual(supervisord.options.mood, -1)

    def test_restart(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        value = interface.restart()
        self.assertEqual(value, True)
        self.assertEqual(supervisord.options.mood, 0)

    def test_reloadConfig(self):
        options = DummyOptions()
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)

        changes = [ [DummyPGroupConfig(options, 'added')],
                    [DummyPGroupConfig(options, 'changed')],
                    [DummyPGroupConfig(options, 'dropped')] ]

        supervisord.diff_to_active = lambda : changes

        value = interface.reloadConfig()
        self.assertEqual(value, [[['added'], ['changed'], ['dropped']]])

    def test_reloadConfig_process_config_raises_ValueError(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        def raise_exc(*arg, **kw):
            raise ValueError('foo')
        options.process_config = raise_exc
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.CANT_REREAD, interface.reloadConfig)

    def test_addProcessGroup(self):
        from supervisor.supervisord import Supervisor
        from supervisor import xmlrpc
        options = DummyOptions()
        supervisord = Supervisor(options)
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
        supervisord.options.process_group_configs = [gconfig]

        interface = self._makeOne(supervisord)

        result = interface.addProcessGroup('group1')
        self.assertTrue(result)
        self.assertEqual(list(supervisord.process_groups.keys()), ['group1'])

        self._assertRPCError(xmlrpc.Faults.ALREADY_ADDED,
                             interface.addProcessGroup, 'group1')
        self.assertEqual(list(supervisord.process_groups.keys()), ['group1'])

        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.addProcessGroup, 'asdf')
        self.assertEqual(list(supervisord.process_groups.keys()), ['group1'])

    def test_removeProcessGroup(self):
        from supervisor.supervisord import Supervisor
        options = DummyOptions()
        supervisord = Supervisor(options)
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
        supervisord.options.process_group_configs = [gconfig]

        interface = self._makeOne(supervisord)

        interface.addProcessGroup('group1')
        result = interface.removeProcessGroup('group1')
        self.assertTrue(result)
        self.assertEqual(list(supervisord.process_groups.keys()), [])

    def test_removeProcessGroup_bad_name(self):
        from supervisor.supervisord import Supervisor
        from supervisor import xmlrpc
        options = DummyOptions()
        supervisord = Supervisor(options)
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
        supervisord.options.process_group_configs = [gconfig]

        interface = self._makeOne(supervisord)

        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.removeProcessGroup, 'asdf')

    def test_removeProcessGroup_still_running(self):
        from supervisor.supervisord import Supervisor
        from supervisor import xmlrpc
        options = DummyOptions()
        supervisord = Supervisor(options)
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
        supervisord.options.process_group_configs = [gconfig]
        process = DummyProcessGroup(gconfig)
        process.unstopped_processes = [123]
        supervisord.process_groups = {'group1':process}
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.STILL_RUNNING,
                             interface.removeProcessGroup, 'group1')


    def test_startProcess_already_started(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'pid', 10)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo')
        self._assertRPCError(xmlrpc.Faults.ALREADY_STARTED,
                             callback)

    def test_startProcess_bad_group_name(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        supervisord = PopulatedDummySupervisor(options, 'group1', pconfig)
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.startProcess, 'group2:foo')

    def test_startProcess_bad_process_name(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        supervisord = PopulatedDummySupervisor(options, 'group1', pconfig)
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.startProcess, 'group1:bar')

    def test_startProcess_file_not_found(self):
        options = DummyOptions()
        pconfig  = DummyPConfig(options, 'foo', '/foo/bar', autostart=False)
        from supervisor.options import NotFound
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        process = supervisord.process_groups['foo'].processes['foo']
        process.execv_arg_exception = NotFound
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NO_FILE,
                             interface.startProcess, 'foo')

    def test_startProcess_file_not_executable(self):
        options = DummyOptions()
        pconfig  = DummyPConfig(options, 'foo', '/foo/bar', autostart=False)
        from supervisor.options import NotExecutable
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        process = supervisord.process_groups['foo'].processes['foo']
        process.execv_arg_exception = NotExecutable
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NOT_EXECUTABLE,
                             interface.startProcess, 'foo')

    def test_startProcess_spawnerr(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        process = supervisord.process_groups['foo'].processes['foo']
        process.spawnerr = 'abc'
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo')
        self._assertRPCError(xmlrpc.Faults.SPAWN_ERROR, callback)

    def test_startProcess(self):
        from supervisor import http
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False,
                               startsecs=.01)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo')
        self.assertEqual(callback(), http.NOT_DONE_YET)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')
        process.state = ProcessStates.RUNNING
        time.sleep(.02)
        result = callback()
        self.assertEqual(result, True)

    def test_startProcess_nowait(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo', wait=False)
        self.assertEqual(callback(), True)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')

    def test_startProcess_nostartsecs(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False,
                               startsecs=0)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo', wait=True)
        self.assertEqual(callback(), True)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')

    def test_startProcess_abnormal_term_process_not_running(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo', 100) # milliseconds
        result = callback()
        from supervisor import http
        self.assertEqual(result, http.NOT_DONE_YET)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')
        process.state = ProcessStates.BACKOFF

        time.sleep(.1)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.ABNORMAL_TERMINATION, callback)

    def test_startProcess_abnormal_term_startsecs_exceeded(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False,
                               startsecs=.01)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo', 100) # milliseconds
        result = callback()
        from supervisor import http
        self.assertEqual(result, http.NOT_DONE_YET)
        supervisord.set_procattr('foo', 'state', ProcessStates.STARTING)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')
        process.state = ProcessStates.STARTING

        time.sleep(.2)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.ABNORMAL_TERMINATION, callback)

    def test_startProcess_splat_calls_startProcessGroup(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, autostart=False,
                               startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo',
                                               pconfig1, pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        interface.startProcess('foo:*')
        self.assertEqual(interface.update_text, 'startProcessGroup')

    def test_startProcessGroup(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1,
                                startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcessGroup('foo')

        from supervisor.http import NOT_DONE_YET

        # create callbacks in startall()
        self.assertEqual(callback(), NOT_DONE_YET)
        # start first process
        self.assertEqual(callback(), NOT_DONE_YET)
        # start second process
        self.assertEqual(callback(), NOT_DONE_YET)

        # wait for timeout 1
        time.sleep(.02)
        self.assertEqual(callback(), NOT_DONE_YET)

        # wait for timeout 2
        time.sleep(.02)
        result = callback()

        self.assertEqual(len(result), 2)

        from supervisor.xmlrpc import Faults

        # XXX not sure about this ordering, I think process1 should
        # probably show up first
        self.assertEqual(result[0]['name'], 'process2')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process1')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

        self.assertEqual(interface.update_text, 'startProcess')

        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.spawned, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.spawned, True)

    def test_startProcessGroup_nowait(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1,
                                startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcessGroup('foo', wait=False)
        from supervisor.http import NOT_DONE_YET
        from supervisor.xmlrpc import Faults

        # create callbacks in startall()
        self.assertEqual(callback(), NOT_DONE_YET)

        # get a result
        result = callback()

        self.assertEqual(len(result), 2)
        self.assertEqual(result[0]['name'], 'process1')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process2')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

    def test_startProcessGroup_badname(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.startProcessGroup, 'foo')


    def test_startAllProcesses(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1,
                                startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startAllProcesses()

        from supervisor.http import NOT_DONE_YET

        # create callbacks in startall()
        self.assertEqual(callback(), NOT_DONE_YET)
        # start first process
        self.assertEqual(callback(), NOT_DONE_YET)
        # start second process
        self.assertEqual(callback(), NOT_DONE_YET)

        # wait for timeout 1
        time.sleep(.02)
        self.assertEqual(callback(), NOT_DONE_YET)

        # wait for timeout 2
        time.sleep(.02)
        result = callback()

        self.assertEqual(len(result), 2)

        from supervisor.xmlrpc import Faults

        # XXX not sure about this ordering, I think process1 should
        # probably show up first
        self.assertEqual(result[0]['name'], 'process2')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process1')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

        self.assertEqual(interface.update_text, 'startProcess')

        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.spawned, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.spawned, True)

    def test_startAllProcesses_nowait(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1,
                                startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startAllProcesses(wait=False)
        from supervisor.http import NOT_DONE_YET
        from supervisor.xmlrpc import Faults

        # create callbacks in startall()
        self.assertEqual(callback(), NOT_DONE_YET)

        # get a result
        result = callback()

        self.assertEqual(len(result), 2)
        self.assertEqual(result[0]['name'], 'process1')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process2')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

    def test_stopProcess(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcess('foo')
        self.assertEqual(interface.update_text, 'stopProcess')
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.backoff, 0)
        self.assertEqual(process.delay, 0)
        self.assertEqual(process.killing, 0)
        from supervisor import http
        self.assertEqual(callback(), http.NOT_DONE_YET)
        self.assertEqual(process.state, ProcessStates.STOPPED)
        self.assertEqual(callback(), True)
        self.assertEqual(len(supervisord.process_groups['foo'].processes), 1)
        self.assertEqual(interface.update_text, 'stopProcess')

    def test_stopProcess_nowait(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcess('foo', wait=False)
        self.assertEqual(callback(), True)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.stop_called, True)
        self.assertEqual(interface.update_text, 'stopProcess')

    def test_stopProcess_bad_name(self):
        from supervisor.xmlrpc import Faults
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(Faults.BAD_NAME,
                             interface.stopProcess, 'foo')

    def test_stopProcess_not_running(self):
        from supervisor.states import ProcessStates
        from supervisor.xmlrpc import Faults
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.EXITED)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcess('foo')
        self._assertRPCError(Faults.NOT_RUNNING, callback)

    def test_stopProcess_failed(self):
        from supervisor.xmlrpc import Faults
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'stop', lambda: 'unstoppable')
        interface = self._makeOne(supervisord)
        callback = interface.stopProcess('foo')
        self._assertRPCError(Faults.FAILED, callback)

    def test_stopProcessGroup(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', '/bin/foo', priority=1)
        pconfig2 = DummyPConfig(options, 'process2', '/bin/foo2', priority=2)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)
        supervisord.set_procattr('process2', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcessGroup('foo')
        self.assertEqual(interface.update_text, 'stopProcessGroup')
        from supervisor import http
        value = http.NOT_DONE_YET
        while 1:
            value = callback()
            if value is not http.NOT_DONE_YET:
                break

        self.assertEqual(value, [
            {'status':80,'group':'foo','name': 'process1','description': 'OK'},
            {'status':80,'group':'foo','name': 'process2','description': 'OK'},
            ] )
        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.stop_called, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.stop_called, True)

    def test_stopProcessGroup_nowait(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)
        supervisord.set_procattr('process2', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcessGroup('foo', wait=False)
        from supervisor.http import NOT_DONE_YET
        from supervisor.xmlrpc import Faults

        # create callbacks in killall()
        self.assertEqual(callback(), NOT_DONE_YET)

        # get a result
        result = callback()

        self.assertEqual(len(result), 2)
        self.assertEqual(result[0]['name'], 'process1')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process2')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

    def test_stopProcessGroup_badname(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.stopProcessGroup, 'foo')

    def test_stopProcess_splat_calls_stopProcessGroup(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, autostart=False,
                               startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo',
                                               pconfig1, pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        interface.stopProcess('foo:*')
        self.assertEqual(interface.update_text, 'stopProcessGroup')

    def test_stopAllProcesses(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', '/bin/foo', priority=1)
        pconfig2 = DummyPConfig(options, 'process2', '/bin/foo2', priority=2)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)
        supervisord.set_procattr('process2', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopAllProcesses()
        self.assertEqual(interface.update_text, 'stopAllProcesses')
        from supervisor import http
        value = http.NOT_DONE_YET
        while 1:
            value = callback()
            if value is not http.NOT_DONE_YET:
                break

        self.assertEqual(value, [
            {'status':80,'group':'foo','name': 'process1','description': 'OK'},
            {'status':80,'group':'foo','name': 'process2','description': 'OK'},
            ] )
        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.stop_called, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.stop_called, True)

    def test_stopAllProcesses_nowait(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)
        supervisord.set_procattr('process2', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopAllProcesses(wait=False)
        from supervisor.http import NOT_DONE_YET
        from supervisor.xmlrpc import Faults

        # create callbacks in killall()
        self.assertEqual(callback(), NOT_DONE_YET)

        # get a result
        result = callback()

        self.assertEqual(len(result), 2)
        self.assertEqual(result[0]['name'], 'process1')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process2')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

    def test_getAllConfigInfo(self):
        options = DummyOptions()
        supervisord = DummySupervisor(options, 'foo')

        pconfig1 = DummyPConfig(options, 'process1', __file__)
        pconfig2 = DummyPConfig(options, 'process2', __file__)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig1, pconfig2])
        supervisord.process_groups = {'group1': DummyProcessGroup(gconfig)}
        supervisord.options.process_group_configs = [gconfig]

        interface = self._makeOne(supervisord)
        configs = interface.getAllConfigInfo()
        self.assertEqual(configs, [{ 'group': 'group1',
                                     'name': 'process1',
                                     'inuse': True,
                                     'autostart': True,
                                     'process_prio': 999,
                                     'group_prio': 999 },
                                   { 'group': 'group1',
                                     'name': 'process2',
                                     'inuse': True,
                                     'autostart': True,
                                     'process_prio': 999,
                                     'group_prio': 999 }])

    def test__interpretProcessInfo(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        start = _NOW -100
        stop  = _NOW -1
        from supervisor.process import ProcessStates
        running = {'name':'running',
                   'pid':1,
                   'state':ProcessStates.RUNNING,
                   'start':start,
                   'stop':stop,
                   'now':_NOW}

        description = interface._interpretProcessInfo(running)
        self.assertEqual(description, 'pid 1, uptime 0:01:40')

        fatal = {'name':'fatal',
                 'pid':2,
                 'state':ProcessStates.FATAL,
                 'start':start,
                 'stop':stop,
                 'now':_NOW,
                 'spawnerr':'Hosed'}

        description = interface._interpretProcessInfo(fatal)
        self.assertEqual(description, 'Hosed')

        fatal2 = {'name':'fatal',
                  'pid':2,
                  'state':ProcessStates.FATAL,
                  'start':start,
                  'stop':stop,
                  'now':_NOW,
                  'spawnerr':'',}

        description = interface._interpretProcessInfo(fatal2)
        self.assertEqual(description, 'unknown error (try "tail fatal")')

        stopped = {'name':'stopped',
                   'pid':3,
                   'state':ProcessStates.STOPPED,
                   'start':start,
                   'stop':stop,
                   'now':_NOW,
                   'spawnerr':'',}

        description = interface._interpretProcessInfo(stopped)
        from datetime import datetime
        stoptime = datetime(*time.localtime(stop)[:7])
        self.assertEqual(description, stoptime.strftime(_TIMEFORMAT))

        stopped2 = {'name':'stopped',
                   'pid':3,
                   'state':ProcessStates.STOPPED,
                   'start':0,
                   'stop':stop,
                   'now':_NOW,
                   'spawnerr':'',}

        description = interface._interpretProcessInfo(stopped2)
        self.assertEqual(description, 'Not started')


    def test_getProcessInfo(self):
        from supervisor.process import ProcessStates

        options = DummyOptions()
        config = DummyPConfig(options, 'foo', '/bin/foo',
                              stdout_logfile='/tmp/fleeb.bar')
        process = DummyProcess(config)
        process.pid = 111
        process.laststart = 10
        process.laststop = 11
        pgroup_config = DummyPGroupConfig(options, name='foo')
        pgroup = DummyProcessGroup(pgroup_config)
        pgroup.processes = {'foo':process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        data = interface.getProcessInfo('foo')

        self.assertEqual(interface.update_text, 'getProcessInfo')
        self.assertEqual(data['logfile'], '/tmp/fleeb.bar')
        self.assertEqual(data['stdout_logfile'], '/tmp/fleeb.bar')
        self.assertEqual(data['stderr_logfile'], '')
        self.assertEqual(data['name'], 'foo')
        self.assertEqual(data['pid'], 111)
        self.assertEqual(data['start'], 10)
        self.assertEqual(data['stop'], 11)
        self.assertEqual(data['state'], ProcessStates.RUNNING)
        self.assertEqual(data['statename'], 'RUNNING')
        self.assertEqual(data['exitstatus'], 0)
        self.assertEqual(data['spawnerr'], '')
        self.assertTrue(data['description'].startswith('pid 111'))

    def test_getProcessInfo_logfile_NONE(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'foo', '/bin/foo',
                              stdout_logfile=None)
        process = DummyProcess(config)
        process.pid = 111
        process.laststart = 10
        process.laststop = 11
        pgroup_config = DummyPGroupConfig(options, name='foo')
        pgroup = DummyProcessGroup(pgroup_config)
        pgroup.processes = {'foo':process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        data = interface.getProcessInfo('foo')
        self.assertEqual(data['logfile'], '')
        self.assertEqual(data['stdout_logfile'], '')

    def test_getProcessInfo_unknown_state(self):
        from supervisor.states import ProcessStates
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.UNKNOWN)
        interface = self._makeOne(supervisord)
        data = interface.getProcessInfo('foo')
        self.assertEqual(data['statename'], 'UNKNOWN')
        self.assertEqual(data['description'], '')

    def test_getProcessInfo_bad_name_when_bad_process(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.getProcessInfo, 'nonexistant')

    def test_getProcessInfo_bad_name_when_no_process(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.getProcessInfo, 'foo:')

    def test_getAllProcessInfo(self):
        from supervisor.process import ProcessStates
        options = DummyOptions()

        p1config = DummyPConfig(options, 'process1', '/bin/process1',
                                priority=1,
                                stdout_logfile='/tmp/process1.log')

        p2config = DummyPConfig(options, 'process2', '/bin/process2',
                                priority=2,
                                stdout_logfile='/tmp/process2.log')

        supervisord = PopulatedDummySupervisor(options, 'gname', p1config,
                                               p2config)
        supervisord.set_procattr('process1', 'pid', 111)
        supervisord.set_procattr('process1', 'laststart', 10)
        supervisord.set_procattr('process1', 'laststop', 11)
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)

        supervisord.set_procattr('process2', 'pid', 0)
        supervisord.set_procattr('process2', 'laststart', 20)
        supervisord.set_procattr('process2', 'laststop', 11)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)

        interface = self._makeOne(supervisord)

        info = interface.getAllProcessInfo()

        self.assertEqual(interface.update_text, 'getProcessInfo')
        self.assertEqual(len(info), 2)

        p1info = info[0]
        self.assertEqual(p1info['logfile'], '/tmp/process1.log')
        self.assertEqual(p1info['stdout_logfile'], '/tmp/process1.log')
        self.assertEqual(p1info['stderr_logfile'], '')
        self.assertEqual(p1info['name'], 'process1')
        self.assertEqual(p1info['pid'], 111)
        self.assertEqual(p1info['start'], 10)
        self.assertEqual(p1info['stop'], 11)
        self.assertEqual(p1info['state'], ProcessStates.RUNNING)
        self.assertEqual(p1info['statename'], 'RUNNING')
        self.assertEqual(p1info['exitstatus'], 0)
        self.assertEqual(p1info['spawnerr'], '')
        self.assertEqual(p1info['group'], 'gname')
        self.assertTrue(p1info['description'].startswith('pid 111'))

        p2info = info[1]
        process2 = supervisord.process_groups['gname'].processes['process2']
        self.assertEqual(p2info['logfile'], '/tmp/process2.log')
        self.assertEqual(p2info['stdout_logfile'], '/tmp/process2.log')
        self.assertEqual(p1info['stderr_logfile'], '')
        self.assertEqual(p2info['name'], 'process2')
        self.assertEqual(p2info['pid'], 0)
        self.assertEqual(p2info['start'], process2.laststart)
        self.assertEqual(p2info['stop'], 11)
        self.assertEqual(p2info['state'], ProcessStates.STOPPED)
        self.assertEqual(p2info['statename'], 'STOPPED')
        self.assertEqual(p2info['exitstatus'], 0)
        self.assertEqual(p2info['spawnerr'], '')
        self.assertEqual(p1info['group'], 'gname')

        from datetime import datetime
        starttime = datetime(*time.localtime(process2.laststart)[:7])
        self.assertEqual(p2info['description'],
                            starttime.strftime(_TIMEFORMAT))

    def test_readProcessStdoutLog_unreadable(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                               stdout_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.NO_FILE,
                             interface.readProcessStdoutLog,
                             'process1', offset=0, length=1)

    def test_readProcessStdoutLog_badargs(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                              stdout_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['process1'].processes['process1']
        logfile = process.config.stdout_logfile

        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.close()
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readProcessStdoutLog,
                                 'process1', offset=-1, length=1)
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readProcessStdoutLog, 'process1',
                                 offset=-1, length=-1)
        finally:
            os.remove(logfile)

    def test_readProcessStdoutLog(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                              stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stdout_logfile
        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.write('y' * 2048)
            f.close()
            data = interface.readProcessStdoutLog('foo', offset=0, length=0)
            self.assertEqual(interface.update_text, 'readProcessStdoutLog')
            self.assertEqual(data, ('x' * 2048) + ('y' * 2048))
            data = interface.readProcessStdoutLog('foo', offset=2048, length=0)
            self.assertEqual(data, 'y' * 2048)
            data = interface.readProcessStdoutLog('foo', offset=0, length=2048)
            self.assertEqual(data, 'x' * 2048)
            data = interface.readProcessStdoutLog('foo', offset=-4, length=0)
            self.assertEqual(data, 'y' * 4)
        finally:
            os.remove(logfile)

    def test_readProcessLogAliasedTo_readProcessStdoutLog(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.readProcessLog,
                         interface.readProcessStdoutLog)

    def test_readProcessStderrLog_unreadable(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                               stderr_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.NO_FILE,
                             interface.readProcessStderrLog,
                             'process1', offset=0, length=1)

    def test_readProcessStderrLog_badargs(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                              stderr_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['process1'].processes['process1']
        logfile = process.config.stderr_logfile

        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.close()
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readProcessStderrLog,
                                 'process1', offset=-1, length=1)
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readProcessStderrLog, 'process1',
                                 offset=-1, length=-1)
        finally:
            os.remove(logfile)

    def test_readProcessStderrLog(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                              stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stderr_logfile
        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.write('y' * 2048)
            f.close()
            data = interface.readProcessStderrLog('foo', offset=0, length=0)
            self.assertEqual(interface.update_text, 'readProcessStderrLog')
            self.assertEqual(data, ('x' * 2048) + ('y' * 2048))
            data = interface.readProcessStderrLog('foo', offset=2048, length=0)
            self.assertEqual(data, 'y' * 2048)
            data = interface.readProcessStderrLog('foo', offset=0, length=2048)
            self.assertEqual(data, 'x' * 2048)
            data = interface.readProcessStderrLog('foo', offset=-4, length=0)
            self.assertEqual(data, 'y' * 4)
        finally:
            os.remove(logfile)

    def test_tailProcessStdoutLog_bad_name(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.tailProcessStdoutLog, 'BAD_NAME', 0, 10)

    def test_tailProcessStdoutLog_all(self):
        # test entire log is returned when offset==0 and logsize < length
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stdout_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                        offset=0,
                                                        length=len(letters))
            self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, letters)
        finally:
            os.remove(logfile)

    def test_tailProcessStdoutLog_none(self):
        # test nothing is returned when offset <= logsize
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stdout_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            # offset==logsize
            data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                        offset=len(letters),
                                                        length=100)
            self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, '')

            # offset > logsize
            data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                        offset=len(letters)+5,
                                                        length=100)
            self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, '')
        finally:
            os.remove(logfile)

    def test_tailProcessStdoutLog_overflow(self):
        # test buffer overflow occurs when logsize > offset+length
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                              stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stdout_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                        offset=0, length=5)
            self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
            self.assertEqual(overflow, True)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, letters[-5:])
        finally:
            os.remove(logfile)

    def test_tailProcessStdoutLog_unreadable(self):
        # test nothing is returned if the log doesn't exist yet
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)

        data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                    offset=0, length=100)
        self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
        self.assertEqual(overflow, False)
        self.assertEqual(offset, 0)
        self.assertEqual(data, '')

    def test_tailProcessLogAliasedTo_tailProcessStdoutLog(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.tailProcessLog,
                         interface.tailProcessStdoutLog)

    def test_tailProcessStderrLog_bad_name(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.tailProcessStderrLog, 'BAD_NAME', 0, 10)

    def test_tailProcessStderrLog_all(self):
        # test entire log is returned when offset==0 and logsize < length
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stderr_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                        offset=0,
                                                        length=len(letters))
            self.assertEqual(interface.update_text, 'tailProcessStderrLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, letters)
        finally:
            os.remove(logfile)

    def test_tailProcessStderrLog_none(self):
        # test nothing is returned when offset <= logsize
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stderr_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            # offset==logsize
            data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                        offset=len(letters),
                                                        length=100)
            self.assertEqual(interface.update_text, 'tailProcessStderrLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, '')

            # offset > logsize
            data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                        offset=len(letters)+5,
                                                        length=100)
            self.assertEqual(interface.update_text, 'tailProcessStderrLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, '')
        finally:
            os.remove(logfile)

    def test_tailProcessStderrLog_overflow(self):
        # test buffer overflow occurs when logsize > offset+length
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                              stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stderr_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                        offset=0, length=5)
            self.assertEqual(interface.update_text, 'tailProcessStderrLog')
            self.assertEqual(overflow, True)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, letters[-5:])
        finally:
            os.remove(logfile)

    def test_tailProcessStderrLog_unreadable(self):
        # test nothing is returned if the log doesn't exist yet
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)

        data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                    offset=0, length=100)
        self.assertEqual(interface.update_text, 'tailProcessStderrLog')
        self.assertEqual(overflow, False)
        self.assertEqual(offset, 0)
        self.assertEqual(data, '')

    def test_clearProcessLogs_bad_name_no_group(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo')
        process = DummyProcess(pconfig)
        pgroup = DummyProcessGroup(None)
        pgroup.processes = {'foo': process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.clearProcessLogs,
                             'badgroup')

    def test_clearProcessLogs_bad_name_no_process(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo')
        process = DummyProcess(pconfig)
        pgroup = DummyProcessGroup(None)
        pgroup.processes = {'foo': process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.clearProcessLogs,
                             'foo:*')

    def test_clearProcessLogs(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo')
        process = DummyProcess(pconfig)
        pgroup = DummyProcessGroup(None)
        pgroup.processes = {'foo': process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        interface.clearProcessLogs('foo')
        self.assertEqual(process.logsremoved, True)

    def test_clearProcessLogs_failed(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo')
        process = DummyProcess(pconfig)
        pgroup = DummyProcessGroup(None)
        pgroup.processes = {'foo': process}
        process.error_at_clear = True
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        self.assertRaises(xmlrpc.RPCError, interface.clearProcessLogs, 'foo')

    def test_clearProcessLogAliasedTo_clearProcessLogs(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.clearProcessLog,
                         interface.clearProcessLogs)

    def test_clearAllProcessLogs(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo', priority=1)
        pconfig2 = DummyPConfig(options, 'process2', 'bar', priority=2)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        interface = self._makeOne(supervisord)
        callback = interface.clearAllProcessLogs()
        callback()
        results = callback()
        from supervisor import xmlrpc
        self.assertEqual(results[0],
                         {'name':'process1',
                          'group':'foo',
                          'status':xmlrpc.Faults.SUCCESS,
                          'description':'OK'})
        self.assertEqual(results[1],
                         {'name':'process2',
                          'group':'foo',
                          'status':xmlrpc.Faults.SUCCESS,
                          'description':'OK'})
        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.logsremoved, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.logsremoved, True)

    def test_clearAllProcessLogs_onefails(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo', priority=1)
        pconfig2 = DummyPConfig(options, 'process2', 'bar', priority=2)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        supervisord.set_procattr('process1', 'error_at_clear', True)
        interface = self._makeOne(supervisord)
        callback = interface.clearAllProcessLogs()
        callback()
        results = callback()
        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.logsremoved, False)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.logsremoved, True)
        self.assertEqual(len(results), 2)
        from supervisor import xmlrpc
        self.assertEqual(results[0],
                         {'name':'process1',
                          'group':'foo',
                          'status':xmlrpc.Faults.FAILED,
                          'description':'FAILED: foo:process1'})
        self.assertEqual(results[1],
                         {'name':'process2',
                          'group':'foo',
                          'status':xmlrpc.Faults.SUCCESS,
                          'description':'OK'})

    def test_clearAllProcessLogs_no_processes(self):
        supervisord = DummySupervisor()
        self.assertEqual(supervisord.process_groups, {})
        interface = self._makeOne(supervisord)
        callback = interface.clearAllProcessLogs()
        results = callback()
        self.assertEqual(results, [])

    def test_sendProcessStdin_raises_incorrect_params_when_not_chars(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1)
        interface   = self._makeOne(supervisord)
        thing_not_chars = 42
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.INCORRECT_PARAMETERS,
                             interface.sendProcessStdin,
                             'process1', thing_not_chars)

    def test_sendProcessStdin_raises_bad_name_when_bad_process(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.sendProcessStdin,
                             'nonexistant', 'chars for stdin')

    def test_sendProcessStdin_raises_bad_name_when_no_process(self):
        options = DummyOptions()
        supervisord = PopulatedDummySupervisor(options, 'foo')
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.sendProcessStdin,
                             'foo:*', 'chars for stdin')

    def test_sendProcessStdin_raises_not_running_when_not_process_pid(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 0)
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NOT_RUNNING,
                            interface.sendProcessStdin,
                            'process1', 'chars for stdin')

    def test_sendProcessStdin_raises_not_running_when_killing(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 42)
        supervisord.set_procattr('process1', 'killing',True)
        interface   = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NOT_RUNNING,
                             interface.sendProcessStdin,
                             'process1', 'chars for stdin')

    def test_sendProcessStdin_raises_no_file_when_write_raises_epipe(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 42)
        supervisord.set_procattr('process1', 'killing', False)
        supervisord.set_procattr('process1', 'write_error', errno.EPIPE)
        interface   = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NO_FILE,
                             interface.sendProcessStdin,
                             'process1', 'chars for stdin')

    def test_sendProcessStdin_reraises_other_oserrors(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 42)
        supervisord.set_procattr('process1', 'killing', False)
        supervisord.set_procattr('process1', 'write_error', errno.EINTR)
        interface   = self._makeOne(supervisord)
        self.assertRaises(OSError,
                          interface.sendProcessStdin,
                          'process1', 'chars for stdin')

    def test_sendProcessStdin_writes_chars_and_returns_true(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 42)
        interface   = self._makeOne(supervisord)
        chars = 'chars for stdin'
        self.assertTrue(interface.sendProcessStdin('process1', chars))
        self.assertEqual('sendProcessStdin', interface.update_text)
        process1 = supervisord.process_groups['process1'].processes['process1']
        self.assertEqual(process1.stdin_buffer, chars)

#    def test_sendProcessStdin_unicode_encoded_to_utf8(self):
#        options = DummyOptions()
#        pconfig1 = DummyPConfig(options, 'process1', 'foo')
#        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
#        supervisord.set_procattr('process1', 'pid', 42)
#        interface   = self._makeOne(supervisord)
#        interface.sendProcessStdin('process1', u'fi\xed')
#        process1 = supervisord.process_groups['process1'].processes['process1']
#        self.assertEqual(process1.stdin_buffer, 'fi\xc3\xad')

    def test_sendRemoteCommEvent_notifies_subscribers(self):
        options = DummyOptions()
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)

        from supervisor import events
        L = []
        def callback(event):
            L.append(event)

        try:
            events.callbacks[:] = [(events.RemoteCommunicationEvent, callback)]
            result = interface.sendRemoteCommEvent('foo', 'bar')
        finally:
            events.callbacks[:] = []
            events.clear()

        self.assertTrue(result)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.type, 'foo')
        self.assertEqual(event.data, 'bar')

    def test_sendRemoteCommEvent_unicode_encoded_to_utf8(self):
        from supervisor.compat import as_string, PY3
        options = DummyOptions()
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)

        from supervisor import events
        L = []
        def callback(event):
            L.append(event)

        try:
            events.callbacks[:] = [(events.RemoteCommunicationEvent, callback)]
            result = interface.sendRemoteCommEvent(
                as_string('fií once'),
                as_string('fií twice'),
                )
        finally:
            events.callbacks[:] = []
            events.clear()

        self.assertTrue(result)
        self.assertEqual(len(L), 1)
        event = L[0]
        if PY3:
            self.assertEqual(event.type, 'fií once')
            self.assertEqual(event.data, 'fií twice')
        else:
            self.assertEqual(event.type, 'fi\xc3\xad once')
            self.assertEqual(event.data, 'fi\xc3\xad twice')


class SystemNamespaceXMLRPCInterfaceTests(TestBase):
    def _getTargetClass(self):
        from supervisor import xmlrpc
        return xmlrpc.SystemNamespaceRPCInterface

    def _makeOne(self):
        from supervisor import rpcinterface
        supervisord = DummySupervisor()
        supervisor = rpcinterface.SupervisorNamespaceRPCInterface(supervisord)
        return self._getTargetClass()(
            [('supervisor', supervisor),
             ]
            )

    def test_ctor(self):
        interface = self._makeOne()
        self.assertTrue(interface.namespaces['supervisor'])
        self.assertTrue(interface.namespaces['system'])

    def test_listMethods(self):
        interface = self._makeOne()
        methods = interface.listMethods()
        methods.sort()
        keys = list(interface._listMethods().keys())
        keys.sort()
        self.assertEqual(methods, keys)

    def test_methodSignature(self):
        from supervisor import xmlrpc
        interface = self._makeOne()
        self._assertRPCError(xmlrpc.Faults.SIGNATURE_UNSUPPORTED,
                             interface.methodSignature,
                             ['foo.bar'])
        result = interface.methodSignature('system.methodSignature')
        self.assertEqual(result, ['array', 'string'])

    def test_allMethodDocs(self):
        from supervisor import xmlrpc
        # belt-and-suspenders test for docstring-as-typing parsing correctness
        # and documentation validity vs. implementation
        _RPCTYPES = ['int', 'double', 'string', 'boolean', 'dateTime.iso8601',
                     'base64', 'binary', 'array', 'struct']
        interface = self._makeOne()
        methods = interface._listMethods()
        for k in methods.keys():
            # if a method doesn't have a @return value, an RPCError is raised.
            # Detect that here.
            try:
                interface.methodSignature(k)
            except xmlrpc.RPCError:
                raise AssertionError('methodSignature for %s raises '
                                     'RPCError (missing @return doc?)' % k)

            # we want to test that the number of arguments implemented in
            # the function is the same as the number of arguments implied by
            # the doc @params, and that they show up in the same order.
            ns_name, method_name = k.split('.', 1)
            namespace = interface.namespaces[ns_name]
            meth = getattr(namespace, method_name)
            try:
                code = meth.func_code
            except Exception:
                code = meth.__code__
            argnames = code.co_varnames[1:code.co_argcount]
            parsed = xmlrpc.gettags(str(meth.__doc__))

            plines = []
            ptypes = []
            pnames = []
            ptexts = []

            rlines = []
            rtypes = []
            rnames = []
            rtexts = []

            for thing in parsed:
                if thing[1] == 'param': # tag name
                    plines.append(thing[0]) # doc line number
                    ptypes.append(thing[2]) # data type
                    pnames.append(thing[3]) # function name
                    ptexts.append(thing[4])  # description
                elif thing[1] == 'return': # tag name
                    rlines.append(thing[0]) # doc line number
                    rtypes.append(thing[2]) # data type
                    rnames.append(thing[3]) # function name
                    rtexts.append(thing[4])  # description
                elif thing[1] is not None:
                    raise AssertionError(
                        'unknown tag type %s for %s, parsed %s' % (thing[1],
                                                                   k,
                                                                   parsed))
            # param tokens

            if len(argnames) != len(pnames):
                raise AssertionError('Incorrect documentation '
                                     '(%s args, %s doc params) in %s'
                                     % (len(argnames), len(pnames), k))
            for docline in plines:
                self.assertTrue(type(docline) == int, (docline,
                                                       type(docline),
                                                       k,
                                                       parsed))
            for doctype in ptypes:
                self.assertTrue(doctype in _RPCTYPES, doctype)
            for x in range(len(pnames)):
                if pnames[x] != argnames[x]:
                    msg = 'Name wrong: (%s vs. %s in %s)\n%s' % (pnames[x],
                                                                 argnames[x],
                                                                 k,
                                                                 parsed)
                    raise AssertionError(msg)
            for doctext in ptexts:
                self.assertTrue(type(doctext) == type(''), doctext)

            # result tokens

            if len(rlines) > 1:
                raise AssertionError(
                    'Duplicate @return values in docs for %s' % k)
            for docline in rlines:
                self.assertTrue(type(docline) == int, (docline,
                                                       type(docline), k))
            for doctype in rtypes:
                self.assertTrue(doctype in _RPCTYPES, doctype)
            for docname in rnames:
                self.assertTrue(type(docname) == type(''), (docname,
                                                            type(docname),
                                                            k))
            for doctext in rtexts:
                self.assertTrue(type(doctext) == type(''), (doctext,
                                                            type(doctext), k))

    def test_multicall_simplevals(self):
        interface = self._makeOne()
        callback = interface.multicall([
            {'methodName':'system.methodHelp', 'params':['system.methodHelp']},
            {'methodName':'system.listMethods', 'params':[]},
            ])
        from supervisor import http
        result = http.NOT_DONE_YET
        while result is http.NOT_DONE_YET:
            result = callback()
        self.assertEqual(result[0], interface.methodHelp('system.methodHelp'))
        self.assertEqual(result[1], interface.listMethods())

    def test_multicall_recursion_guard(self):
        from supervisor import xmlrpc
        interface = self._makeOne()
        callback = interface.multicall([
            {'methodName': 'system.multicall', 'params': []},
        ])

        from supervisor import http
        result = http.NOT_DONE_YET
        while result is http.NOT_DONE_YET:
            result = callback()

        code = xmlrpc.Faults.INCORRECT_PARAMETERS
        desc = xmlrpc.getFaultDescription(code)
        recursion_fault = {'faultCode': code, 'faultString': desc}

        self.assertEqual(result, [recursion_fault])

    def test_multicall_nested_callback(self):
        interface = self._makeOne()
        callback = interface.multicall([
            {'methodName':'supervisor.stopAllProcesses'}])
        from supervisor import http
        result = http.NOT_DONE_YET
        while result is http.NOT_DONE_YET:
            result = callback()
        self.assertEqual(result[0], [])

    def test_methodHelp(self):
        from supervisor import xmlrpc
        interface = self._makeOne()
        self._assertRPCError(xmlrpc.Faults.SIGNATURE_UNSUPPORTED,
                             interface.methodHelp,
                             ['foo.bar'])
        help = interface.methodHelp('system.methodHelp')
        self.assertEqual(help, interface.methodHelp.__doc__)

class DummyRPCInterface:
    def hello(self):
        return 'Hello!'

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_socket_manager
"""Test suite for supervisor.socket_manager"""

import sys
import os
import unittest
import supervisor.medusa.text_socket as socket
import tempfile

from supervisor.tests.base import DummySocketConfig
from supervisor.tests.base import DummyLogger
from supervisor.datatypes import UnixStreamSocketConfig
from supervisor.datatypes import InetStreamSocketConfig

class TestObject:

    def __init__(self):
        self.value = 5

    def getValue(self):
        return self.value

    def setValue(self, val):
        self.value = val

class ProxyTest(unittest.TestCase):

    def setUp(self):
        self.on_deleteCalled = False

    def _getTargetClass(self):
        from supervisor.socket_manager import Proxy
        return Proxy

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def setOnDeleteCalled(self):
        self.on_deleteCalled = True

    def test_proxy_getattr(self):
        proxy = self._makeOne(TestObject())
        self.assertEqual(5, proxy.getValue())

    def test_on_delete(self):
        proxy = self._makeOne(TestObject(), on_delete=self.setOnDeleteCalled)
        self.assertEqual(5, proxy.getValue())
        proxy = None
        self.assertTrue(self.on_deleteCalled)

class ReferenceCounterTest(unittest.TestCase):

    def setUp(self):
        self.running = False

    def start(self):
        self.running = True

    def stop(self):
        self.running = False

    def _getTargetClass(self):
        from supervisor.socket_manager import ReferenceCounter
        return ReferenceCounter

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_incr_and_decr(self):
        ctr = self._makeOne(on_zero=self.stop,on_non_zero=self.start)
        self.assertFalse(self.running)
        ctr.increment()
        self.assertTrue(self.running)
        self.assertEqual(1, ctr.get_count())
        ctr.increment()
        self.assertTrue(self.running)
        self.assertEqual(2, ctr.get_count())
        ctr.decrement()
        self.assertTrue(self.running)
        self.assertEqual(1, ctr.get_count())
        ctr.decrement()
        self.assertFalse(self.running)
        self.assertEqual(0, ctr.get_count())

    def test_decr_at_zero_raises_error(self):
        ctr = self._makeOne(on_zero=self.stop,on_non_zero=self.start)
        self.assertRaises(Exception, ctr.decrement)

class SocketManagerTest(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.socket_manager import SocketManager
        return SocketManager

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_repr(self):
        conf = DummySocketConfig(2)
        sock_manager = self._makeOne(conf)
        expected = "<%s at %s for %s>" % (
            sock_manager.__class__, id(sock_manager), conf.url)
        self.assertEqual(repr(sock_manager), expected)

    def test_get_config(self):
        conf = DummySocketConfig(2)
        sock_manager = self._makeOne(conf)
        self.assertEqual(conf, sock_manager.config())

    def test_tcp_w_hostname(self):
        conf = InetStreamSocketConfig('localhost', 51041)
        sock_manager = self._makeOne(conf)
        self.assertEqual(sock_manager.socket_config, conf)
        sock = sock_manager.get_socket()
        self.assertEqual(sock.getsockname(), ('127.0.0.1', 51041))

    def test_tcp_w_ip(self):
        conf = InetStreamSocketConfig('127.0.0.1', 51041)
        sock_manager = self._makeOne(conf)
        self.assertEqual(sock_manager.socket_config, conf)
        sock = sock_manager.get_socket()
        self.assertEqual(sock.getsockname(), ('127.0.0.1', 51041))

    def test_unix(self):
        (tf_fd, tf_name) = tempfile.mkstemp()
        conf = UnixStreamSocketConfig(tf_name)
        sock_manager = self._makeOne(conf)
        self.assertEqual(sock_manager.socket_config, conf)
        sock = sock_manager.get_socket()
        self.assertEqual(sock.getsockname(), tf_name)
        sock = None
        os.close(tf_fd)

    def test_socket_lifecycle(self):
        conf = DummySocketConfig(2)
        sock_manager = self._makeOne(conf)
        # Assert that sockets are created on demand
        self.assertFalse(sock_manager.is_prepared())
        # Get two socket references
        sock = sock_manager.get_socket()
        self.assertTrue(sock_manager.is_prepared()) #socket created on demand
        sock_id = id(sock._get())
        sock2 = sock_manager.get_socket()
        sock2_id = id(sock2._get())
        # Assert that they are not the same proxy object
        self.assertNotEqual(sock, sock2)
        # Assert that they are the same underlying socket
        self.assertEqual(sock_id, sock2_id)
        # Socket not actually closed yet b/c ref ct is 2
        self.assertEqual(2, sock_manager.get_socket_ref_count())
        self.assertTrue(sock_manager.is_prepared())
        self.assertFalse(sock_manager.socket.close_called)
        sock = None
        # Socket not actually closed yet b/c ref ct is 1
        self.assertTrue(sock_manager.is_prepared())
        self.assertFalse(sock_manager.socket.close_called)
        sock2 = None
        # Socket closed
        self.assertFalse(sock_manager.is_prepared())
        self.assertTrue(sock_manager.socket.close_called)

        # Get a new socket reference
        sock3 = sock_manager.get_socket()
        self.assertTrue(sock_manager.is_prepared())
        sock3_id = id(sock3._get())
        # Assert that it is not the same socket
        self.assertNotEqual(sock_id, sock3_id)
        # Drop ref ct to zero
        del sock3
        # Now assert that socket is closed
        self.assertFalse(sock_manager.is_prepared())
        self.assertTrue(sock_manager.socket.close_called)

    def test_logging(self):
        conf = DummySocketConfig(1)
        logger = DummyLogger()
        sock_manager = self._makeOne(conf, logger=logger)
        # socket open
        sock = sock_manager.get_socket()
        self.assertEqual(len(logger.data), 1)
        self.assertEqual('Creating socket %s' % repr(conf), logger.data[0])
        # socket close
        del sock
        self.assertEqual(len(logger.data), 2)
        self.assertEqual('Closing socket %s' % repr(conf), logger.data[1])

    def test_prepare_socket(self):
        conf = DummySocketConfig(1)
        sock_manager = self._makeOne(conf)
        sock = sock_manager.get_socket()
        self.assertTrue(sock_manager.is_prepared())
        self.assertFalse(sock.bind_called)
        self.assertTrue(sock.listen_called)
        self.assertEqual(sock.listen_backlog, socket.SOMAXCONN)
        self.assertFalse(sock.close_called)

    def test_tcp_socket_already_taken(self):
        conf = InetStreamSocketConfig('127.0.0.1', 51041)
        sock_manager = self._makeOne(conf)
        sock = sock_manager.get_socket()
        sock_manager2 = self._makeOne(conf)
        self.assertRaises(socket.error, sock_manager2.get_socket)
        del sock

    def test_unix_bad_sock(self):
        conf = UnixStreamSocketConfig('/notthere/foo.sock')
        sock_manager = self._makeOne(conf)
        self.assertRaises(socket.error, sock_manager.get_socket)

    def test_close_requires_prepared_socket(self):
        conf = InetStreamSocketConfig('127.0.0.1', 51041)
        sock_manager = self._makeOne(conf)
        self.assertFalse(sock_manager.is_prepared())
        try:
            sock_manager._close()
            self.fail()
        except Exception as e:
            self.assertEqual(e.args[0], 'Socket has not been prepared')

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_states
"""Test suite for supervisor.states"""

import sys
import unittest
from supervisor import states

class TopLevelProcessStateTests(unittest.TestCase):
    def test_module_has_process_states(self):
        self.assertTrue(hasattr(states, 'ProcessStates'))
    
    def test_stopped_states_do_not_overlap_with_running_states(self):
        for state in states.STOPPED_STATES:
            self.assertFalse(state in states.RUNNING_STATES)

    def test_running_states_do_not_overlap_with_stopped_states(self):
        for state in states.RUNNING_STATES:
            self.assertFalse(state in states.STOPPED_STATES)

    def test_getProcessStateDescription_returns_string_when_found(self):
        state = states.ProcessStates.STARTING
        self.assertEqual(states.getProcessStateDescription(state),
            'STARTING')

    def test_getProcessStateDescription_returns_None_when_not_found(self):
        self.assertEqual(states.getProcessStateDescription(3.14159),
            None)

class TopLevelSupervisorStateTests(unittest.TestCase):
    def test_module_has_supervisor_states(self):
        self.assertTrue(hasattr(states, 'SupervisorStates'))

    def test_getSupervisorStateDescription_returns_string_when_found(self):
        state = states.SupervisorStates.RUNNING
        self.assertEqual(states.getSupervisorStateDescription(state),
            'RUNNING')

    def test_getSupervisorStateDescription_returns_None_when_not_found(self):
        self.assertEqual(states.getSupervisorStateDescription(3.14159),
            None)

class TopLevelEventListenerStateTests(unittest.TestCase):
    def test_module_has_eventlistener_states(self):
        self.assertTrue(hasattr(states, 'EventListenerStates'))

    def test_getEventListenerStateDescription_returns_string_when_found(self):
        state = states.EventListenerStates.ACKNOWLEDGED
        self.assertEqual(states.getEventListenerStateDescription(state),
            'ACKNOWLEDGED')

    def test_getEventListenerStateDescription_returns_None_when_not_found(self):
        self.assertEqual(states.getEventListenerStateDescription(3.14159),
            None)
    

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_supervisorctl
import sys
import unittest
try:
    from StringIO import StringIO
    import xmlrpclib
except ImportError:
    from io import StringIO
    import xmlrpc.client as xmlrpclib

from supervisor.tests.base import DummyRPCServer

class ControllerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.supervisorctl import Controller
        return Controller

    def _makeOne(self, options):
        return self._getTargetClass()(options)

    def test_ctor(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        self.assertEqual(controller.prompt, options.prompt + '> ')

    def test__upcheck(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        result = controller.upcheck()
        self.assertEqual(result, True)

    def test__upcheck_wrong_server_version(self):
        options = DummyClientOptions()
        options._server.supervisor.getVersion = lambda *x: '1.0'
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        result = controller.upcheck()
        self.assertEqual(result, False)
        self.assertEqual(controller.stdout.getvalue(),
                         'Sorry, this version of supervisorctl expects'
                         ' to talk to a server with API version 3.0, but'
                         ' the remote version is 1.0.\n')

    def test__upcheck_unknown_method(self):
        options = DummyClientOptions()
        from supervisor.xmlrpc import Faults
        def getVersion():
            raise xmlrpclib.Fault(Faults.UNKNOWN_METHOD, 'duh')
        options._server.supervisor.getVersion = getVersion
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        result = controller.upcheck()
        self.assertEqual(result, False)
        self.assertEqual(controller.stdout.getvalue(),
                         'Sorry, supervisord responded but did not recognize'
                         ' the supervisor namespace commands that'
                         ' supervisorctl uses to control it.  Please check'
                         ' that the [rpcinterface:supervisor] section is'
                         ' enabled in the configuration file'
                         ' (see sample.conf).\n')

    def test__upcheck_reraises_other_xmlrpc_faults(self):
        options = DummyClientOptions()
        from supervisor.xmlrpc import Faults
        def f(*arg, **kw):
            raise xmlrpclib.Fault(Faults.FAILED, '')
        options._server.supervisor.getVersion = f
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        self.assertRaises(xmlrpclib.Fault, controller.upcheck)

    def test__upcheck_catches_socket_error_ECONNREFUSED(self):
        options = DummyClientOptions()
        import supervisor.medusa.text_socket as socket
        import errno
        def raise_fault(*arg, **kw):
            raise socket.error(errno.ECONNREFUSED, 'nobody home')
        options._server.supervisor.getVersion = raise_fault

        controller = self._makeOne(options)
        controller.stdout = StringIO()

        result = controller.upcheck()
        self.assertEqual(result, False)

        output = controller.stdout.getvalue()
        self.assertTrue('refused connection' in output)

    def test__upcheck_catches_socket_error_ENOENT(self):
        options = DummyClientOptions()
        import supervisor.medusa.text_socket as socket
        import errno
        def raise_fault(*arg, **kw):
            raise socket.error(errno.ENOENT, 'nobody home')
        options._server.supervisor.getVersion = raise_fault

        controller = self._makeOne(options)
        controller.stdout = StringIO()

        result = controller.upcheck()
        self.assertEqual(result, False)

        output = controller.stdout.getvalue()
        self.assertTrue('no such file' in output)

    def test__upcheck_reraises_other_socket_faults(self):
        options = DummyClientOptions()
        import supervisor.medusa.text_socket as socket
        import errno
        def f(*arg, **kw):
            raise socket.error(errno.EBADF, '')
        options._server.supervisor.getVersion = f
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        self.assertRaises(socket.error, controller.upcheck)

    def test_onecmd(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        plugin = DummyPlugin()
        controller.options.plugins = (plugin,)
        result = controller.onecmd('help')
        self.assertEqual(result, None)
        self.assertEqual(plugin.helped, True)

    def test_onecmd_multi_colonseparated(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        plugin = DummyPlugin()
        controller.options.plugins = (plugin,)
        result = controller.onecmd('help; help')
        self.assertEqual(result, None)
        self.assertEqual(controller.cmdqueue, [' help'])
        self.assertEqual(plugin.helped, True)

    def test_onecmd_empty_does_not_repeat_previous_cmd(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        plugin = DummyPlugin()
        controller.options.plugins = (plugin,)
        plugin.helped = False
        controller.onecmd('help')
        self.assertTrue(plugin.helped)
        plugin.helped = False
        controller.onecmd('')
        self.assertFalse(plugin.helped)

    def test_onecmd_clears_completion_cache(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        controller._complete_info = {}
        controller.onecmd('help')
        self.assertEqual(controller._complete_info, None)

    def test_complete_action_empty(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help']
        result = controller.complete('', 0, line='')
        self.assertEqual(result, 'help ')
        result = controller.complete('', 1, line='')
        self.assertEqual(result, None)

    def test_complete_action_partial(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help']
        result = controller.complete('h', 0, line='h')
        self.assertEqual(result, 'help ')
        result = controller.complete('h', 1, line='h')
        self.assertEqual(result, None)

    def test_complete_action_whole(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help']
        result = controller.complete('help', 0, line='help')
        self.assertEqual(result, 'help ')

    def test_complete_unknown_action_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        result = controller.complete('bad', 0, line='bad')
        self.assertEqual(result, None)

    def test_complete_unknown_action_arg_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'add']
        result = controller.complete('', 1, line='bad ')
        self.assertEqual(result, None)

    def test_complete_help_empty(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('', 0, line='help ')
        self.assertEqual(result, 'help ')
        result = controller.complete('', 1, line='help ')
        self.assertEqual(result, 'start ')
        result = controller.complete('', 2, line='help ')
        self.assertEqual(result, None)

    def test_complete_help_action(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('he', 0, line='help he')
        self.assertEqual(result, 'help ')
        result = controller.complete('he', 1, line='help he')
        self.assertEqual(result, None)

    def test_complete_start_empty(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('', 0, line='start ')
        self.assertEqual(result, 'foo ')
        result = controller.complete('', 1, line='start ')
        self.assertEqual(result, 'bar ')
        result = controller.complete('', 2, line='start ')
        self.assertEqual(result, 'baz:baz_01 ')
        result = controller.complete('', 3, line='start ')
        self.assertEqual(result, 'baz:* ')
        result = controller.complete('', 4, line='start ')
        self.assertEqual(result, None)

    def test_complete_start_no_colon(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('f', 0, line='start f')
        self.assertEqual(result, 'foo ')
        result = controller.complete('f', 1, line='start f')
        self.assertEqual(result, None)

    def test_complete_start_with_colon(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('foo:', 0, line='start foo:')
        self.assertEqual(result, 'foo:foo ')
        result = controller.complete('foo:', 1, line='start foo:')
        self.assertEqual(result, 'foo:* ')
        result = controller.complete('foo:', 2, line='start foo:')
        self.assertEqual(result, None)

    def test_complete_start_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('bad', 0, line='start bad')
        self.assertEqual(result, None)

    def test_complete_caches_process_info(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('', 0, line='start ')
        self.assertNotEqual(result, None)
        def f(*arg, **kw):
            raise Exception("should not have called getAllProcessInfo")
        controller.options._server.supervisor.getAllProcessInfo = f
        controller.complete('', 1, line='start ')

    def test_complete_add_empty(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'add']
        result = controller.complete('', 0, line='add ')
        self.assertEqual(result, 'foo ')
        result = controller.complete('', 1, line='add ')
        self.assertEqual(result, 'bar ')
        result = controller.complete('', 2, line='add ')
        self.assertEqual(result, 'baz ')
        result = controller.complete('', 3, line='add ')
        self.assertEqual(result, None)

    def test_complete_add_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'add']
        result = controller.complete('bad', 0, line='add bad')
        self.assertEqual(result, None)

    def test_complete_add_group(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'add']
        result = controller.complete('f', 0, line='add f')
        self.assertEqual(result, 'foo ')
        result = controller.complete('f', 1, line='add f')
        self.assertEqual(result, None)

    def test_complete_reload_arg_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'reload']
        result = controller.complete('', 1, line='reload ')
        self.assertEqual(result, None)

    def test_complete_semicolon_separated_commands(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('f', 0, line='help;start f')
        self.assertEqual(result, 'foo ')
        result = controller.complete('f', 1, line='help;start f')
        self.assertEqual(result, None)

    def test_nohelp(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        self.assertEqual(controller.nohelp, '*** No help on %s')

    def test_do_help(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        results = controller.do_help('')
        helpval = controller.stdout.getvalue()
        self.assertEqual(results, None)
        self.assertEqual(helpval, 'foo helped')

    def test_do_help_for_help(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        results = controller.do_help("help")
        self.assertEqual(results, None)
        helpval = controller.stdout.getvalue()
        self.assertTrue("help\t\tPrint a list" in helpval)

    def test_get_supervisor_returns_serverproxy_supervisor_namespace(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)

        proxy = controller.get_supervisor()
        expected = options.getServerProxy().supervisor
        self.assertEqual(proxy, expected)

    def test_get_server_proxy_with_no_args_returns_serverproxy(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)

        proxy = controller.get_server_proxy()
        expected = options.getServerProxy()
        self.assertEqual(proxy, expected)

    def test_get_server_proxy_with_namespace_returns_that_namespace(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)

        proxy = controller.get_server_proxy('system')
        expected = options.getServerProxy().system
        self.assertEqual(proxy, expected)

    def test_real_controller_initialization(self):
        from supervisor.options import ClientOptions
        args = [] # simulating starting without parameters
        options = ClientOptions()

        # No default config file search in case they would exist
        self.assertTrue(len(options.searchpaths) > 0)
        options.searchpaths = []

        options.realize(args, doc=__doc__)
        self._makeOne(options) # should not raise


class TestControllerPluginBase(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.supervisorctl import ControllerPluginBase
        return ControllerPluginBase

    def _makeOne(self, *arg, **kw):
        klass = self._getTargetClass()
        options = DummyClientOptions()
        ctl = DummyController(options)
        plugin = klass(ctl, *arg, **kw)
        return plugin

    def test_do_help_noarg(self):
        plugin = self._makeOne()
        result = plugin.do_help(None)
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), '\n')
        self.assertEqual(len(plugin.ctl.topics_printed), 1)
        topics = plugin.ctl.topics_printed[0]
        self.assertEqual(topics[0], 'unnamed commands (type help <topic>):')
        self.assertEqual(topics[1], [])
        self.assertEqual(topics[2], 15)
        self.assertEqual(topics[3], 80)

    def test_do_help_witharg(self):
        plugin = self._makeOne()
        result = plugin.do_help('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), 'no help on foo\n')
        self.assertEqual(len(plugin.ctl.topics_printed), 0)

class TestDefaultControllerPlugin(unittest.TestCase):

    def _getTargetClass(self):
        from supervisor.supervisorctl import DefaultControllerPlugin
        return DefaultControllerPlugin

    def _makeOne(self, *arg, **kw):
        klass = self._getTargetClass()
        options = DummyClientOptions()
        ctl = DummyController(options)
        plugin = klass(ctl, *arg, **kw)
        return plugin

    def test_tail_toofewargs(self):
        plugin = self._makeOne()
        result = plugin.do_tail('')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: too few arguments')

    def test_tail_toomanyargs(self):
        plugin = self._makeOne()
        result = plugin.do_tail('one two three four')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: too many arguments')

    def test_tail_f_noprocname(self):
        plugin = self._makeOne()
        result = plugin.do_tail('-f')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: tail requires process name')

    def test_tail_bad_modifier(self):
        plugin = self._makeOne()
        result = plugin.do_tail('-z foo')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: bad argument -z')

    def test_tail_defaults(self):
        plugin = self._makeOne()
        result = plugin.do_tail('foo')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 12)
        self.assertEqual(lines[0], 'output line')

    def test_tail_no_file(self):
        plugin = self._makeOne()
        result = plugin.do_tail('NO_FILE')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 2)
        self.assertEqual(lines[0], 'NO_FILE: ERROR (no log file)')

    def test_tail_failed(self):
        plugin = self._makeOne()
        result = plugin.do_tail('FAILED')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 2)
        self.assertEqual(lines[0], 'FAILED: ERROR (unknown error reading log)')

    def test_tail_bad_name(self):
        plugin = self._makeOne()
        result = plugin.do_tail('BAD_NAME')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 2)
        self.assertEqual(lines[0], 'BAD_NAME: ERROR (no such process name)')

    def test_tail_bytesmodifier(self):
        plugin = self._makeOne()
        result = plugin.do_tail('-10 foo')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 3)
        self.assertEqual(lines[0], 'tput line')

    def test_tail_explicit_channel_stdout_nomodifier(self):
        plugin = self._makeOne()
        result = plugin.do_tail('foo stdout')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 12)
        self.assertEqual(lines[0], 'output line')

    def test_tail_explicit_channel_stderr_nomodifier(self):
        plugin = self._makeOne()
        result = plugin.do_tail('foo stderr')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 12)
        self.assertEqual(lines[0], 'output line')

    def test_tail_explicit_channel_unrecognized(self):
        plugin = self._makeOne()
        result = plugin.do_tail('foo fudge')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().strip()
        self.assertEqual(value, "Error: bad channel 'fudge'")

    def test_tail_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.readProcessStdoutLog = f
        plugin.do_tail('foo')
        self.assertEqual(called, [])

    def test_status_help(self):
        plugin = self._makeOne()
        plugin.help_status()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("status <name>" in out)

    def test_status_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.getAllProcessInfo = f
        plugin.do_status('')
        self.assertEqual(called, [])

    def test_status_table_process_column_min_width(self):
        plugin = self._makeOne()
        result = plugin.do_status('')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split("\n")
        self.assertEqual(lines[0].index("RUNNING"), 33)

    def test_status_table_process_column_expands(self):
        plugin = self._makeOne()
        options = plugin.ctl.options
        def f(*arg, **kw):
            from supervisor.states import ProcessStates
            return [{'name': 'foo'*50, # long name
                     'group':'foo',
                     'pid': 11,
                     'state': ProcessStates.RUNNING,
                     'statename': 'RUNNING',
                     'start': 0,
                     'stop': 0,
                     'spawnerr': '',
                     'now': 0,
                     'description':'foo description'},
                    {
                    'name': 'bar', # short name
                    'group': 'bar',
                    'pid': 12,
                    'state': ProcessStates.FATAL,
                    'statename': 'RUNNING',
                    'start': 0,
                    'stop': 0,
                    'spawnerr': '',
                    'now': 0,
                    'description': 'bar description',
                    }]
        options._server.supervisor.getAllProcessInfo = f
        self.assertEqual(plugin.do_status(''), None)
        lines = plugin.ctl.stdout.getvalue().split("\n")
        self.assertEqual(lines[0].index("RUNNING"), 157)
        self.assertEqual(lines[1].index("RUNNING"), 157)

    def test_status_all_processes_no_arg(self):
        plugin = self._makeOne()
        result = plugin.do_status('')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])
        self.assertEqual(value[1].split(None, 2),
                         ['bar', 'FATAL', 'bar description'])
        self.assertEqual(value[2].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_status_all_processes_all_arg(self):
        plugin = self._makeOne()
        result = plugin.do_status('all')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])
        self.assertEqual(value[1].split(None, 2),
                         ['bar', 'FATAL', 'bar description'])
        self.assertEqual(value[2].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_status_process_name(self):
        plugin = self._makeOne()
        result = plugin.do_status('foo')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().strip()
        self.assertEqual(value.split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])

    def test_status_group_name(self):
        plugin = self._makeOne()
        result = plugin.do_status('baz:*')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_status_mixed_names(self):
        plugin = self._makeOne()
        result = plugin.do_status('foo baz:*')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])
        self.assertEqual(value[1].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_status_bad_group_name(self):
        plugin = self._makeOne()
        result = plugin.do_status('badgroup:*')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0], "badgroup: ERROR (no such group)")

    def test_status_bad_process_name(self):
        plugin = self._makeOne()
        result = plugin.do_status('badprocess')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0], "badprocess: ERROR (no such process)")

    def test_status_bad_process_name_with_group(self):
        plugin = self._makeOne()
        result = plugin.do_status('badgroup:badprocess')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0], "badgroup:badprocess: "
                                   "ERROR (no such process)")

    def test_start_help(self):
        plugin = self._makeOne()
        plugin.help_start()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("start <name>" in out)

    def test_start_fail(self):
        plugin = self._makeOne()
        result = plugin.do_start('')
        self.assertEqual(result, None)
        expected = "Error: start requires a process name"
        self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0], expected)

    def test_start_badname(self):
        plugin = self._makeOne()
        result = plugin.do_start('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such process)\n')

    def test_start_no_file(self):
        plugin = self._makeOne()
        result = plugin.do_start('NO_FILE')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'NO_FILE: ERROR (no such file)\n')

    def test_start_not_executable(self):
        plugin = self._makeOne()
        result = plugin.do_start('NOT_EXECUTABLE')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'NOT_EXECUTABLE: ERROR (file is not executable)\n')

    def test_start_alreadystarted(self):
        plugin = self._makeOne()
        result = plugin.do_start('ALREADY_STARTED')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ALREADY_STARTED: ERROR (already started)\n')

    def test_start_spawnerror(self):
        plugin = self._makeOne()
        result = plugin.do_start('SPAWN_ERROR')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'SPAWN_ERROR: ERROR (spawn error)\n')

    def test_start_abnormaltermination(self):
        plugin = self._makeOne()
        result = plugin.do_start('ABNORMAL_TERMINATION')
        self.assertEqual(result, None)
        expected = 'ABNORMAL_TERMINATION: ERROR (abnormal termination)\n'
        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)

    def test_start_one_success(self):
        plugin = self._makeOne()
        result = plugin.do_start('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: started\n')

    def test_start_one_with_group_name_success(self):
        plugin = self._makeOne()
        result = plugin.do_start('foo:foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: started\n')

    def test_start_many(self):
        plugin = self._makeOne()
        result = plugin.do_start('foo bar')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: started\nbar: started\n')

    def test_start_group(self):
        plugin = self._makeOne()
        result = plugin.do_start('foo:')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo:foo_00: started\n'
                         'foo:foo_01: started\n')

    def test_start_group_bad_name(self):
        plugin = self._makeOne()
        result = plugin.do_start('BAD_NAME:')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such group)\n')

    def test_start_all(self):
        plugin = self._makeOne()
        result = plugin.do_start('all')
        self.assertEqual(result, None)

        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: started\n'
                         'foo2: started\n'
                         'failed_group:failed: ERROR (spawn error)\n')

    def test_start_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.startAllProcesses = f
        supervisor.startProcessGroup = f
        plugin.do_start('foo')
        self.assertEqual(called, [])

    def test_stop_help(self):
        plugin = self._makeOne()
        plugin.help_stop()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("stop <name>" in out)

    def test_stop_fail(self):
        plugin = self._makeOne()
        result = plugin.do_stop('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
                         "Error: stop requires a process name")

    def test_stop_badname(self):
        plugin = self._makeOne()
        result = plugin.do_stop('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such process)\n')

    def test_stop_notrunning(self):
        plugin = self._makeOne()
        result = plugin.do_stop('NOT_RUNNING')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'NOT_RUNNING: ERROR (not running)\n')

    def test_stop_failed(self):
        plugin = self._makeOne()
        result = plugin.do_stop('FAILED')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), 'FAILED\n')

    def test_stop_one_success(self):
        plugin = self._makeOne()
        result = plugin.do_stop('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\n')

    def test_stop_one_with_group_name_success(self):
        plugin = self._makeOne()
        result = plugin.do_stop('foo:foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\n')

    def test_stop_many(self):
        plugin = self._makeOne()
        result = plugin.do_stop('foo bar')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\n'
                         'bar: stopped\n')

    def test_stop_group(self):
        plugin = self._makeOne()
        result = plugin.do_stop('foo:')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo:foo_00: stopped\n'
                         'foo:foo_01: stopped\n')

    def test_stop_group_bad_name(self):
        plugin = self._makeOne()
        result = plugin.do_stop('BAD_NAME:')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such group)\n')

    def test_stop_all(self):
        plugin = self._makeOne()
        result = plugin.do_stop('all')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\n'
                         'foo2: stopped\n'
                         'failed_group:failed: ERROR (no such process)\n')

    def test_stop_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.stopAllProcesses = f
        supervisor.stopProcessGroup = f
        plugin.do_stop('foo')
        self.assertEqual(called, [])

    def test_restart_help(self):
        plugin = self._makeOne()
        plugin.help_restart()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("restart <name>" in out)

    def test_restart_fail(self):
        plugin = self._makeOne()
        result = plugin.do_restart('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
                         'Error: restart requires a process name')

    def test_restart_one(self):
        plugin = self._makeOne()
        result = plugin.do_restart('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\nfoo: started\n')

    def test_restart_all(self):
        plugin = self._makeOne()
        result = plugin.do_restart('all')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\nfoo2: stopped\n'
                         'failed_group:failed: ERROR (no such process)\n'
                         'foo: started\nfoo2: started\n'
                         'failed_group:failed: ERROR (spawn error)\n')

    def test_restart_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.stopAllProcesses = f
        supervisor.stopProcessGroup = f
        plugin.do_restart('foo')
        self.assertEqual(called, [])

    def test_clear_help(self):
        plugin = self._makeOne()
        plugin.help_clear()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("clear <name>" in out)

    def test_clear_fail(self):
        plugin = self._makeOne()
        result = plugin.do_clear('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
                         "Error: clear requires a process name")

    def test_clear_badname(self):
        plugin = self._makeOne()
        result = plugin.do_clear('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such process)\n')

    def test_clear_one_success(self):
        plugin = self._makeOne()
        result = plugin.do_clear('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: cleared\n')

    def test_clear_one_with_group_success(self):
        plugin = self._makeOne()
        result = plugin.do_clear('foo:foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: cleared\n')

    def test_clear_many(self):
        plugin = self._makeOne()
        result = plugin.do_clear('foo bar')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: cleared\nbar: cleared\n')

    def test_clear_all(self):
        plugin = self._makeOne()
        result = plugin.do_clear('all')
        self.assertEqual(result, None)

        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: cleared\n'
                         'foo2: cleared\n'
                         'failed_group:failed: ERROR (failed)\n')

    def test_clear_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.clearAllProcessLogs = f
        supervisor.clearProcessLogs = f
        plugin.do_clear('foo')
        self.assertEqual(called, [])

    def test_open_help(self):
        plugin = self._makeOne()
        plugin.help_open()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("open <url>" in out)

    def test_open_fail(self):
        plugin = self._makeOne()
        result = plugin.do_open('badname')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: url must be http:// or unix://\n')

    def test_open_succeed(self):
        plugin = self._makeOne()
        result = plugin.do_open('http://localhost:9002')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])
        self.assertEqual(value[1].split(None, 2),
                         ['bar', 'FATAL', 'bar description'])
        self.assertEqual(value[2].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_version_help(self):
        plugin = self._makeOne()
        plugin.help_version()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Show the version of the remote supervisord" in out)

    def test_version(self):
        plugin = self._makeOne()
        plugin.do_version(None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), '3000\n')

    def test_version_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.getSupervisorVersion = f
        plugin.do_version('')
        self.assertEqual(called, [])

    def test_reload_help(self):
        plugin = self._makeOne()
        plugin.help_reload()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Restart the remote supervisord" in out)

    def test_reload_fail(self):
        plugin = self._makeOne()
        options = plugin.ctl.options
        options._server.supervisor._restartable = False
        result = plugin.do_reload('')
        self.assertEqual(result, None)
        self.assertEqual(options._server.supervisor._restarted, False)

    def test_reload(self):
        plugin = self._makeOne()
        options = plugin.ctl.options
        result = plugin.do_reload('')
        self.assertEqual(result, None)
        self.assertEqual(options._server.supervisor._restarted, True)

    def test_shutdown_help(self):
        plugin = self._makeOne()
        plugin.help_shutdown()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Shut the remote supervisord down" in out)

    def test_shutdown(self):
        plugin = self._makeOne()
        options = plugin.ctl.options
        result = plugin.do_shutdown('')
        self.assertEqual(result, None)
        self.assertEqual(options._server.supervisor._shutdown, True)

    def test_shutdown_catches_xmlrpc_fault_shutdown_state(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc

        def raise_fault(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'bye')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        result = plugin.do_shutdown('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: already shutting down\n')

    def test_shutdown_reraises_other_xmlrpc_faults(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc

        def raise_fault(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'ouch')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        self.assertRaises(xmlrpclib.Fault,
                          plugin.do_shutdown, '')

    def test_shutdown_catches_socket_error_ECONNREFUSED(self):
        plugin = self._makeOne()
        import supervisor.medusa.text_socket as socket
        import errno

        def raise_fault(*arg, **kw):
            raise socket.error(errno.ECONNREFUSED, 'nobody home')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        result = plugin.do_shutdown('')
        self.assertEqual(result, None)

        output = plugin.ctl.stdout.getvalue()
        self.assertTrue('refused connection (already shut down?)' in output)

    def test_shutdown_catches_socket_error_ENOENT(self):
        plugin = self._makeOne()
        import supervisor.medusa.text_socket as socket
        import errno

        def raise_fault(*arg, **kw):
            raise socket.error(errno.ENOENT, 'no file')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        result = plugin.do_shutdown('')
        self.assertEqual(result, None)

        output = plugin.ctl.stdout.getvalue()
        self.assertTrue('no such file (already shut down?)' in output)

    def test_shutdown_reraises_other_socket_errors(self):
        plugin = self._makeOne()
        import supervisor.medusa.text_socket as socket
        import errno

        def raise_fault(*arg, **kw):
            raise socket.error(errno.EPERM, 'denied')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        self.assertRaises(socket.error,
                          plugin.do_shutdown, '')

    def test__formatChanges(self):
        plugin = self._makeOne()
        # Don't explode, plz
        plugin._formatChanges([['added'], ['changed'], ['removed']])
        plugin._formatChanges([[], [], []])

    def test_reread_help(self):
        plugin = self._makeOne()
        plugin.help_reread()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Reload the daemon's configuration files" in out)

    def test_reread(self):
        plugin = self._makeOne()
        calls = []
        plugin._formatChanges = lambda x: calls.append(x)
        result = plugin.do_reread(None)
        self.assertEqual(result, None)
        self.assertEqual(calls[0], [['added'], ['changed'], ['removed']])

    def test_reread_cant_reread(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc
        def reloadConfig(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'cant')
        plugin.ctl.options._server.supervisor.reloadConfig = reloadConfig
        plugin.do_reread(None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: cant\n')

    def test_reread_shutdown_state(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc
        def reloadConfig(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
        plugin.ctl.options._server.supervisor.reloadConfig = reloadConfig
        plugin.do_reread(None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: supervisor shutting down\n')

    def test_reread_reraises_other_faults(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc
        def reloadConfig(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.FAILED, '')
        plugin.ctl.options._server.supervisor.reloadConfig = reloadConfig
        self.assertRaises(xmlrpclib.Fault, plugin.do_reread, '')

    def test__formatConfigInfo(self):
        info = { 'group': 'group1',
                 'name': 'process1',
                 'inuse': True,
                 'autostart': True,
                 'process_prio': 999,
                 'group_prio': 999 }
        plugin = self._makeOne()
        result = plugin._formatConfigInfo(info)
        self.assertTrue('in use' in result)
        info = { 'group': 'group1',
                 'name': 'process1',
                 'inuse': False,
                 'autostart': False,
                 'process_prio': 999,
                 'group_prio': 999 }
        result = plugin._formatConfigInfo(info)
        self.assertTrue('avail' in result)

    def test_avail_help(self):
        plugin = self._makeOne()
        plugin.help_avail()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Display all configured" in out)

    def test_avail(self):
        calls = []
        plugin = self._makeOne()

        class FakeSupervisor(object):
            def getAllConfigInfo(self):
                return [{ 'group': 'group1', 'name': 'process1',
                          'inuse': False, 'autostart': False,
                          'process_prio': 999, 'group_prio': 999 }]

        plugin.ctl.get_supervisor = lambda : FakeSupervisor()
        plugin.ctl.output = calls.append
        result = plugin.do_avail('')
        self.assertEqual(result, None)

    def test_avail_shutdown_state(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def getAllConfigInfo():
            from supervisor import xmlrpc
            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
        supervisor.getAllConfigInfo = getAllConfigInfo

        result = plugin.do_avail('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: supervisor shutting down\n')

    def test_avail_reraises_other_faults(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def getAllConfigInfo():
            from supervisor import xmlrpc
            raise xmlrpclib.Fault(xmlrpc.Faults.FAILED, '')
        supervisor.getAllConfigInfo = getAllConfigInfo

        self.assertRaises(xmlrpclib.Fault, plugin.do_avail, '')

    def test_add_help(self):
        plugin = self._makeOne()
        plugin.help_add()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("add <name>" in out)

    def test_add(self):
        plugin = self._makeOne()
        result = plugin.do_add('foo')
        self.assertEqual(result, None)
        supervisor = plugin.ctl.options._server.supervisor
        self.assertEqual(supervisor.processes, ['foo'])

    def test_add_already_added(self):
        plugin = self._makeOne()
        result = plugin.do_add('ALREADY_ADDED')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: process group already active\n')

    def test_add_bad_name(self):
        plugin = self._makeOne()
        result = plugin.do_add('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: no such process/group: BAD_NAME\n')

    def test_add_shutdown_state(self):
        plugin = self._makeOne()
        result = plugin.do_add('SHUTDOWN_STATE')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: shutting down\n')

    def test_add_reraises_other_faults(self):
        plugin = self._makeOne()
        self.assertRaises(xmlrpclib.Fault, plugin.do_add, 'FAILED')

    def test_remove_help(self):
        plugin = self._makeOne()
        plugin.help_remove()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("remove <name>" in out)

    def test_remove(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.processes = ['foo']
        result = plugin.do_remove('foo')
        self.assertEqual(result, None)
        self.assertEqual(supervisor.processes, [])

    def test_remove_bad_name(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.processes = ['foo']
        result = plugin.do_remove('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: no such process/group: BAD_NAME\n')

    def test_remove_still_running(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.processes = ['foo']
        result = plugin.do_remove('STILL_RUNNING')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: process/group still running: STILL_RUNNING\n')

    def test_remove_reraises_other_faults(self):
        plugin = self._makeOne()
        self.assertRaises(xmlrpclib.Fault, plugin.do_remove, 'FAILED')

    def test_update_help(self):
        plugin = self._makeOne()
        plugin.help_update()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Reload config and add/remove" in out)

    def test_update_not_on_shutdown(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor
        def reloadConfig():
            from supervisor import xmlrpc
            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'blah')
        supervisor.reloadConfig = reloadConfig
        supervisor.processes = ['removed']
        plugin.do_update('')
        self.assertEqual(supervisor.processes, ['removed'])

    def test_update_added_procs(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def reloadConfig():
            return [[['new_proc'], [], []]]
        supervisor.reloadConfig = reloadConfig

        result = plugin.do_update('')
        self.assertEqual(result, None)
        self.assertEqual(supervisor.processes, ['new_proc'])

    def test_update_with_gname(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def reloadConfig():
            return [[['added1', 'added2'], ['changed'], ['removed']]]
        supervisor.reloadConfig = reloadConfig
        supervisor.processes = ['changed', 'removed']

        plugin.do_update('changed')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'removed']))

        plugin.do_update('added1 added2')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'removed', 'added1', 'added2']))

        plugin.do_update('removed')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'added1', 'added2']))

        supervisor.processes = ['changed', 'removed']
        plugin.do_update('removed added1')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'added1']))

        supervisor.processes = ['changed', 'removed']
        plugin.do_update('all')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'added1', 'added2']))


    def test_update_changed_procs(self):
        from supervisor import xmlrpc

        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        calls = []
        def reloadConfig():
            return [[[], ['changed_group'], []]]
        supervisor.reloadConfig = reloadConfig
        supervisor.startProcess = lambda x: calls.append(('start', x))

        supervisor.addProcessGroup('changed_group') # fake existence
        results = [{'name':        'changed_process',
                    'group':       'changed_group',
                    'status':      xmlrpc.Faults.SUCCESS,
                    'description': 'blah'}]
        def stopProcessGroup(name):
            calls.append(('stop', name))
            return results
        supervisor.stopProcessGroup = stopProcessGroup

        plugin.do_update('')
        self.assertEqual(calls, [('stop', 'changed_group')])

        supervisor.addProcessGroup('changed_group') # fake existence
        calls[:] = []
        results[:] = [{'name':        'changed_process1',
                       'group':       'changed_group',
                       'status':      xmlrpc.Faults.NOT_RUNNING,
                       'description': 'blah'},
                      {'name':        'changed_process2',
                       'group':       'changed_group',
                       'status':      xmlrpc.Faults.FAILED,
                       'description': 'blah'}]

        plugin.do_update('')
        self.assertEqual(calls, [('stop', 'changed_group')])

        supervisor.addProcessGroup('changed_group') # fake existence
        calls[:] = []
        results[:] = [{'name':        'changed_process1',
                       'group':       'changed_group',
                       'status':      xmlrpc.Faults.FAILED,
                       'description': 'blah'},
                      {'name':        'changed_process2',
                       'group':       'changed_group',
                       'status':      xmlrpc.Faults.SUCCESS,
                       'description': 'blah'}]

        plugin.do_update('')
        self.assertEqual(calls, [('stop', 'changed_group')])

    def test_update_removed_procs(self):
        from supervisor import xmlrpc

        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def reloadConfig():
            return [[[], [], ['removed_group']]]
        supervisor.reloadConfig = reloadConfig

        results = [{'name':        'removed_process',
                    'group':       'removed_group',
                    'status':      xmlrpc.Faults.SUCCESS,
                    'description': 'blah'}]
        supervisor.processes = ['removed_group']

        def stopProcessGroup(name):
            return results
        supervisor.stopProcessGroup = stopProcessGroup

        plugin.do_update('')
        self.assertEqual(supervisor.processes, [])

        results[:] = [{'name':        'removed_process',
                       'group':       'removed_group',
                       'status':      xmlrpc.Faults.NOT_RUNNING,
                       'description': 'blah'}]
        supervisor.processes = ['removed_group']

        plugin.do_update('')
        self.assertEqual(supervisor.processes, [])

        results[:] = [{'name':        'removed_process',
                       'group':       'removed_group',
                       'status':      xmlrpc.Faults.FAILED,
                       'description': 'blah'}]
        supervisor.processes = ['removed_group']

        plugin.do_update('')
        self.assertEqual(supervisor.processes, ['removed_group'])

    def test_update_reraises_other_faults(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def reloadConfig():
            from supervisor import xmlrpc
            raise xmlrpclib.Fault(xmlrpc.Faults.FAILED, 'FAILED')
        supervisor.reloadConfig = reloadConfig

        self.assertRaises(xmlrpclib.Fault, plugin.do_update, '')

    def test_pid_help(self):
        plugin = self._makeOne()
        plugin.help_pid()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("pid <name>" in out)

    def test_pid_supervisord(self):
        plugin = self._makeOne()
        result = plugin.do_pid('')
        self.assertEqual(result, None)
        options = plugin.ctl.options
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 2)
        self.assertEqual(lines[0], str(options._server.supervisor.getPID()))

    def test_pid_allprocesses(self):
        plugin = self._makeOne()
        result = plugin.do_pid('all')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().strip()
        self.assertEqual(value.split(), ['11', '12', '13'])

    def test_pid_badname(self):
        plugin = self._makeOne()
        result = plugin.do_pid('BAD_NAME')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().strip()
        self.assertEqual(value, 'No such process BAD_NAME')

    def test_pid_oneprocess(self):
        plugin = self._makeOne()
        result = plugin.do_pid('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue().strip(), '11')

    def test_pid_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.getPID = f
        plugin.do_pid('')
        self.assertEqual(called, [])

    def test_maintail_help(self):
        plugin = self._makeOne()
        plugin.help_maintail()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("tail of supervisor main log file" in out)

    def test_maintail_toomanyargs(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('foo bar')
        self.assertEqual(result, None)
        val = plugin.ctl.stdout.getvalue()
        self.assertTrue(val.startswith('Error: too many'), val)

    def test_maintail_minus_string_fails(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('-wrong')
        self.assertEqual(result, None)
        val = plugin.ctl.stdout.getvalue()
        self.assertTrue(val.startswith('Error: bad argument -wrong'), val)

    def test_maintail_wrong(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('wrong')
        self.assertEqual(result, None)
        val = plugin.ctl.stdout.getvalue()
        self.assertTrue(val.startswith('Error: bad argument wrong'), val)

    def test_maintail_dashf(self):
        plugin = self._makeOne()
        plugin.listener = DummyListener()
        result = plugin.do_maintail('-f')
        self.assertEqual(result, None)
        errors = plugin.listener.errors
        self.assertEqual(len(errors), 1)
        error = errors[0]
        self.assertEqual(plugin.listener.closed,
                         'http://localhost:65532/mainlogtail')
        self.assertEqual(error[0],
                         'http://localhost:65532/mainlogtail')
        self.assertTrue('Cannot connect' in error[1])

    def test_maintail_bad_modifier(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('-z')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: bad argument -z')

    def test_maintail_nobytes(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')

    def test_maintail_dashbytes(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('-100')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')

    def test_maintail_readlog_error_nofile(self):
        plugin = self._makeOne()
        supervisor_rpc = plugin.ctl.get_supervisor()
        from supervisor import xmlrpc
        supervisor_rpc._readlog_error = xmlrpc.Faults.NO_FILE
        result = plugin.do_maintail('-100')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'supervisord: ERROR (no log file)\n')

    def test_maintail_readlog_error_failed(self):
        plugin = self._makeOne()
        supervisor_rpc = plugin.ctl.get_supervisor()
        from supervisor import xmlrpc
        supervisor_rpc._readlog_error = xmlrpc.Faults.FAILED
        result = plugin.do_maintail('-100')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'supervisord: ERROR (unknown error reading log)\n')

    def test_maintail_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.readLog = f
        plugin.do_maintail('')
        self.assertEqual(called, [])

    def test_fg_help(self):
        plugin = self._makeOne()
        plugin.help_fg()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("fg <process>" in out)

    def test_fg_too_few_args(self):
        plugin = self._makeOne()
        result = plugin.do_fg('')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: no process name supplied')

    def test_fg_too_many_args(self):
        plugin = self._makeOne()
        result = plugin.do_fg('foo bar')
        self.assertEqual(result, None)
        line = plugin.ctl.stdout.getvalue()
        self.assertEqual(line, 'Error: too many process names supplied\n')

    def test_fg_badprocname(self):
        plugin = self._makeOne()
        result = plugin.do_fg('BAD_NAME')
        self.assertEqual(result, None)
        line = plugin.ctl.stdout.getvalue()
        self.assertEqual(line, 'Error: bad process name supplied\n')

    def test_fg_procnotrunning(self):
        plugin = self._makeOne()
        result = plugin.do_fg('bar')
        self.assertEqual(result, None)
        line = plugin.ctl.stdout.getvalue()
        self.assertEqual(line, 'Error: process not running\n')
        result = plugin.do_fg('baz_01')
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(result, None)
        self.assertEqual(lines[-2], 'Error: process not running')

    def test_fg_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.getProcessInfo = f
        plugin.do_fg('foo')
        self.assertEqual(called, [])

    def test_exit_help(self):
        plugin = self._makeOne()
        plugin.help_exit()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Exit the supervisor shell" in out)

    def test_quit_help(self):
        plugin = self._makeOne()
        plugin.help_quit()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Exit the supervisor shell" in out)

class DummyListener:
    def __init__(self):
        self.errors = []
    def error(self, url, msg):
        self.errors.append((url, msg))
    def close(self, url):
        self.closed = url

class DummyPluginFactory:
    def __init__(self, ctl, **kw):
        self.ctl = ctl

    def do_help(self, arg):
        self.ctl.stdout.write('foo helped')

class DummyClientOptions:
    def __init__(self):
        self.prompt = 'supervisor'
        self.serverurl = 'http://localhost:65532'
        self.username = 'chrism'
        self.password = '123'
        self.history_file = None
        self.plugins = ()
        self._server = DummyRPCServer()
        self.interactive = False
        self.plugin_factories = [('dummy', DummyPluginFactory, {})]

    def getServerProxy(self):
        return self._server

class DummyController:
    nohelp = 'no help on %s'
    def __init__(self, options):
        self.options = options
        self.topics_printed = []
        self.stdout = StringIO()

    def upcheck(self):
        return True

    def get_supervisor(self):
        return self.get_server_proxy('supervisor')

    def get_server_proxy(self, namespace=None):
        proxy = self.options.getServerProxy()
        if namespace is None:
            return proxy
        else:
            return getattr(proxy, namespace)

    def output(self, data):
        self.stdout.write(data + '\n')

    def print_topics(self, doc_headers, cmds_doc, rows, cols):
        self.topics_printed.append((doc_headers, cmds_doc, rows, cols))

class DummyPlugin:
    def __init__(self, controller=None):
        self.ctl = controller

    def do_help(self, arg):
        self.helped = True

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_supervisord
import unittest
import time
import signal
import sys
import os
import tempfile
import shutil

from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyPGroupConfig
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyProcessGroup
from supervisor.tests.base import DummyDispatcher

try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO

class EntryPointTests(unittest.TestCase):
    def test_main_noprofile(self):
        from supervisor.supervisord import main
        conf = os.path.join(
            os.path.abspath(os.path.dirname(__file__)), 'fixtures',
            'donothing.conf')
        new_stdout = StringIO()
        old_stdout = sys.stdout
        try:
            tempdir = tempfile.mkdtemp()
            log = os.path.join(tempdir, 'log')
            pid = os.path.join(tempdir, 'pid')
            sys.stdout = new_stdout
            main(args=['-c', conf, '-l', log, '-j', pid, '-n'],
                 test=True)
        finally:
            sys.stdout = old_stdout
            shutil.rmtree(tempdir)
        output = new_stdout.getvalue()
        self.assertTrue(output.find('supervisord started') != 1, output)

    if sys.version_info[:2] >= (2, 4):
        def test_main_profile(self):
            from supervisor.supervisord import main
            conf = os.path.join(
                os.path.abspath(os.path.dirname(__file__)), 'fixtures',
                'donothing.conf')
            new_stdout = StringIO()
            old_stdout = sys.stdout
            try:
                tempdir = tempfile.mkdtemp()
                log = os.path.join(tempdir, 'log')
                pid = os.path.join(tempdir, 'pid')
                sys.stdout = new_stdout
                main(args=['-c', conf, '-l', log, '-j', pid, '-n',
                           '--profile_options=cumulative,calls'], test=True)
            finally:
                sys.stdout = old_stdout
                shutil.rmtree(tempdir)
            output = new_stdout.getvalue()
            self.assertTrue(output.find('cumulative time, call count') != -1,
                            output)

class SupervisordTests(unittest.TestCase):
    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.supervisord import Supervisor
        return Supervisor

    def _makeOne(self, options):
        return self._getTargetClass()(options)

    def test_main_first(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfigs = [DummyPGroupConfig(options,'foo', pconfigs=[pconfig])]
        options.process_group_configs = gconfigs
        options.test = True
        options.first = True
        supervisord = self._makeOne(options)
        supervisord.main()
        self.assertEqual(options.environment_processed, True)
        self.assertEqual(options.fds_cleaned_up, False)
        self.assertEqual(options.rlimits_set, True)
        self.assertEqual(options.make_logger_messages,
                         (['setuid_called'], [], ['rlimits_set']))
        self.assertEqual(options.autochildlogdir_cleared, True)
        self.assertEqual(len(supervisord.process_groups), 1)
        self.assertEqual(supervisord.process_groups['foo'].config.options,
                         options)
        self.assertEqual(options.environment_processed, True)
        self.assertEqual(options.httpservers_opened, True)
        self.assertEqual(options.signals_set, True)
        self.assertEqual(options.daemonized, True)
        self.assertEqual(options.pidfile_written, True)
        self.assertEqual(options.cleaned_up, True)

    def test_main_notfirst(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfigs = [DummyPGroupConfig(options,'foo', pconfigs=[pconfig])]
        options.process_group_configs = gconfigs
        options.test = True
        options.first = False
        supervisord = self._makeOne(options)
        supervisord.main()
        self.assertEqual(options.environment_processed, True)
        self.assertEqual(options.fds_cleaned_up, True)
        self.assertFalse(hasattr(options, 'rlimits_set'))
        self.assertEqual(options.make_logger_messages,
                         (['setuid_called'], [], []))
        self.assertEqual(options.autochildlogdir_cleared, True)
        self.assertEqual(len(supervisord.process_groups), 1)
        self.assertEqual(supervisord.process_groups['foo'].config.options,
                         options)
        self.assertEqual(options.environment_processed, True)
        self.assertEqual(options.httpservers_opened, True)
        self.assertEqual(options.signals_set, True)
        self.assertEqual(options.daemonized, False)
        self.assertEqual(options.pidfile_written, True)
        self.assertEqual(options.cleaned_up, True)

    def test_reap(self):
        options = DummyOptions()
        options.waitpid_return = 1, 1
        pconfig = DummyPConfig(options, 'process', 'process', '/bin/process1')
        process = DummyProcess(pconfig)
        process.drained = False
        process.killing = 1
        process.laststop = None
        process.waitstatus = None, None
        options.pidhistory = {1:process}
        supervisord = self._makeOne(options)

        supervisord.reap(once=True)
        self.assertEqual(process.finished, (1,1))

    def test_reap_unknown_pid(self):
        options = DummyOptions()
        options.waitpid_return = 2, 0 # pid, status
        pconfig = DummyPConfig(options, 'process', 'process', '/bin/process1')
        process = DummyProcess(pconfig)
        process.drained = False
        process.killing = True
        process.laststop = None
        process.waitstatus = None, None
        options.pidhistory = {1: process}
        supervisord = self._makeOne(options)

        supervisord.reap(once=True)
        self.assertEqual(process.finished, None)
        self.assertEqual(options.logger.data[0],
                         'reaped unknown pid 2')

    def test_handle_sigterm(self):
        options = DummyOptions()
        options._signal = signal.SIGTERM
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, -1)
        self.assertEqual(options.logger.data[0],
                         'received SIGTERM indicating exit request')

    def test_handle_sigint(self):
        options = DummyOptions()
        options._signal = signal.SIGINT
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, -1)
        self.assertEqual(options.logger.data[0],
                         'received SIGINT indicating exit request')

    def test_handle_sigquit(self):
        options = DummyOptions()
        options._signal = signal.SIGQUIT
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, -1)
        self.assertEqual(options.logger.data[0],
                         'received SIGQUIT indicating exit request')

    def test_handle_sighup(self):
        options = DummyOptions()
        options._signal = signal.SIGHUP
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, 0)
        self.assertEqual(options.logger.data[0],
                         'received SIGHUP indicating restart request')

    def test_handle_sigchld(self):
        options = DummyOptions()
        options._signal = signal.SIGCHLD
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, 1)
        # supervisor.options.signame(signal.SIGCHLD) may return "SIGCLD"
        # on linux or other systems where SIGCHLD = SIGCLD.
        msgs = ('received SIGCHLD indicating a child quit',
                'received SIGCLD indicating a child quit')
        self.assertTrue(options.logger.data[0] in msgs)

    def test_handle_sigusr2(self):
        options = DummyOptions()
        options._signal = signal.SIGUSR2
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        from supervisor.process import ProcessStates
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        process1.delay = time.time() - 1
        supervisord = self._makeOne(options)
        pconfigs = [DummyPConfig(options, 'foo', 'foo', '/bin/foo')]
        options.process_group_configs = DummyPGroupConfig(
            options, 'foo',
            pconfigs=pconfigs)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, 1)
        self.assertEqual(options.logs_reopened, True)
        self.assertEqual(options.logger.data[0],
                         'received SIGUSR2 indicating log reopen request')

    def test_handle_unknown_signal(self):
        options = DummyOptions()
        options._signal = signal.SIGUSR1
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, 1)
        self.assertEqual(options.logger.data[0],
                         'received SIGUSR1 indicating nothing')

    def test_diff_add_remove(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)

        pconfig = DummyPConfig(options, 'process1', 'process1')
        group1 = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])

        pconfig = DummyPConfig(options, 'process2', 'process2')
        group2 = DummyPGroupConfig(options, 'group2', pconfigs=[pconfig])

        new = [group1, group2]

        added, changed, removed = supervisord.diff_to_active()
        self.assertEqual(added, [])
        self.assertEqual(changed, [])
        self.assertEqual(removed, [])

        added, changed, removed = supervisord.diff_to_active(new)
        self.assertEqual(added, new)
        self.assertEqual(changed, [])
        self.assertEqual(removed, [])

        supervisord.options.process_group_configs = new
        added, changed, removed = supervisord.diff_to_active()
        self.assertEqual(added, new)

        supervisord.add_process_group(group1)
        supervisord.add_process_group(group2)

        pconfig = DummyPConfig(options, 'process3', 'process3')
        new_group1 = DummyPGroupConfig(options, pconfigs=[pconfig])

        pconfig = DummyPConfig(options, 'process4', 'process4')
        new_group2 = DummyPGroupConfig(options, pconfigs=[pconfig])

        new = [group2, new_group1, new_group2]

        added, changed, removed = supervisord.diff_to_active(new)
        self.assertEqual(added, [new_group1, new_group2])
        self.assertEqual(changed, [])
        self.assertEqual(removed, [group1])

    def test_diff_changed(self):
        from supervisor.options import ProcessConfig, ProcessGroupConfig

        options = DummyOptions()
        supervisord = self._makeOne(options)

        def make_pconfig(name, command, **params):
            result = {
                'name': name, 'command': command,
                'directory': None, 'umask': None, 'priority': 999, 'autostart': True,
                'autorestart': True, 'startsecs': 10, 'startretries': 999,
                'uid': None, 'stdout_logfile': None, 'stdout_capture_maxbytes': 0,
                'stdout_events_enabled': False,
                'stdout_logfile_backups': 0, 'stdout_logfile_maxbytes': 0,
                'stdout_syslog': False,
                'stderr_logfile': None, 'stderr_capture_maxbytes': 0,
                'stderr_events_enabled': False,
                'stderr_logfile_backups': 0, 'stderr_logfile_maxbytes': 0,
                'stderr_syslog': False,
                'redirect_stderr': False,
                'stopsignal': None, 'stopwaitsecs': 10,
                'stopasgroup': False,
                'killasgroup': False,
                'exitcodes': (0,2), 'environment': None, 'serverurl': None,
            }
            result.update(params)
            return ProcessConfig(options, **result)

        def make_gconfig(name, pconfigs):
            return ProcessGroupConfig(options, name, 25, pconfigs)

        pconfig = make_pconfig('process1', 'process1', uid='new')
        group1 = make_gconfig('group1', [pconfig])

        pconfig = make_pconfig('process2', 'process2')
        group2 = make_gconfig('group2', [pconfig])
        new = [group1, group2]

        pconfig = make_pconfig('process1', 'process1', uid='old')
        group3 = make_gconfig('group1', [pconfig])

        pconfig = make_pconfig('process2', 'process2')
        group4 = make_gconfig('group2', [pconfig])
        supervisord.add_process_group(group3)
        supervisord.add_process_group(group4)

        added, changed, removed = supervisord.diff_to_active(new)

        self.assertEqual([added, removed], [[], []])
        self.assertEqual(changed, [group1])

        options = DummyOptions()
        supervisord = self._makeOne(options)

        pconfig1 = make_pconfig('process1', 'process1')
        pconfig2 = make_pconfig('process2', 'process2')
        group1 = make_gconfig('group1', [pconfig1, pconfig2])
        new = [group1]

        supervisord.add_process_group(make_gconfig('group1', [pconfig1]))

        added, changed, removed = supervisord.diff_to_active(new)
        self.assertEqual([added, removed], [[], []])
        self.assertEqual(changed, [group1])

    def test_add_process_group(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
        options.process_group_configs = [gconfig]
        supervisord = self._makeOne(options)

        self.assertEqual(supervisord.process_groups, {})

        result = supervisord.add_process_group(gconfig)
        self.assertEqual(list(supervisord.process_groups.keys()), ['foo'])
        self.assertTrue(result)

        group = supervisord.process_groups['foo']
        result = supervisord.add_process_group(gconfig)
        self.assertEqual(group, supervisord.process_groups['foo'])
        self.assertTrue(not result)

    def test_add_process_group_event(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        events.subscribe(events.ProcessGroupAddedEvent, callback)
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
        options.process_group_configs = [gconfig]
        supervisord = self._makeOne(options)

        supervisord.add_process_group(gconfig)

        options.test = True
        supervisord.runforever()
        self.assertEqual(L, [1])

    def test_remove_process_group(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfig = DummyPGroupConfig(options, 'foo', pconfigs=[pconfig])
        supervisord = self._makeOne(options)

        self.assertRaises(KeyError, supervisord.remove_process_group, 'asdf')

        supervisord.add_process_group(gconfig)
        result = supervisord.remove_process_group('foo')
        self.assertEqual(supervisord.process_groups, {})
        self.assertTrue(result)

        supervisord.add_process_group(gconfig)
        supervisord.process_groups['foo'].unstopped_processes = [DummyProcess(None)]
        result = supervisord.remove_process_group('foo')
        self.assertEqual(list(supervisord.process_groups.keys()), ['foo'])
        self.assertTrue(not result)

    def test_remove_process_group_event(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        events.subscribe(events.ProcessGroupRemovedEvent, callback)
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
        options.process_group_configs = [gconfig]
        supervisord = self._makeOne(options)

        supervisord.add_process_group(gconfig)
        supervisord.process_groups['foo'].stopped_processes = [DummyProcess(None)]
        supervisord.remove_process_group('foo')
        options.test = True
        supervisord.runforever()

        self.assertEqual(L, [1])

    def test_runforever_emits_generic_startup_event(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        events.subscribe(events.SupervisorStateChangeEvent, callback)
        options = DummyOptions()
        supervisord = self._makeOne(options)
        options.test = True
        supervisord.runforever()
        self.assertEqual(L, [1])

    def test_runforever_emits_generic_specific_event(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(2)
        events.subscribe(events.SupervisorRunningEvent, callback)
        options = DummyOptions()
        options.test = True
        supervisord = self._makeOne(options)
        supervisord.runforever()
        self.assertEqual(L, [2])

    def test_runforever_calls_tick(self):
        options = DummyOptions()
        options.test = True
        supervisord = self._makeOne(options)
        self.assertEqual(len(supervisord.ticks), 0)
        supervisord.runforever()
        self.assertEqual(len(supervisord.ticks), 3)

    def test_runforever_select_eintr(self):
        options = DummyOptions()
        import errno
        options.select_error = errno.EINTR
        supervisord = self._makeOne(options)
        options.test = True
        supervisord.runforever()
        self.assertEqual(options.logger.data[0], 'EINTR encountered in select')

    def test_runforever_select_uncaught_exception(self):
        options = DummyOptions()
        import errno
        options.select_error = errno.EBADF
        supervisord = self._makeOne(options)
        import select
        options.test = True
        self.assertRaises(select.error, supervisord.runforever)

    def test_runforever_select_dispatchers(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
        pgroup = DummyProcessGroup(gconfig)
        readable = DummyDispatcher(readable=True)
        writable = DummyDispatcher(writable=True)
        error = DummyDispatcher(writable=True, error=OSError)
        pgroup.dispatchers = {6:readable, 7:writable, 8:error}
        supervisord.process_groups = {'foo': pgroup}
        options.select_result = [6], [7, 8], []
        options.test = True
        supervisord.runforever()
        self.assertEqual(pgroup.transitioned, True)
        self.assertEqual(readable.read_event_handled, True)
        self.assertEqual(writable.write_event_handled, True)
        self.assertEqual(error.error_handled, True)

    def test_runforever_select_dispatcher_exitnow(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
        pgroup = DummyProcessGroup(gconfig)
        from supervisor.medusa import asyncore_25 as asyncore
        exitnow = DummyDispatcher(readable=True, error=asyncore.ExitNow)
        pgroup.dispatchers = {6:exitnow}
        supervisord.process_groups = {'foo': pgroup}
        options.select_result = [6], [], []
        options.test = True
        self.assertRaises(asyncore.ExitNow, supervisord.runforever)

    def test_runforever_stopping_emits_events(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        gconfig = DummyPGroupConfig(options)
        pgroup = DummyProcessGroup(gconfig)
        supervisord.process_groups = {'foo': pgroup}
        supervisord.options.mood = -1
        L = []
        def callback(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.SupervisorStateChangeEvent, callback)
        from supervisor.medusa import asyncore_25 as asyncore
        options.test = True
        self.assertRaises(asyncore.ExitNow, supervisord.runforever)
        self.assertTrue(pgroup.all_stopped)
        self.assertTrue(isinstance(L[0], events.SupervisorRunningEvent))
        self.assertTrue(isinstance(L[0], events.SupervisorStateChangeEvent))
        self.assertTrue(isinstance(L[1], events.SupervisorStoppingEvent))
        self.assertTrue(isinstance(L[1], events.SupervisorStateChangeEvent))

    def test_exit(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
        pgroup = DummyProcessGroup(gconfig)
        L = []
        def callback():
            L.append(1)
        supervisord.process_groups = {'foo': pgroup}
        supervisord.options.mood = 0
        supervisord.options.test = True
        from supervisor.medusa import asyncore_25 as asyncore
        self.assertRaises(asyncore.ExitNow, supervisord.runforever)
        self.assertEqual(pgroup.all_stopped, True)

    def test_exit_delayed(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
        process = DummyProcess(pconfig)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
        pgroup = DummyProcessGroup(gconfig)
        pgroup.unstopped_processes = [process]
        L = []
        def callback():
            L.append(1)
        supervisord.process_groups = {'foo': pgroup}
        supervisord.options.mood = 0
        supervisord.options.test = True
        supervisord.runforever()
        self.assertNotEqual(supervisord.lastshutdownreport, 0)

    def test_getSupervisorStateDescription(self):
        from supervisor.states import getSupervisorStateDescription
        from supervisor.states import SupervisorStates
        result = getSupervisorStateDescription(SupervisorStates.RUNNING)
        self.assertEqual(result, 'RUNNING')

    def test_tick(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(event)
        events.subscribe(events.TickEvent, callback)
        options = DummyOptions()
        supervisord = self._makeOne(options)

        supervisord.tick(now=0)
        self.assertEqual(supervisord.ticks[5], 0)
        self.assertEqual(supervisord.ticks[60], 0)
        self.assertEqual(supervisord.ticks[3600], 0)
        self.assertEqual(len(L), 0)

        supervisord.tick(now=6)
        self.assertEqual(supervisord.ticks[5], 5)
        self.assertEqual(supervisord.ticks[60], 0)
        self.assertEqual(supervisord.ticks[3600], 0)
        self.assertEqual(len(L), 1)
        self.assertEqual(L[-1].__class__, events.Tick5Event)

        supervisord.tick(now=61)
        self.assertEqual(supervisord.ticks[5], 60)
        self.assertEqual(supervisord.ticks[60], 60)
        self.assertEqual(supervisord.ticks[3600], 0)
        self.assertEqual(len(L), 3)
        self.assertEqual(L[-1].__class__, events.Tick60Event)

        supervisord.tick(now=3601)
        self.assertEqual(supervisord.ticks[5], 3600)
        self.assertEqual(supervisord.ticks[60], 3600)
        self.assertEqual(supervisord.ticks[3600], 3600)
        self.assertEqual(len(L), 6)
        self.assertEqual(L[-1].__class__, events.Tick3600Event)

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_web
import sys
import unittest

from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import DummyRequest

class DeferredWebProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.web import DeferredWebProducer
        return DeferredWebProducer

    def _makeOne(self, request, callback):
        producer = self._getTargetClass()(request, callback)
        return producer

    def test_ctor(self):
        request = DummyRequest('/index.html', [], '', '')
        callback = lambda *x: None
        callback.delay = 1
        producer = self._makeOne(request, callback)
        self.assertEqual(producer.callback, callback)
        self.assertEqual(producer.request, request)
        self.assertEqual(producer.finished, False)
        self.assertEqual(producer.delay, 1)

    def test_more_not_done_yet(self):
        request = DummyRequest('/index.html', [], '', '')
        from supervisor.http import NOT_DONE_YET
        callback = lambda *x: NOT_DONE_YET
        callback.delay = 1
        producer = self._makeOne(request, callback)
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_exception_caught(self):
        request = DummyRequest('/index.html', [], '', '')
        def callback(*arg):
            raise ValueError('foo')
        callback.delay = 1
        producer = self._makeOne(request, callback)
        self.assertEqual(producer.more(), None)
        logdata = request.channel.server.logger.logged
        self.assertEqual(len(logdata), 1)
        logged = logdata[0]
        self.assertEqual(logged[0], 'Web interface error')
        self.assertTrue(logged[1].startswith('Traceback'), logged[1])
        self.assertEqual(producer.finished, True)
        self.assertEqual(request._error, 500)

    def test_sendresponse_redirect(self):
        request = DummyRequest('/index.html', [], '', '')
        callback = lambda *arg: None
        callback.delay = 1
        producer = self._makeOne(request, callback)
        response = {'headers': {'Location':'abc'}}
        result = producer.sendresponse(response)
        self.assertEqual(result, None)
        self.assertEqual(request._error, 301)
        self.assertEqual(request.headers['Content-Type'], 'text/plain')
        self.assertEqual(request.headers['Content-Length'], 0)

    def test_sendresponse_withbody_and_content_type(self):
        request = DummyRequest('/index.html', [], '', '')
        callback = lambda *arg: None
        callback.delay = 1
        producer = self._makeOne(request, callback)
        response = {'body': 'abc', 'headers':{'Content-Type':'text/html'}}
        result = producer.sendresponse(response)
        self.assertEqual(result, None)
        self.assertEqual(request.headers['Content-Type'], 'text/html')
        self.assertEqual(request.headers['Content-Length'], 3)
        self.assertEqual(request.producers[0], 'abc')

class UIHandlerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.web import supervisor_ui_handler
        return supervisor_ui_handler

    def _makeOne(self):
        supervisord = DummySupervisor()
        handler = self._getTargetClass()(supervisord)
        return handler

    def test_handle_request_no_view_method(self):
        request = DummyRequest('/foo.css', [], '', '', {'PATH_INFO':'/foo.css'})
        handler = self._makeOne()
        data = handler.handle_request(request)
        self.assertEqual(data, None)

    def test_handle_request_default(self):
        request = DummyRequest('/index.html', [], '', '',
                               {'PATH_INFO':'/index.html'})
        handler = self._makeOne()
        data = handler.handle_request(request)
        self.assertEqual(data, None)
        self.assertEqual(request.channel.producer.request, request)
        from supervisor.web import StatusView
        self.assertEqual(request.channel.producer.callback.__class__,StatusView)

    def test_handle_request_index_html(self):
        request = DummyRequest('/index.html', [], '', '',
                               {'PATH_INFO':'/index.html'})
        handler = self._makeOne()
        handler.handle_request(request)
        from supervisor.web import StatusView
        view = request.channel.producer.callback
        self.assertEqual(view.__class__, StatusView)
        self.assertEqual(view.context.template, 'ui/status.html')

    def test_handle_request_tail_html(self):
        request = DummyRequest('/tail.html', [], '', '',
                               {'PATH_INFO':'/tail.html'})
        handler = self._makeOne()
        handler.handle_request(request)
        from supervisor.web import TailView
        view = request.channel.producer.callback
        self.assertEqual(view.__class__, TailView)
        self.assertEqual(view.context.template, 'ui/tail.html')

    def test_handle_request_ok_html(self):
        request = DummyRequest('/tail.html', [], '', '',
                               {'PATH_INFO':'/ok.html'})
        handler = self._makeOne()
        handler.handle_request(request)
        from supervisor.web import OKView
        view = request.channel.producer.callback
        self.assertEqual(view.__class__, OKView)
        self.assertEqual(view.context.template, None)


class StatusViewTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.web import StatusView
        return StatusView

    def _makeOne(self, context):
        klass = self._getTargetClass()
        return klass(context)

    def test_make_callback_noaction(self):
        context = DummyContext()
        context.supervisord = DummySupervisor()
        context.template = 'ui/status.html'
        context.form = {}
        view = self._makeOne(context)
        self.assertRaises(ValueError, view.make_callback, 'process', None)

    def test_render_noaction(self):
        context = DummyContext()
        context.supervisord = DummySupervisor()
        context.template = 'ui/status.html'
        context.request = DummyRequest('/foo', [], '', '')
        context.form = {}
        context.response = {}
        view = self._makeOne(context)
        data = view.render()
        self.assertTrue(data.startswith('<!DOCTYPE html PUBLIC'), data)

    def test_render_refresh(self):
        context = DummyContext()
        context.supervisord = DummySupervisor()
        context.template = 'ui/status.html'
        context.response = {}
        context.form = {'action':'refresh'}
        view = self._makeOne(context)
        data = view.render()
        from supervisor.http import NOT_DONE_YET
        self.assertTrue(data is NOT_DONE_YET, data)

class DummyContext:
    pass

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_xmlrpc
import sys
import unittest

from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import DummyRequest
from supervisor.tests.base import DummySupervisorRPCNamespace

try:
    import xmlrpclib
    import httplib
except ImportError:
    import xmlrpc.client as xmlrpclib
    import http.client as httplib

class GetFaultDescriptionTests(unittest.TestCase):
    def test_returns_description_for_known_fault(self):
        from supervisor import xmlrpc
        desc = xmlrpc.getFaultDescription(xmlrpc.Faults.SHUTDOWN_STATE)
        self.assertEqual(desc, 'SHUTDOWN_STATE')

    def test_returns_unknown_for_unknown_fault(self):
        from supervisor import xmlrpc
        desc = xmlrpc.getFaultDescription(999999)
        self.assertEqual(desc, 'UNKNOWN')

class RPCErrorTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.xmlrpc import RPCError
        return RPCError

    def _makeOne(self, code, extra=None):
        return self._getTargetClass()(code, extra)

    def test_sets_text_with_fault_name_only(self):
        from supervisor import xmlrpc
        e = self._makeOne(xmlrpc.Faults.FAILED)
        self.assertEqual(e.text, 'FAILED')

    def test_sets_text_with_fault_name_and_extra(self):
        from supervisor import xmlrpc
        e = self._makeOne(xmlrpc.Faults.FAILED, 'oops')
        self.assertEqual(e.text, 'FAILED: oops')

class XMLRPCMarshallingTests(unittest.TestCase):
    def test_xmlrpc_marshal(self):
        from supervisor import xmlrpc
        data = xmlrpc.xmlrpc_marshal(1)
        self.assertEqual(data, xmlrpclib.dumps((1,), methodresponse=True))
        fault = xmlrpclib.Fault(1, 'foo')
        data = xmlrpc.xmlrpc_marshal(fault)
        self.assertEqual(data, xmlrpclib.dumps(fault))

class XMLRPCHandlerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.xmlrpc import supervisor_xmlrpc_handler
        return supervisor_xmlrpc_handler

    def _makeOne(self, supervisord, subinterfaces):
        return self._getTargetClass()(supervisord, subinterfaces)

    def test_ctor(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        self.assertEqual(handler.supervisord, supervisor)
        from supervisor.xmlrpc import RootRPCInterface
        self.assertEqual(handler.rpcinterface.__class__, RootRPCInterface)

    def test_match(self):
        class DummyRequest2:
            def __init__(self, uri):
                self.uri = uri
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        self.assertEqual(handler.match(DummyRequest2('/RPC2')), True)
        self.assertEqual(handler.match(DummyRequest2('/nope')), False)

    def test_continue_request_nosuchmethod(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = xmlrpclib.dumps(('a', 'b'), 'supervisor.noSuchMethod')
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 2
        else:
            expected = 3
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-2],
                         'XML-RPC method called: supervisor.noSuchMethod()')
        self.assertEqual(logdata[-1],
           ('XML-RPC method supervisor.noSuchMethod() returned fault: '
            '[1] UNKNOWN_METHOD'))
        self.assertEqual(len(request.producers), 1)
        xml_response = request.producers[0]
        self.assertRaises(xmlrpclib.Fault, xmlrpclib.loads, xml_response)

    def test_continue_request_methodsuccess(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = xmlrpclib.dumps((), 'supervisor.getAPIVersion')
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 2
        else:
            expected = 3
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-2],
               'XML-RPC method called: supervisor.getAPIVersion()')
        self.assertEqual(logdata[-1],
            'XML-RPC method supervisor.getAPIVersion() returned successfully')
        self.assertEqual(len(request.producers), 1)
        xml_response = request.producers[0]
        response = xmlrpclib.loads(xml_response)
        from supervisor.rpcinterface import API_VERSION
        self.assertEqual(response[0][0], API_VERSION)
        self.assertEqual(request._done, True)
        self.assertEqual(request.headers['Content-Type'], 'text/xml')
        self.assertEqual(request.headers['Content-Length'], len(xml_response))

    def test_continue_request_no_params_in_request(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = '<?xml version="1.0" encoding="UTF-8"?>' \
               '<methodCall>' \
               '<methodName>supervisor.getAPIVersion</methodName>' \
               '</methodCall>'
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 2
        else:
            expected = 3
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-2],
               'XML-RPC method called: supervisor.getAPIVersion()')
        self.assertEqual(logdata[-1],
            'XML-RPC method supervisor.getAPIVersion() returned successfully')
        self.assertEqual(len(request.producers), 1)
        xml_response = request.producers[0]
        response = xmlrpclib.loads(xml_response)
        from supervisor.rpcinterface import API_VERSION
        self.assertEqual(response[0][0], API_VERSION)
        self.assertEqual(request._done, True)
        self.assertEqual(request.headers['Content-Type'], 'text/xml')
        self.assertEqual(request.headers['Content-Length'], len(xml_response))

    def test_continue_request_400_if_method_name_is_empty(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = '<?xml version="1.0" encoding="UTF-8"?>' \
               '<methodCall><methodName></methodName></methodCall>'
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 1
        else:
            expected = 2
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-1],
               'XML-RPC request received with no method name')
        self.assertEqual(len(request.producers), 0)
        self.assertEqual(request._error, 400)

    def test_continue_request_500(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = xmlrpclib.dumps((), 'supervisor.raiseError')
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 2
        else:
            expected = 3
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-2],
               'XML-RPC method called: supervisor.raiseError()')
        self.assertTrue(logdata[-1].startswith('Traceback'))
        self.assertTrue(logdata[-1].endswith('ValueError: error\n'))
        self.assertEqual(len(request.producers), 0)
        self.assertEqual(request._error, 500)

class TraverseTests(unittest.TestCase):
    def test_underscore(self):
        from supervisor import xmlrpc
        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, None, '_', None)

    def test_notfound(self):
        from supervisor import xmlrpc
        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, None, 'foo', None)

    def test_badparams(self):
        from supervisor import xmlrpc
        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, self,
                          'test_badparams', (1, 2, 3))

    def test_success(self):
        from supervisor import xmlrpc
        L = []
        class Dummy:
            def foo(self, a):
                L.append(a)
        dummy = Dummy()
        xmlrpc.traverse(dummy, 'foo', [1])
        self.assertEqual(L, [1])

class SupervisorTransportTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.xmlrpc import SupervisorTransport
        return SupervisorTransport

    def _makeOne(self, *arg, **kw):
        return self._getTargetClass()(*arg, **kw)

    def test_ctor_unix(self):
        from supervisor import xmlrpc
        transport = self._makeOne('user', 'pass', 'unix:///foo/bar')
        conn = transport._get_connection()
        self.assertTrue(isinstance(conn, xmlrpc.UnixStreamHTTPConnection))
        self.assertEqual(conn.host, 'localhost')
        self.assertEqual(conn.socketfile, '/foo/bar')

    def test__get_connection_http_9001(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1:9001/')
        conn = transport._get_connection()
        self.assertTrue(isinstance(conn, httplib.HTTPConnection))
        self.assertEqual(conn.host, '127.0.0.1')
        self.assertEqual(conn.port, 9001)

    def test__get_connection_http_80(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
        conn = transport._get_connection()
        self.assertTrue(isinstance(conn, httplib.HTTPConnection))
        self.assertEqual(conn.host, '127.0.0.1')
        self.assertEqual(conn.port, 80)

    def test_request_non_200_response(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
        dummy_conn = DummyConnection(400, '')
        def getconn():
            return dummy_conn
        transport._get_connection = getconn
        self.assertRaises(xmlrpclib.ProtocolError,
                          transport.request, 'localhost', '/', '')
        self.assertEqual(transport.connection, None)
        self.assertEqual(dummy_conn.closed, True)

    def test_request_400_response(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
        dummy_conn = DummyConnection(400, '')
        def getconn():
            return dummy_conn
        transport._get_connection = getconn
        self.assertRaises(xmlrpclib.ProtocolError,
                          transport.request, 'localhost', '/', '')
        self.assertEqual(transport.connection, None)
        self.assertEqual(dummy_conn.closed, True)
        self.assertEqual(dummy_conn.requestargs[0], 'POST')
        self.assertEqual(dummy_conn.requestargs[1], '/')
        self.assertEqual(dummy_conn.requestargs[2], '')
        self.assertEqual(dummy_conn.requestargs[3]['Content-Length'], '0')
        self.assertEqual(dummy_conn.requestargs[3]['Content-Type'], 'text/xml')
        self.assertEqual(dummy_conn.requestargs[3]['Authorization'],
                         'Basic dXNlcjpwYXNz')
        self.assertEqual(dummy_conn.requestargs[3]['Accept'], 'text/xml')

    def test_request_200_response(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
        response = """<?xml version="1.0"?>
        <methodResponse>
        <params>
        <param>
        <value><string>South Dakota</string></value>
        </param>
        </params>
        </methodResponse>"""
        dummy_conn = DummyConnection(200, response)
        def getconn():
            return dummy_conn
        transport._get_connection = getconn
        result = transport.request('localhost', '/', '')
        self.assertEqual(transport.connection, dummy_conn)
        self.assertEqual(dummy_conn.closed, False)
        self.assertEqual(dummy_conn.requestargs[0], 'POST')
        self.assertEqual(dummy_conn.requestargs[1], '/')
        self.assertEqual(dummy_conn.requestargs[2], '')
        self.assertEqual(dummy_conn.requestargs[3]['Content-Length'], '0')
        self.assertEqual(dummy_conn.requestargs[3]['Content-Type'], 'text/xml')
        self.assertEqual(dummy_conn.requestargs[3]['Authorization'],
                         'Basic dXNlcjpwYXNz')
        self.assertEqual(dummy_conn.requestargs[3]['Accept'], 'text/xml')
        self.assertEqual(result, ('South Dakota',))

    def test_works_with_py25(self):
        instance = self._makeOne('username', 'password', 'http://127.0.0.1')
        # the test is just to insure that this method can be called; failure
        # would be an AttributeError for _use_datetime under Python 2.5
        parser, unmarshaller = instance.getparser() # this uses _use_datetime

class IterparseLoadsTests(unittest.TestCase):
    def test_iterparse_loads_methodcall(self):
        s = """<?xml version="1.0"?>
        <methodCall>
        <methodName>examples.getStateName</methodName>
        <params>
        <param>
        <value><i4>41</i4></value>
        </param>
        <param>
        <value><int>14</int></value>
        </param>
        <param>
        <value><boolean>1</boolean></value>
        </param>
        <param>
        <value><string>hello world</string></value>
        </param>
        <param>
        <value><double>-12.214</double></value>
        </param>
        <param>
        <value><dateTime.iso8601>19980717T14:08:55</dateTime.iso8601></value>
        </param>
        <param>
        <value><base64>eW91IGNhbid0IHJlYWQgdGhpcyE=</base64></value>
        </param>
        <param>
        <struct>
          <member><name>k</name><value><i4>5</i4></value></member>
        </struct>
        </param>
        <param>
        <array>
          <data>
            <value><i4>12</i4></value>
            <value><i4>34</i4></value>
          </data>
        </array>
        </param>
        <param>
        <struct>
        <member>
          <name>k</name>
          <value><array><data><value><i4>1</i4></value></data></array></value>
        </member>
        </struct>
        </param>
        </params>
        </methodCall>
        """
        from supervisor.xmlrpc import loads
        if loads is None:
            return # no cElementTree
        result = loads(s)
        params, method = result
        import datetime
        self.assertEqual(method, 'examples.getStateName')
        self.assertEqual(params[0], 41)
        self.assertEqual(params[1], 14)
        self.assertEqual(params[2], True)
        self.assertEqual(params[3], 'hello world')
        self.assertEqual(params[4], -12.214)
        self.assertEqual(params[5], datetime.datetime(1998, 7, 17, 14, 8, 55))
        self.assertEqual(params[6], "you can't read this!")
        self.assertEqual(params[7], {'k': 5})
        self.assertEqual(params[8], [12, 34])
        self.assertEqual(params[9], {'k': [1]})

class TestDeferredXMLRPCResponse(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.xmlrpc import DeferredXMLRPCResponse
        return DeferredXMLRPCResponse

    def _makeOne(self, request=None, callback=None):
        if request is None:
            request = DummyRequest(None, None, None, None, None)
        if callback is None:
            callback = Dummy()
            callback.delay = 1
        return self._getTargetClass()(request, callback)

    def test_ctor(self):
        callback = Dummy()
        callback.delay = 1
        inst = self._makeOne(request='request', callback=callback)
        self.assertEqual(inst.callback, callback)
        self.assertEqual(inst.delay, 1.0)
        self.assertEqual(inst.request, 'request')
        self.assertEqual(inst.finished, False)

    def test_more_finished(self):
        inst = self._makeOne()
        inst.finished = True
        result = inst.more()
        self.assertEqual(result,  '')

    def test_more_callback_returns_not_done_yet(self):
        from supervisor.http import NOT_DONE_YET
        def callback():
            return NOT_DONE_YET
        callback.delay = 1
        inst = self._makeOne(callback=callback)
        self.assertEqual(inst.more(), NOT_DONE_YET)

    def test_more_callback_raises_RPCError(self):
        from supervisor.xmlrpc import RPCError, Faults
        def callback():
            raise RPCError(Faults.UNKNOWN_METHOD)
        callback.delay = 1
        inst = self._makeOne(callback=callback)
        self.assertEqual(inst.more(), None)
        self.assertEqual(len(inst.request.producers), 1)
        self.assertTrue('UNKNOWN_METHOD' in inst.request.producers[0])
        self.assertTrue(inst.finished)

    def test_more_callback_returns_value(self):
        def callback():
            return 'abc'
        callback.delay = 1
        inst = self._makeOne(callback=callback)
        self.assertEqual(inst.more(), None)
        self.assertEqual(len(inst.request.producers), 1)
        self.assertTrue('abc' in inst.request.producers[0])
        self.assertTrue(inst.finished)

    def test_more_callback_raises_unexpected_exception(self):
        def callback():
            raise ValueError('foo')
        callback.delay = 1
        inst = self._makeOne(callback=callback)
        inst.traceback = Dummy()
        called = []
        inst.traceback.print_exc = lambda: called.append(True)
        self.assertEqual(inst.more(), None)
        self.assertEqual(inst.request._error, 500)
        self.assertTrue(inst.finished)
        self.assertTrue(called)
        
class DummyResponse:
    def __init__(self, status=200, body='', reason='reason'):
        self.status = status
        self.body = body
        self.reason = reason

    def read(self):
        return self.body

class Dummy(object):
    pass

class DummyConnection:
    closed = False
    def __init__(self, status=200, body='', reason='reason'):
        self.response = DummyResponse(status, body, reason)

    def getresponse(self):
        return self.response

    def request(self, *arg, **kw):
        self.requestargs = arg
        self.requestkw = kw

    def close(self):
        self.closed = True

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = trackrefs
##############################################################################
#
# Copyright (c) 2007 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################

""" Code from the zope.testing module to help track down memory leaks

TrackRefs works only in a python compiled with the --with-pydebug flag.
An example of how to use TrackRefs in a function is below.

glen = 0
rc = 0

def doit():
    newglen = gc.collect()
    global glen
    if newglen > glen:
        print
        print "-------------------------------------"
        print "more garbage", newglen - glen
        glen = newglen
        print "-------------------------------------"
        print
    if refs:
        newrc = sys.gettotalrefcount()
        global rc
        if newrc > rc:
            refs.update()
            refs.detailed_refcounts(newrc, rc)
        rc = newrc
"""

import sys
import gc

class TrackRefs(object):
    """Object to track reference counts across test runs."""

    def __init__(self):
        self.type2count = {}
        self.type2all = {}
        self.delta = None
        self.n = 0
        self.update()
        self.delta = None

    def update(self):
        gc.collect()
        obs = sys.getobjects(0)
        type2count = {}
        type2all = {}
        n = 0
        for o in obs:
            if type(o) is str and o == '<dummy key>':
                # avoid dictionary madness
                continue

            all = sys.getrefcount(o) - 3
            n += all

            t = type(o)
            try:
                t = o.__class__
            except Exception:
                pass

            if t in type2count:
                type2count[t] += 1
                type2all[t] += all
            else:
                type2count[t] = 1
                type2all[t] = all


        ct = [(
               type_or_class_title(t),
               type2count[t] - self.type2count.get(t, 0),
               type2all[t] - self.type2all.get(t, 0),
               )
              for t in type2count.keys()]
        ct += [(
                type_or_class_title(t),
                - self.type2count[t],
                - self.type2all[t],
                )
               for t in self.type2count.keys()
               if t not in type2count]
        ct.sort()
        self.delta = ct
        self.type2count = type2count
        self.type2all = type2all
        self.n = n


    def output(self):
        printed = False
        s1 = s2 = 0
        for t, delta1, delta2 in self.delta:
            if delta1 or delta2:
                if not printed:
                    print (
                        '    Leak details, changes in instances and refcounts'
                        ' by type/class:')
                    print("    %-55s %6s %6s" % ('type/class', 'insts', 'refs'))
                    print("    %-55s %6s %6s" % ('-' * 55, '-----', '----'))
                    printed = True
                print("    %-55s %6d %6d" % (t, delta1, delta2))
                s1 += delta1
                s2 += delta2

        if printed:
            print("    %-55s %6s %6s" % ('-' * 55, '-----', '----'))
            print("    %-55s %6s %6s" % ('total', s1, s2))

        self.delta = None

    def detailed_refcounts(self, rc, prev):
        """Report a change in reference counts, with extra detail."""
        print ("  sum detail refcount=%-8d"
               " sys refcount=%-8d"
               " change=%-6d"
               % (self.n, rc, rc - prev))
        self.output()

def type_or_class_title(t):
    module = getattr(t, '__module__', '__builtin__')
    if module == '__builtin__':
        return t.__name__
    return "%s.%s" % (module, t.__name__)


########NEW FILE########
__FILENAME__ = web
import os
import re
import time
import traceback
import datetime

import meld3

from supervisor.compat import StringIO
from supervisor.compat import urllib
from supervisor.compat import parse_qs
from supervisor.compat import parse_qsl
from supervisor.compat import as_string

from supervisor.medusa import producers
from supervisor.medusa.http_server import http_date
from supervisor.medusa.http_server import get_header
from supervisor.medusa.xmlrpc_handler import collector

from supervisor.process import ProcessStates
from supervisor.http import NOT_DONE_YET

from supervisor.options import VERSION
from supervisor.options import make_namespec
from supervisor.options import split_namespec

from supervisor.xmlrpc import SystemNamespaceRPCInterface
from supervisor.xmlrpc import RootRPCInterface
from supervisor.xmlrpc import Faults
from supervisor.xmlrpc import RPCError

from supervisor.rpcinterface import SupervisorNamespaceRPCInterface

class DeferredWebProducer:
    """ A medusa producer that implements a deferred callback; requires
    a subclass of asynchat.async_chat that handles NOT_DONE_YET sentinel """
    CONNECTION = re.compile ('Connection: (.*)', re.IGNORECASE)

    def __init__(self, request, callback):
        self.callback = callback
        self.request = request
        self.finished = False
        self.delay = float(callback.delay)

    def more(self):
        if self.finished:
            return ''
        try:
            response = self.callback()
            if response is NOT_DONE_YET:
                return NOT_DONE_YET

            self.finished = True
            return self.sendresponse(response)

        except:
            io = StringIO()
            traceback.print_exc(file=io)
            # this should go to the main supervisor log file
            self.request.channel.server.logger.log('Web interface error',
                                                  io.getvalue())
            self.finished = True
            self.request.error(500)

    def sendresponse(self, response):

        headers = response.get('headers', {})
        for header in headers:
            self.request[header] = headers[header]

        if 'Content-Type' not in self.request:
            self.request['Content-Type'] = 'text/plain'

        if headers.get('Location'):
            self.request['Content-Length'] = 0
            self.request.error(301)
            return

        body = response.get('body', '')
        self.request['Content-Length'] = len(body)

        self.request.push(body)

        connection = get_header(self.CONNECTION, self.request.header)

        close_it = 0
        wrap_in_chunking = 0

        if self.request.version == '1.0':
            if connection == 'keep-alive':
                if not self.request.has_key('Content-Length'):
                    close_it = 1
                else:
                    self.request['Connection'] = 'Keep-Alive'
            else:
                close_it = 1
        elif self.request.version == '1.1':
            if connection == 'close':
                close_it = 1
            elif 'Content-Length' not in self.request:
                if 'Transfer-Encoding' in self.request:
                    if not self.request['Transfer-Encoding'] == 'chunked':
                        close_it = 1
                elif self.request.use_chunked:
                    self.request['Transfer-Encoding'] = 'chunked'
                    wrap_in_chunking = 1
                else:
                    close_it = 1
        elif self.request.version is None:
            close_it = 1

        outgoing_header = producers.simple_producer (
            self.request.build_reply_header())

        if close_it:
            self.request['Connection'] = 'close'

        if wrap_in_chunking:
            outgoing_producer = producers.chunked_producer (
                    producers.composite_producer (self.request.outgoing)
                    )
            # prepend the header
            outgoing_producer = producers.composite_producer(
                [outgoing_header, outgoing_producer]
                )
        else:
            # prepend the header
            self.request.outgoing.insert(0, outgoing_header)
            outgoing_producer = producers.composite_producer (
                self.request.outgoing)

        # apply a few final transformations to the output
        self.request.channel.push_with_producer (
                # globbing gives us large packets
                producers.globbing_producer (
                        # hooking lets us log the number of bytes sent
                        producers.hooked_producer (
                                outgoing_producer,
                                self.request.log
                                )
                        )
                )

        self.request.channel.current_request = None

        if close_it:
            self.request.channel.close_when_done()

class ViewContext:
    def __init__(self, **kw):
        self.__dict__.update(kw)

class MeldView:

    content_type = 'text/html'
    delay = .5

    def __init__(self, context):
        self.context = context
        template = self.context.template
        if not os.path.isabs(template):
            here = os.path.abspath(os.path.dirname(__file__))
            template = os.path.join(here, template)
        self.root = meld3.parse_xml(template)
        self.callback = None

    def __call__(self):
        body = self.render()
        if body is NOT_DONE_YET:
            return NOT_DONE_YET

        response = self.context.response
        headers = response['headers']
        headers['Content-Type'] = self.content_type
        headers['Pragma'] = 'no-cache'
        headers['Cache-Control'] = 'no-cache'
        headers['Expires'] = http_date.build_http_date(0)
        response['body'] = as_string(body)
        return response

    def render(self):
        pass

    def clone(self):
        return self.root.clone()

class TailView(MeldView):
    def render(self):
        supervisord = self.context.supervisord
        form = self.context.form

        if not 'processname' in form:
            tail = 'No process name found'
            processname = None
        else:
            processname = form['processname']
            offset = 0
            limit = form.get('limit', '1024')
            limit = min(-1024, int(limit)*-1 if limit.isdigit() else -1024)
            if not processname:
                tail = 'No process name found'
            else:
                rpcinterface = SupervisorNamespaceRPCInterface(supervisord)
                try:
                    tail = rpcinterface.readProcessLog(processname, limit, offset)
                    tail = rpcinterface.readProcessLog(processname, -1024, 0)
                except RPCError as e:
                    if e.code == Faults.NO_FILE:
                        tail = 'No file for %s' % processname
                    else:
                        raise

        root = self.clone()

        title = root.findmeld('title')
        title.content('Supervisor tail of process %s' % processname)
        tailbody = root.findmeld('tailbody')
        tailbody.content(tail)

        refresh_anchor = root.findmeld('refresh_anchor')
        if processname is not None:
            refresh_anchor.attributes(
                href='tail.html?processname=%s&limit=%s' % (
                    urllib.quote(processname), urllib.quote(str(abs(limit)))
                    )
            )
        else:
            refresh_anchor.deparent()

        return as_string(root.write_xhtmlstring())

class StatusView(MeldView):
    def actions_for_process(self, process):
        state = process.get_state()
        processname = urllib.quote(make_namespec(process.group.config.name,
                                                 process.config.name))
        start = {
        'name':'Start',
        'href':'index.html?processname=%s&amp;action=start' % processname,
        'target':None,
        }
        restart = {
        'name':'Restart',
        'href':'index.html?processname=%s&amp;action=restart' % processname,
        'target':None,
        }
        stop = {
        'name':'Stop',
        'href':'index.html?processname=%s&amp;action=stop' % processname,
        'target':None,
        }
        clearlog = {
        'name':'Clear Log',
        'href':'index.html?processname=%s&amp;action=clearlog' % processname,
        'target':None,
        }
        tailf = {
        'name':'Tail -f',
        'href':'logtail/%s' % processname,
        'target':'_blank'
        }
        if state == ProcessStates.RUNNING:
            actions = [restart, stop, clearlog, tailf]
        elif state in (ProcessStates.STOPPED, ProcessStates.EXITED,
                       ProcessStates.FATAL):
            actions = [start, None, clearlog, tailf]
        else:
            actions = [None, None, clearlog, tailf]
        return actions

    def css_class_for_state(self, state):
        if state == ProcessStates.RUNNING:
            return 'statusrunning'
        elif state in (ProcessStates.FATAL, ProcessStates.BACKOFF):
            return 'statuserror'
        else:
            return 'statusnominal'

    def make_callback(self, namespec, action):
        supervisord = self.context.supervisord

        # the rpc interface code is already written to deal properly in a
        # deferred world, so just use it
        main =   ('supervisor', SupervisorNamespaceRPCInterface(supervisord))
        system = ('system', SystemNamespaceRPCInterface([main]))

        rpcinterface = RootRPCInterface([main, system])

        if action:

            if action == 'refresh':
                def donothing():
                    message = 'Page refreshed at %s' % time.ctime()
                    return message
                donothing.delay = 0.05
                return donothing

            elif action == 'stopall':
                callback = rpcinterface.supervisor.stopAllProcesses()
                def stopall():
                    if callback() is NOT_DONE_YET:
                        return NOT_DONE_YET
                    else:
                        return 'All stopped at %s' % time.ctime()
                stopall.delay = 0.05
                return stopall

            elif action == 'restartall':
                callback = rpcinterface.system.multicall(
                    [ {'methodName':'supervisor.stopAllProcesses'},
                      {'methodName':'supervisor.startAllProcesses'} ] )
                def restartall():
                    result = callback()
                    if result is NOT_DONE_YET:
                        return NOT_DONE_YET
                    return 'All restarted at %s' % time.ctime()
                restartall.delay = 0.05
                return restartall

            elif namespec:
                def wrong():
                    return 'No such process named %s' % namespec
                wrong.delay = 0.05
                group_name, process_name = split_namespec(namespec)
                group = supervisord.process_groups.get(group_name)
                if group is None:
                    return wrong
                process = group.processes.get(process_name)
                if process is None:
                    return wrong

                elif action == 'stop':
                    callback = rpcinterface.supervisor.stopProcess(namespec)
                    def stopprocess():
                        result = callback()
                        if result is NOT_DONE_YET:
                            return NOT_DONE_YET
                        return 'Process %s stopped' % namespec
                    stopprocess.delay = 0.05
                    return stopprocess

                elif action == 'restart':
                    callback = rpcinterface.system.multicall(
                        [ {'methodName':'supervisor.stopProcess',
                           'params': [namespec]},
                          {'methodName':'supervisor.startProcess',
                           'params': [namespec]},
                          ]
                        )
                    def restartprocess():
                        result = callback()
                        if result is NOT_DONE_YET:
                            return NOT_DONE_YET
                        return 'Process %s restarted' % namespec
                    restartprocess.delay = 0.05
                    return restartprocess

                elif action == 'start':
                    try:
                        callback = rpcinterface.supervisor.startProcess(
                            namespec)
                    except RPCError as e:
                        if e.code == Faults.SPAWN_ERROR:
                            def spawnerr():
                                return 'Process %s spawn error' % namespec
                            spawnerr.delay = 0.05
                            return spawnerr
                    def startprocess():
                        if callback() is NOT_DONE_YET:
                            return NOT_DONE_YET
                        return 'Process %s started' % namespec
                    startprocess.delay = 0.05
                    return startprocess

                elif action == 'clearlog':
                    callback = rpcinterface.supervisor.clearProcessLog(
                        namespec)
                    def clearlog():
                        return 'Log for %s cleared' % namespec
                    clearlog.delay = 0.05
                    return clearlog

        raise ValueError(action)

    def render(self):
        form = self.context.form
        response = self.context.response
        processname = form.get('processname')
        action = form.get('action')
        message = form.get('message')

        if action:
            if not self.callback:
                self.callback = self.make_callback(processname, action)
                return NOT_DONE_YET

            else:
                message =  self.callback()
                if message is NOT_DONE_YET:
                    return NOT_DONE_YET
                if message is not None:
                    server_url = form['SERVER_URL']
                    location = server_url + '?message=%s' % urllib.quote(
                        message)
                    response['headers']['Location'] = location

        supervisord = self.context.supervisord
        rpcinterface = RootRPCInterface(
            [('supervisor',
              SupervisorNamespaceRPCInterface(supervisord))]
            )

        processnames = []
        for group in supervisord.process_groups.values():
            for gprocname in group.processes.keys():
                processnames.append((group.config.name, gprocname))

        processnames.sort()

        data = []
        for groupname, processname in processnames:
            actions = self.actions_for_process(
                supervisord.process_groups[groupname].processes[processname])
            sent_name = make_namespec(groupname, processname)
            info = rpcinterface.supervisor.getProcessInfo(sent_name)
            data.append({
                'status':info['statename'],
                'name':processname,
                'group':groupname,
                'actions':actions,
                'state':info['state'],
                'description':info['description'],
                })

        root = self.clone()

        if message is not None:
            statusarea = root.findmeld('statusmessage')
            statusarea.attrib['class'] = 'status_msg'
            statusarea.content(message)

        if data:
            iterator = root.findmeld('tr').repeat(data)
            shaded_tr = False

            for tr_element, item in iterator:
                status_text = tr_element.findmeld('status_text')
                status_text.content(item['status'].lower())
                status_text.attrib['class'] = self.css_class_for_state(
                    item['state'])

                info_text = tr_element.findmeld('info_text')
                info_text.content(item['description'])

                anchor = tr_element.findmeld('name_anchor')
                processname = make_namespec(item['group'], item['name'])
                anchor.attributes(href='tail.html?processname=%s' %
                                  urllib.quote(processname))
                anchor.content(processname)

                actions = item['actions']
                actionitem_td = tr_element.findmeld('actionitem_td')

                for li_element, actionitem in actionitem_td.repeat(actions):
                    anchor = li_element.findmeld('actionitem_anchor')
                    if actionitem is None:
                        anchor.attrib['class'] = 'hidden'
                    else:
                        anchor.attributes(href=actionitem['href'],
                                          name=actionitem['name'])
                        anchor.content(actionitem['name'])
                        if actionitem['target']:
                            anchor.attributes(target=actionitem['target'])
                if shaded_tr:
                    tr_element.attrib['class'] = 'shade'
                shaded_tr = not shaded_tr
        else:
            table = root.findmeld('statustable')
            table.replace('No programs to manage')

        root.findmeld('supervisor_version').content(VERSION)
        copyright_year = str(datetime.date.today().year)
        root.findmeld('copyright_date').content(copyright_year)

        return as_string(root.write_xhtmlstring())

class OKView:
    delay = 0
    def __init__(self, context):
        self.context = context

    def __call__(self):
        return {'body':'OK'}

VIEWS = {
    'index.html': {
          'template':'ui/status.html',
          'view':StatusView
          },
    'tail.html': {
           'template':'ui/tail.html',
           'view':TailView,
           },
    'ok.html': {
           'template':None,
           'view':OKView,
           },
    }


class supervisor_ui_handler:
    IDENT = 'Supervisor Web UI HTTP Request Handler'

    def __init__(self, supervisord):
        self.supervisord = supervisord

    def match(self, request):
        if request.command not in ('POST', 'GET'):
            return False

        path, params, query, fragment = request.split_uri()

        while path.startswith('/'):
            path = path[1:]

        if not path:
            path = 'index.html'

        for viewname in VIEWS.keys():
            if viewname == path:
                return True

    def handle_request(self, request):
        if request.command == 'POST':
            request.collector = collector(self, request)
        else:
            self.continue_request('', request)

    def continue_request (self, data, request):
        form = {}
        cgi_env = request.cgi_environment()
        form.update(cgi_env)
        if 'QUERY_STRING' not in form:
            form['QUERY_STRING'] = ''

        query = form['QUERY_STRING']

        # we only handle x-www-form-urlencoded values from POSTs
        form_urlencoded = parse_qsl(data)
        query_data = parse_qs(query)

        for k, v in query_data.items():
            # ignore dupes
            form[k] = v[0]

        for k, v in form_urlencoded:
            # ignore dupes
            form[k] = v

        form['SERVER_URL'] = request.get_server_url()

        path = form['PATH_INFO']
        # strip off all leading slashes
        while path and path[0] == '/':
            path = path[1:]
        if not path:
            path = 'index.html'

        viewinfo = VIEWS.get(path)
        if viewinfo is None:
            # this should never happen if our match method works
            return

        response = {'headers': {}}

        viewclass = viewinfo['view']
        viewtemplate = viewinfo['template']
        context = ViewContext(template=viewtemplate,
                              request = request,
                              form = form,
                              response = response,
                              supervisord=self.supervisord)
        view = viewclass(context)
        pushproducer = request.channel.push_with_producer
        pushproducer(DeferredWebProducer(request, view))


########NEW FILE########
__FILENAME__ = xmlrpc
import types
import re
import traceback
import sys

from supervisor.compat import xmlrpclib
from supervisor.compat import func_attribute
from supervisor.compat import StringIO
from supervisor.compat import urllib
from supervisor.compat import as_bytes
from supervisor.compat import as_string
from supervisor.compat import encodestring
from supervisor.compat import decodestring
from supervisor.compat import httplib
from supervisor.compat import iterparse

import supervisor.medusa.text_socket as socket
from supervisor.medusa.http_server import get_header
from supervisor.medusa.xmlrpc_handler import xmlrpc_handler
from supervisor.medusa import producers

from supervisor.http import NOT_DONE_YET

class Faults:
    UNKNOWN_METHOD = 1
    INCORRECT_PARAMETERS = 2
    BAD_ARGUMENTS = 3
    SIGNATURE_UNSUPPORTED = 4
    SHUTDOWN_STATE = 6
    BAD_NAME = 10
    NO_FILE = 20
    NOT_EXECUTABLE = 21
    FAILED = 30
    ABNORMAL_TERMINATION = 40
    SPAWN_ERROR = 50
    ALREADY_STARTED = 60
    NOT_RUNNING = 70
    SUCCESS = 80
    ALREADY_ADDED = 90
    STILL_RUNNING = 91
    CANT_REREAD = 92

def getFaultDescription(code):
    for faultname in Faults.__dict__:
        if getattr(Faults, faultname) == code:
            return faultname
    return 'UNKNOWN'

class RPCError(Exception):
    def __init__(self, code, extra=None):
        self.code = code
        self.text = getFaultDescription(code)
        if extra is not None:
            self.text = '%s: %s' % (self.text, extra)

class DeferredXMLRPCResponse:
    """ A medusa producer that implements a deferred callback; requires
    a subclass of asynchat.async_chat that handles NOT_DONE_YET sentinel """
    CONNECTION = re.compile ('Connection: (.*)', re.IGNORECASE)
    traceback = traceback # for testing override

    def __init__(self, request, callback):
        self.callback = callback
        self.request = request
        self.finished = False
        self.delay = float(callback.delay)

    def more(self):
        if self.finished:
            return ''
        try:
            try:
                value = self.callback()
                if value is NOT_DONE_YET:
                    return NOT_DONE_YET
            except RPCError as err:
                value = xmlrpclib.Fault(err.code, err.text)

            body = xmlrpc_marshal(value)

            self.finished = True

            return self.getresponse(body)

        except:
            # report unexpected exception back to server
            self.traceback.print_exc()
            self.finished = True
            self.request.error(500)

    def getresponse(self, body):
        self.request['Content-Type'] = 'text/xml'
        self.request['Content-Length'] = len(body)
        self.request.push(body)
        connection = get_header(self.CONNECTION, self.request.header)

        close_it = 0
        wrap_in_chunking = 0

        if self.request.version == '1.0':
            if connection == 'keep-alive':
                if 'Content-Length' not in self.request:
                    close_it = 1
                else:
                    self.request['Connection'] = 'Keep-Alive'
            else:
                close_it = 1
        elif self.request.version == '1.1':
            if connection == 'close':
                close_it = 1
            elif 'Content-Length' not in self.request:
                if 'Transfer-Encoding' in self.request:
                    if not self.request['Transfer-Encoding'] == 'chunked':
                        close_it = 1
                elif self.request.use_chunked:
                    self.request['Transfer-Encoding'] = 'chunked'
                    wrap_in_chunking = 1
                else:
                    close_it = 1
        elif self.request.version is None:
            close_it = 1

        outgoing_header = producers.simple_producer (
            self.request.build_reply_header())

        if close_it:
            self.request['Connection'] = 'close'

        if wrap_in_chunking:
            outgoing_producer = producers.chunked_producer (
                    producers.composite_producer (self.request.outgoing)
                    )
            # prepend the header
            outgoing_producer = producers.composite_producer(
                [outgoing_header, outgoing_producer]
                )
        else:
            # prepend the header
            self.request.outgoing.insert(0, outgoing_header)
            outgoing_producer = producers.composite_producer (
                self.request.outgoing)

        # apply a few final transformations to the output
        self.request.channel.push_with_producer (
                # globbing gives us large packets
                producers.globbing_producer (
                        # hooking lets us log the number of bytes sent
                        producers.hooked_producer (
                                outgoing_producer,
                                self.request.log
                                )
                        )
                )

        self.request.channel.current_request = None

        if close_it:
            self.request.channel.close_when_done()

def xmlrpc_marshal(value):
    ismethodresponse = not isinstance(value, xmlrpclib.Fault)
    if ismethodresponse:
        if not isinstance(value, tuple):
            value = (value,)
        body = xmlrpclib.dumps(value,  methodresponse=ismethodresponse)
    else:
        body = xmlrpclib.dumps(value)
    return body

class SystemNamespaceRPCInterface:
    def __init__(self, namespaces):
        self.namespaces = {}
        for name, inst in namespaces:
            self.namespaces[name] = inst
        self.namespaces['system'] = self

    def _listMethods(self):
        methods = {}
        for ns_name in self.namespaces:
            namespace = self.namespaces[ns_name]
            for method_name in namespace.__class__.__dict__:
                # introspect; any methods that don't start with underscore
                # are published
                func = getattr(namespace, method_name)
                meth = getattr(func, func_attribute, None)
                if meth is not None:
                    if not method_name.startswith('_'):
                        sig = '%s.%s' % (ns_name, method_name)
                        methods[sig] = str(func.__doc__)
        return methods

    def listMethods(self):
        """ Return an array listing the available method names

        @return array result  An array of method names available (strings).
        """
        methods = self._listMethods()
        keys = list(methods.keys())
        keys.sort()
        return keys

    def methodHelp(self, name):
        """ Return a string showing the method's documentation

        @param string name   The name of the method.
        @return string result The documentation for the method name.
        """
        methods = self._listMethods()
        for methodname in methods.keys():
            if methodname == name:
                return methods[methodname]
        raise RPCError(Faults.SIGNATURE_UNSUPPORTED)

    def methodSignature(self, name):
        """ Return an array describing the method signature in the
        form [rtype, ptype, ptype...] where rtype is the return data type
        of the method, and ptypes are the parameter data types that the
        method accepts in method argument order.

        @param string name  The name of the method.
        @return array result  The result.
        """
        methods = self._listMethods()
        for method in methods:
            if method == name:
                rtype = None
                ptypes = []
                parsed = gettags(methods[method])
                for thing in parsed:
                    if thing[1] == 'return': # tag name
                        rtype = thing[2] # datatype
                    elif thing[1] == 'param': # tag name
                        ptypes.append(thing[2]) # datatype
                if rtype is None:
                    raise RPCError(Faults.SIGNATURE_UNSUPPORTED)
                return [rtype] + ptypes
        raise RPCError(Faults.SIGNATURE_UNSUPPORTED)

    def multicall(self, calls):
        """Process an array of calls, and return an array of
        results. Calls should be structs of the form {'methodName':
        string, 'params': array}. Each result will either be a
        single-item array containing the result value, or a struct of
        the form {'faultCode': int, 'faultString': string}. This is
        useful when you need to make lots of small calls without lots
        of round trips.

        @param array calls  An array of call requests
        @return array result  An array of results
        """
        producers = []

        for call in calls:
            try:
                name = call['methodName']
                params = call.get('params', [])
                if name == 'system.multicall':
                    # Recursive system.multicall forbidden
                    raise RPCError(Faults.INCORRECT_PARAMETERS)
                root = AttrDict(self.namespaces)
                value = traverse(root, name, params)
            except RPCError as inst:
                value = {'faultCode': inst.code,
                         'faultString': inst.text}
            except:
                errmsg = "%s:%s" % (sys.exc_info()[0], sys.exc_info()[1])
                value = {'faultCode': 1, 'faultString': errmsg}
            producers.append(value)

        results = []

        def multiproduce():
            """ Run through all the producers in order """
            if not producers:
                return []

            callback = producers.pop(0)

            if isinstance(callback, types.FunctionType):
                try:
                    value = callback()
                except RPCError as inst:
                    value = {'faultCode':inst.code, 'faultString':inst.text}

                if value is NOT_DONE_YET:
                    # push it back in the front of the queue because we
                    # need to finish the calls in requested order
                    producers.insert(0, callback)
                    return NOT_DONE_YET
            else:
                value = callback

            results.append(value)

            if producers:
                # only finish when all producers are finished
                return NOT_DONE_YET

            return results

        multiproduce.delay = .05
        return multiproduce

class AttrDict(dict):
    # hack to make a dict's getattr equivalent to its getitem
    def __getattr__(self, name):
        return self[name]

class RootRPCInterface:
    def __init__(self, subinterfaces):
        for name, rpcinterface in subinterfaces:
            setattr(self, name, rpcinterface)

class supervisor_xmlrpc_handler(xmlrpc_handler):
    path = '/RPC2'
    IDENT = 'Supervisor XML-RPC Handler'
    def __init__(self, supervisord, subinterfaces):
        self.rpcinterface = RootRPCInterface(subinterfaces)
        self.supervisord = supervisord
        if loads:
            self.loads = loads
        else:
            self.supervisord.options.logger.warn(
                'cElementTree not installed, using slower XML parser for '
                'XML-RPC'
                )
            self.loads = xmlrpclib.loads

    def match(self, request):
        return request.uri.startswith(self.path)

    def continue_request (self, data, request):
        logger = self.supervisord.options.logger

        try:

            params, method = self.loads(data)

            # no <methodName> in the request or name is an empty string
            if not method:
                logger.trace('XML-RPC request received with no method name')
                request.error(400)
                return

            # we allow xml-rpc clients that do not send empty <params>
            # when there are no parameters for the method call
            if params is None:
                params = ()

            try:
                logger.trace('XML-RPC method called: %s()' % method)
                value = self.call(method, params)
                # application-specific: instead of we never want to
                # marshal None (even though we could by saying allow_none=True
                # in dumps within xmlrpc_marshall), this is meant as
                # a debugging fixture, see issue 223.
                assert value is not None, (
                    'return value from method %r with params %r is None' %
                    (method, params)
                    )
                logger.trace('XML-RPC method %s() returned successfully' %
                             method)
            except RPCError as err:
                # turn RPCError reported by method into a Fault instance
                value = xmlrpclib.Fault(err.code, err.text)
                logger.trace('XML-RPC method %s() returned fault: [%d] %s' % (
                    method,
                    err.code, err.text))

            if isinstance(value, types.FunctionType):
                # returning a function from an RPC method implies that
                # this needs to be a deferred response (it needs to block).
                pushproducer = request.channel.push_with_producer
                pushproducer(DeferredXMLRPCResponse(request, value))

            else:
                # if we get anything but a function, it implies that this
                # response doesn't need to be deferred, we can service it
                # right away.
                body = xmlrpc_marshal(value)
                request['Content-Type'] = 'text/xml'
                request['Content-Length'] = len(body)
                request.push(body)
                request.done()

        except:
            io = StringIO()
            traceback.print_exc(file=io)
            val = io.getvalue()
            logger.critical(val)
            # internal error, report as HTTP server error
            request.error(500)

    def call(self, method, params):
        return traverse(self.rpcinterface, method, params)

def traverse(ob, method, params):
    path = method.split('.')
    for name in path:
        if name.startswith('_'):
            # security (don't allow things that start with an underscore to
            # be called remotely)
            raise RPCError(Faults.UNKNOWN_METHOD)
        ob = getattr(ob, name, None)
        if ob is None:
            raise RPCError(Faults.UNKNOWN_METHOD)

    try:
        return ob(*params)
    except TypeError:
        raise RPCError(Faults.INCORRECT_PARAMETERS)

class SupervisorTransport(xmlrpclib.Transport):
    """
    Provides a Transport for xmlrpclib that uses
    httplib.HTTPConnection in order to support persistent
    connections.  Also support basic auth and UNIX domain socket
    servers.
    """
    connection = None

    _use_datetime = 0 # python 2.5 fwd compatibility
    def __init__(self, username=None, password=None, serverurl=None):
        xmlrpclib.Transport.__init__(self)
        self.username = username
        self.password = password
        self.verbose = False
        self.serverurl = serverurl
        if serverurl.startswith('http://'):
            type, uri = urllib.splittype(serverurl)
            host, path = urllib.splithost(uri)
            host, port = urllib.splitport(host)
            if port is None:
                port = 80
            else:
                port = int(port)
            def get_connection(host=host, port=port):
                return httplib.HTTPConnection(host, port)
            self._get_connection = get_connection
        elif serverurl.startswith('unix://'):
            def get_connection(serverurl=serverurl):
                # we use 'localhost' here because domain names must be
                # < 64 chars (or we'd use the serverurl filename)
                conn = UnixStreamHTTPConnection('localhost')
                conn.socketfile = serverurl[7:]
                return conn
            self._get_connection = get_connection
        else:
            raise ValueError('Unknown protocol for serverurl %s' % serverurl)

    def request(self, host, handler, request_body, verbose=0):
        if not self.connection:
            self.connection = self._get_connection()
            self.headers = {
                "User-Agent" : self.user_agent,
                "Content-Type" : "text/xml",
                "Accept": "text/xml"
                }

            # basic auth
            if self.username is not None and self.password is not None:
                unencoded = "%s:%s" % (self.username, self.password)
                encoded = as_string(encodestring(as_bytes(unencoded)))
                encoded = encoded.replace('\n', '')
                encoded = encoded.replace('\012', '')
                self.headers["Authorization"] = "Basic %s" % encoded

        self.headers["Content-Length"] = str(len(request_body))

        self.connection.request('POST', handler, request_body, self.headers)

        r = self.connection.getresponse()

        if r.status != 200:
            self.connection.close()
            self.connection = None
            raise xmlrpclib.ProtocolError(host + handler,
                                          r.status,
                                          r.reason,
                                          '' )
        data = r.read()
        p, u = self.getparser()
        p.feed(data)
        p.close()
        return u.close()

class UnixStreamHTTPConnection(httplib.HTTPConnection):
    def connect(self):
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        # we abuse the host parameter as the socketname
        self.sock.connect(self.socketfile)

def gettags(comment):
    """ Parse documentation strings into JavaDoc-like tokens """

    tags = []

    tag = None
    datatype = None
    name = None
    tag_lineno = lineno = 0
    tag_text = []

    for line in comment.split('\n'):
        line = line.strip()
        if line.startswith("@"):
            tags.append((tag_lineno, tag, datatype, name, '\n'.join(tag_text)))
            parts = line.split(None, 3)
            if len(parts) == 1:
                datatype = ''
                name = ''
                tag_text = []
            elif len(parts) == 2:
                datatype = parts[1]
                name = ''
                tag_text = []
            elif len(parts) == 3:
                datatype = parts[1]
                name = parts[2]
                tag_text = []
            elif len(parts) == 4:
                datatype = parts[1]
                name = parts[2]
                tag_text = [parts[3].lstrip()]
            tag = parts[0][1:]
            tag_lineno = lineno
        else:
            if line:
                tag_text.append(line)
        lineno += 1

    tags.append((tag_lineno, tag, datatype, name, '\n'.join(tag_text)))

    return tags


if iterparse is not None:
    import datetime, time

    def make_datetime(text):
        return datetime.datetime(
            *time.strptime(text, "%Y%m%dT%H:%M:%S")[:6]
        )

    unmarshallers = {
        "int": lambda x: int(x.text),
        "i4": lambda x: int(x.text),
        "boolean": lambda x: x.text == "1",
        "string": lambda x: x.text or "",
        "double": lambda x: float(x.text),
        "dateTime.iso8601": lambda x: make_datetime(x.text),
        "array": lambda x: x[0].text,
        "data": lambda x: [v.text for v in x],
        "struct": lambda x: dict([(k.text or "", v.text) for k, v in x]),
        "base64": lambda x: as_string(decodestring(as_bytes(x.text or ""))),
        "value": lambda x: x[0].text,
        "param": lambda x: x[0].text,
    }

    def loads(data):
        params = method = None
        for action, elem in iterparse(StringIO(data)):
            unmarshall = unmarshallers.get(elem.tag)
            if unmarshall:
                data = unmarshall(elem)
                elem.clear()
                elem.text = data
            elif elem.tag == "methodName":
                method = elem.text
            elif elem.tag == "params":
                params = tuple([v.text for v in elem])
        return params, method
else:
    loads = None

########NEW FILE########

3cd05f91966d96535d4adf3b6a87f101f1d764c8
__FILENAME__ = conf
# -*- coding: utf-8 -*-
#
# Supervisor documentation build configuration file
#
# This file is execfile()d with the current directory set to its containing
# dir.
#
# The contents of this file are pickled, so don't put values in the
# namespace that aren't pickleable (module imports are okay, they're
# removed automatically).
#
# All configuration values have a default value; values that are commented
# out serve to show the default value.

import sys, os
from datetime import date

# If your extensions are in another directory, add it here. If the
# directory is relative to the documentation root, use os.path.abspath to
# make it absolute, like shown here.
#sys.path.append(os.path.abspath('some/directory'))

parent = os.path.dirname(os.path.dirname(__file__))
sys.path.append(os.path.abspath(parent))
wd = os.getcwd()
os.chdir(parent)
os.system('%s setup.py test -q' % sys.executable)
os.chdir(wd)

for item in os.listdir(parent):
    if item.endswith('.egg'):
        sys.path.append(os.path.join(parent, item))

version_txt = os.path.join(parent, 'supervisor/version.txt')
supervisor_version = open(version_txt).read().strip()

# General configuration
# ---------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General substitutions.
project = 'Supervisor'
year = date.today().year
copyright = '2004-%d, Agendaless Consulting and Contributors' % year

# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
#
# The short X.Y version.
version = supervisor_version
# The full version, including alpha/beta/rc tags.
release = version

# There are two options for replacing |today|: either, you set today to
# some non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# List of directories, relative to source directories, that shouldn't be
# searched for source files.
#exclude_dirs = []

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'


# Options for HTML output
# -----------------------

# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
html_style = 'repoze.css'

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as
# html_title.
#html_short_title = None

# The name of an image file (within the static path) to place at the top of
# the sidebar.
html_logo = '.static/logo_hi.gif'

# The name of an image file (within the static path) to use as favicon of
# the docs.  This file should be a Windows icon file (.ico) being 16x16 or
# 32x32 pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets)
# here, relative to this directory. They are copied after the builtin
# static files, so a file named "default.css" will overwrite the builtin
# "default.css".
html_static_path = ['.static']

# If not '', a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_use_modindex = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, the reST sources are included in the HTML build as
# _sources/<name>.
#html_copy_source = True

# If true, an OpenSearch description file will be output, and all pages
# will contain a <link> tag referring to it.  The value of this option must
# be the base URL from which the finished HTML is served.
#html_use_opensearch = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'supervisor'


# Options for LaTeX output
# ------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, document class [howto/manual]).
latex_documents = [
  ('index', 'supervisor.tex', 'supervisor Documentation',
   'Supervisor Developers', 'manual'),
]

# The name of an image file (relative to this directory) to place at the
# top of the title page.
latex_logo = '.static/logo_hi.gif'

# For "manual" documents, if this is true, then toplevel headings are
# parts, not chapters.
#latex_use_parts = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_use_modindex = True

########NEW FILE########
__FILENAME__ = childutils
import sys
import time

from supervisor.compat import xmlrpclib
from supervisor.compat import long

from supervisor.xmlrpc import SupervisorTransport
from supervisor.events import ProcessCommunicationEvent
from supervisor.dispatchers import PEventListenerDispatcher

def getRPCTransport(env):
    u = env.get('SUPERVISOR_USERNAME', '')
    p = env.get('SUPERVISOR_PASSWORD', '')
    return SupervisorTransport(u, p, env['SUPERVISOR_SERVER_URL'])

def getRPCInterface(env):
    # dumbass ServerProxy won't allow us to pass in a non-HTTP url,
    # so we fake the url we pass into it and always use the transport's
    # 'serverurl' to figure out what to attach to
    return xmlrpclib.ServerProxy('http://127.0.0.1', getRPCTransport(env))

def get_headers(line):
    return dict([ x.split(':') for x in line.split() ])

def eventdata(payload):
    headerinfo, data = payload.split('\n', 1)
    headers = get_headers(headerinfo)
    return headers, data

def get_asctime(now=None):
    if now is None: # for testing
        now = time.time() # pragma: no cover
    msecs = (now - long(now)) * 1000
    part1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now))
    asctime = '%s,%03d' % (part1, msecs)
    return asctime

class ProcessCommunicationsProtocol:
    def send(self, msg, fp=sys.stdout):
        fp.write(ProcessCommunicationEvent.BEGIN_TOKEN)
        fp.write(msg)
        fp.write(ProcessCommunicationEvent.END_TOKEN)

    def stdout(self, msg):
        return self.send(msg, sys.stdout)

    def stderr(self, msg):
        return self.send(msg, sys.stderr)

pcomm = ProcessCommunicationsProtocol()

class EventListenerProtocol:
    def wait(self, stdin=sys.stdin, stdout=sys.stdout):
        self.ready(stdout)
        line = stdin.readline()
        headers = get_headers(line)
        payload = stdin.read(int(headers['len']))
        return headers, payload

    def ready(self, stdout=sys.stdout):
        stdout.write(PEventListenerDispatcher.READY_FOR_EVENTS_TOKEN)
        stdout.flush()

    def ok(self, stdout=sys.stdout):
        self.send('OK', stdout)

    def fail(self, stdout=sys.stdout):
        self.send('FAIL', stdout)

    def send(self, data, stdout=sys.stdout):
        resultlen = len(data)
        result = '%s%s\n%s' % (PEventListenerDispatcher.RESULT_TOKEN_START,
                               str(resultlen),
                               data)
        stdout.write(result)
        stdout.flush()

listener = EventListenerProtocol()

########NEW FILE########
__FILENAME__ = compat
from __future__ import absolute_import

import sys
PY3 = sys.version>'3'

if PY3: # pragma: no cover
    long = int
    basestring = str
    unichr = chr
    raw_input = input
    class unicode(str):
        def __init__(self, string, encoding, errors):
            str.__init__(self, string)
    def as_bytes(s): return s if isinstance(s,bytes) else s.encode('utf8')
    def as_string(s): return s if isinstance(s,str) else s.decode('utf8')
    from functools import reduce

else: # pragma: no cover
    long = long
    raw_input = raw_input
    unicode = unicode
    basestring = basestring
    def as_bytes(s): return s if isinstance(s, str) else s.encode('utf-8')
    def as_string(s): return s if isinstance(s, unicode) else s.decode('utf-8')
    reduce = reduce

def print_function(*args,**kwargs): # pragma: no cover
    kwargs.get('file', sys.stdout).write(' '.join(i for i in args)+kwargs.get('end','\n'))

def total_ordering(cls): # pragma: no cover
    """Class decorator that fills in missing ordering methods"""
    convert = {
        '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
                   ('__le__', lambda self, other: self < other or self == other),
                   ('__ge__', lambda self, other: not self < other)],
        '__le__': [('__ge__', lambda self, other: not self <= other or self == other),
                   ('__lt__', lambda self, other: self <= other and not self == other),
                   ('__gt__', lambda self, other: not self <= other)],
        '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
                   ('__ge__', lambda self, other: self > other or self == other),
                   ('__le__', lambda self, other: not self > other)],
        '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
                   ('__gt__', lambda self, other: self >= other and not self == other),
                   ('__lt__', lambda self, other: not self >= other)]
    }
    roots = set(dir(cls)) & set(convert)
    if not roots:
        raise ValueError('must define at least one ordering operation: < > <= >=')
    root = max(roots)       # prefer __lt__ to __le__ to __gt__ to __ge__
    for opname, opfunc in convert[root]:
        if opname not in roots:
            opfunc.__name__ = opname
            try:
                op = getattr(int, opname)
            except AttributeError: # py25 int has no __gt__
                pass
            else:
                opfunc.__doc__ = op.__doc__
            setattr(cls, opname, opfunc)
    return cls

try: # pragma: no cover
    import xmlrpc.client as xmlrpclib
except ImportError: # pragma: no cover
    import xmlrpclib

try: # pragma: no cover
    import urllib.parse as urlparse
    import urllib.parse as urllib
except ImportError: # pragma: no cover
    import urlparse
    import urllib

if PY3: # pragma: no cover
    from base64 import encodebytes as encodestring
else: # pragma: no cover
    from base64 import encodestring

try: # pragma: no cover
    from hashlib import sha1
except ImportError: # pragma: no cover
    from sha import new as sha1

try: # pragma: no cover
    import syslog
except ImportError: # pragma: no cover
    syslog = None

try: # pragma: no cover
    import configparser as ConfigParser
except ImportError: # pragma: no cover
   import ConfigParser

try: # pragma: no cover
    from StringIO import StringIO
except ImportError: # pragma: no cover
    from io import StringIO

try: # pragma: no cover
    from sys import maxint
except ImportError: # pragma: no cover
    from sys import maxsize as maxint

try: # pragma: no cover
    from urllib.parse import parse_qs, parse_qsl
except ImportError: # pragma: no cover
    from cgi import parse_qs, parse_qsl

try: # pragma: no cover
    import http.client as httplib
except ImportError: # pragma: no cover
    import httplib

try: # pragma: no cover
    from base64 import decodebytes as decodestring, encodebytes as encodestring
except ImportError: # pragma: no cover
    from base64 import decodestring, encodestring


if PY3: # pragma: no cover
    func_attribute = '__func__'
else: # pragma: no cover
    func_attribute = 'im_func'

try: # pragma: no cover
    # Python 2.6 contains a version of cElementTree inside it.
    from xml.etree.ElementTree import iterparse
except ImportError: # pragma: no cover
    try:
        # Failing that, try cElementTree instead.
        from cElementTree import iterparse
    except ImportError:
        iterparse = None

try: # pragma: no cover
    from xmlrpc.client import Fault
except ImportError: # pragma: no cover
    from xmlrpclib import Fault

try: # pragma: no cover
    from string import ascii_letters as letters
except ImportError: # pragma: no cover
    from string import letters

try: # pragma: no cover
    from hashlib import md5
except ImportError: # pragma: no cover
    from md5 import md5

try: # pragma: no cover
    import thread
except ImportError: # pragma: no cover
    import _thread as thread

########NEW FILE########
__FILENAME__ = confecho
import pkg_resources
import sys
from supervisor.compat import as_string

def main(out=sys.stdout):
    config = pkg_resources.resource_string(__name__, 'skel/sample.conf')
    out.write(as_string(config))

########NEW FILE########
__FILENAME__ = datatypes
import grp
import os
import pwd
import signal
import sys
import supervisor.medusa.text_socket as socket
import shlex

from supervisor.compat import urlparse
from supervisor.compat import long
from supervisor.loggers import getLevelNumByDescription

# I dont know why we bother, this doesn't run on Windows, but just
# in case it ever does, avoid this bug magnet by leaving it.
if sys.platform[:3] == "win": # pragma: no cover
    DEFAULT_HOST = "localhost"
else:
    DEFAULT_HOST = ""

here = None

def set_here(v):
    global here
    here = v

def process_or_group_name(name):
    """Ensures that a process or group name is not created with
       characters that break the eventlistener protocol"""
    s = str(name).strip()
    if ' ' in s or ':' in s:
        raise ValueError("Invalid name: " + repr(name))
    return s

def integer(value):
    try:
        return int(value)
    except (ValueError, OverflowError):
        return long(value) # why does this help ValueError? (CM)

TRUTHY_STRINGS = ('yes', 'true', 'on', '1')
FALSY_STRINGS  = ('no', 'false', 'off', '0')

def boolean(s):
    """Convert a string value to a boolean value."""
    ss = str(s).lower()
    if ss in TRUTHY_STRINGS:
        return True
    elif ss in FALSY_STRINGS:
        return False
    else:
        raise ValueError("not a valid boolean value: " + repr(s))

def list_of_strings(arg):
    if not arg:
        return []
    try:
        return [x.strip() for x in arg.split(',')]
    except:
        raise ValueError("not a valid list of strings: " + repr(arg))

def list_of_ints(arg):
    if not arg:
        return []
    else:
        try:
            return list(map(int, arg.split(",")))
        except:
            raise ValueError("not a valid list of ints: " + repr(arg))

def list_of_exitcodes(arg):
    try:
        vals = list_of_ints(arg)
        for val in vals:
            if (val > 255) or (val < 0):
                raise ValueError('Invalid exit code "%s"' % val)
        return vals
    except:
        raise ValueError("not a valid list of exit codes: " + repr(arg))

def dict_of_key_value_pairs(arg):
    """ parse KEY=val,KEY2=val2 into {'KEY':'val', 'KEY2':'val2'}
        Quotes can be used to allow commas in the value
    """
    lexer = shlex.shlex(str(arg), posix=True)
    lexer.wordchars += '/.+-():'

    tokens = list(lexer)
    tokens_len = len(tokens)

    D = {}
    i = 0
    while i < tokens_len:
        k_eq_v = tokens[i:i+3]
        if len(k_eq_v) != 3 or k_eq_v[1] != '=':
            raise ValueError("Unexpected end of key/value pairs")
        D[k_eq_v[0]] = k_eq_v[2]
        i += 4
    return D

class Automatic:
    pass

LOGFILE_NONES = ('none', 'off', None)
LOGFILE_AUTOS = (Automatic, 'auto')

def logfile_name(val):
    if hasattr(val, 'lower'):
        coerced = val.lower()
    else:
        coerced = val

    if coerced in LOGFILE_NONES:
        return None
    elif coerced in LOGFILE_AUTOS:
        return Automatic
    else:
        return existing_dirpath(val)

class RangeCheckedConversion:
    """Conversion helper that range checks another conversion."""

    def __init__(self, conversion, rmin=None, rmax=None):
        self._min = rmin
        self._max = rmax
        self._conversion = conversion

    def __call__(self, value):
        v = self._conversion(value)
        if self._min is not None and v < self._min:
            raise ValueError("%s is below lower bound (%s)"
                             % (repr(v), repr(self._min)))
        if self._max is not None and v > self._max:
            raise ValueError("%s is above upper bound (%s)"
                             % (repr(v), repr(self._max)))
        return v

port_number = RangeCheckedConversion(integer, rmin=1, rmax=0xffff).__call__

def inet_address(s):
    # returns (host, port) tuple
    host = ''
    if ":" in s:
        host, s = s.split(":", 1)
        if not s:
            raise ValueError("no port number specified in %r" % s)
        port = port_number(s)
        host = host.lower()
    else:
        try:
            port = port_number(s)
        except ValueError:
            raise ValueError("not a valid port number: %r " %s)
    if not host or host == '*':
        host = DEFAULT_HOST
    return host, port

class SocketAddress:
    def __init__(self, s):
        # returns (family, address) tuple
        if "/" in s or s.find(os.sep) >= 0 or ":" not in s:
            self.family = getattr(socket, "AF_UNIX", None)
            self.address = s
        else:
            self.family = socket.AF_INET
            self.address = inet_address(s)

class SocketConfig:
    """ Abstract base class which provides a uniform abstraction
    for TCP vs Unix sockets """
    url = '' # socket url
    addr = None #socket addr

    def __repr__(self):
        return '<%s at %s for %s>' % (self.__class__,
                                      id(self),
                                      self.url)

    def __str__(self):
        return str(self.url)

    def __eq__(self, other):
        if not isinstance(other, SocketConfig):
            return False

        if self.url != other.url:
            return False

        return True

    def __ne__(self, other):
        return not self.__eq__(other)

    def addr(self): # pragma: no cover
        raise NotImplementedError

    def create_and_bind(self): # pragma: no cover
        raise NotImplementedError

class InetStreamSocketConfig(SocketConfig):
    """ TCP socket config helper """

    host = None # host name or ip to bind to
    port = None # integer port to bind to

    def __init__(self, host, port):
        self.host = host.lower()
        self.port = port_number(port)
        self.url = 'tcp://%s:%d' % (self.host, self.port)

    def addr(self):
        return self.host, self.port

    def create_and_bind(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.bind(self.addr())
        except:
            sock.close()
            raise
        return sock

class UnixStreamSocketConfig(SocketConfig):
    """ Unix domain socket config helper """

    path = None # Unix domain socket path
    mode = None # Unix permission mode bits for socket
    owner = None # Tuple (uid, gid) for Unix ownership of socket
    sock = None # socket object

    def __init__(self, path, **kwargs):
        self.path = path
        self.url = 'unix://%s' % path
        self.mode = kwargs.get('mode', None)
        self.owner = kwargs.get('owner', None)

    def addr(self):
        return self.path

    def create_and_bind(self):
        if os.path.exists(self.path):
            os.unlink(self.path)
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            sock.bind(self.addr())
            self._chown()
            self._chmod()
        except:
            sock.close()
            if os.path.exists(self.path):
                os.unlink(self.path)
            raise
        return sock

    def get_mode(self):
        return self.mode

    def get_owner(self):
        return self.owner

    def _chmod(self):
        if self.mode is not None:
            try:
                os.chmod(self.path, self.mode)
            except Exception as e:
                raise ValueError("Could not change permissions of socket "
                                    + "file: %s" % e)

    def _chown(self):
        if self.owner is not None:
            try:
                os.chown(self.path, self.owner[0], self.owner[1])
            except Exception as e:
                raise ValueError("Could not change ownership of socket file: "
                                    + "%s" % e)

def colon_separated_user_group(arg):
    """ Find a user ID and group ID from a string like 'user:group'.  Returns
        a tuple (uid, gid).  If the string only contains a user like 'user'
        then (uid, -1) will be returned.  Raises ValueError if either
        the user or group can't be resolved to valid IDs on the system. """
    try:
        parts = arg.split(':', 1)
        if len(parts) == 1:
            uid = name_to_uid(parts[0])
            gid = -1
        else:
            uid = name_to_uid(parts[0])
            gid = name_to_gid(parts[1])
        return (uid, gid)
    except:
        raise ValueError('Invalid user:group definition %s' % arg)

def name_to_uid(name):
    """ Find a user ID from a string containing a user name or ID.
        Raises ValueError if the string can't be resolved to a valid
        user ID on the system. """
    try:
        uid = int(name)
    except ValueError:
        try:
            pwdrec = pwd.getpwnam(name)
        except KeyError:
            raise ValueError("Invalid user name %s" % name)
        uid = pwdrec[2]
    else:
        try:
            pwd.getpwuid(uid) # check if uid is valid
        except KeyError:
            raise ValueError("Invalid user id %s" % name)
    return uid

def name_to_gid(name):
    """ Find a group ID from a string containing a group name or ID.
        Raises ValueError if the string can't be resolved to a valid
        group ID on the system. """
    try:
        gid = int(name)
    except ValueError:
        try:
            grprec = grp.getgrnam(name)
        except KeyError:
            raise ValueError("Invalid group name %s" % name)
        gid = grprec[2]
    else:
        try:
            grp.getgrgid(gid) # check if gid is valid
        except KeyError:
            raise ValueError("Invalid group id %s" % name)
    return gid

def gid_for_uid(uid):
    pwrec = pwd.getpwuid(uid)
    return pwrec[3]

def octal_type(arg):
    try:
        return int(arg, 8)
    except (TypeError, ValueError):
        raise ValueError('%s can not be converted to an octal type' % arg)

def existing_directory(v):
    nv = v % {'here':here}
    nv = os.path.expanduser(nv)
    if os.path.isdir(nv):
        return nv
    raise ValueError('%s is not an existing directory' % v)

def existing_dirpath(v):
    nv = v % {'here':here}
    nv = os.path.expanduser(nv)
    dir = os.path.dirname(nv)
    if not dir:
        # relative pathname with no directory component
        return nv
    if os.path.isdir(dir):
        return nv
    raise ValueError('The directory named as part of the path %s '
                     'does not exist.' % v)

def logging_level(value):
    s = str(value).lower()
    level = getLevelNumByDescription(s)
    if level is None:
        raise ValueError('bad logging level name %r' % value)
    return level

class SuffixMultiplier:
    # d is a dictionary of suffixes to integer multipliers.  If no suffixes
    # match, default is the multiplier.  Matches are case insensitive.  Return
    # values are in the fundamental unit.
    def __init__(self, d, default=1):
        self._d = d
        self._default = default
        # all keys must be the same size
        self._keysz = None
        for k in d.keys():
            if self._keysz is None:
                self._keysz = len(k)
            else:
                assert self._keysz == len(k)

    def __call__(self, v):
        v = v.lower()
        for s, m in self._d.items():
            if v[-self._keysz:] == s:
                return int(v[:-self._keysz]) * m
        return int(v) * self._default

byte_size = SuffixMultiplier({'kb': 1024,
                              'mb': 1024*1024,
                              'gb': 1024*1024*long(1024),})

def url(value):
    # earlier Python 2.6 urlparse (2.6.4 and under) can't parse unix:// URLs,
    # later ones can but we need to straddle
    uri = value.replace('unix://', 'http://', 1).strip()
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
    if scheme and (netloc or path):
        return value
    raise ValueError("value %s is not a URL" % value)

# all valid signal numbers
SIGNUMS = [ getattr(signal, k) for k in dir(signal) if k.startswith('SIG') ]

def signal_number(value):
    try:
        num = int(value)
    except (ValueError, TypeError):
        name = value.strip().upper()
        if not name.startswith('SIG'):
            name = 'SIG' + name
        num = getattr(signal, name, None)
        if num is None:
            raise ValueError('value %s is not a valid signal name' % value)
    if num not in SIGNUMS:
        raise ValueError('value %s is not a valid signal number' % value)
    return num

class RestartWhenExitUnexpected:
    pass

class RestartUnconditionally:
    pass

def auto_restart(value):
    value = str(value.lower())
    computed_value  = value
    if value in TRUTHY_STRINGS:
        computed_value = RestartUnconditionally
    elif value in FALSY_STRINGS:
        computed_value = False
    elif value == 'unexpected':
        computed_value = RestartWhenExitUnexpected
    if computed_value not in (RestartWhenExitUnexpected,
                              RestartUnconditionally, False):
        raise ValueError("invalid 'autorestart' value %r" % value)
    return computed_value

def profile_options(value):
    options = [x.lower() for x in list_of_strings(value) ]
    sort_options = []
    callers = False
    for thing in options:
        if thing != 'callers':
            sort_options.append(thing)
        else:
            callers = True
    return sort_options, callers

########NEW FILE########
__FILENAME__ = dispatchers
import warnings
import errno
from supervisor.medusa.asyncore_25 import compact_traceback

from supervisor.events import notify
from supervisor.events import EventRejectedEvent
from supervisor.events import ProcessLogStderrEvent
from supervisor.events import ProcessLogStdoutEvent
from supervisor.states import EventListenerStates
from supervisor import loggers

def find_prefix_at_end(haystack, needle):
    l = len(needle) - 1
    while l and not haystack.endswith(needle[:l]):
        l -= 1
    return l

class PDispatcher:
    """ Asyncore dispatcher for mainloop, representing a process channel
    (stdin, stdout, or stderr).  This class is abstract. """

    closed = False # True if close() has been called

    def __init__(self, process, channel, fd):
        self.process = process  # process which "owns" this dispatcher
        self.channel = channel  # 'stderr' or 'stdout'
        self.fd = fd
        self.closed = False     # True if close() has been called

    def __repr__(self):
        return '<%s at %s for %s (%s)>' % (self.__class__.__name__,
                                           id(self),
                                           self.process,
                                           self.channel)

    def readable(self):
        raise NotImplementedError

    def writable(self):
        raise NotImplementedError

    def handle_read_event(self):
        raise NotImplementedError

    def handle_write_event(self):
        raise NotImplementedError

    def handle_error(self):
        nil, t, v, tbinfo = compact_traceback()

        self.process.config.options.logger.critical(
            'uncaptured python exception, closing channel %s (%s:%s %s)' % (
                repr(self),
                t,
                v,
                tbinfo
                )
            )
        self.close()

    def close(self):
        if not self.closed:
            self.process.config.options.logger.debug(
                'fd %s closed, stopped monitoring %s' % (self.fd, self))
            self.closed = True

    def flush(self):
        pass

class POutputDispatcher(PDispatcher):
    """
    A Process Output (stdout/stderr) dispatcher. Serves several purposes:

    - capture output sent within <!--XSUPERVISOR:BEGIN--> and
      <!--XSUPERVISOR:END--> tags and signal a ProcessCommunicationEvent
      by calling notify(event).
    - route the output to the appropriate log handlers as specified in the
      config.
    """

    capturemode = False # are we capturing process event data
    mainlog = None #  the process' "normal" logger
    capturelog = None # the logger while we're in capturemode
    childlog = None # the current logger (event or main)
    output_buffer = '' # data waiting to be logged

    def __init__(self, process, event_type, fd):
        """
        Initialize the dispatcher.

        `event_type` should be one of ProcessLogStdoutEvent or
        ProcessLogStderrEvent
        """
        self.process = process
        self.event_type = event_type
        self.fd = fd
        self.channel = channel = self.event_type.channel

        self._setup_logging(process.config, channel)

        capture_maxbytes = getattr(process.config,
                                   '%s_capture_maxbytes' % channel)
        if capture_maxbytes:
            self.capturelog = loggers.handle_boundIO(
                self.process.config.options.getLogger(),
                fmt='%(message)s',
                maxbytes=capture_maxbytes,
                )

        self.childlog = self.mainlog

        # all code below is purely for minor speedups
        begintoken = self.event_type.BEGIN_TOKEN
        endtoken = self.event_type.END_TOKEN
        self.begintoken_data = (begintoken, len(begintoken))
        self.endtoken_data = (endtoken, len(endtoken))
        self.mainlog_level = loggers.LevelsByName.DEBG
        config = self.process.config
        self.log_to_mainlog = config.options.loglevel <= self.mainlog_level
        self.stdout_events_enabled = config.stdout_events_enabled
        self.stderr_events_enabled = config.stderr_events_enabled

    def _setup_logging(self, config, channel):
        """
        Configure the main log according to the process' configuration and
        channel. Sets `mainlog` on self. Returns nothing.
        """

        logfile = getattr(config, '%s_logfile' % channel)
        if not logfile:
            return

        maxbytes = getattr(config, '%s_logfile_maxbytes' % channel)
        backups = getattr(config, '%s_logfile_backups' % channel)
        fmt = '%(message)s'
        if logfile == 'syslog':
            warnings.warn("Specifying 'syslog' for filename is deprecated. "
                "Use %s_syslog instead." % channel, DeprecationWarning)
            fmt = ' '.join((config.name, fmt))
        self.mainlog = loggers.handle_file(
            config.options.getLogger(),
            filename=logfile,
            fmt=fmt,
            rotating=not not maxbytes, # optimization
            maxbytes=maxbytes,
            backups=backups)

        if getattr(config, '%s_syslog' % channel, False):
            fmt = config.name + ' %(message)s'
            loggers.handle_syslog(self.mainlog, fmt)

    def removelogs(self):
        for log in (self.mainlog, self.capturelog):
            if log is not None:
                for handler in log.handlers:
                    handler.remove()
                    handler.reopen()

    def reopenlogs(self):
        for log in (self.mainlog, self.capturelog):
            if log is not None:
                for handler in log.handlers:
                    handler.reopen()

    def _log(self, data):
        if data:
            config = self.process.config
            if config.options.strip_ansi:
                data = stripEscapes(data)
            if self.childlog:
                self.childlog.info(data)
            if self.log_to_mainlog:
                msg = '%(name)r %(channel)s output:\n%(data)s'
                config.options.logger.log(
                    self.mainlog_level, msg, name=config.name,
                    channel=self.channel, data=data)
            if self.channel == 'stdout':
                if self.stdout_events_enabled:
                    notify(
                        ProcessLogStdoutEvent(self.process,
                            self.process.pid, data)
                    )
            else: # channel == stderr
                if self.stderr_events_enabled:
                    notify(
                        ProcessLogStderrEvent(self.process,
                            self.process.pid, data)
                    )

    def record_output(self):
        if self.capturelog is None:
            # shortcut trying to find capture data
            data = self.output_buffer
            self.output_buffer = ''
            self._log(data)
            return

        if self.capturemode:
            token, tokenlen = self.endtoken_data
        else:
            token, tokenlen = self.begintoken_data

        if len(self.output_buffer) <= tokenlen:
            return # not enough data

        data = self.output_buffer
        self.output_buffer = ''

        try:
            before, after = data.split(token, 1)
        except ValueError:
            after = None
            index = find_prefix_at_end(data, token)
            if index:
                self.output_buffer = self.output_buffer + data[-index:]
                data = data[:-index]
            self._log(data)
        else:
            self._log(before)
            self.toggle_capturemode()
            self.output_buffer = after

        if after:
            self.record_output()

    def toggle_capturemode(self):
        self.capturemode = not self.capturemode

        if self.capturelog is not None:
            if self.capturemode:
                self.childlog = self.capturelog
            else:
                for handler in self.capturelog.handlers:
                    handler.flush()
                data = self.capturelog.getvalue()
                channel = self.channel
                procname = self.process.config.name
                event = self.event_type(self.process, self.process.pid, data)
                notify(event)

                msg = "%(procname)r %(channel)s emitted a comm event"
                self.process.config.options.logger.debug(msg,
                                                         procname=procname,
                                                         channel=channel)
                for handler in self.capturelog.handlers:
                    handler.remove()
                    handler.reopen()
                self.childlog = self.mainlog

    def writable(self):
        return False

    def readable(self):
        if self.closed:
            return False
        return True

    def handle_read_event(self):
        data = self.process.config.options.readfd(self.fd)
        self.output_buffer += data
        self.record_output()
        if not data:
            # if we get no data back from the pipe, it means that the
            # child process has ended.  See
            # mail.python.org/pipermail/python-dev/2004-August/046850.html
            self.close()

class PEventListenerDispatcher(PDispatcher):
    """ An output dispatcher that monitors and changes a process'
    listener_state """
    childlog = None # the logger
    state_buffer = ''  # data waiting to be reviewed for state changes

    READY_FOR_EVENTS_TOKEN = 'READY\n'
    RESULT_TOKEN_START = 'RESULT '
    READY_FOR_EVENTS_LEN = len(READY_FOR_EVENTS_TOKEN)
    RESULT_TOKEN_START_LEN = len(RESULT_TOKEN_START)

    def __init__(self, process, channel, fd):
        PDispatcher.__init__(self, process, channel, fd)
        # the initial state of our listener is ACKNOWLEDGED; this is a
        # "busy" state that implies we're awaiting a READY_FOR_EVENTS_TOKEN
        self.process.listener_state = EventListenerStates.ACKNOWLEDGED
        self.process.event = None
        self.result = ''
        self.resultlen = None

        logfile = getattr(process.config, '%s_logfile' % channel)

        if logfile:
            maxbytes = getattr(process.config, '%s_logfile_maxbytes' % channel)
            backups = getattr(process.config, '%s_logfile_backups' % channel)
            self.childlog = loggers.handle_file(
                process.config.options.getLogger(),
                logfile,
                '%(message)s',
                rotating=not not maxbytes, # optimization
                maxbytes=maxbytes,
                backups=backups,
            )

    def removelogs(self):
        if self.childlog is not None:
            for handler in self.childlog.handlers:
                handler.remove()
                handler.reopen()

    def reopenlogs(self):
        if self.childlog is not None:
            for handler in self.childlog.handlers:
                handler.reopen()


    def writable(self):
        return False

    def readable(self):
        if self.closed:
            return False
        return True

    def handle_read_event(self):
        data = self.process.config.options.readfd(self.fd)
        if data:
            self.state_buffer += data
            procname = self.process.config.name
            msg = '%r %s output:\n%s' % (procname, self.channel, data)
            self.process.config.options.logger.debug(msg)

            if self.childlog:
                if self.process.config.options.strip_ansi:
                    data = stripEscapes(data)
                self.childlog.info(data)
        else:
            # if we get no data back from the pipe, it means that the
            # child process has ended.  See
            # mail.python.org/pipermail/python-dev/2004-August/046850.html
            self.close()

        self.handle_listener_state_change()

    def handle_listener_state_change(self):
        data = self.state_buffer

        if not data:
            return

        process = self.process
        procname = process.config.name
        state = process.listener_state

        if state == EventListenerStates.UNKNOWN:
            # this is a fatal state
            self.state_buffer = ''
            return

        if state == EventListenerStates.ACKNOWLEDGED:
            if len(data) < self.READY_FOR_EVENTS_LEN:
                # not enough info to make a decision
                return
            elif data.startswith(self.READY_FOR_EVENTS_TOKEN):
                msg = '%s: ACKNOWLEDGED -> READY' % procname
                process.config.options.logger.debug(msg)
                process.listener_state = EventListenerStates.READY
                tokenlen = self.READY_FOR_EVENTS_LEN
                self.state_buffer = self.state_buffer[tokenlen:]
                process.event = None
            else:
                msg = '%s: ACKNOWLEDGED -> UNKNOWN' % procname
                process.config.options.logger.debug(msg)
                process.listener_state = EventListenerStates.UNKNOWN
                self.state_buffer = ''
                process.event = None
            if self.state_buffer:
                # keep going til its too short
                self.handle_listener_state_change()
            else:
                return

        elif state == EventListenerStates.READY:
            # the process sent some spurious data, be a hardass about it
            msg = '%s: READY -> UNKNOWN' % procname
            process.config.options.logger.debug(msg)
            process.listener_state = EventListenerStates.UNKNOWN
            self.state_buffer = ''
            process.event = None
            return

        elif state == EventListenerStates.BUSY:
            if self.resultlen is None:
                # we haven't begun gathering result data yet
                pos = data.find('\n')
                if pos == -1:
                    # we can't make a determination yet, we dont have a full
                    # results line
                    return

                result_line = self.state_buffer[:pos]
                self.state_buffer = self.state_buffer[pos+1:] # rid LF
                resultlen = result_line[self.RESULT_TOKEN_START_LEN:]
                try:
                    self.resultlen = int(resultlen)
                except ValueError:
                    msg = ('%s: BUSY -> UNKNOWN (bad result line %r)'
                           % (procname, result_line))
                    process.config.options.logger.debug(msg)
                    process.listener_state = EventListenerStates.UNKNOWN
                    self.state_buffer = ''
                    notify(EventRejectedEvent(process, process.event))
                    process.event = None
                    return

            else:
                needed = self.resultlen - len(self.result)

                if needed:
                    self.result += self.state_buffer[:needed]
                    self.state_buffer = self.state_buffer[needed:]
                    needed = self.resultlen - len(self.result)

                if not needed:
                    self.handle_result(self.result)
                    self.process.event = None
                    self.result = ''
                    self.resultlen = None

            if self.state_buffer:
                # keep going til its too short
                self.handle_listener_state_change()

    def handle_result(self, result):
        process = self.process
        procname = process.config.name

        try:
            self.process.group.config.result_handler(process.event, result)
            msg = '%s: BUSY -> ACKNOWLEDGED (processed)' % procname
            process.listener_state = EventListenerStates.ACKNOWLEDGED
        except RejectEvent:
            msg = '%s: BUSY -> ACKNOWLEDGED (rejected)' % procname
            process.listener_state = EventListenerStates.ACKNOWLEDGED
            notify(EventRejectedEvent(process, process.event))
        except:
            msg = '%s: BUSY -> UNKNOWN' % procname
            process.listener_state = EventListenerStates.UNKNOWN
            notify(EventRejectedEvent(process, process.event))

        process.config.options.logger.debug(msg)

class PInputDispatcher(PDispatcher):
    """ Input (stdin) dispatcher """

    def __init__(self, process, channel, fd):
        PDispatcher.__init__(self, process, channel, fd)
        self.input_buffer = ''

    def writable(self):
        if self.input_buffer and not self.closed:
            return True
        return False

    def readable(self):
        return False

    def flush(self):
        # other code depends on this raising EPIPE if the pipe is closed
        sent = self.process.config.options.write(self.fd,
                                                 self.input_buffer)
        self.input_buffer = self.input_buffer[sent:]

    def handle_write_event(self):
        if self.input_buffer:
            try:
                self.flush()
            except OSError as why:
                if why.args[0] == errno.EPIPE:
                    self.input_buffer = ''
                    self.close()
                else:
                    raise

ANSI_ESCAPE_BEGIN = '\x1b['
ANSI_TERMINATORS = ('H', 'f', 'A', 'B', 'C', 'D', 'R', 's', 'u', 'J',
                    'K', 'h', 'l', 'p', 'm')

def stripEscapes(s):
    """
    Remove all ANSI color escapes from the given string.
    """
    result = ''
    show = 1
    i = 0
    L = len(s)
    while i < L:
        if show == 0 and s[i] in ANSI_TERMINATORS:
            show = 1
        elif show:
            n = s.find(ANSI_ESCAPE_BEGIN, i)
            if n == -1:
                return result + s[i:]
            else:
                result = result + s[i:n]
                i = n
                show = 0
        i += 1
    return result

class RejectEvent(Exception):
    """ The exception type expected by a dispatcher when a handler wants
    to reject an event """

def default_handler(event, response):
    if response != 'OK':
        raise RejectEvent(response)

########NEW FILE########
__FILENAME__ = events
from supervisor.states import getProcessStateDescription

callbacks = []

def subscribe(type, callback):
    callbacks.append((type, callback))
    
def notify(event):
    for type, callback in callbacks:
        if isinstance(event, type):
            callback(event)

def clear():
    callbacks[:] = []

class Event:
    """ Abstract event type """
    pass

class ProcessLogEvent(Event):
    """ Abstract """
    channel = None
    def __init__(self, process, pid, data):
        self.process = process
        self.pid = pid
        self.data = data

    def __str__(self):
        groupname = ''
        if self.process.group is not None:
            groupname = self.process.group.config.name
        return 'processname:%s groupname:%s pid:%s channel:%s\n%s' % (
            self.process.config.name,
            groupname,
            self.pid,
            self.channel,
            self.data)

class ProcessLogStdoutEvent(ProcessLogEvent):
    channel = 'stdout'

class ProcessLogStderrEvent(ProcessLogEvent):
    channel = 'stderr'

class ProcessCommunicationEvent(Event):
    """ Abstract """
    # event mode tokens
    BEGIN_TOKEN = '<!--XSUPERVISOR:BEGIN-->'
    END_TOKEN   = '<!--XSUPERVISOR:END-->'

    def __init__(self, process, pid, data):
        self.process = process
        self.pid = pid
        self.data = data

    def __str__(self):
        groupname = ''
        if self.process.group is not None:
            groupname = self.process.group.config.name
        return 'processname:%s groupname:%s pid:%s\n%s' % (
            self.process.config.name,
            groupname,
            self.pid,
            self.data)

class ProcessCommunicationStdoutEvent(ProcessCommunicationEvent):
    channel = 'stdout'

class ProcessCommunicationStderrEvent(ProcessCommunicationEvent):
    channel = 'stderr'

class RemoteCommunicationEvent(Event):
    def __init__(self, type, data):
        self.type = type
        self.data = data

    def __str__(self):
        return 'type:%s\n%s' % (self.type, self.data)

class SupervisorStateChangeEvent(Event):
    """ Abstract class """
    def __str__(self):
        return ''

class SupervisorRunningEvent(SupervisorStateChangeEvent):
    pass

class SupervisorStoppingEvent(SupervisorStateChangeEvent):
    pass

class EventRejectedEvent: # purposely does not subclass Event 
    def __init__(self, process, event):
        self.process = process
        self.event = event

class ProcessStateEvent(Event):
    """ Abstract class, never raised directly """
    frm = None
    to = None
    def __init__(self, process, from_state, expected=True):
        self.process = process
        self.from_state = from_state
        self.expected = expected
        # we eagerly render these so if the process pid, etc changes beneath
        # us, we stash the values at the time the event was sent
        self.extra_values = self.get_extra_values()

    def __str__(self):
        groupname = ''
        if self.process.group is not None:
            groupname = self.process.group.config.name
        L = [('processname', self.process.config.name), ('groupname', groupname),
             ('from_state', getProcessStateDescription(self.from_state))]
        L.extend(self.extra_values)
        s = ' '.join( [ '%s:%s' % (name, val) for (name, val) in L ] )
        return s

    def get_extra_values(self):
        return []

class ProcessStateFatalEvent(ProcessStateEvent):
    pass

class ProcessStateUnknownEvent(ProcessStateEvent):
    pass

class ProcessStateStartingOrBackoffEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('tries', int(self.process.backoff))]

class ProcessStateBackoffEvent(ProcessStateStartingOrBackoffEvent):
    pass

class ProcessStateStartingEvent(ProcessStateStartingOrBackoffEvent):
    pass

class ProcessStateExitedEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('expected', int(self.expected)), ('pid', self.process.pid)]

class ProcessStateRunningEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('pid', self.process.pid)]

class ProcessStateStoppingEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('pid', self.process.pid)]

class ProcessStateStoppedEvent(ProcessStateEvent):
    def get_extra_values(self):
        return [('pid', self.process.pid)]

class ProcessGroupEvent(Event):
    def __init__(self, group):
        self.group = group
    def __str__(self):
        return 'groupname:%s\n' % self.group

class ProcessGroupAddedEvent(ProcessGroupEvent):
    pass

class ProcessGroupRemovedEvent(ProcessGroupEvent):
    pass

class TickEvent(Event):
    """ Abstract """
    def __init__(self, when, supervisord):
        self.when = when
        self.supervisord = supervisord

    def __str__(self):
        return 'when:%s' % self.when

class Tick5Event(TickEvent):
    period = 5

class Tick60Event(TickEvent):
    period = 60

class Tick3600Event(TickEvent):
    period = 3600

TICK_EVENTS = [ Tick5Event, Tick60Event, Tick3600Event ] # imported elsewhere

class EventTypes:
    EVENT = Event # abstract
    PROCESS_STATE = ProcessStateEvent # abstract
    PROCESS_STATE_STOPPED = ProcessStateStoppedEvent
    PROCESS_STATE_EXITED = ProcessStateExitedEvent
    PROCESS_STATE_STARTING = ProcessStateStartingEvent
    PROCESS_STATE_STOPPING = ProcessStateStoppingEvent
    PROCESS_STATE_BACKOFF = ProcessStateBackoffEvent
    PROCESS_STATE_FATAL = ProcessStateFatalEvent
    PROCESS_STATE_RUNNING = ProcessStateRunningEvent
    PROCESS_STATE_UNKNOWN = ProcessStateUnknownEvent
    PROCESS_COMMUNICATION = ProcessCommunicationEvent # abstract
    PROCESS_COMMUNICATION_STDOUT = ProcessCommunicationStdoutEvent
    PROCESS_COMMUNICATION_STDERR = ProcessCommunicationStderrEvent
    PROCESS_LOG = ProcessLogEvent
    PROCESS_LOG_STDOUT = ProcessLogStdoutEvent
    PROCESS_LOG_STDERR = ProcessLogStderrEvent     
    REMOTE_COMMUNICATION = RemoteCommunicationEvent
    SUPERVISOR_STATE_CHANGE = SupervisorStateChangeEvent # abstract
    SUPERVISOR_STATE_CHANGE_RUNNING = SupervisorRunningEvent
    SUPERVISOR_STATE_CHANGE_STOPPING = SupervisorStoppingEvent
    TICK = TickEvent # abstract
    TICK_5 = Tick5Event
    TICK_60 = Tick60Event
    TICK_3600 = Tick3600Event
    PROCESS_GROUP = ProcessGroupEvent # abstract
    PROCESS_GROUP_ADDED = ProcessGroupAddedEvent
    PROCESS_GROUP_REMOVED = ProcessGroupRemovedEvent

def getEventNameByType(requested):
    for name, typ in EventTypes.__dict__.items():
        if typ is requested:
            return name

def register(name, event):
    setattr(EventTypes, name, event)

########NEW FILE########
__FILENAME__ = http
import os
import stat
import time
import sys
import supervisor.medusa.text_socket as socket
import errno
import pwd
import weakref

from supervisor.compat import urllib
from supervisor.compat import sha1
from supervisor.compat import as_bytes
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import http_date
from supervisor.medusa import http_server
from supervisor.medusa import producers
from supervisor.medusa import filesys
from supervisor.medusa import default_handler

from supervisor.medusa.auth_handler import auth_handler

class NOT_DONE_YET:
    pass

class deferring_chunked_producer:
    """A producer that implements the 'chunked' transfer coding for HTTP/1.1.
    Here is a sample usage:
            request['Transfer-Encoding'] = 'chunked'
            request.push (
                    producers.chunked_producer (your_producer)
                    )
            request.done()
    """

    def __init__ (self, producer, footers=None):
        self.producer = producer
        self.footers = footers
        self.delay = 0.1

    def more (self):
        if self.producer:
            data = self.producer.more()
            if data is NOT_DONE_YET:
                return NOT_DONE_YET
            elif data:
                return '%x\r\n%s\r\n' % (len(data), data)
            else:
                self.producer = None
                if self.footers:
                    return '\r\n'.join(['0'] + self.footers) + '\r\n\r\n'
                else:
                    return '0\r\n\r\n'
        else:
            return ''

class deferring_composite_producer:
    """combine a fifo of producers into one"""
    def __init__ (self, producers):
        self.producers = producers
        self.delay = 0.1

    def more (self):
        while len(self.producers):
            p = self.producers[0]
            d = p.more()
            if d is NOT_DONE_YET:
                return NOT_DONE_YET
            if d:
                return d
            else:
                self.producers.pop(0)
        else:
            return ''


class deferring_globbing_producer:
    """
    'glob' the output from a producer into a particular buffer size.
    helps reduce the number of calls to send().  [this appears to
    gain about 30% performance on requests to a single channel]
    """

    def __init__ (self, producer, buffer_size=1<<16):
        self.producer = producer
        self.buffer = ''
        self.buffer_size = buffer_size
        self.delay = 0.1

    def more (self):
        while len(self.buffer) < self.buffer_size:
            data = self.producer.more()
            if data is NOT_DONE_YET:
                return NOT_DONE_YET
            if data:
                self.buffer = self.buffer + data
            else:
                break
        r = self.buffer
        self.buffer = ''
        return r


class deferring_hooked_producer:
    """
    A producer that will call <function> when it empties,.
    with an argument of the number of bytes produced.  Useful
    for logging/instrumentation purposes.
    """

    def __init__ (self, producer, function):
        self.producer = producer
        self.function = function
        self.bytes = 0
        self.delay = 0.1

    def more (self):
        if self.producer:
            result = self.producer.more()
            if result is NOT_DONE_YET:
                return NOT_DONE_YET
            if not result:
                self.producer = None
                self.function (self.bytes)
            else:
                self.bytes += len(result)
            return result
        else:
            return ''


class deferring_http_request(http_server.http_request):
    """ The medusa http_request class uses the default set of producers in
    medusa.producers.  We can't use these because they don't know anything
    about deferred responses, so we override various methods here.  This was
    added to support tail -f like behavior on the logtail handler """

    def done(self, *arg, **kw):

        """ I didn't want to override this, but there's no way around
        it in order to support deferreds - CM

        finalize this transaction - send output to the http channel"""

        # ----------------------------------------
        # persistent connection management
        # ----------------------------------------

        #  --- BUCKLE UP! ----

        connection = http_server.get_header(http_server.CONNECTION,self.header)
        connection = connection.lower()

        close_it = 0
        wrap_in_chunking = 0
        globbing = 1

        if self.version == '1.0':
            if connection == 'keep-alive':
                if not 'Content-Length' in self:
                    close_it = 1
                else:
                    self['Connection'] = 'Keep-Alive'
            else:
                close_it = 1
        elif self.version == '1.1':
            if connection == 'close':
                close_it = 1
            elif not 'Content-Length' in self:
                if 'Transfer-Encoding' in self:
                    if not self['Transfer-Encoding'] == 'chunked':
                        close_it = 1
                elif self.use_chunked:
                    self['Transfer-Encoding'] = 'chunked'
                    wrap_in_chunking = 1
                    # globbing slows down tail -f output, so only use it if
                    # we're not in chunked mode
                    globbing = 0
                else:
                    close_it = 1
        elif self.version is None:
            # Although we don't *really* support http/0.9 (because
            # we'd have to use \r\n as a terminator, and it would just
            # yuck up a lot of stuff) it's very common for developers
            # to not want to type a version number when using telnet
            # to debug a server.
            close_it = 1

        outgoing_header = producers.simple_producer(self.build_reply_header())

        if close_it:
            self['Connection'] = 'close'

        if wrap_in_chunking:
            outgoing_producer = deferring_chunked_producer(
                    deferring_composite_producer(self.outgoing)
                    )
            # prepend the header
            outgoing_producer = deferring_composite_producer(
                [outgoing_header, outgoing_producer]
                )
        else:
            # prepend the header
            self.outgoing.insert(0, outgoing_header)
            outgoing_producer = deferring_composite_producer(self.outgoing)

        # hook logging into the output
        outgoing_producer = deferring_hooked_producer(outgoing_producer,
                                                      self.log)

        if globbing:
            outgoing_producer = deferring_globbing_producer(outgoing_producer)

        self.channel.push_with_producer(outgoing_producer)

        self.channel.current_request = None

        if close_it:
            self.channel.close_when_done()

    def log (self, bytes):
        """ We need to override this because UNIX domain sockets return
        an empty string for the addr rather than a (host, port) combination """
        if self.channel.addr:
            host = self.channel.addr[0]
            port = self.channel.addr[1]
        else:
            host = 'localhost'
            port = 0
        self.channel.server.logger.log (
                host,
                '%d - - [%s] "%s" %d %d\n' % (
                        port,
                        self.log_date_string (time.time()),
                        self.request,
                        self.reply_code,
                        bytes
                        )
                )

    def cgi_environment(self):
        env = {}

        # maps request some headers to environment variables.
        # (those that don't start with 'HTTP_')
        header2env= {'content-length'    : 'CONTENT_LENGTH',
                     'content-type'      : 'CONTENT_TYPE',
                     'connection'        : 'CONNECTION_TYPE'}

        workdir = os.getcwd()
        (path, params, query, fragment) = self.split_uri()

        if params:
            path = path + params # undo medusa bug!

        while path and path[0] == '/':
            path = path[1:]
        if '%' in path:
            path = http_server.unquote(path)
        if query:
            query = query[1:]

        server = self.channel.server
        env['REQUEST_METHOD'] = self.command.upper()
        env['SERVER_PORT'] = str(server.port)
        env['SERVER_NAME'] = server.server_name
        env['SERVER_SOFTWARE'] = server.SERVER_IDENT
        env['SERVER_PROTOCOL'] = "HTTP/" + self.version
        env['channel.creation_time'] = self.channel.creation_time
        env['SCRIPT_NAME'] = ''
        env['PATH_INFO'] = '/' + path
        env['PATH_TRANSLATED'] = os.path.normpath(os.path.join(
                workdir, env['PATH_INFO']))
        if query:
            env['QUERY_STRING'] = query
        env['GATEWAY_INTERFACE'] = 'CGI/1.1'
        if self.channel.addr:
            env['REMOTE_ADDR'] = self.channel.addr[0]
        else:
            env['REMOTE_ADDR'] = '127.0.0.1'

        for header in self.header:
            key,value=header.split(":",1)
            key=key.lower()
            value=value.strip()
            if key in header2env and value:
                env[header2env.get(key)]=value
            else:
                key='HTTP_%s' % ("_".join(key.split( "-"))).upper()
                if value and key not in env:
                    env[key]=value
        return env

    def get_server_url(self):
        """ Functionality that medusa's http request doesn't have; set an
        attribute named 'server_url' on the request based on the Host: header
        """
        default_port={'http': '80', 'https': '443'}
        environ = self.cgi_environment()
        if (environ.get('HTTPS') in ('on', 'ON') or
            environ.get('SERVER_PORT_SECURE') == "1"):
            # XXX this will currently never be true
            protocol = 'https'
        else:
            protocol = 'http'

        if 'HTTP_HOST' in environ:
            host = environ['HTTP_HOST'].strip()
            hostname, port = urllib.splitport(host)
        else:
            hostname = environ['SERVER_NAME'].strip()
            port = environ['SERVER_PORT']

        if port is None or default_port[protocol] == port:
            host = hostname
        else:
            host = hostname + ':' + port
        server_url = '%s://%s' % (protocol, host)
        if server_url[-1:]=='/':
            server_url=server_url[:-1]
        return server_url

class deferring_http_channel(http_server.http_channel):

    # use a 4096-byte buffer size instead of the default 65536-byte buffer in
    # order to spew tail -f output faster (speculative)
    ac_out_buffer_size = 4096

    delay = False
    writable_check = time.time()

    def writable(self, t=time.time):
        now = t()
        if self.delay:
            # we called a deferred producer via this channel (see refill_buffer)
            last_writable_check = self.writable_check
            self.writable_check = now
            elapsed = now - last_writable_check
            if elapsed > self.delay:
                return True
            else:
                return False

        return http_server.http_channel.writable(self)

    def refill_buffer (self):
        """ Implement deferreds """
        while 1:
            if len(self.producer_fifo):
                p = self.producer_fifo.first()
                # a 'None' in the producer fifo is a sentinel,
                # telling us to close the channel.
                if p is None:
                    if not self.ac_out_buffer:
                        self.producer_fifo.pop()
                        self.close()
                    return
                elif isinstance(p, str):
                    self.producer_fifo.pop()
                    self.ac_out_buffer += p
                    return

                data = p.more()

                if data is NOT_DONE_YET:
                    self.delay = p.delay
                    return

                elif data:
                    self.ac_out_buffer = self.ac_out_buffer + data
                    self.delay = False
                    return
                else:
                    self.producer_fifo.pop()
            else:
                return

    def found_terminator (self):
        """ We only override this to use 'deferring_http_request' class
        instead of the normal http_request class; it sucks to need to override
        this """
        if self.current_request:
            self.current_request.found_terminator()
        else:
            header = self.in_buffer
            self.in_buffer = ''
            lines = header.split('\r\n')

            # --------------------------------------------------
            # crack the request header
            # --------------------------------------------------

            while lines and not lines[0]:
                # as per the suggestion of http-1.1 section 4.1, (and
                # Eric Parker <eparker@zyvex.com>), ignore a leading
                # blank lines (buggy browsers tack it onto the end of
                # POST requests)
                lines = lines[1:]

            if not lines:
                self.close_when_done()
                return

            request = lines[0]

            command, uri, version = http_server.crack_request (request)
            header = http_server.join_headers (lines[1:])

            # unquote path if necessary (thanks to Skip Montanaro for pointing
            # out that we must unquote in piecemeal fashion).
            rpath, rquery = http_server.splitquery(uri)
            if '%' in rpath:
                if rquery:
                    uri = http_server.unquote (rpath) + '?' + rquery
                else:
                    uri = http_server.unquote (rpath)

            r = deferring_http_request (self, request, command, uri, version,
                                         header)
            self.request_counter.increment()
            self.server.total_requests.increment()

            if command is None:
                self.log_info ('Bad HTTP request: %s' % repr(request), 'error')
                r.error (400)
                return

            # --------------------------------------------------
            # handler selection and dispatch
            # --------------------------------------------------
            for h in self.server.handlers:
                if h.match (r):
                    try:
                        self.current_request = r
                        # This isn't used anywhere.
                        # r.handler = h # CYCLE
                        h.handle_request (r)
                    except:
                        self.server.exceptions.increment()
                        (file, fun, line), t, v, tbinfo = \
                               asyncore.compact_traceback()
                        self.server.log_info(
                            'Server Error: %s, %s: file: %s line: %s' %
                            (t,v,file,line),
                            'error')
                        try:
                            r.error (500)
                        except:
                            pass
                    return

            # no handlers, so complain
            r.error (404)

class supervisor_http_server(http_server.http_server):
    channel_class = deferring_http_channel
    ip = None

    def prebind(self, sock, logger_object):
        """ Override __init__ to do logger setup earlier so it can
        go to our logger object instead of stdout """
        from supervisor.medusa import logger

        if not logger_object:
            logger_object = logger.file_logger(sys.stdout)

        logger_object = logger.unresolving_logger(logger_object)
        self.logger = logger_object

        asyncore.dispatcher.__init__ (self)
        self.set_socket(sock)

        self.handlers = []

        sock.setblocking(0)
        self.set_reuse_addr()

    def postbind(self):
        from supervisor.medusa.counter import counter
        from supervisor.medusa.http_server import VERSION_STRING

        self.listen(1024)

        self.total_clients = counter()
        self.total_requests = counter()
        self.exceptions = counter()
        self.bytes_out = counter()
        self.bytes_in  = counter()

        self.log_info (
                'Medusa (V%s) started at %s'
                '\n\tHostname: %s'
                '\n\tPort:%s'
                '\n' % (
                        VERSION_STRING,
                        time.ctime(time.time()),
                        self.server_name,
                        self.port,
                        )
                )

    def log_info(self, message, type='info'):
        ip = ''
        if getattr(self, 'ip', None) is not None:
            ip = self.ip
        self.logger.log(ip, message)

class supervisor_af_inet_http_server(supervisor_http_server):
    """ AF_INET version of supervisor HTTP server """

    def __init__(self, ip, port, logger_object):
        self.ip = ip
        self.port = port
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.prebind(sock, logger_object)
        self.bind((ip, port))

        if not ip:
            self.log_info('Computing default hostname', 'warning')
            hostname = socket.gethostname()
            try:
                ip = socket.gethostbyname(hostname)
            except socket.error:
                raise ValueError(
                    'Could not determine IP address for hostname %s, '
                    'please try setting an explicit IP address in the "port" '
                    'setting of your [inet_http_server] section.  For example, '
                    'instead of "port = 9001", try "port = 127.0.0.1:9001."'
                    % hostname)
        try:
            self.server_name = socket.gethostbyaddr (ip)[0]
        except socket.error:
            self.log_info('Cannot do reverse lookup', 'warning')
            self.server_name = ip       # use the IP address as the "hostname"

        self.postbind()

class supervisor_af_unix_http_server(supervisor_http_server):
    """ AF_UNIX version of supervisor HTTP server """

    def __init__(self, socketname, sockchmod, sockchown, logger_object):
        self.ip = socketname
        self.port = socketname

        # XXX this is insecure.  We really should do something like
        # http://developer.apple.com/samplecode/CFLocalServer/listing6.html
        # (see also http://developer.apple.com/technotes/tn2005/tn2083.html#SECUNIXDOMAINSOCKETS)
        # but it would be very inconvenient for the user to need to get all
        # the directory setup right.

        tempname = "%s.%d" % (socketname, os.getpid())

        try:
            os.unlink(tempname)
        except OSError:
            pass

        while 1:
            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            try:
                sock.bind(tempname)
                os.chmod(tempname, sockchmod)
                try:
                    # hard link
                    os.link(tempname, socketname)
                except OSError:
                    # Lock contention, or stale socket.
                    used = self.checkused(socketname)
                    if used:
                        # cooperate with 'openhttpserver' in supervisord
                        raise socket.error(errno.EADDRINUSE)

                    # Stale socket -- delete, sleep, and try again.
                    msg = "Unlinking stale socket %s\n" % socketname
                    sys.stderr.write(msg)
                    try:
                        os.unlink(socketname)
                    except:
                        pass
                    sock.close()
                    time.sleep(.3)
                    continue
                else:
                    try:
                        os.chown(socketname, sockchown[0], sockchown[1])
                    except OSError as why:
                        if why.args[0] == errno.EPERM:
                            msg = ('Not permitted to chown %s to uid/gid %s; '
                                   'adjust "sockchown" value in config file or '
                                   'on command line to values that the '
                                   'current user (%s) can successfully chown')
                            raise ValueError(msg % (socketname,
                                                    repr(sockchown),
                                                    pwd.getpwuid(
                                                        os.geteuid())[0],
                                                    ),
                                             )
                        else:
                            raise
                    self.prebind(sock, logger_object)
                    break

            finally:
                try:
                    os.unlink(tempname)
                except OSError:
                    pass

        self.server_name = '<unix domain socket>'
        self.postbind()

    def checkused(self, socketname):
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            s.connect(socketname)
            s.send("GET / HTTP/1.0\r\n\r\n")
            s.recv(1)
            s.close()
        except socket.error:
            return False
        else:
            return True

class tail_f_producer:
    def __init__(self, request, filename, head):
        self.file = open(filename, 'rb')
        self.request = weakref.ref(request)
        self.delay = 0.1
        sz = self.fsize()
        if sz >= head:
            self.sz = sz - head
        else:
            self.sz = 0

    def __del__(self):
        if self.file:
            self.file.close()

    def more(self):
        try:
            newsz = self.fsize()
        except OSError:
            # file descriptor was closed
            return ''
        bytes_added = newsz - self.sz
        if bytes_added < 0:
            self.sz = 0
            return "==> File truncated <==\n"
        if bytes_added > 0:
            self.file.seek(-bytes_added, 2)
            bytes = self.file.read(bytes_added)
            self.sz = newsz
            return bytes
        return NOT_DONE_YET

    def fsize(self):
        return os.fstat(self.file.fileno())[stat.ST_SIZE]

class logtail_handler:
    IDENT = 'Logtail HTTP Request Handler'
    path = '/logtail'

    def __init__(self, supervisord):
        self.supervisord = supervisord

    def match(self, request):
        return request.uri.startswith(self.path)

    def handle_request(self, request):
        if request.command != 'GET':
            request.error (400) # bad request
            return

        path, params, query, fragment = request.split_uri()

        if '%' in path:
            path = http_server.unquote(path)

        # strip off all leading slashes
        while path and path[0] == '/':
            path = path[1:]

        path, process_name_and_channel = path.split('/', 1)

        try:
            process_name, channel = process_name_and_channel.split('/', 1)
        except ValueError:
            # no channel specified, default channel to stdout
            process_name = process_name_and_channel
            channel = 'stdout'

        from supervisor.options import split_namespec
        group_name, process_name = split_namespec(process_name)

        group = self.supervisord.process_groups.get(group_name)
        if group is None:
            request.error(404) # not found
            return

        process = group.processes.get(process_name)
        if process is None:
            request.error(404) # not found
            return

        logfile = getattr(process.config, '%s_logfile' % channel, None)

        if logfile is None or not os.path.exists(logfile):
            # XXX problematic: processes that don't start won't have a log
            # file and we probably don't want to go into fatal state if we try
            # to read the log of a process that did not start.
            request.error(410) # gone
            return

        mtime = os.stat(logfile)[stat.ST_MTIME]
        request['Last-Modified'] = http_date.build_http_date(mtime)
        request['Content-Type'] = 'text/plain'
        # the lack of a Content-Length header makes the outputter
        # send a 'Transfer-Encoding: chunked' response

        request.push(tail_f_producer(request, logfile, 1024))

        request.done()

class mainlogtail_handler:
    IDENT = 'Main Logtail HTTP Request Handler'
    path = '/mainlogtail'

    def __init__(self, supervisord):
        self.supervisord = supervisord

    def match(self, request):
        return request.uri.startswith(self.path)

    def handle_request(self, request):
        if request.command != 'GET':
            request.error (400) # bad request
            return

        logfile = self.supervisord.options.logfile

        if logfile is None or not os.path.exists(logfile):
            request.error(410) # gone
            return

        mtime = os.stat(logfile)[stat.ST_MTIME]
        request['Last-Modified'] = http_date.build_http_date(mtime)
        request['Content-Type'] = 'text/plain'
        # the lack of a Content-Length header makes the outputter
        # send a 'Transfer-Encoding: chunked' response

        request.push(tail_f_producer(request, logfile, 1024))

        request.done()

def make_http_servers(options, supervisord):
    servers = []
    class LogWrapper:
        def log(self, msg):
            if msg.endswith('\n'):
                msg = msg[:-1]
            options.logger.trace(msg)
    wrapper = LogWrapper()

    for config in options.server_configs:
        family = config['family']

        if family == socket.AF_INET:
            host, port = config['host'], config['port']
            hs = supervisor_af_inet_http_server(host, port,
                                                logger_object=wrapper)
        elif family == socket.AF_UNIX:
            socketname = config['file']
            sockchmod = config['chmod']
            sockchown = config['chown']
            hs = supervisor_af_unix_http_server(socketname,sockchmod, sockchown,
                                                logger_object=wrapper)
        else:
            raise ValueError('Cannot determine socket type %r' % family)

        from supervisor.xmlrpc import supervisor_xmlrpc_handler
        from supervisor.xmlrpc import SystemNamespaceRPCInterface
        from supervisor.web import supervisor_ui_handler

        subinterfaces = []
        for name, factory, d in options.rpcinterface_factories:
            try:
                inst = factory(supervisord, **d)
            except:
                import traceback; traceback.print_exc()
                raise ValueError('Could not make %s rpc interface' % name)
            subinterfaces.append((name, inst))
            options.logger.info('RPC interface %r initialized' % name)

        subinterfaces.append(('system',
                              SystemNamespaceRPCInterface(subinterfaces)))
        xmlrpchandler = supervisor_xmlrpc_handler(supervisord, subinterfaces)
        tailhandler = logtail_handler(supervisord)
        maintailhandler = mainlogtail_handler(supervisord)
        uihandler = supervisor_ui_handler(supervisord)
        here = os.path.abspath(os.path.dirname(__file__))
        templatedir = os.path.join(here, 'ui')
        filesystem = filesys.os_filesystem(templatedir)
        defaulthandler = default_handler.default_handler(filesystem)

        username = config['username']
        password = config['password']

        if username:
            # wrap the xmlrpc handler and tailhandler in an authentication
            # handler
            users = {username:password}
            xmlrpchandler = supervisor_auth_handler(users, xmlrpchandler)
            tailhandler = supervisor_auth_handler(users, tailhandler)
            maintailhandler = supervisor_auth_handler(users, maintailhandler)
            uihandler = supervisor_auth_handler(users, uihandler)
            defaulthandler = supervisor_auth_handler(users, defaulthandler)
        else:
            options.logger.critical(
                'Server %r running without any HTTP '
                'authentication checking' % config['section'])
        # defaulthandler must be consulted last as its match method matches
        # everything, so it's first here (indicating last checked)
        hs.install_handler(defaulthandler)
        hs.install_handler(uihandler)
        hs.install_handler(maintailhandler)
        hs.install_handler(tailhandler)
        hs.install_handler(xmlrpchandler) # last for speed (first checked)
        servers.append((config, hs))

    return servers

class encrypted_dictionary_authorizer:
    def __init__ (self, dict):
        self.dict = dict

    def authorize(self, auth_info):
        username, password = auth_info
        if username in self.dict:
            stored_password = self.dict[username]
            if stored_password.startswith('{SHA}'):
                password_hash = sha1(as_bytes(password)).hexdigest()
                return stored_password[5:] == password_hash
            else:
                return stored_password == password
        else:
            return False

class supervisor_auth_handler(auth_handler):
    def __init__(self, dict, handler, realm='default'):
        auth_handler.__init__(self, dict, handler, realm)
        # override the authorizer with one that knows about SHA hashes too
        self.authorizer = encrypted_dictionary_authorizer(dict)


########NEW FILE########
__FILENAME__ = http_client
# this code based on Daniel Krech's RDFLib HTTP client code (see rdflib.net)

import sys
import supervisor.medusa.text_socket as socket

from supervisor.compat import print_function
from supervisor.compat import urlparse
from supervisor.compat import as_bytes
from supervisor.compat import as_string
from supervisor.compat import encodestring
from supervisor.medusa import asynchat_25 as asynchat

CR="\x0d"
LF="\x0a"
CRLF=CR+LF

class Listener(object):

    def status(self, url, status):
        pass

    def error(self, url, error):
        print_function(url, error)

    def response_header(self, url, name, value):
        pass

    def done(self, url):
        pass

    def feed(self, url, data):
        sys.stdout.write(data)
        sys.stdout.flush()

    def close(self, url):
        pass

class HTTPHandler(asynchat.async_chat):
    def __init__(
        self,
        listener,
        username='',
        password=None,
        conn=None,
        map=None
        ):
        asynchat.async_chat.__init__(self, conn, map)
        self.listener = listener
        self.user_agent = 'Supervisor HTTP Client'
        self.buffer = ''
        self.set_terminator(CRLF)
        self.connected = 0
        self.part = self.status_line
        self.chunk_size = 0
        self.chunk_read = 0
        self.length_read = 0
        self.length = 0
        self.encoding = None
        self.username = username
        self.password = password
        self.url = None
        self.error_handled = False

    def get(self, serverurl, path=''):
        if self.url is not None:
            raise AssertionError('Already doing a get')
        self.url = serverurl + path
        scheme, host, path_ignored, params, query, fragment = urlparse.urlparse(
            self.url)
        if not scheme in ("http", "unix"):
            raise NotImplementedError
        self.host = host
        if ":" in host:
            hostname, port = host.split(":", 1)
            port = int(port)
        else:
            hostname = host
            port = 80

        self.path = path
        self.port = port

        if scheme == "http":
            ip = hostname
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
            self.connect((ip, self.port))
        elif scheme == "unix":
            socketname = serverurl[7:]
            self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
            self.connect(socketname)

    def close(self):
        self.listener.close(self.url)
        self.connected = 0
        self.del_channel()
        self.socket.close()
        self.url = "CLOSED"

    def header(self, name, value):
        self.push('%s: %s' % (name, value))
        self.push(CRLF)

    def handle_error(self):
        if self.error_handled:
            return
        if 1 or self.connected:
            t,v,tb = sys.exc_info()
            msg = 'Cannot connect, error: %s (%s)' % (t, v)
            self.listener.error(self.url, msg)
            self.part = self.ignore
            self.close()
            self.error_handled = True
            del t
            del v
            del tb

    def handle_connect(self):
        self.connected = 1
        method = "GET"
        version = "HTTP/1.1"
        self.push("%s %s %s" % (method, self.path, version))
        self.push(CRLF)
        self.header("Host", self.host)

        self.header('Accept-Encoding', 'chunked')
        self.header('Accept', '*/*')
        self.header('User-agent', self.user_agent)
        if self.password:
            auth = '%s:%s' % (self.username, self.password)
            auth = as_string(encodestring(as_bytes(auth))).strip()
            self.header('Authorization', 'Basic %s' % auth)
        self.push(CRLF)
        self.push(CRLF)


    def feed(self, data):
        self.listener.feed(self.url, data)

    def collect_incoming_data(self, bytes):
        self.buffer = self.buffer + bytes
        if self.part==self.body:
            self.feed(self.buffer)
            self.buffer = ''

    def found_terminator(self):
        self.part()
        self.buffer = ''

    def ignore(self):
        self.buffer = ''

    def status_line(self):
        line = self.buffer

        version, status, reason = line.split(None, 2)
        status = int(status)
        if not version.startswith('HTTP/'):
            raise ValueError(line)

        self.listener.status(self.url, status)

        if status == 200:
            self.part = self.headers
        else:
            self.part = self.ignore
            msg = 'Cannot read, status code %s' % status
            self.listener.error(self.url, msg)
            self.close()
        return version, status, reason

    def headers(self):
        line = self.buffer
        if not line:
            if self.encoding=="chunked":
                self.part = self.chunked_size
            else:
                self.part = self.body
                self.set_terminator(self.length)
        else:
            name, value = line.split(":", 1)
            if name and value:
                name = name.lower()
                value = value.strip()
                if name=="transfer-encoding":
                    self.encoding = value
                elif name=="content-length":
                    self.length = int(value)
                self.response_header(name, value)

    def response_header(self, name, value):
        self.listener.response_header(self.url, name, value)

    def body(self):
        self.done()
        self.close()

    def done(self):
        self.listener.done(self.url)

    def chunked_size(self):
        line = self.buffer
        if not line:
            return
        chunk_size = int(line.split()[0], 16)
        if chunk_size==0:
            self.part = self.trailer
        else:
            self.set_terminator(chunk_size)
            self.part = self.chunked_body
        self.length += chunk_size

    def chunked_body(self):
        line = self.buffer
        self.set_terminator(CRLF)
        self.part = self.chunked_size
        self.feed(line)

    def trailer(self):
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
        # trailer        = *(entity-header CRLF)
        line = self.buffer
        if line==CRLF:
            self.done()
            self.close()

########NEW FILE########
__FILENAME__ = loggers
"""
Logger implementation loosely modeled on PEP 282.  We don't use the
PEP 282 logger implementation in the stdlib ('logging') because it's
idiosyncratic and a bit slow for our purposes (we don't use threads).
"""

# This module must not depend on any non-stdlib modules to
# avoid circular import problems

import os
import errno
import sys
import time
import traceback

from supervisor.compat import syslog
from supervisor.compat import long

class LevelsByName:
    CRIT = 50   # messages that probably require immediate user attention
    ERRO = 40   # messages that indicate a potentially ignorable error condition
    WARN = 30   # messages that indicate issues which aren't errors
    INFO = 20   # normal informational output
    DEBG = 10   # messages useful for users trying to debug configurations
    TRAC = 5    # messages useful to developers trying to debug plugins
    BLAT = 3    # messages useful for developers trying to debug supervisor

class LevelsByDescription:
    critical = LevelsByName.CRIT
    error = LevelsByName.ERRO
    warn = LevelsByName.WARN
    info = LevelsByName.INFO
    debug = LevelsByName.DEBG
    trace = LevelsByName.TRAC
    blather = LevelsByName.BLAT

def _levelNumbers():
    bynumber = {}
    for name, number in LevelsByName.__dict__.items():
        if not name.startswith('_'):
            bynumber[number] = name
    return bynumber

LOG_LEVELS_BY_NUM = _levelNumbers()

def getLevelNumByDescription(description):
    num = getattr(LevelsByDescription, description, None)
    return num

class Handler:
    fmt = '%(message)s'
    level = LevelsByName.INFO
    def __init__(self, stream=None):
        self.stream = stream
        self.closed = False

    def setFormat(self, fmt):
        self.fmt = fmt

    def setLevel(self, level):
        self.level = level

    def flush(self):
        try:
            self.stream.flush()
        except IOError as why:
            # if supervisor output is piped, EPIPE can be raised at exit
            if why.args[0] != errno.EPIPE:
                raise

    def close(self):
        if not self.closed:
            if hasattr(self.stream, 'fileno'):
                fd = self.stream.fileno()
                if fd < 3: # don't ever close stdout or stderr
                    return
            self.stream.close()
            self.closed = True

    def emit(self, record):
        try:
            msg = self.fmt % record.asdict()
            try:
                self.stream.write(msg)
            except UnicodeError:
                self.stream.write(msg.encode("UTF-8"))
            self.flush()
        except:
            self.handleError()

    def handleError(self):
        ei = sys.exc_info()
        traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
        del ei

class FileHandler(Handler):
    """File handler which supports reopening of logs.
    """

    def __init__(self, filename, mode="a"):
        Handler.__init__(self, open(filename, mode))
        self.baseFilename = filename
        self.mode = mode

    def reopen(self):
        self.close()
        self.stream = open(self.baseFilename, self.mode)
        self.closed = False

    def remove(self):
        self.close()
        try:
            os.remove(self.baseFilename)
        except OSError as why:
            if why.args[0] != errno.ENOENT:
                raise

class StreamHandler(Handler):
    def __init__(self, strm=None):
        Handler.__init__(self, strm)

    def remove(self):
        if hasattr(self.stream, 'clear'):
            self.stream.clear()

    def reopen(self):
        pass

class BoundIO:
    def __init__(self, maxbytes, buf=''):
        self.maxbytes = maxbytes
        self.buf = buf

    def flush(self):
        pass

    def close(self):
        self.clear()

    def write(self, s):
        slen = len(s)
        if len(self.buf) + slen > self.maxbytes:
            self.buf = self.buf[slen:]
        self.buf += s

    def getvalue(self):
        return self.buf

    def clear(self):
        self.buf = ''

class RotatingFileHandler(FileHandler):
    def __init__(self, filename, mode='a', maxBytes=512*1024*1024,
                 backupCount=10):
        """
        Open the specified file and use it as the stream for logging.

        By default, the file grows indefinitely. You can specify particular
        values of maxBytes and backupCount to allow the file to rollover at
        a predetermined size.

        Rollover occurs whenever the current log file is nearly maxBytes in
        length. If backupCount is >= 1, the system will successively create
        new files with the same pathname as the base file, but with extensions
        ".1", ".2" etc. appended to it. For example, with a backupCount of 5
        and a base file name of "app.log", you would get "app.log",
        "app.log.1", "app.log.2", ... through to "app.log.5". The file being
        written to is always "app.log" - when it gets filled up, it is closed
        and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
        exist, then they are renamed to "app.log.2", "app.log.3" etc.
        respectively.

        If maxBytes is zero, rollover never occurs.
        """
#        FileHandler.__init__(self, filename, mode)
        if maxBytes > 0:
            mode = 'a' # doesn't make sense otherwise!
        FileHandler.__init__(self, filename, mode)
        self.maxBytes = maxBytes
        self.backupCount = backupCount
        self.counter = 0
        self.every = 10

    def __del__(self):
        if self.stream:
            try:
                self.stream.close()
            except OSError:
                pass

    def emit(self, record):
        """
        Emit a record.

        Output the record to the file, catering for rollover as described
        in doRollover().
        """
        FileHandler.emit(self, record)
        self.doRollover()

    def _remove(self, fn): # pragma: no cover
        # this is here to service stubbing in unit tests
        return os.remove(fn)

    def _rename(self, src, tgt): # pragma: no cover
        # this is here to service stubbing in unit tests
        return os.rename(src, tgt)

    def _exists(self, fn): # pragma: no cover
        # this is here to service stubbing in unit tests
        return os.path.exists(fn)

    def removeAndRename(self, sfn, dfn):
        if self._exists(dfn):
            try:
                self._remove(dfn)
            except OSError as why:
                # catch race condition (destination already deleted)
                if why.args[0] != errno.ENOENT:
                    raise
        try:
            self._rename(sfn, dfn)
        except OSError as why:
            # catch exceptional condition (source deleted)
            # E.g. cleanup script removes active log.
            if why.args[0] != errno.ENOENT:
                raise

    def doRollover(self):
        """
        Do a rollover, as described in __init__().
        """
        if self.maxBytes <= 0:
            return

        if not (self.stream.tell() >= self.maxBytes):
            return

        self.stream.close()
        if self.backupCount > 0:
            for i in range(self.backupCount - 1, 0, -1):
                sfn = "%s.%d" % (self.baseFilename, i)
                dfn = "%s.%d" % (self.baseFilename, i + 1)
                if os.path.exists(sfn):
                    self.removeAndRename(sfn, dfn)
            dfn = self.baseFilename + ".1"
            self.removeAndRename(self.baseFilename, dfn)
        self.stream = open(self.baseFilename, 'w')

class LogRecord:
    def __init__(self, level, msg, **kw):
        self.level = level
        self.msg = msg
        self.kw = kw
        self.dictrepr = None

    def asdict(self):
        if self.dictrepr is None:
            now = time.time()
            msecs = (now - long(now)) * 1000
            part1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now))
            asctime = '%s,%03d' % (part1, msecs)
            levelname = LOG_LEVELS_BY_NUM[self.level]
            if self.kw:
                msg = self.msg % self.kw
            else:
                msg = self.msg
            self.dictrepr = {'message':msg, 'levelname':levelname,
                             'asctime':asctime}
        return self.dictrepr

class Logger:
    def __init__(self, level=None, handlers=None):
        if level is None:
            level = LevelsByName.INFO
        self.level = level

        if handlers is None:
            handlers = []
        self.handlers = handlers

    def close(self):
        for handler in self.handlers:
            handler.close()

    def blather(self, msg, **kw):
        if LevelsByName.BLAT >= self.level:
            self.log(LevelsByName.BLAT, msg, **kw)

    def trace(self, msg, **kw):
        if LevelsByName.TRAC >= self.level:
            self.log(LevelsByName.TRAC, msg, **kw)

    def debug(self, msg, **kw):
        if LevelsByName.DEBG >= self.level:
            self.log(LevelsByName.DEBG, msg, **kw)

    def info(self, msg, **kw):
        if LevelsByName.INFO >= self.level:
            self.log(LevelsByName.INFO, msg, **kw)

    def warn(self, msg, **kw):
        if LevelsByName.WARN >= self.level:
            self.log(LevelsByName.WARN, msg, **kw)

    def error(self, msg, **kw):
        if LevelsByName.ERRO >= self.level:
            self.log(LevelsByName.ERRO, msg, **kw)

    def critical(self, msg, **kw):
        if LevelsByName.CRIT >= self.level:
            self.log(LevelsByName.CRIT, msg, **kw)

    def log(self, level, msg, **kw):
        record = LogRecord(level, msg, **kw)
        for handler in self.handlers:
            if level >= handler.level:
                handler.emit(record)

    def addHandler(self, hdlr):
        self.handlers.append(hdlr)

    def getvalue(self):
        raise NotImplementedError

class SyslogHandler(Handler):
    def __init__(self):
        Handler.__init__(self)
        assert syslog is not None, "Syslog module not present"

    def close(self):
        pass

    def reopen(self):
        pass

    def _syslog(self, msg): # pragma: no cover
        # this exists only for unit test stubbing
        syslog.syslog(msg)

    def emit(self, record):
        try:
            params = record.asdict()
            message = params['message']
            for line in message.rstrip('\n').split('\n'):
                params['message'] = line
                msg = self.fmt % params
                try:
                    self._syslog(msg)
                except UnicodeError:
                    self._syslog(msg.encode("UTF-8"))
        except:
            self.handleError()

def getLogger(level=None):
    return Logger(level)

_2MB = 1<<21

def handle_boundIO(logger, fmt, maxbytes=_2MB):
    io = BoundIO(maxbytes)
    handler = StreamHandler(io)
    handler.setLevel(logger.level)
    handler.setFormat(fmt)
    logger.addHandler(handler)
    logger.getvalue = io.getvalue

    return logger

def handle_stdout(logger, fmt):
    handler = StreamHandler(sys.stdout)
    handler.setFormat(fmt)
    handler.setLevel(logger.level)
    logger.addHandler(handler)

def handle_syslog(logger, fmt):
    handler = SyslogHandler()
    handler.setFormat(fmt)
    handler.setLevel(logger.level)
    logger.addHandler(handler)

def handle_file(logger, filename, fmt, rotating=False, maxbytes=0, backups=0):
    if filename == 'syslog':
        handler = SyslogHandler()

    else:
        if rotating is False:
            handler = FileHandler(filename)
        else:
            handler = RotatingFileHandler(filename, 'a', maxbytes, backups)

    handler.setFormat(fmt)
    handler.setLevel(logger.level)
    logger.addHandler(handler)

    return logger

########NEW FILE########
__FILENAME__ = asynchat_25
# -*- Mode: Python; tab-width: 4 -*-
#       Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp
#       Author: Sam Rushing <rushing@nightmare.com>

# ======================================================================
# Copyright 1996 by Sam Rushing
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Sam
# Rushing not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================

r"""A class supporting chat-style (command/response) protocols.

This class adds support for 'chat' style protocols - where one side
sends a 'command', and the other sends a response (examples would be
the common internet protocols - smtp, nntp, ftp, etc..).

The handle_read() method looks at the input stream for the current
'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n'
for multi-line output), calling self.found_terminator() on its
receipt.

for example:
Say you build an async nntp client using this class.  At the start
of the connection, you'll have self.terminator set to '\r\n', in
order to process the single-line greeting.  Just before issuing a
'LIST' command you'll set it to '\r\n.\r\n'.  The output of the LIST
command will be accumulated (using your own 'collect_incoming_data'
method) up to the terminator, and then control will be returned to
you - by calling your self.found_terminator() method.
"""

import supervisor.medusa.text_socket as socket
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.compat import long

class async_chat (asyncore.dispatcher):
    """This is an abstract class.  You must derive from this class, and add
    the two methods collect_incoming_data() and found_terminator()"""

    # these are overridable defaults

    ac_in_buffer_size       = 4096
    ac_out_buffer_size      = 4096

    def __init__ (self, conn=None, map=None):
        self.ac_in_buffer = ''
        self.ac_out_buffer = ''
        self.producer_fifo = fifo()
        asyncore.dispatcher.__init__ (self, conn, map)

    def collect_incoming_data(self, data):
        raise NotImplementedError("must be implemented in subclass")

    def found_terminator(self):
        raise NotImplementedError("must be implemented in subclass")

    def set_terminator (self, term):
        """Set the input delimiter.  Can be a fixed string of any length, an integer, or None"""
        self.terminator = term

    def get_terminator (self):
        return self.terminator

    # grab some more data from the socket,
    # throw it to the collector method,
    # check for the terminator,
    # if found, transition to the next state.

    def handle_read (self):

        try:
            data = self.recv (self.ac_in_buffer_size)
        except socket.error:
            self.handle_error()
            return

        self.ac_in_buffer += data

        # Continue to search for self.terminator in self.ac_in_buffer,
        # while calling self.collect_incoming_data.  The while loop
        # is necessary because we might read several data+terminator
        # combos with a single recv(1024).

        while self.ac_in_buffer:
            lb = len(self.ac_in_buffer)
            terminator = self.get_terminator()
            if not terminator:
                # no terminator, collect it all
                self.collect_incoming_data (self.ac_in_buffer)
                self.ac_in_buffer = ''
            elif isinstance(terminator, int) or isinstance(terminator, long):
                # numeric terminator
                n = terminator
                if lb < n:
                    self.collect_incoming_data (self.ac_in_buffer)
                    self.ac_in_buffer = ''
                    self.terminator -= lb
                else:
                    self.collect_incoming_data (self.ac_in_buffer[:n])
                    self.ac_in_buffer = self.ac_in_buffer[n:]
                    self.terminator = 0
                    self.found_terminator()
            else:
                # 3 cases:
                # 1) end of buffer matches terminator exactly:
                #    collect data, transition
                # 2) end of buffer matches some prefix:
                #    collect data to the prefix
                # 3) end of buffer does not match any prefix:
                #    collect data
                terminator_len = len(terminator)
                index = self.ac_in_buffer.find(terminator)
                if index != -1:
                    # we found the terminator
                    if index > 0:
                        # don't bother reporting the empty string (source of subtle bugs)
                        self.collect_incoming_data (self.ac_in_buffer[:index])
                    self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:]
                    # This does the Right Thing if the terminator is changed here.
                    self.found_terminator()
                else:
                    # check for a prefix of the terminator
                    index = find_prefix_at_end (self.ac_in_buffer, terminator)
                    if index:
                        if index != lb:
                            # we found a prefix, collect up to the prefix
                            self.collect_incoming_data (self.ac_in_buffer[:-index])
                            self.ac_in_buffer = self.ac_in_buffer[-index:]
                        break
                    else:
                        # no prefix, collect it all
                        self.collect_incoming_data (self.ac_in_buffer)
                        self.ac_in_buffer = ''

    def handle_write (self):
        self.initiate_send ()

    def handle_close (self):
        self.close()

    def push (self, data):
        self.producer_fifo.push (simple_producer (data))
        self.initiate_send()

    def push_with_producer (self, producer):
        self.producer_fifo.push (producer)
        self.initiate_send()

    def readable (self):
        """predicate for inclusion in the readable for select()"""
        return len(self.ac_in_buffer) <= self.ac_in_buffer_size

    def writable (self):
        """predicate for inclusion in the writable for select()"""
        # return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
        # this is about twice as fast, though not as clear.
        return not (
                (self.ac_out_buffer == '') and
                self.producer_fifo.is_empty() and
                self.connected
                )

    def close_when_done (self):
        """automatically close this channel once the outgoing queue is empty"""
        self.producer_fifo.push (None)

    # refill the outgoing buffer by calling the more() method
    # of the first producer in the queue
    def refill_buffer (self):
        while 1:
            if len(self.producer_fifo):
                p = self.producer_fifo.first()
                # a 'None' in the producer fifo is a sentinel,
                # telling us to close the channel.
                if p is None:
                    if not self.ac_out_buffer:
                        self.producer_fifo.pop()
                        self.close()
                    return
                elif isinstance(p, str):
                    self.producer_fifo.pop()
                    self.ac_out_buffer += p
                    return
                data = p.more()
                if data:
                    self.ac_out_buffer = self.ac_out_buffer + data
                    return
                else:
                    self.producer_fifo.pop()
            else:
                return

    def initiate_send (self):
        obs = self.ac_out_buffer_size
        # try to refill the buffer
        if len (self.ac_out_buffer) < obs:
            self.refill_buffer()

        if self.ac_out_buffer and self.connected:
            # try to send the buffer
            try:
                num_sent = self.send (self.ac_out_buffer[:obs])
                if num_sent:
                    self.ac_out_buffer = self.ac_out_buffer[num_sent:]

            except socket.error:
                self.handle_error()
                return

    def discard_buffers (self):
        # Emergencies only!
        self.ac_in_buffer = ''
        self.ac_out_buffer = ''
        while self.producer_fifo:
            self.producer_fifo.pop()


class simple_producer:

    def __init__ (self, data, buffer_size=512):
        self.data = data
        self.buffer_size = buffer_size

    def more (self):
        if len (self.data) > self.buffer_size:
            result = self.data[:self.buffer_size]
            self.data = self.data[self.buffer_size:]
            return result
        else:
            result = self.data
            self.data = ''
            return result

class fifo:
    def __init__ (self, list=None):
        if not list:
            self.list = []
        else:
            self.list = list

    def __len__ (self):
        return len(self.list)

    def is_empty (self):
        return self.list == []

    def first (self):
        return self.list[0]

    def push (self, data):
        self.list.append(data)

    def pop (self):
        if self.list:
            return 1, self.list.pop(0)
        else:
            return 0, None

# Given 'haystack', see if any prefix of 'needle' is at its end.  This
# assumes an exact match has already been checked.  Return the number of
# characters matched.
# for example:
# f_p_a_e ("qwerty\r", "\r\n") => 1
# f_p_a_e ("qwertydkjf", "\r\n") => 0
# f_p_a_e ("qwerty\r\n", "\r\n") => <undefined>

# this could maybe be made faster with a computed regex?
# [answer: no; circa Python-2.0, Jan 2001]
# new python:   28961/s
# old python:   18307/s
# re:        12820/s
# regex:     14035/s

def find_prefix_at_end (haystack, needle):
    l = len(needle) - 1
    while l and not haystack.endswith(needle[:l]):
        l -= 1
    return l

########NEW FILE########
__FILENAME__ = asyncore_25
# -*- Mode: Python -*-
#   Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
#   Author: Sam Rushing <rushing@nightmare.com>

# ======================================================================
# Copyright 1996 by Sam Rushing
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Sam
# Rushing not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================

"""Basic infrastructure for asynchronous socket service clients and servers.

There are only two ways to have a program on a single processor do "more
than one thing at a time".  Multi-threaded programming is the simplest and
most popular way to do it, but there is another very different technique,
that lets you have nearly all the advantages of multi-threading, without
actually using multiple threads. it's really only practical if your program
is largely I/O bound. If your program is CPU bound, then preemptive
scheduled threads are probably what you really need. Network servers are
rarely CPU-bound, however.

If your operating system supports the select() system call in its I/O
library (and nearly all do), then you can use it to juggle multiple
communication channels at once; doing other work while your I/O is taking
place in the "background."  Although this strategy can seem strange and
complex, especially at first, it is in many ways easier to understand and
control than multi-threaded programming. The module documented here solves
many of the difficult problems for you, making the task of building
sophisticated high-performance network servers and clients a snap.
"""

import select
import supervisor.medusa.text_socket as socket
import sys
import time

import os
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, \
     ENOTCONN, ESHUTDOWN, EINTR, EISCONN, errorcode

from supervisor.compat import as_string, as_bytes

try:
    socket_map
except NameError:
    socket_map = {}

class ExitNow(Exception):
    pass

def read(obj):
    try:
        obj.handle_read_event()
    except ExitNow:
        raise
    except:
        obj.handle_error()

def write(obj):
    try:
        obj.handle_write_event()
    except ExitNow:
        raise
    except:
        obj.handle_error()

def _exception (obj):
    try:
        obj.handle_expt_event()
    except ExitNow:
        raise
    except:
        obj.handle_error()

def readwrite(obj, flags):
    try:
        if flags & (select.POLLIN | select.POLLPRI):
            obj.handle_read_event()
        if flags & select.POLLOUT:
            obj.handle_write_event()
        if flags & (select.POLLERR | select.POLLHUP | select.POLLNVAL):
            obj.handle_expt_event()
    except ExitNow:
        raise
    except:
        obj.handle_error()

def poll(timeout=0.0, map=None):
    if map is None:
        map = socket_map
    if map:
        r = []; w = []; e = []
        for fd, obj in map.items():
            is_r = obj.readable()
            is_w = obj.writable()
            if is_r:
                r.append(fd)
            if is_w:
                w.append(fd)
            if is_r or is_w:
                e.append(fd)
        if [] == r == w == e:
            time.sleep(timeout)
        else:
            try:
                r, w, e = select.select(r, w, e, timeout)
            except select.error as err:
                if err.args[0] != EINTR:
                    raise
                else:
                    return

        for fd in r:
            obj = map.get(fd)
            if obj is None:
                continue
            read(obj)

        for fd in w:
            obj = map.get(fd)
            if obj is None:
                continue
            write(obj)

        for fd in e:
            obj = map.get(fd)
            if obj is None:
                continue
            _exception(obj)

def poll2(timeout=0.0, map=None):
    # Use the poll() support added to the select module in Python 2.0
    if map is None:
        map = socket_map
    if timeout is not None:
        # timeout is in milliseconds
        timeout = int(timeout*1000)
    pollster = select.poll()
    if map:
        for fd, obj in map.items():
            flags = 0
            if obj.readable():
                flags |= select.POLLIN | select.POLLPRI
            if obj.writable():
                flags |= select.POLLOUT
            if flags:
                # Only check for exceptions if object was either readable
                # or writable.
                flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL
                pollster.register(fd, flags)
        try:
            r = pollster.poll(timeout)
        except select.error as err:
            if err.args[0] != EINTR:
                raise
            r = []
        for fd, flags in r:
            obj = map.get(fd)
            if obj is None:
                continue
            readwrite(obj, flags)

poll3 = poll2                           # Alias for backward compatibility

def loop(timeout=30.0, use_poll=False, map=None, count=None):
    if map is None:
        map = socket_map

    if use_poll and hasattr(select, 'poll'):
        poll_fun = poll2
    else:
        poll_fun = poll

    if count is None:
        while map:
            poll_fun(timeout, map)

    else:
        while map and count > 0:
            poll_fun(timeout, map)
            count -= 1

class dispatcher:

    debug = False
    connected = False
    accepting = False
    closing = False
    addr = None

    def __init__(self, sock=None, map=None):
        if map is None:
            self._map = socket_map
        else:
            self._map = map

        if sock:
            self.set_socket(sock, map)
            # I think it should inherit this anyway
            self.socket.setblocking(0)
            self.connected = True
            # XXX Does the constructor require that the socket passed
            # be connected?
            try:
                self.addr = sock.getpeername()
            except socket.error:
                # The addr isn't crucial
                pass
        else:
            self.socket = None

    def __repr__(self):
        status = [self.__class__.__module__+"."+self.__class__.__name__]
        if self.accepting and self.addr:
            status.append('listening')
        elif self.connected:
            status.append('connected')
        if self.addr is not None:
            try:
                status.append('%s:%d' % self.addr)
            except TypeError:
                status.append(repr(self.addr))
        return '<%s at %#x>' % (' '.join(status), id(self))

    def add_channel(self, map=None):
        #self.log_info('adding channel %s' % self)
        if map is None:
            map = self._map
        map[self._fileno] = self

    def del_channel(self, map=None):
        fd = self._fileno
        if map is None:
            map = self._map
        if fd in map:
            #self.log_info('closing channel %d:%s' % (fd, self))
            del map[fd]
        self._fileno = None

    def create_socket(self, family, type):
        self.family_and_type = family, type
        self.socket = socket.socket(family, type)
        self.socket.setblocking(0)
        self._fileno = self.socket.fileno()
        self.add_channel()

    def set_socket(self, sock, map=None):
        self.socket = sock
##        self.__dict__['socket'] = sock
        self._fileno = sock.fileno()
        self.add_channel(map)

    def set_reuse_addr(self):
        # try to re-use a server port if possible
        try:
            self.socket.setsockopt(
                socket.SOL_SOCKET, socket.SO_REUSEADDR,
                self.socket.getsockopt(socket.SOL_SOCKET,
                                       socket.SO_REUSEADDR) | 1
                )
        except socket.error:
            pass

    # ==================================================
    # predicates for select()
    # these are used as filters for the lists of sockets
    # to pass to select().
    # ==================================================

    def readable(self):
        return True

    def writable(self):
        return True

    # ==================================================
    # socket object methods.
    # ==================================================

    def listen(self, num):
        self.accepting = True
        if os.name == 'nt' and num > 5:
            num = 1
        return self.socket.listen(num)

    def bind(self, addr):
        self.addr = addr
        return self.socket.bind(addr)

    def connect(self, address):
        self.connected = False
        err = self.socket.connect_ex(address)
        # XXX Should interpret Winsock return values
        if err in (EINPROGRESS, EALREADY, EWOULDBLOCK):
            return
        if err in (0, EISCONN):
            self.addr = address
            self.connected = True
            self.handle_connect()
        else:
            raise socket.error(err, errorcode[err])

    def accept(self):
        # XXX can return either an address pair or None
        try:
            conn, addr = self.socket.accept()
            return conn, addr
        except socket.error as why:
            if why.args[0] == EWOULDBLOCK:
                pass
            else:
                raise

    def send(self, data):
        try:
            result = self.socket.send(data)
            return result
        except socket.error as why:
            if why.args[0] == EWOULDBLOCK:
                return 0
            else:
                raise

    def recv(self, buffer_size):
        try:
            data = self.socket.recv(buffer_size)
            if not data:
                # a closed connection is indicated by signaling
                # a read condition, and having recv() return 0.
                self.handle_close()
                return ''
            else:
                return data
        except socket.error as why:
            # winsock sometimes throws ENOTCONN
            if why.args[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:
                self.handle_close()
                return ''
            else:
                raise

    def close(self):
        self.del_channel()
        self.socket.close()

    # cheap inheritance, used to pass all other attribute
    # references to the underlying socket object.
    def __getattr__(self, attr):
        return getattr(self.socket, attr)

    # log and log_info may be overridden to provide more sophisticated
    # logging and warning methods. In general, log is for 'hit' logging
    # and 'log_info' is for informational, warning and error logging.

    def log(self, message):
        sys.stderr.write('log: %s\n' % str(message))

    def log_info(self, message, type='info'):
        if __debug__ or type != 'info':
            print('%s: %s' % (type, message))

    def handle_read_event(self):
        if self.accepting:
            # for an accepting socket, getting a read implies
            # that we are connected
            if not self.connected:
                self.connected = True
            self.handle_accept()
        elif not self.connected:
            self.handle_connect()
            self.connected = True
            self.handle_read()
        else:
            self.handle_read()

    def handle_write_event(self):
        # getting a write implies that we are connected
        if not self.connected:
            self.handle_connect()
            self.connected = True
        self.handle_write()

    def handle_expt_event(self):
        self.handle_expt()

    def handle_error(self):
        nil, t, v, tbinfo = compact_traceback()

        # sometimes a user repr method will crash.
        try:
            self_repr = repr(self)
        except:
            self_repr = '<__repr__(self) failed for object at %0x>' % id(self)

        self.log_info(
            'uncaptured python exception, closing channel %s (%s:%s %s)' % (
                self_repr,
                t,
                v,
                tbinfo
                ),
            'error'
            )
        self.close()

    def handle_expt(self):
        self.log_info('unhandled exception', 'warning')

    def handle_read(self):
        self.log_info('unhandled read event', 'warning')

    def handle_write(self):
        self.log_info('unhandled write event', 'warning')

    def handle_connect(self):
        self.log_info('unhandled connect event', 'warning')

    def handle_accept(self):
        self.log_info('unhandled accept event', 'warning')

    def handle_close(self):
        self.log_info('unhandled close event', 'warning')
        self.close()

# ---------------------------------------------------------------------------
# adds simple buffered output capability, useful for simple clients.
# [for more sophisticated usage use asynchat.async_chat]
# ---------------------------------------------------------------------------

class dispatcher_with_send(dispatcher):

    def __init__(self, sock=None, map=None):
        dispatcher.__init__(self, sock, map)
        self.out_buffer = ''

    def initiate_send(self):
        num_sent = dispatcher.send(self, self.out_buffer[:512])
        self.out_buffer = self.out_buffer[num_sent:]

    def handle_write(self):
        self.initiate_send()

    def writable(self):
        return (not self.connected) or len(self.out_buffer)

    def send(self, data):
        if self.debug:
            self.log_info('sending %s' % repr(data))
        self.out_buffer = self.out_buffer + data
        self.initiate_send()

# ---------------------------------------------------------------------------
# used for debugging.
# ---------------------------------------------------------------------------

def compact_traceback():
    t, v, tb = sys.exc_info()
    tbinfo = []
    assert tb # Must have a traceback
    while tb:
        tbinfo.append((
            tb.tb_frame.f_code.co_filename,
            tb.tb_frame.f_code.co_name,
            str(tb.tb_lineno)
            ))
        tb = tb.tb_next

    # just to be safe
    del tb

    file, function, line = tbinfo[-1]
    info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo])
    return (file, function, line), t, v, info

def close_all(map=None):
    if map is None:
        map = socket_map
    for x in map.values():
        x.socket.close()
    map.clear()

# Asynchronous File I/O:
#
# After a little research (reading man pages on various unixen, and
# digging through the linux kernel), I've determined that select()
# isn't meant for doing asynchronous file i/o.
# Heartening, though - reading linux/mm/filemap.c shows that linux
# supports asynchronous read-ahead.  So _MOST_ of the time, the data
# will be sitting in memory for us already when we go to read it.
#
# What other OS's (besides NT) support async file i/o?  [VMS?]
#
# Regardless, this is useful for pipes, and stdin/stdout...

if os.name == 'posix':
    import fcntl

    class file_wrapper:
        # here we override just enough to make a file
        # look like a socket for the purposes of asyncore.

        def __init__(self, fd):
            self.fd = fd

        def recv(self, buffersize):
            return as_string(os.read(self.fd, buffersize))

        def send(self, s):
            return os.write(self.fd, as_bytes(s))

        read = recv
        write = send

        def close(self):
            os.close(self.fd)

        def fileno(self):
            return self.fd

    class file_dispatcher(dispatcher):

        def __init__(self, fd, map=None):
            dispatcher.__init__(self, None, map)
            self.connected = True
            self.set_file(fd)
            # set it to non-blocking mode
            flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
            flags |= os.O_NONBLOCK
            fcntl.fcntl(fd, fcntl.F_SETFL, flags)

        def set_file(self, fd):
            self._fileno = fd
            self.socket = file_wrapper(fd)
            self.add_channel()

########NEW FILE########
__FILENAME__ = auth_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: auth_handler.py,v 1.6 2002/11/25 19:40:23 akuchling Exp $'

# support for 'basic' authentication.

import re
import time

from supervisor.compat import encodestring, decodestring
from supervisor.compat import md5
from supervisor.compat import as_string, as_bytes
from supervisor.compat import print_function

import supervisor.medusa.counter as counter
import supervisor.medusa.default_handler as default_handler

get_header = default_handler.get_header

import supervisor.medusa.producers as producers

# This is a 'handler' that wraps an authorization method
# around access to the resources normally served up by
# another handler.

# does anyone support digest authentication? (rfc2069)

class auth_handler:
    def __init__ (self, dict, handler, realm='default'):
        self.authorizer = dictionary_authorizer (dict)
        self.handler = handler
        self.realm = realm
        self.pass_count = counter.counter()
        self.fail_count = counter.counter()

    def match (self, request):
        # by default, use the given handler's matcher
        return self.handler.match (request)

    def handle_request (self, request):
        # authorize a request before handling it...
        scheme = get_header (AUTHORIZATION, request.header)

        if scheme:
            scheme = scheme.lower()
            if scheme == 'basic':
                cookie = get_header (AUTHORIZATION, request.header, 2)
                try:
                    decoded = as_string(decodestring(as_bytes(cookie)))
                except:
                    print_function('malformed authorization info <%s>' % cookie)
                    request.error (400)
                    return
                auth_info = decoded.split(':', 1)
                if self.authorizer.authorize (auth_info):
                    self.pass_count.increment()
                    request.auth_info = auth_info
                    self.handler.handle_request (request)
                else:
                    self.handle_unauthorized (request)
            #elif scheme == 'digest':
            #       print 'digest: ',AUTHORIZATION.group(2)
            else:
                print('unknown/unsupported auth method: %s' % scheme)
                self.handle_unauthorized(request)
        else:
            # list both?  prefer one or the other?
            # you could also use a 'nonce' here. [see below]
            #auth = 'Basic realm="%s" Digest realm="%s"' % (self.realm, self.realm)
            #nonce = self.make_nonce (request)
            #auth = 'Digest realm="%s" nonce="%s"' % (self.realm, nonce)
            #request['WWW-Authenticate'] = auth
            #print 'sending header: %s' % request['WWW-Authenticate']
            self.handle_unauthorized (request)

    def handle_unauthorized (self, request):
        # We are now going to receive data that we want to ignore.
        # to ignore the file data we're not interested in.
        self.fail_count.increment()
        request.channel.set_terminator (None)
        request['Connection'] = 'close'
        request['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
        request.error (401)

    def make_nonce (self, request):
        """A digest-authentication <nonce>, constructed as suggested in RFC 2069"""
        ip = request.channel.server.ip
        now = str(long(time.time()))
        if now[-1:] == 'L':
            now = now[:-1]
        private_key = str (id (self))
        nonce = ':'.join([ip, now, private_key])
        return self.apply_hash (nonce)

    def apply_hash (self, s):
        """Apply MD5 to a string <s>, then wrap it in base64 encoding."""
        m = md5()
        m.update (s)
        d = m.digest()
        # base64.encodestring tacks on an extra linefeed.
        return encodestring (d)[:-1]

    def status (self):
        # Thanks to mwm@contessa.phone.net (Mike Meyer)
        r = [
                producers.simple_producer (
                        '<li>Authorization Extension : '
                        '<b>Unauthorized requests:</b> %s<ul>' % self.fail_count
                        )
                ]
        if hasattr (self.handler, 'status'):
            r.append (self.handler.status())
        r.append (
                producers.simple_producer ('</ul>')
                )
        return producers.composite_producer(r)

class dictionary_authorizer:
    def __init__ (self, dict):
        self.dict = dict

    def authorize (self, auth_info):
        [username, password] = auth_info
        if username in self.dict and self.dict[username] == password:
            return 1
        else:
            return 0

AUTHORIZATION = re.compile (
        #               scheme  challenge
        'Authorization: ([^ ]+) (.*)',
        re.IGNORECASE
        )

########NEW FILE########
__FILENAME__ = chat_server
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1997-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID = '$Id: chat_server.py,v 1.4 2002/03/20 17:37:48 amk Exp $'

VERSION = RCS_ID.split()[2]

import supervisor.medusa.text_socket as socket
import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat
import supervisor.medusa.status_handler as status_handler

class chat_channel (asynchat.async_chat):

    def __init__ (self, server, sock, addr):
        asynchat.async_chat.__init__ (self, sock)
        self.server = server
        self.addr = addr
        self.set_terminator ('\r\n')
        self.data = ''
        self.nick = None
        self.push ('nickname?: ')

    def collect_incoming_data (self, data):
        self.data = self.data + data

    def found_terminator (self):
        line = self.data
        self.data = ''
        if self.nick is None:
            self.nick = line.split()[0]
            if not self.nick:
                self.nick = None
                self.push ('huh? gimme a nickname: ')
            else:
                self.greet()
        else:
            if not line:
                pass
            elif line[0] != '/':
                self.server.push_line (self, line)
            else:
                self.handle_command (line)

    def greet (self):
        self.push ('Hello, %s\r\n' % self.nick)
        num_channels = len(self.server.channels)-1
        if num_channels == 0:
            self.push ('[Kinda lonely in here... you\'re the only caller!]\r\n')
        else:
            self.push ('[There are %d other callers]\r\n' % (len(self.server.channels)-1))
            nicks = [x.get_nick() for x in list(self.server.channels.keys())]
            self.push ('\r\n  '.join(nicks) + '\r\n')
            self.server.push_line (self, '[joined]')

    def handle_command (self, command):
        command_line = command.split()
        name = 'cmd_%s' % command_line[0][1:]
        if hasattr (self, name):
            # make sure it's a method...
            method = getattr (self, name)
            if type(method) == type(self.handle_command):
                method (command_line[1:])
            else:
                self.push ('unknown command: %s' % command_line[0])

    def cmd_quit (self, args):
        self.server.push_line (self, '[left]')
        self.push ('Goodbye!\r\n')
        self.close_when_done()

    # alias for '/quit' - '/q'
    cmd_q = cmd_quit

    def push_line (self, nick, line):
        self.push ('%s: %s\r\n' % (nick, line))

    def handle_close (self):
        self.close()

    def close (self):
        del self.server.channels[self]
        asynchat.async_chat.close (self)

    def get_nick (self):
        if self.nick is not None:
            return self.nick
        else:
            return 'Unknown'

class chat_server (asyncore.dispatcher):

    SERVER_IDENT = 'Chat Server (V%s)' % VERSION

    channel_class = chat_channel

    spy = 1

    def __init__ (self, ip='', port=8518):
        asyncore.dispatcher.__init__(self)
        self.port = port
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.bind ((ip, port))
        print('%s started on port %d' % (self.SERVER_IDENT, port))
        self.listen (5)
        self.channels = {}
        self.count = 0

    def handle_accept (self):
        conn, addr = self.accept()
        self.count += 1
        print('client #%d - %s:%d' % (self.count, addr[0], addr[1]))
        self.channels[self.channel_class (self, conn, addr)] = 1

    def push_line (self, from_channel, line):
        nick = from_channel.get_nick()
        if self.spy:
            print('%s: %s' % (nick, line))
        for c in self.channels.keys():
            if c is not from_channel:
                c.push ('%s: %s\r\n' % (nick, line))

    def status (self):
        lines = [
                '<h2>%s</h2>'                                           % self.SERVER_IDENT,
                '<br>Listening on Port: %d'                     % self.port,
                '<br><b>Total Sessions:</b> %d'         % self.count,
                '<br><b>Current Sessions:</b> %d'       % (len(self.channels))
                ]
        return status_handler.lines_producer (lines)

    def writable (self):
        return 0

if __name__ == '__main__':
    import sys

    if len(sys.argv) > 1:
        port = int(sys.argv[1])
    else:
        port = 8518

    s = chat_server ('', port)
    asyncore.loop()

########NEW FILE########
__FILENAME__ = counter
# -*- Mode: Python -*-

# It is tempting to add an __int__ method to this class, but it's not
# a good idea.  This class tries to gracefully handle integer
# overflow, and to hide this detail from both the programmer and the
# user.  Note that the __str__ method can be relied on for printing out
# the value of a counter:
#
# >>> print 'Total Client: %s' % self.total_clients
#
# If you need to do arithmetic with the value, then use the 'as_long'
# method, the use of long arithmetic is a reminder that the counter
# will overflow.

class counter:
    """general-purpose counter"""

    def __init__ (self, initial_value=0):
        self.value = initial_value

    def increment (self, delta=1):
        result = self.value
        try:
            self.value = self.value + delta
        except OverflowError:
            self.value = long(self.value) + delta
        return result

    def decrement (self, delta=1):
        result = self.value
        try:
            self.value = self.value - delta
        except OverflowError:
            self.value = long(self.value) - delta
        return result

    def as_long (self):
        return long(self.value)

    def __nonzero__ (self):
        return self.value != 0

    __bool__ = __nonzero__

    def __repr__ (self):
        return '<counter value=%s at %x>' % (self.value, id(self))

    def __str__ (self):
        s = str(long(self.value))
        if s[-1:] == 'L':
            s = s[:-1]
        return s


########NEW FILE########
__FILENAME__ = default_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1997 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID = '$Id: default_handler.py,v 1.8 2002/08/01 18:15:45 akuchling Exp $'

# standard python modules
import mimetypes
import re
import stat

# medusa modules
import supervisor.medusa.http_date as http_date
import supervisor.medusa.http_server as http_server
import supervisor.medusa.status_handler as status_handler
import supervisor.medusa.producers as producers

unquote = http_server.unquote

# This is the 'default' handler.  it implements the base set of
# features expected of a simple file-delivering HTTP server.  file
# services are provided through a 'filesystem' object, the very same
# one used by the FTP server.
#
# You can replace or modify this handler if you want a non-standard
# HTTP server.  You can also derive your own handler classes from
# it.
#
# support for handling POST requests is available in the derived
# class <default_with_post_handler>, defined below.
#

from supervisor.medusa.counter import counter

class default_handler:

    valid_commands = ['GET', 'HEAD']

    IDENT = 'Default HTTP Request Handler'

    # Pathnames that are tried when a URI resolves to a directory name
    directory_defaults = [
            'index.html',
            'default.html'
            ]

    default_file_producer = producers.file_producer

    def __init__ (self, filesystem):
        self.filesystem = filesystem
        # count total hits
        self.hit_counter = counter()
        # count file deliveries
        self.file_counter = counter()
        # count cache hits
        self.cache_counter = counter()

    hit_counter = 0

    def __repr__ (self):
        return '<%s (%s hits) at %x>' % (
                self.IDENT,
                self.hit_counter,
                id (self)
                )

    # always match, since this is a default
    def match (self, request):
        return 1

    # handle a file request, with caching.

    def handle_request (self, request):

        if request.command not in self.valid_commands:
            request.error (400) # bad request
            return

        self.hit_counter.increment()

        path, params, query, fragment = request.split_uri()

        if '%' in path:
            path = unquote (path)

        # strip off all leading slashes
        while path and path[0] == '/':
            path = path[1:]

        if self.filesystem.isdir (path):
            if path and path[-1] != '/':
                request['Location'] = 'http://%s/%s/' % (
                        request.channel.server.server_name,
                        path
                        )
                request.error (301)
                return

            # we could also generate a directory listing here,
            # may want to move this into another method for that
            # purpose
            found = 0
            if path and path[-1] != '/':
                path += '/'
            for default in self.directory_defaults:
                p = path + default
                if self.filesystem.isfile (p):
                    path = p
                    found = 1
                    break
            if not found:
                request.error (404) # Not Found
                return

        elif not self.filesystem.isfile (path):
            request.error (404) # Not Found
            return

        file_length = self.filesystem.stat (path)[stat.ST_SIZE]

        ims = get_header_match (IF_MODIFIED_SINCE, request.header)

        length_match = 1
        if ims:
            length = ims.group (4)
            if length:
                try:
                    length = int(length)
                    if length != file_length:
                        length_match = 0
                except:
                    pass

        ims_date = 0

        if ims:
            ims_date = http_date.parse_http_date (ims.group (1))

        try:
            mtime = self.filesystem.stat (path)[stat.ST_MTIME]
        except:
            request.error (404)
            return

        if length_match and ims_date:
            if mtime <= ims_date:
                request.reply_code = 304
                request.done()
                self.cache_counter.increment()
                return
        try:
            file = self.filesystem.open (path, 'rb')
        except IOError:
            request.error (404)
            return

        request['Last-Modified'] = http_date.build_http_date (mtime)
        request['Content-Length'] = file_length
        self.set_content_type (path, request)

        if request.command == 'GET':
            request.push (self.default_file_producer (file))

        self.file_counter.increment()
        request.done()

    def set_content_type (self, path, request):
        typ, encoding = mimetypes.guess_type(path)
        if typ is not None:
            request['Content-Type'] = typ
        else:
            # TODO: test a chunk off the front of the file for 8-bit
            # characters, and use application/octet-stream instead.
            request['Content-Type'] = 'text/plain'

    def status (self):
        return producers.simple_producer (
                '<li>%s' % status_handler.html_repr (self)
                + '<ul>'
                + '  <li><b>Total Hits:</b> %s'                 % self.hit_counter
                + '  <li><b>Files Delivered:</b> %s'    % self.file_counter
                + '  <li><b>Cache Hits:</b> %s'                 % self.cache_counter
                + '</ul>'
                )

# HTTP/1.0 doesn't say anything about the "; length=nnnn" addition
# to this header.  I suppose its purpose is to avoid the overhead
# of parsing dates...
IF_MODIFIED_SINCE = re.compile (
        'If-Modified-Since: ([^;]+)((; length=([0-9]+)$)|$)',
        re.IGNORECASE
        )

USER_AGENT = re.compile ('User-Agent: (.*)', re.IGNORECASE)

CONTENT_TYPE = re.compile (
        r'Content-Type: ([^;]+)((; boundary=([A-Za-z0-9\'\(\)+_,./:=?-]+)$)|$)',
        re.IGNORECASE
        )

get_header = http_server.get_header
get_header_match = http_server.get_header_match

def get_extension (path):
    dirsep = path.rfind('/')
    dotsep = path.rfind('.')
    if dotsep > dirsep:
        return path[dotsep+1:]
    else:
        return ''

########NEW FILE########
__FILENAME__ = publish
# -*- Mode: Python -*-

# Demonstrates use of the auth and put handlers to support publishing
# web pages via HTTP.

# It is also possible to set up the ftp server to do essentially the
# same thing.

# Security Note: Using HTTP with the 'Basic' authentication scheme is
# only slightly more secure than using FTP: both techniques involve
# sending a unencrypted password of the network (http basic auth
# base64-encodes the username and password).  The 'Digest' scheme is
# much more secure, but not widely supported yet. <sigh>

from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import default_handler
from supervisor.medusa import http_server
from supervisor.medusa import put_handler
from supervisor.medusa import auth_handler
from supervisor.medusa import filesys

# For this demo, we'll just use a dictionary of usernames/passwords.
# You can of course use anything that supports the mapping interface,
# and it would be pretty easy to set this up to use the crypt module
# on unix.

users = { 'mozart' : 'jupiter', 'beethoven' : 'pastoral' }

# The filesystem we will be giving access to
fs = filesys.os_filesystem('/home/medusa')

# The 'default' handler - delivers files for the HTTP GET method.
dh = default_handler.default_handler(fs)

# Supports the HTTP PUT method...
ph = put_handler.put_handler(fs, '/.*')

# ... but be sure to wrap it with an auth handler:
ah = auth_handler.auth_handler(users, ph)

# Create a Web Server
hs = http_server.http_server(ip='', port=8080)

# install the handlers we created:

hs.install_handler(dh) # for GET
hs.install_handler(ah) # for PUT

asyncore.loop()

########NEW FILE########
__FILENAME__ = script_server
# -*- Mode: Python -*-

import re, sys
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import http_server
from supervisor.medusa import default_handler
from supervisor.medusa import logger
from supervisor.medusa import script_handler
from supervisor.medusa import filesys

PUBLISHING_ROOT='/home/medusa'
CONTENT_LENGTH = re.compile ('Content-Length: ([0-9]+)', re.IGNORECASE)

class sample_input_collector:
    def __init__ (self, request, length):
        self.request = request
        self.length = length

    def collect_incoming_data (self, data):
        print('data from %s: <%s>' % (self.request, repr(data)))


class post_script_handler (script_handler.script_handler):

    def handle_request (self, request):
        if request.command == 'post':
            cl = default_handler.get_header(CONTENT_LENGTH, request.header)
            ic = sample_input_collector(request, cl)
            request.collector = ic
            print(request.header)

        return script_handler.script_handler.handle_request (self, request)

lg = logger.file_logger (sys.stdout)
fs = filesys.os_filesystem (PUBLISHING_ROOT)
dh = default_handler.default_handler (fs)
ph = post_script_handler (fs)
hs = http_server.http_server ('', 8081, logger_object = lg)

hs.install_handler (dh)
hs.install_handler (ph)

asyncore.loop()

########NEW FILE########
__FILENAME__ = simple_anon_ftpd
# -*- Mode: Python -*-

from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import ftp_server

# create a 'dummy' authorizer (one that lets everyone in) that returns
# a read-only filesystem rooted at '/home/ftp'

authorizer = ftp_server.dummy_authorizer('/home/ftp')

# Create an ftp server using this authorizer, running on port 8021
# [the standard port is 21, but you are probably already running
#  a server there]

fs = ftp_server.ftp_server(authorizer, port=8021)

# Run the async main loop
asyncore.loop()

# to test this server, try
# $ ftp myhost 8021
# when using the standard bsd ftp client,
# $ ncftp -p 8021 myhost
# when using ncftp, and
# ftp://myhost:8021/
# from a web browser.


########NEW FILE########
__FILENAME__ = start_medusa
# -*- Mode: Python -*-

#
# Sample/Template Medusa Startup Script.
#
# This file acts as a configuration file and startup script for Medusa.
#
# You should make a copy of this file, then add, change or comment out
# appropriately.  Then you can start up the server by simply typing
#
# $ python start_medusa.py
#

import os
import sys

from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import http_server
from supervisor.medusa import ftp_server
from supervisor.medusa import chat_server
from supervisor.medusa import monitor
from supervisor.medusa import filesys
from supervisor.medusa import default_handler
from supervisor.medusa import status_handler
from supervisor.medusa import resolver
from supervisor.medusa import logger

if len(sys.argv) > 1:
    # process a few convenient arguments
    [HOSTNAME, IP_ADDRESS, PUBLISHING_ROOT] = sys.argv[1:]
else:
    HOSTNAME                        = 'www.nightmare.com'
    # This is the IP address of the network interface you want
    # your servers to be visible from.  This can be changed to ''
    # to listen on all interfaces.
    IP_ADDRESS                      = ''

    # Root of the http and ftp server's published filesystems.
    PUBLISHING_ROOT         = '/home/www'

HTTP_PORT               = 8080 # The standard port is 80
FTP_PORT                = 8021 # The standard port is 21
CHAT_PORT               = 8888
MONITOR_PORT    = 9999

# ===========================================================================
# Caching DNS Resolver
# ===========================================================================
# The resolver is used to resolve incoming IP address (for logging),
# and also to resolve hostnames for HTTP Proxy requests.  I recommend
# using a nameserver running on the local machine, but you can also
# use a remote nameserver.

rs = resolver.caching_resolver ('127.0.0.1')

# ===========================================================================
# Logging.
# ===========================================================================

# There are several types of logging objects. Multiple loggers may be combined,
# See 'logger.py' for more details.

# This will log to stdout:
lg = logger.file_logger (sys.stdout)

# This will log to syslog:
#lg = logger.syslog_logger ('/dev/log')

# This will wrap the logger so that it will
#  1) keep track of the last 500 entries
#  2) display an entry in the status report with a hyperlink
#     to view these log entries.
#
#  If you decide to comment this out, be sure to remove the
#  logger object from the list of status objects below.
#

lg = status_handler.logger_for_status (lg)

# ===========================================================================
# Filesystem Object.
# ===========================================================================
# An abstraction for the file system.  Filesystem objects can be
# combined and implemented in interesting ways.  The default type
# simply remaps a directory to root.

fs = filesys.os_filesystem (PUBLISHING_ROOT)

# ===========================================================================
# Default HTTP handler
# ===========================================================================

# The 'default' handler for the HTTP server is one that delivers
# files normally - this is the expected behavior of a web server.
# Note that you needn't use it:  Your web server might not want to
# deliver files!

# This default handler uses the filesystem object we just constructed.

dh = default_handler.default_handler (fs)

# ===========================================================================
# HTTP Server
# ===========================================================================
hs = http_server.http_server (IP_ADDRESS, HTTP_PORT, rs, lg)

# Here we install the default handler created above.
hs.install_handler (dh)

# ===========================================================================
# Unix user `public_html' directory support
# ===========================================================================
if os.name == 'posix':
    from supervisor.medusa import unix_user_handler
    uh = unix_user_handler.unix_user_handler ('public_html')
    hs.install_handler (uh)

# ===========================================================================
# FTP Server
# ===========================================================================

# Here we create an 'anonymous' ftp server.
# Note: the ftp server is read-only by default. [in this mode, all
# 'write-capable' commands are unavailable]

ftp = ftp_server.ftp_server (
        ftp_server.anon_authorizer (
                PUBLISHING_ROOT
                ),
        ip=IP_ADDRESS,
        port=FTP_PORT,
        resolver=rs,
        logger_object=lg
        )

# ===========================================================================
# Monitor Server:
# ===========================================================================

# This creates a secure monitor server, binding to the loopback
# address on port 9999, with password 'fnord'.  The monitor server
# can be used to examine and control the server while it is running.
# If you wish to access the server from another machine, you will
# need to use '' or some other IP instead of '127.0.0.1'.
ms = monitor.secure_monitor_server ('fnord', '127.0.0.1', MONITOR_PORT)

# ===========================================================================
# Chat Server
# ===========================================================================

# The chat server is a simple IRC-like server: It is meant as a
# demonstration of how to write new servers and plug them into medusa.
# It's a very simple server (it took about 2 hours to write), but it
# could be easily extended. For example, it could be integrated with
# the web server, perhaps providing navigational tools to browse
# through a series of discussion groups, listing the number of current
# users, authentication, etc...

cs = chat_server.chat_server (IP_ADDRESS, CHAT_PORT)

# ===========================================================================
# Status Handler
# ===========================================================================

# These are objects that can report their status via the HTTP server.
# You may comment out any of these, or add more of your own.  The only
# requirement for a 'status-reporting' object is that it have a method
# 'status' that will return a producer, which will generate an HTML
# description of the status of the object.

status_objects = [
        hs,
        ftp,
        ms,
        cs,
        rs,
        lg
        ]

# Create a status handler.  By default it binds to the URI '/status'...
sh = status_handler.status_extension(status_objects)
# ... and install it on the web server.
hs.install_handler (sh)

# become 'nobody'
try:
    if os.name == 'posix':
        if hasattr (os, 'seteuid'):
            import pwd
            [uid, gid] = pwd.getpwnam ('nobody')[2:4]
            os.setegid (gid)
            os.seteuid (uid)
except Exception:
    pass

# Finally, start up the server loop!  This loop will not exit until
# all clients and servers are closed.  You may cleanly shut the system
# down by sending SIGINT (a.k.a. KeyboardInterrupt).
asyncore.loop()

########NEW FILE########
__FILENAME__ = winFTPserver
#
# winFTPServer.py -- FTP server that uses Win32 user API
#
# Contributed by John Abel
#
# For it to authenticate users correctly, the user running the
# script must be added to the security policy "Act As Part Of The OS".
# This is needed for the LogonUser to work.  A pain, but something that MS
# forgot to mention in the API.


import win32security, win32con, win32api, win32net
import ntsecuritycon, pywintypes
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import ftp_server, filesys

class Win32Authorizer:


    def authorize (self, channel, userName, passWord):
        self.AdjustPrivilege( ntsecuritycon.SE_CHANGE_NOTIFY_NAME )
        self.AdjustPrivilege( ntsecuritycon.SE_ASSIGNPRIMARYTOKEN_NAME )
        self.AdjustPrivilege( ntsecuritycon.SE_TCB_NAME )
        try:
            logonHandle = win32security.LogonUser( userName,
                                                   None,
                                                   passWord,
                                                    win32con.LOGON32_LOGON_INTERACTIVE,
                                                    win32con.LOGON32_PROVIDER_DEFAULT )
        except pywintypes.error, ErrorMsg:
            return 0, ErrorMsg.args[2], None

        userInfo = win32net.NetUserGetInfo( None, userName, 1 )

        return 1, 'Login successful', filesys.os_filesystem( userInfo[ 'home_dir' ] )

    def AdjustPrivilege( self, priv ):
        flags = ntsecuritycon.TOKEN_ADJUST_PRIVILEGES | ntsecuritycon.TOKEN_QUERY
        htoken =  win32security.OpenProcessToken(win32api.GetCurrentProcess(), flags)
        id = win32security.LookupPrivilegeValue(None, priv)
        newPrivileges = [(id, ntsecuritycon.SE_PRIVILEGE_ENABLED)]
        win32security.AdjustTokenPrivileges(htoken, 0, newPrivileges)

def start_Server():
#    ftpServ = ftp_server.ftp_server( ftp_server.anon_authorizer( "D:\MyDocuments\MyDownloads"), port=21 )
    ftpServ = ftp_server.ftp_server( Win32Authorizer(), port=21 )
    asyncore.loop()

if __name__ == "__main__":
    print "Starting FTP Server"
    start_Server()

########NEW FILE########
__FILENAME__ = event_loop
# -*- Mode: Python -*-

# This is an alternative event loop that supports 'schedulable events'.
# You can specify an event callback to take place after <n> seconds.

# Important usage note: The granularity of the time-check is limited
# by the <timeout> argument to 'go()'; if there is little or no
# activity and you specify a 30-second timeout interval, then the
# schedule of events may only be checked at those 30-second intervals.
# In other words, if you need 1-second resolution, you will have to
# poll at 1-second intervals.  This facility is more useful for longer
# timeouts ("if the channel doesn't close in 5 minutes, then forcibly
# close it" would be a typical usage).

import supervisor.medusa.asyncore_25 as asyncore
import bisect
import time

socket_map = asyncore.socket_map

class event_loop:

    def __init__ (self):
        self.events = []
        self.num_channels = 0
        self.max_channels = 0

    def go (self, timeout=30.0, granularity=15):
        global socket_map
        last_event_check = 0
        while socket_map:
            now = int(time.time())
            if (now - last_event_check) >= granularity:
                last_event_check = now
                fired = []
                # yuck. i want my lisp.
                i = j = 0
                while i < len(self.events):
                    when, what = self.events[i]
                    if now >= when:
                        fired.append (what)
                        j = i + 1
                    else:
                        break
                    i += 1
                if fired:
                    self.events = self.events[j:]
                    for what in fired:
                        what (self, now)
            # sample the number of channels
            n = len(asyncore.socket_map)
            self.num_channels = n
            if n > self.max_channels:
                self.max_channels = n
            asyncore.poll (timeout)

    def schedule (self, delta, callback):
        now = int (time.time())
        bisect.insort (self.events, (now + delta, callback))

    def __len__ (self):
        return len(self.events)

class test (asyncore.dispatcher):

    def __init__ (self):
        asyncore.dispatcher.__init__ (self)

    def handle_connect (self):
        print('Connected!')

    def writable (self):
        return not self.connected

    def connect_timeout_callback (self, event_loop, when):
        if not self.connected:
            print('Timeout on connect')
            self.close()

    def periodic_thing_callback (self, event_loop, when):
        print('A Periodic Event has Occurred!')
        # re-schedule it.
        event_loop.schedule (self, 15, self.periodic_thing_callback)

if __name__ == '__main__':
    import supervisor.medusa.text_socket as socket
    el = event_loop()
    t = test ()
    t.create_socket (socket.AF_INET, socket.SOCK_STREAM)
    el.schedule (10, t.connect_timeout_callback)
    el.schedule (15, t.periodic_thing_callback)
    t.connect (('squirl', 80))
    el.go(1.0)

########NEW FILE########
__FILENAME__ = filesys
# -*- Mode: Python -*-
#       $Id: filesys.py,v 1.9 2003/12/24 16:10:56 akuchling Exp $
#       Author: Sam Rushing <rushing@nightmare.com>
#
# Generic filesystem interface.
#

# We want to provide a complete wrapper around any and all
# filesystem operations.

# this class is really just for documentation,
# identifying the API for a filesystem object.

# opening files for reading, and listing directories, should
# return a producer.

from supervisor.compat import long

class abstract_filesystem:
    def __init__ (self):
        pass

    def current_directory (self):
        """Return a string representing the current directory."""
        pass

    def listdir (self, path, long=0):
        """Return a listing of the directory at 'path' The empty string
        indicates the current directory.  If 'long' is set, instead
        return a list of (name, stat_info) tuples
        """
        pass

    def open (self, path, mode):
        """Return an open file object"""
        pass

    def stat (self, path):
        """Return the equivalent of os.stat() on the given path."""
        pass

    def isdir (self, path):
        """Does the path represent a directory?"""
        pass

    def isfile (self, path):
        """Does the path represent a plain file?"""
        pass

    def cwd (self, path):
        """Change the working directory."""
        pass

    def cdup (self):
        """Change to the parent of the current directory."""
        pass


    def longify (self, path):
        """Return a 'long' representation of the filename
        [for the output of the LIST command]"""
        pass

# standard wrapper around a unix-like filesystem, with a 'false root'
# capability.

# security considerations: can symbolic links be used to 'escape' the
# root?  should we allow it?  if not, then we could scan the
# filesystem on startup, but that would not help if they were added
# later.  We will probably need to check for symlinks in the cwd method.

# what to do if wd is an invalid directory?

import os
import stat
import re

def safe_stat (path):
    try:
        return path, os.stat (path)
    except:
        return None

class os_filesystem:
    path_module = os.path

    # set this to zero if you want to disable pathname globbing.
    # [we currently don't glob, anyway]
    do_globbing = 1

    def __init__ (self, root, wd='/'):
        self.root = root
        self.wd = wd

    def current_directory (self):
        return self.wd

    def isfile (self, path):
        p = self.normalize (self.path_module.join (self.wd, path))
        return self.path_module.isfile (self.translate(p))

    def isdir (self, path):
        p = self.normalize (self.path_module.join (self.wd, path))
        return self.path_module.isdir (self.translate(p))

    def cwd (self, path):
        p = self.normalize (self.path_module.join (self.wd, path))
        translated_path = self.translate(p)
        if not self.path_module.isdir (translated_path):
            return 0
        else:
            old_dir = os.getcwd()
            # temporarily change to that directory, in order
            # to see if we have permission to do so.
            can = 0
            try:
                try:
                    os.chdir (translated_path)
                    can = 1
                    self.wd = p
                except:
                    pass
            finally:
                if can:
                    os.chdir (old_dir)
            return can

    def cdup (self):
        return self.cwd ('..')

    def listdir (self, path, long=0):
        p = self.translate (path)
        # I think we should glob, but limit it to the current
        # directory only.
        ld = os.listdir (p)
        if not long:
            return list_producer (ld, None)
        else:
            old_dir = os.getcwd()
            try:
                os.chdir (p)
                # if os.stat fails we ignore that file.
                result = [_f for _f in map (safe_stat, ld) if _f]
            finally:
                os.chdir (old_dir)
            return list_producer (result, self.longify)

    # TODO: implement a cache w/timeout for stat()
    def stat (self, path):
        p = self.translate (path)
        return os.stat (p)

    def open (self, path, mode):
        p = self.translate (path)
        return open (p, mode)

    def unlink (self, path):
        p = self.translate (path)
        return os.unlink (p)

    def mkdir (self, path):
        p = self.translate (path)
        return os.mkdir (p)

    def rmdir (self, path):
        p = self.translate (path)
        return os.rmdir (p)

    def rename(self, src, dst):
        return os.rename(self.translate(src),self.translate(dst))

    # utility methods
    def normalize (self, path):
        # watch for the ever-sneaky '/+' path element
        path = re.sub('/+', '/', path)
        p = self.path_module.normpath (path)
        # remove 'dangling' cdup's.
        if len(p) > 2 and p[:3] == '/..':
            p = '/'
        return p

    def translate (self, path):
        # we need to join together three separate
        # path components, and do it safely.
        # <real_root>/<current_directory>/<path>
        # use the operating system's path separator.
        path = os.sep.join(path.split('/'))
        p = self.normalize (self.path_module.join (self.wd, path))
        p = self.normalize (self.path_module.join (self.root, p[1:]))
        return p

    def longify (self, path_stat_info_tuple):
        (path, stat_info) = path_stat_info_tuple
        return unix_longify (path, stat_info)

    def __repr__ (self):
        return '<unix-style fs root:%s wd:%s>' % (
                self.root,
                self.wd
                )

if os.name == 'posix':

    class unix_filesystem (os_filesystem):
        pass

    class schizophrenic_unix_filesystem (os_filesystem):
        PROCESS_UID     = os.getuid()
        PROCESS_EUID    = os.geteuid()
        PROCESS_GID     = os.getgid()
        PROCESS_EGID    = os.getegid()

        def __init__ (self, root, wd='/', persona=(None, None)):
            os_filesystem.__init__ (self, root, wd)
            self.persona = persona

        def become_persona (self):
            if self.persona is not (None, None):
                uid, gid = self.persona
                # the order of these is important!
                os.setegid (gid)
                os.seteuid (uid)

        def become_nobody (self):
            if self.persona is not (None, None):
                os.seteuid (self.PROCESS_UID)
                os.setegid (self.PROCESS_GID)

        # cwd, cdup, open, listdir
        def cwd (self, path):
            try:
                self.become_persona()
                return os_filesystem.cwd (self, path)
            finally:
                self.become_nobody()

        def cdup (self):
            try:
                self.become_persona()
                return os_filesystem.cdup (self)
            finally:
                self.become_nobody()

        def open (self, filename, mode):
            try:
                self.become_persona()
                return os_filesystem.open (self, filename, mode)
            finally:
                self.become_nobody()

        def listdir (self, path, long=0):
            try:
                self.become_persona()
                return os_filesystem.listdir (self, path, long)
            finally:
                self.become_nobody()

# For the 'real' root, we could obtain a list of drives, and then
# use that.  Doesn't win32 provide such a 'real' filesystem?
# [yes, I think something like this "\\.\c\windows"]

class msdos_filesystem (os_filesystem):
    def longify (self, path_stat_info_tuple):
        (path, stat_info) = path_stat_info_tuple
        return msdos_longify (path, stat_info)

# A merged filesystem will let you plug other filesystems together.
# We really need the equivalent of a 'mount' capability - this seems
# to be the most general idea.  So you'd use a 'mount' method to place
# another filesystem somewhere in the hierarchy.

# Note: this is most likely how I will handle ~user directories
# with the http server.

class merged_filesystem:
    def __init__ (self, *fsys):
        pass

# this matches the output of NT's ftp server (when in
# MSDOS mode) exactly.

def msdos_longify (file, stat_info):
    if stat.S_ISDIR (stat_info[stat.ST_MODE]):
        dir = '<DIR>'
    else:
        dir = '     '
    date = msdos_date (stat_info[stat.ST_MTIME])
    return '%s       %s %8d %s' % (
            date,
            dir,
            stat_info[stat.ST_SIZE],
            file
            )

def msdos_date (t):
    try:
        info = time.gmtime (t)
    except:
        info = time.gmtime (0)
    # year, month, day, hour, minute, second, ...
    hour = info[3]
    if hour > 11:
        merid = 'PM'
        hour -= 12
    else:
        merid = 'AM'
    return '%02d-%02d-%02d  %02d:%02d%s' % (
            info[1],
            info[2],
            info[0]%100,
            hour,
            info[4],
            merid
            )

months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

mode_table = {
        '0':'---',
        '1':'--x',
        '2':'-w-',
        '3':'-wx',
        '4':'r--',
        '5':'r-x',
        '6':'rw-',
        '7':'rwx'
        }

import time

def unix_longify (file, stat_info):
    # for now, only pay attention to the lower bits
    mode = ('%o' % stat_info[stat.ST_MODE])[-3:]
    mode = ''.join([mode_table[x] for x in mode])
    if stat.S_ISDIR (stat_info[stat.ST_MODE]):
        dirchar = 'd'
    else:
        dirchar = '-'
    date = ls_date (long(time.time()), stat_info[stat.ST_MTIME])
    return '%s%s %3d %-8d %-8d %8d %s %s' % (
            dirchar,
            mode,
            stat_info[stat.ST_NLINK],
            stat_info[stat.ST_UID],
            stat_info[stat.ST_GID],
            stat_info[stat.ST_SIZE],
            date,
            file
            )

# Emulate the unix 'ls' command's date field.
# it has two formats - if the date is more than 180
# days in the past, then it's like this:
# Oct 19  1995
# otherwise, it looks like this:
# Oct 19 17:33

def ls_date (now, t):
    try:
        info = time.gmtime (t)
    except:
        info = time.gmtime (0)
    # 15,600,000 == 86,400 * 180
    if (now - t) > 15600000:
        return '%s %2d  %d' % (
                months[info[1]-1],
                info[2],
                info[0]
                )
    else:
        return '%s %2d %02d:%02d' % (
                months[info[1]-1],
                info[2],
                info[3],
                info[4]
                )

# ===========================================================================
# Producers
# ===========================================================================

class list_producer:
    def __init__ (self, list, func=None):
        self.list = list
        self.func = func

    # this should do a pushd/popd
    def more (self):
        if not self.list:
            return ''
        else:
            # do a few at a time
            bunch = self.list[:50]
            if self.func is not None:
                bunch = map (self.func, bunch)
            self.list = self.list[50:]
            return '\r\n'.join(bunch) + '\r\n'


########NEW FILE########
__FILENAME__ = ftp_server
# -*- Mode: Python -*-

#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: ftp_server.py,v 1.11 2003/12/24 16:05:28 akuchling Exp $'

# An extensible, configurable, asynchronous FTP server.
#
# All socket I/O is non-blocking, however file I/O is currently
# blocking.  Eventually file I/O may be made non-blocking, too, if it
# seems necessary.  Currently the only CPU-intensive operation is
# getting and formatting a directory listing.  [this could be moved
# into another process/directory server, or another thread?]
#
# Only a subset of RFC 959 is implemented, but much of that RFC is
# vestigial anyway.  I've attempted to include the most commonly-used
# commands, using the feature set of wu-ftpd as a guide.

import os
import stat
import sys
import time

from supervisor.compat import letters

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat
import supervisor.medusa.text_socket as socket
from supervisor.medusa.producers import file_producer

# TODO: implement a directory listing cache.  On very-high-load
# servers this could save a lot of disk abuse, and possibly the
# work of computing emulated unix ls output.

# Potential security problem with the FTP protocol?  I don't think
# there's any verification of the origin of a data connection.  Not
# really a problem for the server (since it doesn't send the port
# command, except when in PASV mode) But I think a data connection
# could be spoofed by a program with access to a sniffer - it could
# watch for a PORT command to go over a command channel, and then
# connect to that port before the server does.

# Unix user id's:
# In order to support assuming the id of a particular user,
# it seems there are two options:
# 1) fork, and seteuid in the child
# 2) carefully control the effective uid around filesystem accessing
#    methods, using try/finally. [this seems to work]

VERSION = RCS_ID.split()[2]

from supervisor.medusa.counter import counter
import supervisor.medusa.producers as producers
import supervisor.medusa.status_handler as status_handler
import supervisor.medusa.logger as logger

class ftp_channel (asynchat.async_chat):

    # defaults for a reliable __repr__
    addr = ('unknown','0')

    # unset this in a derived class in order
    # to enable the commands in 'self.write_commands'
    read_only = 1
    write_commands = ['appe','dele','mkd','rmd','rnfr','rnto','stor','stou']

    restart_position = 0

    # comply with (possibly troublesome) RFC959 requirements
    # This is necessary to correctly run an active data connection
    # through a firewall that triggers on the source port (expected
    # to be 'L-1', or 20 in the normal case).
    bind_local_minus_one = 0

    def __init__ (self, server, conn, addr):
        self.server = server
        self.current_mode = 'a'
        self.addr = addr
        asynchat.async_chat.__init__ (self, conn)
        self.set_terminator ('\r\n')

        # client data port.  Defaults to 'the same as the control connection'.
        self.client_addr = (addr[0], 21)

        self.client_dc = None
        self.in_buffer = ''
        self.closing = 0
        self.passive_acceptor = None
        self.passive_connection = None
        self.filesystem = None
        self.authorized = 0
        # send the greeting
        self.respond (
                '220 %s FTP server (Medusa Async V%s [experimental]) ready.' % (
                        self.server.hostname,
                        VERSION
                        )
                )

#       def __del__ (self):
#               print 'ftp_channel.__del__()'

    # --------------------------------------------------
    # async-library methods
    # --------------------------------------------------

    def handle_expt (self):
        # this is handled below.  not sure what I could
        # do here to make that code less kludgish.
        pass

    def collect_incoming_data (self, data):
        self.in_buffer = self.in_buffer + data
        if len(self.in_buffer) > 4096:
            # silently truncate really long lines
            # (possible denial-of-service attack)
            self.in_buffer = ''

    def found_terminator (self):

        line = self.in_buffer

        if not len(line):
            return

        sp = line.find(' ')
        if sp != -1:
            line = [line[:sp], line[sp+1:]]
        else:
            line = [line]

        command = line[0].lower()
        # watch especially for 'urgent' abort commands.
        if command.find('abor') != -1:
            # strip off telnet sync chars and the like...
            while command and command[0] not in letters:
                command = command[1:]

        fun_name = 'cmd_%s' % command
        if command != 'pass':
            self.log ('<== %s' % repr(self.in_buffer)[1:-1])
        else:
            self.log ('<== %s' % line[0]+' <password>')
        self.in_buffer = ''
        if not hasattr (self, fun_name):
            self.command_not_understood (line[0])
            return
        if hasattr(self,'_rnfr_src') and fun_name!='cmd_rnto':
            del self._rnfr_src
            self.respond ('503 RNTO Command expected!')
            return

        fun = getattr (self, fun_name)
        if (not self.authorized) and (command not in ('user', 'pass', 'help', 'quit')):
            self.respond ('530 Please log in with USER and PASS')
        elif not self.check_command_authorization (command):
            self.command_not_authorized (command)
        else:
            try:
                fun(*(line,))
            except:
                self.server.total_exceptions.increment()
                (file, fun, line), t,v, tbinfo = asyncore.compact_traceback()
                if self.client_dc:
                    try:
                        self.client_dc.close()
                    except:
                        pass
                self.respond (
                        '451 Server Error: %s, %s: file: %s line: %s' % (
                                t,v,file,line,
                                )
                        )

    closed = 0
    def close (self):
        if not self.closed:
            self.closed = 1
            if self.passive_acceptor:
                self.passive_acceptor.close()
            if self.client_dc:
                self.client_dc.close()
            self.server.closed_sessions.increment()
            asynchat.async_chat.close (self)

    # --------------------------------------------------
    # filesystem interface functions.
    # override these to provide access control or perform
    # other functions.
    # --------------------------------------------------

    def cwd (self, line):
        return self.filesystem.cwd (line[1])

    def cdup (self, line):
        return self.filesystem.cdup()

    def open (self, path, mode):
        return self.filesystem.open (path, mode)

    # returns a producer
    def listdir (self, path, long=0):
        return self.filesystem.listdir (path, long)

    def get_dir_list (self, line, long=0):
        # we need to scan the command line for arguments to '/bin/ls'...
        args = line[1:]
        path_args = []
        for arg in args:
            if arg[0] != '-':
                path_args.append (arg)
            else:
                # ignore arguments
                pass
        if len(path_args) < 1:
            dir = '.'
        else:
            dir = path_args[0]
        return self.listdir (dir, long)

    # --------------------------------------------------
    # authorization methods
    # --------------------------------------------------

    def check_command_authorization (self, command):
        if command in self.write_commands and self.read_only:
            return 0
        else:
            return 1

    # --------------------------------------------------
    # utility methods
    # --------------------------------------------------

    def log (self, message):
        self.server.logger.log (
                self.addr[0],
                '%d %s' % (
                        self.addr[1], message
                        )
                )

    def respond (self, resp):
        self.log ('==> %s' % resp)
        self.push (resp + '\r\n')

    def command_not_understood (self, command):
        self.respond ("500 '%s': command not understood." % command)

    def command_not_authorized (self, command):
        self.respond (
                "530 You are not authorized to perform the '%s' command" % (
                        command
                        )
                )

    def make_xmit_channel (self):
        # In PASV mode, the connection may or may _not_ have been made
        # yet.  [although in most cases it is... FTP Explorer being
        # the only exception I've yet seen].  This gets somewhat confusing
        # because things may happen in any order...
        pa = self.passive_acceptor
        if pa:
            if pa.ready:
                # a connection has already been made.
                conn, addr = self.passive_acceptor.ready
                cdc = xmit_channel (self, addr)
                cdc.set_socket (conn)
                cdc.connected = 1
                self.passive_acceptor.close()
                self.passive_acceptor = None
            else:
                # we're still waiting for a connect to the PASV port.
                cdc = xmit_channel (self)
        else:
            # not in PASV mode.
            ip, port = self.client_addr
            cdc = xmit_channel (self, self.client_addr)
            cdc.create_socket (socket.AF_INET, socket.SOCK_STREAM)
            if self.bind_local_minus_one:
                cdc.bind (('', self.server.port - 1))
            try:
                cdc.connect ((ip, port))
            except socket.error:
                self.respond ("425 Can't build data connection")
        self.client_dc = cdc

    # pretty much the same as xmit, but only right on the verge of
    # being worth a merge.
    def make_recv_channel (self, fd):
        pa = self.passive_acceptor
        if pa:
            if pa.ready:
                # a connection has already been made.
                conn, addr = pa.ready
                cdc = recv_channel (self, addr, fd)
                cdc.set_socket (conn)
                cdc.connected = 1
                self.passive_acceptor.close()
                self.passive_acceptor = None
            else:
                # we're still waiting for a connect to the PASV port.
                cdc = recv_channel (self, None, fd)
        else:
            # not in PASV mode.
            ip, port = self.client_addr
            cdc = recv_channel (self, self.client_addr, fd)
            cdc.create_socket (socket.AF_INET, socket.SOCK_STREAM)
            try:
                cdc.connect ((ip, port))
            except socket.error:
                self.respond ("425 Can't build data connection")
        self.client_dc = cdc

    type_map = {
            'a':'ASCII',
            'i':'Binary',
            'e':'EBCDIC',
            'l':'Binary'
            }

    type_mode_map = {
            'a':'t',
            'i':'b',
            'e':'b',
            'l':'b'
            }

    # --------------------------------------------------
    # command methods
    # --------------------------------------------------

    def cmd_type (self, line):
        """specify data transfer type"""
        # ascii, ebcdic, image, local <byte size>
        t = line[1].lower()
        # no support for EBCDIC
        # if t not in ['a','e','i','l']:
        if t not in ['a','i','l']:
            self.command_not_understood(line.join())
        elif t == 'l' and (len(line) > 2 and line[2] != '8'):
            self.respond ('504 Byte size must be 8')
        else:
            self.current_mode = t
            self.respond ('200 Type set to %s.' % self.type_map[t])


    def cmd_quit (self, line):
        """terminate session"""
        self.respond ('221 Goodbye.')
        self.close_when_done()

    def cmd_port (self, line):
        """specify data connection port"""
        info = line[1].split (',')
        ip = '.'.join (info[:4])
        port = int(info[4])*256 + int(info[5])
        # how many data connections at a time?
        # I'm assuming one for now...
        # TODO: we should (optionally) verify that the
        # ip number belongs to the client.  [wu-ftpd does this?]
        self.client_addr = (ip, port)
        self.respond ('200 PORT command successful.')

    def new_passive_acceptor (self):
        # ensure that only one of these exists at a time.
        if self.passive_acceptor is not None:
            self.passive_acceptor.close()
            self.passive_acceptor = None
        self.passive_acceptor = passive_acceptor (self)
        return self.passive_acceptor

    def cmd_pasv (self, line):
        """prepare for server-to-server transfer"""
        pc = self.new_passive_acceptor()
        port = pc.addr[1]
        ip_addr = pc.control_channel.getsockname()[0]
        self.respond (
                '227 Entering Passive Mode (%s,%d,%d)' % (
                        ip_addr.replace('.', ','),
                        port/256,
                        port%256
                        )
                )
        self.client_dc = None

    def cmd_nlst (self, line):
        """give name list of files in directory"""
        # ncftp adds the -FC argument for the user-visible 'nlist'
        # command.  We could try to emulate ls flags, but not just yet.
        if '-FC' in line:
            line.remove ('-FC')
        try:
            dir_list_producer = self.get_dir_list (line, 0)
        except os.error as why:
            self.respond ('550 Could not list directory: %s' % why)
            return
        self.respond (
                '150 Opening %s mode data connection for file list' % (
                        self.type_map[self.current_mode]
                        )
                )
        self.make_xmit_channel()
        self.client_dc.push_with_producer (dir_list_producer)
        self.client_dc.close_when_done()

    def cmd_list (self, line):
        """give a list of files in a directory"""
        try:
            dir_list_producer = self.get_dir_list (line, 1)
        except os.error as why:
            self.respond ('550 Could not list directory: %s' % why)
            return
        self.respond (
                '150 Opening %s mode data connection for file list' % (
                        self.type_map[self.current_mode]
                        )
                )
        self.make_xmit_channel()
        self.client_dc.push_with_producer (dir_list_producer)
        self.client_dc.close_when_done()

    def cmd_cwd (self, line):
        """change working directory"""
        if self.cwd (line):
            self.respond ('250 CWD command successful.')
        else:
            self.respond ('550 No such directory.')

    def cmd_cdup (self, line):
        """change to parent of current working directory"""
        if self.cdup(line):
            self.respond ('250 CDUP command successful.')
        else:
            self.respond ('550 No such directory.')

    def cmd_pwd (self, line):
        """print the current working directory"""
        self.respond (
                '257 "%s" is the current directory.' % (
                        self.filesystem.current_directory()
                        )
                )

    # modification time
    # example output:
    # 213 19960301204320
    def cmd_mdtm (self, line):
        """show last modification time of file"""
        filename = line[1]
        if not self.filesystem.isfile (filename):
            self.respond ('550 "%s" is not a file' % filename)
        else:
            mtime = time.gmtime(self.filesystem.stat(filename)[stat.ST_MTIME])
            self.respond (
                    '213 %4d%02d%02d%02d%02d%02d' % (
                            mtime[0],
                            mtime[1],
                            mtime[2],
                            mtime[3],
                            mtime[4],
                            mtime[5]
                            )
                    )

    def cmd_noop (self, line):
        """do nothing"""
        self.respond ('200 NOOP command successful.')

    def cmd_size (self, line):
        """return size of file"""
        filename = line[1]
        if not self.filesystem.isfile (filename):
            self.respond ('550 "%s" is not a file' % filename)
        else:
            self.respond (
                    '213 %d' % (self.filesystem.stat(filename)[stat.ST_SIZE])
                    )

    def cmd_retr (self, line):
        """retrieve a file"""
        if len(line) < 2:
            self.command_not_understood(line.join())
        else:
            file = line[1]
            if not self.filesystem.isfile (file):
                self.log_info ('checking %s' % file)
                self.respond ('550 No such file')
            else:
                try:
                    # FIXME: for some reason, 'rt' isn't working on win95
                    mode = 'r'+self.type_mode_map[self.current_mode]
                    fd = self.open (file, mode)
                except IOError as why:
                    self.respond ('553 could not open file for reading: %s' % (repr(why)))
                    return
                self.respond (
                        "150 Opening %s mode data connection for file '%s'" % (
                                self.type_map[self.current_mode],
                                file
                                )
                        )
                self.make_xmit_channel()

                if self.restart_position:
                    # try to position the file as requested, but
                    # give up silently on failure (the 'file object'
                    # may not support seek())
                    try:
                        fd.seek (self.restart_position)
                    except:
                        pass
                    self.restart_position = 0

                self.client_dc.push_with_producer (
                        file_producer (fd)
                        )
                self.client_dc.close_when_done()

    def cmd_stor (self, line, mode='wb'):
        """store a file"""
        if len (line) < 2:
            self.command_not_understood(line.join())
        else:
            if self.restart_position:
                self.restart_position = 0
                self.respond ('553 restart on STOR not yet supported')
                return
            file = line[1]
            # todo: handle that type flag
            try:
                fd = self.open (file, mode)
            except IOError as why:
                self.respond ('553 could not open file for writing: %s' % (repr(why)))
                return
            self.respond (
                    '150 Opening %s connection for %s' % (
                            self.type_map[self.current_mode],
                            file
                            )
                    )
            self.make_recv_channel (fd)

    def cmd_abor (self, line):
        """abort operation"""
        if self.client_dc:
            self.client_dc.close()
        self.respond ('226 ABOR command successful.')

    def cmd_appe (self, line):
        """append to a file"""
        return self.cmd_stor (line, 'ab')

    def cmd_dele (self, line):
        if len (line) != 2:
            self.command_not_understood(line.join())
        else:
            file = line[1]
            if self.filesystem.isfile (file):
                try:
                    self.filesystem.unlink (file)
                    self.respond ('250 DELE command successful.')
                except:
                    self.respond ('550 error deleting file.')
            else:
                self.respond ('550 %s: No such file.' % file)

    def cmd_mkd (self, line):
        if len (line) != 2:
            self.command_not_understood(line.join())
        else:
            path = line[1]
            try:
                self.filesystem.mkdir (path)
                self.respond ('257 MKD command successful.')
            except:
                self.respond ('550 error creating directory.')

    def cmd_rnfr (self, line):
        if not hasattr(self.filesystem,'rename'):
            self.respond('502 RNFR not implemented.')
            return

        if len(line)!=2:
            self.command_not_understood(line.join())
        else:
            src = line[1]
            try:
                assert self.filesystem.isfile(src)
                self._rfnr_src = src
                self.respond('350 RNFR file exists, ready for destination name.')
            except:
                self.respond('550 %s: No such file.' % src)

    def cmd_rnto (self, line):
        src = getattr(self,'_rfnr_src',None)
        if not src:
            self.respond('503 RNTO command unexpected.')
            return

        if len(line)!=2:
            self.command_not_understood(line.join())
        else:
            dst = line[1]
            try:
                self.filesystem.rename(src,dst)
                self.respond('250 RNTO command successful.')
            except:
                t, v = sys.exc_info[:2]
                self.respond('550 %s: %s.' % (str(t),str(v)))
        try:
            del self._rfnr_src
        except:
            pass

    def cmd_rmd (self, line):
        if len (line) != 2:
            self.command_not_understood(line.join())
        else:
            path = line[1]
            try:
                self.filesystem.rmdir (path)
                self.respond ('250 RMD command successful.')
            except:
                self.respond ('550 error removing directory.')

    def cmd_user (self, line):
        """specify user name"""
        if len(line) > 1:
            self.user = line[1]
            self.respond ('331 Password required.')
        else:
            self.command_not_understood(line.join())

    def cmd_pass (self, line):
        """specify password"""
        if len(line) < 2:
            pw = ''
        else:
            pw = line[1]
        result, message, fs = self.server.authorizer.authorize (self, self.user, pw)
        if result:
            self.respond ('230 %s' % message)
            self.filesystem = fs
            self.authorized = 1
            self.log_info('Successful login: Filesystem=%s' % repr(fs))
        else:
            self.respond ('530 %s' % message)

    def cmd_rest (self, line):
        """restart incomplete transfer"""
        try:
            pos = int(line[1])
        except ValueError:
            self.command_not_understood(line.join())
        self.restart_position = pos
        self.respond ('350 Restarting at %d. Send STORE or RETRIEVE to initiate transfer.' % pos)

    def cmd_stru (self, line):
        """obsolete - set file transfer structure"""
        if line[1] in 'fF':
            # f == 'file'
            self.respond ('200 STRU F Ok')
        else:
            self.respond ('504 Unimplemented STRU type')

    def cmd_mode (self, line):
        """obsolete - set file transfer mode"""
        if line[1] in 'sS':
            # f == 'file'
            self.respond ('200 MODE S Ok')
        else:
            self.respond ('502 Unimplemented MODE type')

# The stat command has two personalities.  Normally it returns status
# information about the current connection.  But if given an argument,
# it is equivalent to the LIST command, with the data sent over the
# control connection.  Strange.  But wuftpd, ftpd, and nt's ftp server
# all support it.
#
##      def cmd_stat (self, line):
##              'return status of server'
##              pass

    def cmd_syst (self, line):
        """show operating system type of server system"""
        # Replying to this command is of questionable utility, because
        # this server does not behave in a predictable way w.r.t. the
        # output of the LIST command.  We emulate Unix ls output, but
        # on win32 the pathname can contain drive information at the front
        # Currently, the combination of ensuring that os.sep == '/'
        # and removing the leading slash when necessary seems to work.
        # [cd'ing to another drive also works]
        #
        # This is how wuftpd responds, and is probably
        # the most expected.  The main purpose of this reply is so that
        # the client knows to expect Unix ls-style LIST output.
        self.respond ('215 UNIX Type: L8')
        # one disadvantage to this is that some client programs
        # assume they can pass args to /bin/ls.
        # a few typical responses:
        # 215 UNIX Type: L8 (wuftpd)
        # 215 Windows_NT version 3.51
        # 215 VMS MultiNet V3.3
        # 500 'SYST': command not understood. (SVR4)

    def cmd_help (self, line):
        """give help information"""
        # find all the methods that match 'cmd_xxxx',
        # use their docstrings for the help response.
        attrs = dir(self.__class__)
        help_lines = []
        for attr in attrs:
            if attr[:4] == 'cmd_':
                x = getattr (self, attr)
                if type(x) == type(self.cmd_help):
                    if x.__doc__:
                        help_lines.append ('\t%s\t%s' % (attr[4:], x.__doc__))
        if help_lines:
            self.push ('214-The following commands are recognized\r\n')
            self.push_with_producer (producers.lines_producer (help_lines))
            self.push ('214\r\n')
        else:
            self.push ('214-\r\n\tHelp Unavailable\r\n214\r\n')

class ftp_server (asyncore.dispatcher):
    # override this to spawn a different FTP channel class.
    ftp_channel_class = ftp_channel

    SERVER_IDENT = 'FTP Server (V%s)' % VERSION

    def __init__ (
            self,
            authorizer,
            hostname        =None,
            ip              ='',
            port            =21,
            resolver        =None,
            logger_object=logger.file_logger (sys.stdout)
            ):
        self.ip = ip
        self.port = port
        self.authorizer = authorizer

        if hostname is None:
            self.hostname = socket.gethostname()
        else:
            self.hostname = hostname

        # statistics
        self.total_sessions = counter()
        self.closed_sessions = counter()
        self.total_files_out = counter()
        self.total_files_in = counter()
        self.total_bytes_out = counter()
        self.total_bytes_in = counter()
        self.total_exceptions = counter()
        #
        asyncore.dispatcher.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)

        self.set_reuse_addr()
        self.bind ((self.ip, self.port))
        self.listen (5)

        if not logger_object:
            logger_object = sys.stdout

        if resolver:
            self.logger = logger.resolving_logger (resolver, logger_object)
        else:
            self.logger = logger.unresolving_logger (logger_object)

        self.log_info('FTP server started at %s\n\tAuthorizer:%s\n\tHostname: %s\n\tPort: %d' % (
                time.ctime(time.time()),
                repr (self.authorizer),
                self.hostname,
                self.port)
                )

    def writable (self):
        return 0

    def handle_read (self):
        pass

    def handle_connect (self):
        pass

    def handle_accept (self):
        conn, addr = self.accept()
        self.total_sessions.increment()
        self.log_info('Incoming connection from %s:%d' % (addr[0], addr[1]))
        self.ftp_channel_class (self, conn, addr)

    # return a producer describing the state of the server
    def status (self):

        def nice_bytes (n):
            return ''.join(status_handler.english_bytes(n))

        return producers.lines_producer (
                ['<h2>%s</h2>'                          % self.SERVER_IDENT,
                 '<br>Listening on <b>Host:</b> %s' % self.hostname,
                 '<b>Port:</b> %d'                      % self.port,
                 '<br>Sessions',
                 '<b>Total:</b> %s'                     % self.total_sessions,
                 '<b>Current:</b> %d'           % (self.total_sessions.as_long() - self.closed_sessions.as_long()),
                 '<br>Files',
                 '<b>Sent:</b> %s'                      % self.total_files_out,
                 '<b>Received:</b> %s'          % self.total_files_in,
                 '<br>Bytes',
                 '<b>Sent:</b> %s'                      % nice_bytes (self.total_bytes_out.as_long()),
                 '<b>Received:</b> %s'          % nice_bytes (self.total_bytes_in.as_long()),
                 '<br>Exceptions: %s'           % self.total_exceptions,
                 ]
                )

# ======================================================================
#                                                Data Channel Classes
# ======================================================================

# This socket accepts a data connection, used when the server has been
# placed in passive mode.  Although the RFC implies that we ought to
# be able to use the same acceptor over and over again, this presents
# a problem: how do we shut it off, so that we are accepting
# connections only when we expect them?  [we can't]
#
# wuftpd, and probably all the other servers, solve this by allowing
# only one connection to hit this acceptor.  They then close it.  Any
# subsequent data-connection command will then try for the default
# port on the client side [which is of course never there].  So the
# 'always-send-PORT/PASV' behavior seems required.
#
# Another note: wuftpd will also be listening on the channel as soon
# as the PASV command is sent.  It does not wait for a data command
# first.

# --- we need to queue up a particular behavior:
#  1) xmit : queue up producer[s]
#  2) recv : the file object
#
# It would be nice if we could make both channels the same.  Hmmm..
#

class passive_acceptor (asyncore.dispatcher):
    ready = None

    def __init__ (self, control_channel):
        # connect_fun (conn, addr)
        asyncore.dispatcher.__init__ (self)
        self.control_channel = control_channel
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        # bind to an address on the interface that the
        # control connection is coming from.
        self.bind ((
                self.control_channel.getsockname()[0],
                0
                ))
        self.addr = self.getsockname()
        self.listen (1)

#       def __del__ (self):
#               print 'passive_acceptor.__del__()'

    def log (self, *ignore):
        pass

    def handle_accept (self):
        conn, addr = self.accept()
        dc = self.control_channel.client_dc
        if dc is not None:
            dc.set_socket (conn)
            dc.addr = addr
            dc.connected = 1
            self.control_channel.passive_acceptor = None
        else:
            self.ready = conn, addr
        self.close()


class xmit_channel (asynchat.async_chat):

    # for an ethernet, you want this to be fairly large, in fact, it
    # _must_ be large for performance comparable to an ftpd.  [64k] we
    # ought to investigate automatically-sized buffers...

    ac_out_buffer_size = 16384
    bytes_out = 0

    def __init__ (self, channel, client_addr=None):
        self.channel = channel
        self.client_addr = client_addr
        asynchat.async_chat.__init__ (self)

#       def __del__ (self):
#               print 'xmit_channel.__del__()'

    def log (self, *args):
        pass

    def readable (self):
        return not self.connected

    def writable (self):
        return 1

    def send (self, data):
        result = asynchat.async_chat.send (self, data)
        self.bytes_out += result
        return result

    def handle_error (self):
        # usually this is to catch an unexpected disconnect.
        self.log_info ('unexpected disconnect on data xmit channel', 'error')
        try:
            self.close()
        except:
            pass

    # TODO: there's a better way to do this.  we need to be able to
    # put 'events' in the producer fifo.  to do this cleanly we need
    # to reposition the 'producer' fifo as an 'event' fifo.

    def close (self):
        c = self.channel
        s = c.server
        c.client_dc = None
        s.total_files_out.increment()
        s.total_bytes_out.increment (self.bytes_out)
        if not len(self.producer_fifo):
            c.respond ('226 Transfer complete')
        elif not c.closed:
            c.respond ('426 Connection closed; transfer aborted')
        del c
        del s
        del self.channel
        asynchat.async_chat.close (self)

class recv_channel (asyncore.dispatcher):
    def __init__ (self, channel, client_addr, fd):
        self.channel = channel
        self.client_addr = client_addr
        self.fd = fd
        asyncore.dispatcher.__init__ (self)
        self.bytes_in = counter()

    def log (self, *ignore):
        pass

    def handle_connect (self):
        pass

    def writable (self):
        return 0

    def recv (self, *args):
        result = asyncore.dispatcher.recv(self, *args)
        self.bytes_in.increment(len(result))
        return result

    buffer_size = 8192

    def handle_read (self):
        block = self.recv (self.buffer_size)
        if block:
            try:
                self.fd.write (block)
            except IOError:
                self.log_info ('got exception writing block...', 'error')

    def handle_close (self):
        s = self.channel.server
        s.total_files_in.increment()
        s.total_bytes_in.increment(self.bytes_in.as_long())
        self.fd.close()
        self.channel.respond ('226 Transfer complete.')
        self.close()

import supervisor.medusa.filesys as filesys

# not much of a doorman! 8^)
class dummy_authorizer:
    def __init__ (self, root='/'):
        self.root = root

    def authorize (self, channel, username, password):
        channel.persona = -1, -1
        channel.read_only = 1
        return 1, 'Ok.', filesys.os_filesystem (self.root)

class anon_authorizer:
    def __init__ (self, root='/'):
        self.root = root

    def authorize (self, channel, username, password):
        if username in ('ftp', 'anonymous'):
            channel.persona = -1, -1
            channel.read_only = 1
            return 1, 'Ok.', filesys.os_filesystem (self.root)
        else:
            return 0, 'Password invalid.', None

# ===========================================================================
# Unix-specific improvements
# ===========================================================================

if os.name == 'posix':

    class unix_authorizer:
        # return a trio of (success, reply_string, filesystem)
        def authorize (self, channel, username, password):
            import crypt
            import pwd
            try:
                info = pwd.getpwnam (username)
            except KeyError:
                return 0, 'No such user.', None
            mangled = info[1]
            if crypt.crypt (password, mangled[:2]) == mangled:
                channel.read_only = 0
                fs = filesys.schizophrenic_unix_filesystem (
                        '/',
                        info[5],
                        persona = (info[2], info[3])
                        )
                return 1, 'Login successful.', fs
            else:
                return 0, 'Password invalid.', None

        def __repr__ (self):
            return '<standard unix authorizer>'

    # simple anonymous ftp support
    class unix_authorizer_with_anonymous (unix_authorizer):
        def __init__ (self, root=None, real_users=0):
            self.root = root
            self.real_users = real_users

        def authorize (self, channel, username, password):
            if username.lower() in ('anonymous', 'ftp'):
                import pwd
                try:
                    # ok, here we run into lots of confusion.
                    # on some os', anon runs under user 'nobody',
                    # on others as 'ftp'.  ownership is also critical.
                    # need to investigate.
                    # linux: new linuxen seem to have nobody's UID=-1,
                    #    which is an illegal value.  Use ftp.
                    ftp_user_info = pwd.getpwnam ('ftp')
                    if os.uname()[0].lower() == 'linux':
                        nobody_user_info = pwd.getpwnam ('ftp')
                    else:
                        nobody_user_info = pwd.getpwnam ('nobody')
                    channel.read_only = 1
                    if self.root is None:
                        self.root = ftp_user_info[5]
                    fs = filesys.unix_filesystem (self.root, '/')
                    return 1, 'Anonymous Login Successful', fs
                except KeyError:
                    return 0, 'Anonymous account not set up', None
            elif self.real_users:
                return unix_authorizer.authorize (
                        self,
                        channel,
                        username,
                        password
                        )
            else:
                return 0, 'User logins not allowed', None

# usage: ftp_server /PATH/TO/FTP/ROOT PORT
# for example:
# $ ftp_server /home/users/ftp 8021

if os.name == 'posix':
    def test (port='8021'):
        fs = ftp_server (
                unix_authorizer(),
                port=int(port)
                )
        try:
            asyncore.loop()
        except KeyboardInterrupt:
            fs.log_info('FTP server shutting down. (received SIGINT)', 'warning')
            # close everything down on SIGINT.
            # of course this should be a cleaner shutdown.
            asyncore.close_all()

    if __name__ == '__main__':
        test (sys.argv[1])
# not unix
else:
    def test ():
        ftp_server (dummy_authorizer())
    if __name__ == '__main__':
        test ()

# this is the command list from the wuftpd man page
# '*' means we've implemented it.
# '!' requires write access
#
command_documentation = {
        'abor': 'abort previous command',                                                       #*
        'acct': 'specify account (ignored)',
        'allo': 'allocate storage (vacuously)',
        'appe': 'append to a file',                                                                     #*!
        'cdup': 'change to parent of current working directory',        #*
        'cwd':  'change working directory',                                                     #*
        'dele': 'delete a file',                                                                        #!
        'help': 'give help information',                                                        #*
        'list': 'give list files in a directory',                                       #*
        'mkd':  'make a directory',                                                                     #!
        'mdtm': 'show last modification time of file',                          #*
        'mode': 'specify data transfer mode',
        'nlst': 'give name list of files in directory',                         #*
        'noop': 'do nothing',                                                                           #*
        'pass': 'specify password',                                                                     #*
        'pasv': 'prepare for server-to-server transfer',                        #*
        'port': 'specify data connection port',                                         #*
        'pwd':  'print the current working directory',                          #*
        'quit': 'terminate session',                                                            #*
        'rest': 'restart incomplete transfer',                                          #*
        'retr': 'retrieve a file',                                                                      #*
        'rmd':  'remove a directory',                                                           #!
        'rnfr': 'specify rename-from file name',                                        #*!
        'rnto': 'specify rename-to file name',                                          #*!
        'site': 'non-standard commands (see next section)',
        'size': 'return size of file',                                                          #*
        'stat': 'return status of server',                                                      #*
        'stor': 'store a file',                                                                         #*!
        'stou': 'store a file with a unique name',                                      #!
        'stru': 'specify data transfer structure',
        'syst': 'show operating system type of server system',          #*
        'type': 'specify data transfer type',                                           #*
        'user': 'specify user name',                                                            #*
        'xcup': 'change to parent of current working directory (deprecated)',
        'xcwd': 'change working directory (deprecated)',
        'xmkd': 'make a directory (deprecated)',                                        #!
        'xpwd': 'print the current working directory (deprecated)',
        'xrmd': 'remove a directory (deprecated)',                                      #!
}


# debugging aid (linux)
def get_vm_size ():
    return int(open('/proc/self/stat').readline().split()[22])

def print_vm():
    print('vm: %8dk' % (get_vm_size()/1024))

########NEW FILE########
__FILENAME__ = http_date
# -*- Mode: Python -*-

import re
import time

def concat (*args):
    return ''.join (args)

def join (seq, field=' '):
    return field.join (seq)

def group (s):
    return '(' + s + ')'

short_days = ['sun','mon','tue','wed','thu','fri','sat']
long_days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']

short_day_reg = group (join (short_days, '|'))
long_day_reg = group (join (long_days, '|'))

daymap = {}
for i in range(7):
    daymap[short_days[i]] = i
    daymap[long_days[i]] = i

hms_reg = join (3 * [group('[0-9][0-9]')], ':')

months = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec']

monmap = {}
for i in range(12):
    monmap[months[i]] = i+1

months_reg = group (join (months, '|'))

# From draft-ietf-http-v11-spec-07.txt/3.3.1
#       Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
#       Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
#       Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format

# rfc822 format
rfc822_date = join (
        [concat (short_day_reg,','),    # day
         group('[0-9][0-9]?'),          # date
         months_reg,                    # month
         group('[0-9]+'),               # year
         hms_reg,                       # hour minute second
         'gmt'
         ],
        ' '
        )

rfc822_reg = re.compile (rfc822_date)

def unpack_rfc822(m):
    g = m.group
    i = int
    return (
            i(g(4)),        # year
            monmap[g(3)],   # month
            i(g(2)),        # day
            i(g(5)),        # hour
            i(g(6)),        # minute
            i(g(7)),        # second
            0,
            0,
            0
            )

# rfc850 format
rfc850_date = join (
        [concat (long_day_reg,','),
         join (
                 [group ('[0-9][0-9]?'),
                  months_reg,
                  group ('[0-9]+')
                  ],
                 '-'
                 ),
         hms_reg,
         'gmt'
         ],
        ' '
        )

rfc850_reg = re.compile (rfc850_date)
# they actually unpack the same way
def unpack_rfc850(m):
    g = m.group
    i = int
    return (
            i(g(4)),        # year
            monmap[g(3)],   # month
            i(g(2)),        # day
            i(g(5)),        # hour
            i(g(6)),        # minute
            i(g(7)),        # second
            0,
            0,
            0
            )

# parsedate.parsedate    - ~700/sec.
# parse_http_date       - ~1333/sec.

def build_http_date (when):
    return time.strftime ('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(when))

def parse_http_date (d):
    d = d.lower()
    tz = time.timezone
    m = rfc850_reg.match (d)
    if m and m.end() == len(d):
        retval = int (time.mktime (unpack_rfc850(m)) - tz)
    else:
        m = rfc822_reg.match (d)
        if m and m.end() == len(d):
            retval = int (time.mktime (unpack_rfc822(m)) - tz)
        else:
            return 0
    # Thanks to Craig Silverstein <csilvers@google.com> for pointing
    # out the DST discrepancy
    if time.daylight and time.localtime(retval)[-1] == 1: # DST correction
        retval += tz - time.altzone
    return retval

########NEW FILE########
__FILENAME__ = http_server
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#
RCS_ID =  '$Id: http_server.py,v 1.12 2004/04/21 15:11:44 akuchling Exp $'

# python modules
import re
import supervisor.medusa.text_socket as socket
import sys
import time

# async modules
import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

# medusa modules
import supervisor.medusa.http_date as http_date
import supervisor.medusa.producers as producers
import supervisor.medusa.logger as logger

VERSION_STRING = RCS_ID.split()[2]

from supervisor.medusa.counter import counter
try:
    from urllib import unquote, splitquery
except ImportError:
    from urllib.parse import unquote, splitquery

# ===========================================================================
#                                                       Request Object
# ===========================================================================

class http_request:

    # default reply code
    reply_code = 200

    request_counter = counter()

    # Whether to automatically use chunked encoding when
    #
    #   HTTP version is 1.1
    #   Content-Length is not set
    #   Chunked encoding is not already in effect
    #
    # If your clients are having trouble, you might want to disable this.
    use_chunked = 1

    # by default, this request object ignores user data.
    collector = None

    def __init__ (self, *args):
        # unpack information about the request
        (self.channel, self.request,
         self.command, self.uri, self.version,
         self.header) = args

        self.outgoing = []
        self.reply_headers = {
                'Server'        : 'Medusa/%s' % VERSION_STRING,
                'Date'          : http_date.build_http_date (time.time())
                }

        # New reply header list (to support multiple
        # headers with same name)
        self.__reply_header_list = []

        self.request_number = http_request.request_counter.increment()
        self._split_uri = None
        self._header_cache = {}

    # --------------------------------------------------
    # reply header management
    # --------------------------------------------------
    def __setitem__ (self, key, value):
        self.reply_headers[key] = value

    def __getitem__ (self, key):
        return self.reply_headers[key]

    def __contains__(self, key):
        return key in self.reply_headers

    def has_key (self, key):
        return key in self.reply_headers

    def build_reply_header (self):
        header_items = ['%s: %s' % item for item in self.reply_headers.items()]
        return '\r\n'.join (
            [self.response(self.reply_code)] + header_items) + '\r\n\r\n'

    ####################################################
    # multiple reply header management
    ####################################################
    # These are intended for allowing multiple occurrences
    # of the same header.
    # Usually you can fold such headers together, separating
    # their contents by a comma (e.g. Accept: text/html, text/plain)
    # but the big exception is the Set-Cookie header.
    # dictionary centric.
    #---------------------------------------------------

    def add_header(self, name, value):
        """ Adds a header to the reply headers """
        self.__reply_header_list.append((name, value))

    def clear_headers(self):
        """ Clears the reply header list """

        # Remove things from the old dict as well
        self.reply_headers.clear()

        self.__reply_header_list[:] = []

    def remove_header(self, name, value=None):
        """ Removes the specified header.
        If a value is provided, the name and
        value must match to remove the header.
        If the value is None, removes all headers
        with that name."""

        found_it = 0

        # Remove things from the old dict as well
        if (name in self.reply_headers and
            (value is None or
             self.reply_headers[name] == value)):
            del self.reply_headers[name]
            found_it = 1


        removed_headers = []
        if not value is None:
            if (name, value) in self.__reply_header_list:
                removed_headers = [(name, value)]
                found_it = 1
        else:
            for h in self.__reply_header_list:
                if h[0] == name:
                    removed_headers.append(h)
                    found_it = 1

        if not found_it:
            if value is None:
                search_value = "%s" % name
            else:
                search_value = "%s: %s" % (name, value)

            raise LookupError("Header '%s' not found" % search_value)

        for h in removed_headers:
            self.__reply_header_list.remove(h)


    def get_reply_headers(self):
        """ Get the tuple of headers that will be used
        for generating reply headers"""
        header_tuples = self.__reply_header_list[:]

        # The idea here is to insert the headers from
        # the old header dict into the new header list,
        # UNLESS there's already an entry in the list
        # that would have overwritten the dict entry
        # if the dict was the only storage...
        header_names = [n for n,v in header_tuples]
        for n,v in self.reply_headers.items():
            if n not in header_names:
                header_tuples.append((n,v))
                header_names.append(n)
        # Ok, that should do it.  Now, if there were any
        # headers in the dict that weren't in the list,
        # they should have been copied in.  If the name
        # was already in the list, we didn't copy it,
        # because the value from the dict has been
        # 'overwritten' by the one in the list.

        return header_tuples

    def get_reply_header_text(self):
        """ Gets the reply header (including status and
        additional crlf)"""

        header_tuples = self.get_reply_headers()

        headers = [self.response(self.reply_code)]
        headers += ["%s: %s" % h for h in header_tuples]
        return '\r\n'.join(headers) + '\r\n\r\n'

    #---------------------------------------------------
    # This is the end of the new reply header
    # management section.
    ####################################################


    # --------------------------------------------------
    # split a uri
    # --------------------------------------------------

    # <path>;<params>?<query>#<fragment>
    path_regex = re.compile (
    #      path      params    query   fragment
            r'([^;?#]*)(;[^?#]*)?(\?[^#]*)?(#.*)?'
            )

    def split_uri (self):
        if self._split_uri is None:
            m = self.path_regex.match (self.uri)
            if m.end() != len(self.uri):
                raise ValueError("Broken URI")
            else:
                self._split_uri = m.groups()
        return self._split_uri

    def get_header_with_regex (self, head_reg, group):
        for line in self.header:
            m = head_reg.match (line)
            if m.end() == len(line):
                return m.group (group)
        return ''

    def get_header (self, header):
        header = header.lower()
        hc = self._header_cache
        if header not in hc:
            h = header + ': '
            hl = len(h)
            for line in self.header:
                if line[:hl].lower() == h:
                    r = line[hl:]
                    hc[header] = r
                    return r
            hc[header] = None
            return None
        else:
            return hc[header]

    # --------------------------------------------------
    # user data
    # --------------------------------------------------

    def collect_incoming_data (self, data):
        if self.collector:
            self.collector.collect_incoming_data (data)
        else:
            self.log_info(
                    'Dropping %d bytes of incoming request data' % len(data),
                    'warning'
                    )

    def found_terminator (self):
        if self.collector:
            self.collector.found_terminator()
        else:
            self.log_info (
                    'Unexpected end-of-record for incoming request',
                    'warning'
                    )

    def push (self, thing):
        if type(thing) == type(''):
            self.outgoing.append(producers.simple_producer(thing,
              buffer_size=len(thing)))
        else:
            self.outgoing.append(thing)

    def response (self, code=200):
        message = self.responses[code]
        self.reply_code = code
        return 'HTTP/%s %d %s' % (self.version, code, message)

    def error (self, code):
        self.reply_code = code
        message = self.responses[code]
        s = self.DEFAULT_ERROR_MESSAGE % {
                'code': code,
                'message': message,
                }
        self['Content-Length'] = len(s)
        self['Content-Type'] = 'text/html'
        # make an error reply
        self.push (s)
        self.done()

    # can also be used for empty replies
    reply_now = error

    def done (self):
        """finalize this transaction - send output to the http channel"""

        # ----------------------------------------
        # persistent connection management
        # ----------------------------------------

        #  --- BUCKLE UP! ----

        connection = get_header(CONNECTION, self.header).lower()

        close_it = 0
        wrap_in_chunking = 0

        if self.version == '1.0':
            if connection == 'keep-alive':
                if 'Content-Length' not in self:
                    close_it = 1
                else:
                    self['Connection'] = 'Keep-Alive'
            else:
                close_it = 1
        elif self.version == '1.1':
            if connection == 'close':
                close_it = 1
            elif 'Content-Length' not in self:
                if 'Transfer-Encoding' in self:
                    if not self['Transfer-Encoding'] == 'chunked':
                        close_it = 1
                elif self.use_chunked:
                    self['Transfer-Encoding'] = 'chunked'
                    wrap_in_chunking = 1
                else:
                    close_it = 1
        elif self.version is None:
            # Although we don't *really* support http/0.9 (because we'd have to
            # use \r\n as a terminator, and it would just yuck up a lot of stuff)
            # it's very common for developers to not want to type a version number
            # when using telnet to debug a server.
            close_it = 1

        outgoing_header = producers.simple_producer(self.get_reply_header_text())

        if close_it:
            self['Connection'] = 'close'

        if wrap_in_chunking:
            outgoing_producer = producers.chunked_producer (
                    producers.composite_producer (self.outgoing)
                    )
            # prepend the header
            outgoing_producer = producers.composite_producer(
                [outgoing_header, outgoing_producer]
                )
        else:
            # prepend the header
            self.outgoing.insert(0, outgoing_header)
            outgoing_producer = producers.composite_producer (self.outgoing)

        # apply a few final transformations to the output
        self.channel.push_with_producer (
                # globbing gives us large packets
                producers.globbing_producer (
                        # hooking lets us log the number of bytes sent
                        producers.hooked_producer (
                                outgoing_producer,
                                self.log
                                )
                        )
                )

        self.channel.current_request = None

        if close_it:
            self.channel.close_when_done()

    def log_date_string (self, when):
        gmt = time.gmtime(when)
        if time.daylight and gmt[8]:
            tz = time.altzone
        else:
            tz = time.timezone
        if tz > 0:
            neg = 1
        else:
            neg = 0
            tz = -tz
        h, rem = divmod (tz, 3600)
        m, rem = divmod (rem, 60)
        if neg:
            offset = '-%02d%02d' % (h, m)
        else:
            offset = '+%02d%02d' % (h, m)

        return time.strftime ( '%d/%b/%Y:%H:%M:%S ', gmt) + offset

    def log (self, bytes):
        self.channel.server.logger.log (
                self.channel.addr[0],
                '%d - - [%s] "%s" %d %d\n' % (
                        self.channel.addr[1],
                        self.log_date_string (time.time()),
                        self.request,
                        self.reply_code,
                        bytes
                        )
                )

    responses = {
            100: "Continue",
            101: "Switching Protocols",
            200: "OK",
            201: "Created",
            202: "Accepted",
            203: "Non-Authoritative Information",
            204: "No Content",
            205: "Reset Content",
            206: "Partial Content",
            300: "Multiple Choices",
            301: "Moved Permanently",
            302: "Moved Temporarily",
            303: "See Other",
            304: "Not Modified",
            305: "Use Proxy",
            400: "Bad Request",
            401: "Unauthorized",
            402: "Payment Required",
            403: "Forbidden",
            404: "Not Found",
            405: "Method Not Allowed",
            406: "Not Acceptable",
            407: "Proxy Authentication Required",
            408: "Request Time-out",
            409: "Conflict",
            410: "Gone",
            411: "Length Required",
            412: "Precondition Failed",
            413: "Request Entity Too Large",
            414: "Request-URI Too Large",
            415: "Unsupported Media Type",
            500: "Internal Server Error",
            501: "Not Implemented",
            502: "Bad Gateway",
            503: "Service Unavailable",
            504: "Gateway Time-out",
            505: "HTTP Version not supported"
            }

    # Default error message
    DEFAULT_ERROR_MESSAGE = '\r\n'.join(
            ('<head>',
             '<title>Error response</title>',
             '</head>',
             '<body>',
             '<h1>Error response</h1>',
             '<p>Error code %(code)d.',
             '<p>Message: %(message)s.',
             '</body>',
             ''
             )
            )

    def log_info(self, msg, level):
        pass


# ===========================================================================
#                                                HTTP Channel Object
# ===========================================================================

class http_channel (asynchat.async_chat):

    # use a larger default output buffer
    ac_out_buffer_size = 1<<16

    current_request = None
    channel_counter = counter()

    def __init__ (self, server, conn, addr):
        self.channel_number = http_channel.channel_counter.increment()
        self.request_counter = counter()
        asynchat.async_chat.__init__ (self, conn)
        self.server = server
        self.addr = addr
        self.set_terminator ('\r\n\r\n')
        self.in_buffer = ''
        self.creation_time = int (time.time())
        self.check_maintenance()

    def __repr__ (self):
        ar = asynchat.async_chat.__repr__(self)[1:-1]
        return '<%s channel#: %s requests:%s>' % (
                ar,
                self.channel_number,
                self.request_counter
                )

    # Channel Counter, Maintenance Interval...
    maintenance_interval = 500

    def check_maintenance (self):
        if not self.channel_number % self.maintenance_interval:
            self.maintenance()

    def maintenance (self):
        self.kill_zombies()

    # 30-minute zombie timeout.  status_handler also knows how to kill zombies.
    zombie_timeout = 30 * 60

    def kill_zombies (self):
        now = int (time.time())
        for channel in asyncore.socket_map.values():
            if channel.__class__ == self.__class__:
                if (now - channel.creation_time) > channel.zombie_timeout:
                    channel.close()

    # --------------------------------------------------
    # send/recv overrides, good place for instrumentation.
    # --------------------------------------------------

    # this information needs to get into the request object,
    # so that it may log correctly.
    def send (self, data):
        result = asynchat.async_chat.send (self, data)
        self.server.bytes_out.increment (len(data))
        return result

    def recv (self, buffer_size):
        try:
            result = asynchat.async_chat.recv (self, buffer_size)
            self.server.bytes_in.increment (len(result))
            return result
        except MemoryError:
            # --- Save a Trip to Your Service Provider ---
            # It's possible for a process to eat up all the memory of
            # the machine, and put it in an extremely wedged state,
            # where medusa keeps running and can't be shut down.  This
            # is where MemoryError tends to get thrown, though of
            # course it could get thrown elsewhere.
            sys.exit ("Out of Memory!")

    def handle_error (self):
        t, v = sys.exc_info()[:2]
        if t is SystemExit:
            raise t(v)
        else:
            asynchat.async_chat.handle_error (self)

    def log (self, *args):
        pass

    # --------------------------------------------------
    # async_chat methods
    # --------------------------------------------------

    def collect_incoming_data (self, data):
        if self.current_request:
            # we are receiving data (probably POST data) for a request
            self.current_request.collect_incoming_data (data)
        else:
            # we are receiving header (request) data
            self.in_buffer = self.in_buffer + data

    def found_terminator (self):
        if self.current_request:
            self.current_request.found_terminator()
        else:
            header = self.in_buffer
            self.in_buffer = ''
            lines = header.split('\r\n')

            # --------------------------------------------------
            # crack the request header
            # --------------------------------------------------

            while lines and not lines[0]:
                # as per the suggestion of http-1.1 section 4.1, (and
                # Eric Parker <eparker@zyvex.com>), ignore a leading
                # blank lines (buggy browsers tack it onto the end of
                # POST requests)
                lines = lines[1:]

            if not lines:
                self.close_when_done()
                return

            request = lines[0]

            command, uri, version = crack_request (request)
            header = join_headers (lines[1:])

            # unquote path if necessary (thanks to Skip Montanaro for pointing
            # out that we must unquote in piecemeal fashion).
            rpath, rquery = splitquery(uri)
            if '%' in rpath:
                if rquery:
                    uri = unquote (rpath) + '?' + rquery
                else:
                    uri = unquote (rpath)

            r = http_request (self, request, command, uri, version, header)
            self.request_counter.increment()
            self.server.total_requests.increment()

            if command is None:
                self.log_info ('Bad HTTP request: %s' % repr(request), 'error')
                r.error (400)
                return

            # --------------------------------------------------
            # handler selection and dispatch
            # --------------------------------------------------
            for h in self.server.handlers:
                if h.match (r):
                    try:
                        self.current_request = r
                        # This isn't used anywhere.
                        # r.handler = h # CYCLE
                        h.handle_request (r)
                    except:
                        self.server.exceptions.increment()
                        (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
                        self.log_info(
                                        'Server Error: %s, %s: file: %s line: %s' % (t,v,file,line),
                                        'error')
                        try:
                            r.error (500)
                        except:
                            pass
                    return

            # no handlers, so complain
            r.error (404)

    def writable_for_proxy (self):
        # this version of writable supports the idea of a 'stalled' producer
        # [i.e., it's not ready to produce any output yet] This is needed by
        # the proxy, which will be waiting for the magic combination of
        # 1) hostname resolved
        # 2) connection made
        # 3) data available.
        if self.ac_out_buffer:
            return 1
        elif len(self.producer_fifo):
            p = self.producer_fifo.first()
            if hasattr (p, 'stalled'):
                return not p.stalled()
            else:
                return 1

# ===========================================================================
#                                                HTTP Server Object
# ===========================================================================

class http_server (asyncore.dispatcher):

    SERVER_IDENT = 'HTTP Server (V%s)' % VERSION_STRING

    channel_class = http_channel

    def __init__ (self, ip, port, resolver=None, logger_object=None):
        self.ip = ip
        self.port = port
        asyncore.dispatcher.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)

        self.handlers = []

        if not logger_object:
            logger_object = logger.file_logger (sys.stdout)

        self.set_reuse_addr()
        self.bind ((ip, port))

        # lower this to 5 if your OS complains
        self.listen (1024)

        host, port = self.socket.getsockname()
        if not ip:
            self.log_info('Computing default hostname', 'warning')
            ip = socket.gethostbyname (socket.gethostname())
        try:
            self.server_name = socket.gethostbyaddr (ip)[0]
        except socket.error:
            self.log_info('Cannot do reverse lookup', 'warning')
            self.server_name = ip       # use the IP address as the "hostname"

        self.server_port = port
        self.total_clients = counter()
        self.total_requests = counter()
        self.exceptions = counter()
        self.bytes_out = counter()
        self.bytes_in  = counter()

        if not logger_object:
            logger_object = logger.file_logger (sys.stdout)

        if resolver:
            self.logger = logger.resolving_logger (resolver, logger_object)
        else:
            self.logger = logger.unresolving_logger (logger_object)

        self.log_info (
                'Medusa (V%s) started at %s'
                '\n\tHostname: %s'
                '\n\tPort:%d'
                '\n' % (
                        VERSION_STRING,
                        time.ctime(time.time()),
                        self.server_name,
                        port,
                        )
                )

    def writable (self):
        return 0

    def handle_read (self):
        pass

    def readable (self):
        return self.accepting

    def handle_connect (self):
        pass

    def handle_accept (self):
        self.total_clients.increment()
        try:
            conn, addr = self.accept()
        except socket.error:
            # linux: on rare occasions we get a bogus socket back from
            # accept.  socketmodule.c:makesockaddr complains that the
            # address family is unknown.  We don't want the whole server
            # to shut down because of this.
            self.log_info ('warning: server accept() threw an exception', 'warning')
            return
        except TypeError:
            # unpack non-sequence.  this can happen when a read event
            # fires on a listening socket, but when we call accept()
            # we get EWOULDBLOCK, so dispatcher.accept() returns None.
            # Seen on FreeBSD3.
            self.log_info ('warning: server accept() threw EWOULDBLOCK', 'warning')
            return

        self.channel_class (self, conn, addr)

    def install_handler (self, handler, back=0):
        if back:
            self.handlers.append (handler)
        else:
            self.handlers.insert (0, handler)

    def remove_handler (self, handler):
        self.handlers.remove (handler)

    def status (self):
        from supervisor.medusa.status_handler import english_bytes
        def nice_bytes (n):
            return ''.join(english_bytes (n))

        handler_stats = [_f for _f in map (maybe_status, self.handlers) if _f]

        if self.total_clients:
            ratio = self.total_requests.as_long() / float(self.total_clients.as_long())
        else:
            ratio = 0.0

        return producers.composite_producer (
                [producers.lines_producer (
                        ['<h2>%s</h2>'                                                  % self.SERVER_IDENT,
                        '<br>Listening on: <b>Host:</b> %s'             % self.server_name,
                        '<b>Port:</b> %d'                                               % self.port,
                         '<p><ul>'
                         '<li>Total <b>Clients:</b> %s'                 % self.total_clients,
                         '<b>Requests:</b> %s'                                  % self.total_requests,
                         '<b>Requests/Client:</b> %.1f'                 % ratio,
                         '<li>Total <b>Bytes In:</b> %s'        % (nice_bytes (self.bytes_in.as_long())),
                         '<b>Bytes Out:</b> %s'                         % (nice_bytes (self.bytes_out.as_long())),
                         '<li>Total <b>Exceptions:</b> %s'              % self.exceptions,
                         '</ul><p>'
                         '<b>Extension List</b><ul>',
                         ])] + handler_stats + [producers.simple_producer('</ul>')]
                )

def maybe_status (thing):
    if hasattr (thing, 'status'):
        return thing.status()
    else:
        return None

CONNECTION = re.compile ('Connection: (.*)', re.IGNORECASE)

# merge multi-line headers
# [486dx2: ~500/sec]
def join_headers (headers):
    r = []
    for i in range(len(headers)):
        if headers[i][0] in ' \t':
            r[-1] = r[-1] + headers[i][1:]
        else:
            r.append (headers[i])
    return r

def get_header (head_reg, lines, group=1):
    for line in lines:
        m = head_reg.match (line)
        if m and m.end() == len(line):
            return m.group (group)
    return ''

def get_header_match (head_reg, lines):
    for line in lines:
        m = head_reg.match (line)
        if m and m.end() == len(line):
            return m
    return ''

REQUEST = re.compile ('([^ ]+) ([^ ]+)(( HTTP/([0-9.]+))$|$)')

def crack_request (r):
    m = REQUEST.match (r)
    if m and m.end() == len(r):
        if m.group(3):
            version = m.group(5)
        else:
            version = None
        return m.group(1), m.group(2), version
    else:
        return None, None, None

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('usage: %s <root> <port>' % (sys.argv[0]))
    else:
        import supervisor.medusa.monitor as monitor
        import supervisor.medusa.filesys as filesys
        import supervisor.medusa.default_handler as default_handler
        import supervisor.medusa.status_handler as status_handler
        import supervisor.medusa.ftp_server as ftp_server
        import supervisor.medusa.chat_server as chat_server
        import supervisor.medusa.resolver as resolver
        rs = resolver.caching_resolver ('127.0.0.1')
        lg = logger.file_logger (sys.stdout)
        ms = monitor.secure_monitor_server ('fnord', '127.0.0.1', 9999)
        fs = filesys.os_filesystem (sys.argv[1])
        dh = default_handler.default_handler (fs)
        hs = http_server('', int(sys.argv[2]), rs, lg)
        hs.install_handler (dh)
        ftp = ftp_server.ftp_server (
                ftp_server.dummy_authorizer(sys.argv[1]),
                port=8021,
                resolver=rs,
                logger_object=lg
                )
        cs = chat_server.chat_server ('', 7777)
        sh = status_handler.status_extension([hs,ms,ftp,cs,rs])
        hs.install_handler (sh)
        if '-p' in sys.argv:
            def profile_loop ():
                try:
                    asyncore.loop()
                except KeyboardInterrupt:
                    pass
            import profile
            profile.run ('profile_loop()', 'profile.out')
        else:
            asyncore.loop()

########NEW FILE########
__FILENAME__ = logger
# -*- Mode: Python -*-

import supervisor.medusa.asynchat_25 as asynchat
import supervisor.medusa.text_socket as socket
import time         # these three are for the rotating logger
import os           # |
import stat         # v

#
# three types of log:
# 1) file
#    with optional flushing.  Also, one that rotates the log.
# 2) socket
#    dump output directly to a socket connection. [how do we
#    keep it open?]
# 3) syslog
#    log to syslog via tcp.  this is a per-line protocol.
#

#
# The 'standard' interface to a logging object is simply
# log_object.log (message)
#

# a file-like object that captures output, and
# makes sure to flush it always...  this could
# be connected to:
#  o    stdio file
#  o    low-level file
#  o    socket channel
#  o    syslog output...

class file_logger:

    # pass this either a path or a file object.
    def __init__ (self, file, flush=1, mode='a'):
        if isinstance(file, str):
            if file == '-':
                import sys
                self.file = sys.stdout
            else:
                self.file = open (file, mode)
        else:
            self.file = file
        self.do_flush = flush

    def __repr__ (self):
        return '<file logger: %s>' % self.file

    def write (self, data):
        self.file.write (data)
        self.maybe_flush()

    def writeline (self, line):
        self.file.writeline (line)
        self.maybe_flush()

    def writelines (self, lines):
        self.file.writelines (lines)
        self.maybe_flush()

    def maybe_flush (self):
        if self.do_flush:
            self.file.flush()

    def flush (self):
        self.file.flush()

    def softspace (self, *args):
        pass

    def log (self, message):
        if message[-1] not in ('\r', '\n'):
            self.write (message + '\n')
        else:
            self.write (message)

# like a file_logger, but it must be attached to a filename.
# When the log gets too full, or a certain time has passed,
# it backs up the log and starts a new one.  Note that backing
# up the log is done via "mv" because anything else (cp, gzip)
# would take time, during which medusa would do nothing else.

class rotating_file_logger (file_logger):

    # If freq is non-None we back up "daily", "weekly", or "monthly".
    # Else if maxsize is non-None we back up whenever the log gets
    # to big.  If both are None we never back up.
    def __init__ (self, file, freq=None, maxsize=None, flush=1, mode='a'):
        file_logger.__init__ (self, file, flush, mode)
        self.filename = file
        self.mode = mode
        self.freq = freq
        self.maxsize = maxsize
        self.rotate_when = self.next_backup(self.freq)

    def __repr__ (self):
        return '<rotating-file logger: %s>' % self.file

    # We back up at midnight every 1) day, 2) monday, or 3) 1st of month
    def next_backup (self, freq):
        (yr, mo, day, hr, min, sec, wd, jday, dst) = time.localtime(time.time())
        if freq == 'daily':
            return time.mktime((yr,mo,day+1, 0,0,0, 0,0,-1))
        elif freq == 'weekly':
            return time.mktime((yr,mo,day-wd+7, 0,0,0, 0,0,-1)) # wd(monday)==0
        elif freq == 'monthly':
            return time.mktime((yr,mo+1,1, 0,0,0, 0,0,-1))
        else:
            return None                  # not a date-based backup

    def maybe_flush (self):              # rotate first if necessary
        self.maybe_rotate()
        if self.do_flush:                # from file_logger()
            self.file.flush()

    def maybe_rotate (self):
        if self.freq and time.time() > self.rotate_when:
            self.rotate()
            self.rotate_when = self.next_backup(self.freq)
        elif self.maxsize:               # rotate when we get too big
            try:
                if os.stat(self.filename)[stat.ST_SIZE] > self.maxsize:
                    self.rotate()
            except os.error:             # file not found, probably
                self.rotate()            # will create a new file

    def rotate (self):
        (yr, mo, day, hr, min, sec, wd, jday, dst) = time.localtime(time.time())
        try:
            self.file.close()
            newname = '%s.ends%04d%02d%02d' % (self.filename, yr, mo, day)
            try:
                open(newname, "r").close()      # check if file exists
                newname += "-%02d%02d%02d" % (hr, min, sec)
            except:                             # YEAR_MONTH_DAY is unique
                pass
            os.rename(self.filename, newname)
            self.file = open(self.filename, self.mode)
        except:
            pass

# syslog is a line-oriented log protocol - this class would be
# appropriate for FTP or HTTP logs, but not for dumping stderr to.

# TODO: a simple safety wrapper that will ensure that the line sent
# to syslog is reasonable.

# TODO: async version of syslog_client: now, log entries use blocking
# send()

import supervisor.medusa.m_syslog as m_syslog
syslog_logger = m_syslog.syslog_client

class syslog_logger (m_syslog.syslog_client):
    def __init__ (self, address, facility='user'):
        m_syslog.syslog_client.__init__ (self, address)
        self.facility = m_syslog.facility_names[facility]
        self.address=address

    def __repr__ (self):
        return '<syslog logger address=%s>' % (repr(self.address))

    def log(self, message):
        m_syslog.syslog_client.log (
                self,
                message,
                facility=self.facility,
                priority=m_syslog.LOG_INFO
                )

# log to a stream socket, asynchronously

class socket_logger (asynchat.async_chat):

    def __init__ (self, address):
        asynchat.async_chat.__init__(self)
        if isinstance(address, str):
            self.create_socket (socket.AF_UNIX, socket.SOCK_STREAM)
        else:
            self.create_socket (socket.AF_INET, socket.SOCK_STREAM)

        self.connect (address)
        self.address = address

    def __repr__ (self):
        return '<socket logger: address=%s>' % self.address

    def log (self, message):
        if message[-2:] != '\r\n':
            self.socket.push (message + '\r\n')
        else:
            self.socket.push (message)

# log to multiple places
class multi_logger:
    def __init__ (self, loggers):
        self.loggers = loggers

    def __repr__ (self):
        return '<multi logger: %s>' % (repr(self.loggers))

    def log (self, message):
        for logger in self.loggers:
            logger.log (message)

class resolving_logger:
    """Feed (ip, message) combinations into this logger to get a
    resolved hostname in front of the message.  The message will not
    be logged until the PTR request finishes (or fails)."""

    def __init__ (self, resolver, logger):
        self.resolver = resolver
        self.logger = logger

    class logger_thunk:
        def __init__ (self, message, logger):
            self.message = message
            self.logger = logger

        def __call__ (self, host, ttl, answer):
            if not answer:
                answer = host
            self.logger.log ('%s:%s' % (answer, self.message))

    def log (self, ip, message):
        self.resolver.resolve_ptr (
                ip,
                self.logger_thunk (
                        message,
                        self.logger
                        )
                )

class unresolving_logger:
    """Just in case you don't want to resolve"""
    def __init__ (self, logger):
        self.logger = logger

    def log (self, ip, message):
        self.logger.log ('%s:%s' % (ip, message))


def strip_eol (line):
    while line and line[-1] in '\r\n':
        line = line[:-1]
    return line

class tail_logger:
    """Keep track of the last <size> log messages"""
    def __init__ (self, logger, size=500):
        self.size = size
        self.logger = logger
        self.messages = []

    def log (self, message):
        self.messages.append (strip_eol (message))
        if len (self.messages) > self.size:
            del self.messages[0]
        self.logger.log (message)

########NEW FILE########
__FILENAME__ = medusa_gif
# -*- Mode: Python -*-

# the medusa icon as a python source file.

width = 97
height = 61

data = 'GIF89aa\000=\000\204\000\000\000\000\000\255\255\255\245\245\245ssskkkccc111)))\326\326\326!!!\316\316\316\300\300\300\204\204\000\224\224\224\214\214\214\200\200\200RRR\377\377\377JJJ\367\367\367BBB\347\347\347\000\204\000\020\020\020\265\265\265\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000!\371\004\001\000\000\021\000,\000\000\000\000a\000=\000\000\005\376`$\216di\236h\252\256l\353\276p,\317tm\337x\256\357|m\001@\240E\305\000\364\2164\206R)$\005\201\214\007r\012{X\255\312a\004\260\\>\026\3240\353)\224n\001W+X\334\373\231~\344.\303b\216\024\027x<\273\307\255G,rJiWN\014{S}k"?ti\013EdPQ\207G@_%\000\026yy\\\201\202\227\224<\221Fs$pOjWz\241<r@vO\236\231\233k\247M\2544\203F\177\235\236L#\247\256Z\270,\266BxJ[\276\256A]iE\304\305\262\273E\313\201\275i#\\\303\321\'h\203V\\\177\326\276\216\220P~\335\230_\264\013\342\275\344KF\233\360Q\212\352\246\000\367\274s\361\236\334\347T\341;\341\246\2202\177\3142\211`\242o\325@S\202\264\031\252\207\260\323\256\205\311\036\236\270\002\'\013\302\177\274H\010\324X\002\0176\212\037\376\321\360\032\226\207\244\2674(+^\202\346r\205J\0211\375\241Y#\256f\0127\315>\272\002\325\307g\012(\007\205\312#j\317(\012A\200\224.\241\003\346GS\247\033\245\344\264\366\015L\'PXQl]\266\263\243\232\260?\245\316\371\362\225\035\332\243J\273\332Q\263\357-D\241T\327\270\265\013W&\330\010u\371b\322IW0\214\261]\003\033Va\365Z#\207\213a\030k\2647\262\014p\354\024[n\321N\363\346\317\003\037P\000\235C\302\000\3228(\244\363YaA\005\022\255_\237@\260\000A\212\326\256qbp\321\332\266\011\334=T\023\010"!B\005\003A\010\224\020\220 H\002\337#\020 O\276E\357h\221\327\003\\\000b@v\004\351A.h\365\354\342B\002\011\257\025\\ \220\340\301\353\006\000\024\214\200pA\300\353\012\364\241k/\340\033C\202\003\000\310fZ\011\003V\240R\005\007\354\376\026A\000\000\360\'\202\177\024\004\210\003\000\305\215\360\000\000\015\220\240\332\203\027@\'\202\004\025VpA\000%\210x\321\206\032J\341\316\010\262\211H"l\333\341\200\200>"]P\002\212\011\010`\002\0066FP\200\001\'\024p]\004\027(8B\221\306]\000\201w>\002iB\001\007\340\260"v7J1\343(\257\020\251\243\011\242i\263\017\215\337\035\220\200\221\365m4d\015\016D\251\341iN\354\346Ng\253\200I\240\031\35609\245\2057\311I\302\2007t\231"&`\314\310\244\011e\226(\236\010w\212\300\234\011\012HX(\214\253\311@\001\233^\222pg{% \340\035\224&H\000\246\201\362\215`@\001"L\340\004\030\234\022\250\'\015(V:\302\235\030\240q\337\205\224\212h@\177\006\000\250\210\004\007\310\207\337\005\257-P\346\257\367]p\353\203\271\256:\203\236\211F\340\247\010\3329g\244\010\307*=A\000\203\260y\012\304s#\014\007D\207,N\007\304\265\027\021C\233\207%B\366[m\353\006\006\034j\360\306+\357\274a\204\000\000;'

########NEW FILE########
__FILENAME__ = monitor
# -*- Mode: Python -*-
#       Author: Sam Rushing <rushing@nightmare.com>

#
# python REPL channel.
#
RCS_ID = '$Id: monitor.py,v 1.5 2002/03/23 15:08:06 amk Exp $'

from supervisor.compat import md5
from supervisor.compat import print_function

import supervisor.medusa.text_socket as socket
import sys
import time

VERSION = RCS_ID.split()[2]

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

from supervisor.medusa.counter import counter
import supervisor.medusa.producers as producers

class monitor_channel (asynchat.async_chat):
    try_linemode = 1

    def __init__ (self, server, sock, addr):
        asynchat.async_chat.__init__ (self, sock)
        self.server = server
        self.addr = addr
        self.set_terminator ('\r\n')
        self.data = ''
        # local bindings specific to this channel
        self.local_env = sys.modules['__main__'].__dict__.copy()
        self.push ('Python ' + sys.version + '\r\n')
        self.push (sys.copyright+'\r\n')
        self.push ('Welcome to %s\r\n' % self)
        self.push ("[Hint: try 'from __main__ import *']\r\n")
        self.prompt()
        self.number = server.total_sessions.as_long()
        self.line_counter = counter()
        self.multi_line = []

    def handle_connect (self):
        # send IAC DO LINEMODE
        self.push ('\377\375\"')

    def close (self):
        self.server.closed_sessions.increment()
        asynchat.async_chat.close(self)

    def prompt (self):
        self.push ('>>> ')

    def collect_incoming_data (self, data):
        self.data = self.data + data
        if len(self.data) > 1024:
            # denial of service.
            self.push ('BCNU\r\n')
            self.close_when_done()

    def found_terminator (self):
        line = self.clean_line (self.data)
        self.data = ''
        self.line_counter.increment()
        # check for special case inputs...
        if not line and not self.multi_line:
            self.prompt()
            return
        if line in ['\004', 'exit']:
            self.push ('BCNU\r\n')
            self.close_when_done()
            return
        oldout = sys.stdout
        olderr = sys.stderr
        try:
            p = output_producer(self, olderr)
            sys.stdout = p
            sys.stderr = p
            try:
                # this is, of course, a blocking operation.
                # if you wanted to thread this, you would have
                # to synchronize, etc... and treat the output
                # like a pipe.  Not Fun.
                #
                # try eval first.  If that fails, try exec.  If that fails,
                # hurl.
                try:
                    if self.multi_line:
                        # oh, this is horrible...
                        raise SyntaxError
                    co = compile (line, repr(self), 'eval')
                    result = eval (co, self.local_env)
                    method = 'eval'
                    if result is not None:
                        print(repr(result))
                    self.local_env['_'] = result
                except SyntaxError:
                    try:
                        if self.multi_line:
                            if line and line[0] in [' ','\t']:
                                self.multi_line.append (line)
                                self.push ('... ')
                                return
                            else:
                                self.multi_line.append (line)
                                line = '\n'.join(self.multi_line)
                                co = compile (line, repr(self), 'exec')
                                self.multi_line = []
                        else:
                            co = compile (line, repr(self), 'exec')
                    except SyntaxError as why:
                        if why.args[0] == 'unexpected EOF while parsing':
                            self.push ('... ')
                            self.multi_line.append (line)
                            return
                        else:
                            t,v,tb = sys.exc_info()
                            del tb
                            raise t(v)
                    exec(co in self.local_env)
                    method = 'exec'
            except:
                method = 'exception'
                self.multi_line = []
                (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
                self.log_info('%s %s %s' %(t, v, tbinfo), 'warning')
        finally:
            sys.stdout = oldout
            sys.stderr = olderr
        self.log_info('%s:%s (%s)> %s' % (
                self.number,
                self.line_counter,
                method,
                repr(line))
                )
        self.push_with_producer (p)
        self.prompt()

    # for now, we ignore any telnet option stuff sent to
    # us, and we process the backspace key ourselves.
    # gee, it would be fun to write a full-blown line-editing
    # environment, etc...
    def clean_line (self, line):
        chars = []
        for ch in line:
            oc = ord(ch)
            if oc < 127:
                if oc in [8,177]:
                    # backspace
                    chars = chars[:-1]
                else:
                    chars.append (ch)
        return ''.join(chars)

class monitor_server (asyncore.dispatcher):

    SERVER_IDENT = 'Monitor Server (V%s)' % VERSION

    channel_class = monitor_channel

    def __init__ (self, hostname='127.0.0.1', port=8023):
        asyncore.dispatcher.__init__(self)
        self.hostname = hostname
        self.port = port
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind ((hostname, port))
        self.log_info('%s started on port %d' % (self.SERVER_IDENT, port))
        self.listen (5)
        self.closed             = 0
        self.failed_auths = 0
        self.total_sessions = counter()
        self.closed_sessions = counter()

    def writable (self):
        return 0

    def handle_accept (self):
        conn, addr = self.accept()
        self.log_info('Incoming monitor connection from %s:%d' % addr)
        self.channel_class (self, conn, addr)
        self.total_sessions.increment()

    def status (self):
        return producers.simple_producer (
                '<h2>%s</h2>'                                           % self.SERVER_IDENT
                + '<br><b>Total Sessions:</b> %s'               % self.total_sessions
                + '<br><b>Current Sessions:</b> %d'     % (
                        self.total_sessions.as_long()-self.closed_sessions.as_long()
                        )
                )

def hex_digest (s):
    m = md5()
    m.update(s)
    return ''.join([hex (ord (x))[2:] for x in list(m.digest())])

class secure_monitor_channel (monitor_channel):
    authorized = 0

    def __init__ (self, server, sock, addr):
        asynchat.async_chat.__init__ (self, sock)
        self.server = server
        self.addr = addr
        self.set_terminator ('\r\n')
        self.data = ''
        # local bindings specific to this channel
        self.local_env = {}
        # send timestamp string
        self.timestamp = str(time.time())
        self.count = 0
        self.line_counter = counter()
        self.number = int(server.total_sessions.as_long())
        self.multi_line = []
        self.push (self.timestamp + '\r\n')

    def found_terminator (self):
        if not self.authorized:
            if hex_digest ('%s%s' % (self.timestamp, self.server.password)) != self.data:
                self.log_info ('%s: failed authorization' % self, 'warning')
                self.server.failed_auths += 1
                self.close()
            else:
                self.authorized = 1
                self.push ('Python ' + sys.version + '\r\n')
                self.push (sys.copyright+'\r\n')
                self.push ('Welcome to %s\r\n' % self)
                self.prompt()
                self.data = ''
        else:
            monitor_channel.found_terminator (self)

class secure_encrypted_monitor_channel (secure_monitor_channel):
    """Wrap send() and recv() with a stream cipher"""

    def __init__ (self, server, conn, addr):
        key = server.password
        self.outgoing = server.cipher.new (key)
        self.incoming = server.cipher.new (key)
        secure_monitor_channel.__init__ (self, server, conn, addr)

    def send (self, data):
        # send the encrypted data instead
        ed = self.outgoing.encrypt (data)
        return secure_monitor_channel.send (self, ed)

    def recv (self, block_size):
        data = secure_monitor_channel.recv (self, block_size)
        if data:
            dd = self.incoming.decrypt (data)
            return dd
        else:
            return data

class secure_monitor_server (monitor_server):
    channel_class = secure_monitor_channel

    def __init__ (self, password, hostname='', port=8023):
        monitor_server.__init__ (self, hostname, port)
        self.password = password

    def status (self):
        p = monitor_server.status (self)
        # kludge
        p.data += '<br><b>Failed Authorizations:</b> %d' % self.failed_auths
        return p

# don't try to print from within any of the methods
# of this object. 8^)

class output_producer:
    def __init__ (self, channel, real_stderr):
        self.channel = channel
        self.data = ''
        # use _this_ for debug output
        self.stderr = real_stderr

    def check_data (self):
        if len(self.data) > 1<<16:
            # runaway output, close it.
            self.channel.close()

    def write (self, data):
        lines = data.split('\n')
        data = '\r\n'.join(lines)
        self.data += data
        self.check_data()

    def writeline (self, line):
        self.data = self.data + line + '\r\n'
        self.check_data()

    def writelines (self, lines):
        self.data = self.data + '\r\n'.join(lines) + '\r\n'
        self.check_data()

    def flush (self):
        pass

    def softspace (self, *args):
        pass

    def more (self):
        if self.data:
            result = self.data[:512]
            self.data = self.data[512:]
            return result
        else:
            return ''

if __name__ == '__main__':
    if '-s' in sys.argv:
        sys.argv.remove ('-s')
        print_function('Enter password: ', end='')
        password = raw_input()
    else:
        password = None

    if '-e' in sys.argv:
        sys.argv.remove ('-e')
        encrypt = 1
    else:
        encrypt = 0

    if len(sys.argv) > 1:
        port = int(sys.argv[1])
    else:
        port = 8023

    if password is not None:
        s = secure_monitor_server (password, '', port)
        if encrypt:
            s.channel_class = secure_encrypted_monitor_channel
            import sapphire
            s.cipher = sapphire
    else:
        s = monitor_server ('', port)

    asyncore.loop(use_poll=1)

########NEW FILE########
__FILENAME__ = monitor_client
# -*- Mode: Python -*-

# monitor client, unix version.
import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat
import supervisor.medusa.text_socket as socket
import sys
import os

from supervisor.compat import md5
from supervisor.compat import print_function

class stdin_channel (asyncore.file_dispatcher):
    def handle_read (self):
        data = self.recv(512)
        if not data:
            print_function('\nclosed.')
            self.sock_channel.close()
            try:
                self.close()
            except:
                pass

        data = data.replace('\n', '\r\n')
        self.sock_channel.push (data)

    def writable (self):
        return 0

    def log (self, *ignore):
        pass

class monitor_client (asynchat.async_chat):
    def __init__ (self, password, addr=('',8023), socket_type=socket.AF_INET):
        asynchat.async_chat.__init__ (self)
        self.create_socket (socket_type, socket.SOCK_STREAM)
        self.terminator = '\r\n'
        self.connect (addr)
        self.sent_auth = 0
        self.timestamp = ''
        self.password = password

    def collect_incoming_data (self, data):
        if not self.sent_auth:
            self.timestamp = self.timestamp + data
        else:
            sys.stdout.write (data)
            sys.stdout.flush()

    def found_terminator (self):
        if not self.sent_auth:
            self.push (hex_digest (self.timestamp + self.password) + '\r\n')
            self.sent_auth = 1
        else:
            print_function()

    def handle_close (self):
        # close all the channels, which will make the standard main
        # loop exit.
        for c in asyncore.socket_map.values():
            c.close()

    def log (self, *ignore):
        pass

class encrypted_monitor_client (monitor_client):
    """Wrap push() and recv() with a stream cipher"""

    def init_cipher (self, cipher, key):
        self.outgoing = cipher.new (key)
        self.incoming = cipher.new (key)

    def push (self, data):
        # push the encrypted data instead
        return monitor_client.push (self, self.outgoing.encrypt (data))

    def recv (self, block_size):
        data = monitor_client.recv (self, block_size)
        if data:
            return self.incoming.decrypt (data)
        else:
            return data

def hex_digest (s):
    m = md5.md5()
    m.update(s)
    return ''.join([hex (ord (x))[2:] for x in m.digest()])

if __name__ == '__main__':
    if len(sys.argv) == 1:
        print_function('Usage: %s host port' % sys.argv[0])
        sys.exit(0)

    if '-e' in sys.argv:
        encrypt = 1
        sys.argv.remove ('-e')
    else:
        encrypt = 0

    sys.stderr.write ('Enter Password: ')
    sys.stderr.flush()
    try:
        os.system ('stty -echo')
        p = raw_input()
        print_function()
    finally:
        os.system ('stty echo')
    stdin = stdin_channel (0)
    if len(sys.argv) > 1:
        if encrypt:
            client = encrypted_monitor_client(p, (sys.argv[1], int(sys.argv[2])))
            import sapphire
            client.init_cipher (sapphire, p)
        else:
            client = monitor_client (p, (sys.argv[1], int(sys.argv[2])))
    else:
        # default to local host, 'standard' port
        client = monitor_client (p)
    stdin.sock_channel = client
    asyncore.loop()

########NEW FILE########
__FILENAME__ = monitor_client_win32
# -*- Mode: Python -*-

# monitor client, win32 version

# since we can't do select() on stdin/stdout, we simply
# use threads and blocking sockets.  <sigh>

import supervisor.medusa.text_socket as socket
import sys
import supervisor.medusa.thread as thread

from supervisor.compat import md5
from supervisor.compat import print_function

def hex_digest (s):
    m = md5.md5()
    m.update (s)
    return ''.join([hex (ord (x))[2:] for x in list(m.digest())])

def reader (lock, sock, password):
    # first grab the timestamp
    ts = sock.recv (1024)[:-2]
    sock.send (hex_digest (ts+password) + '\r\n')
    while 1:
        d = sock.recv (1024)
        if not d:
            lock.release()
            print('Connection closed.  Hit <return> to exit')
            thread.exit()
        sys.stdout.write (d)
        sys.stdout.flush()

def writer (lock, sock, barrel="just kidding"):
    while lock.locked():
        sock.send (
                sys.stdin.readline()[:-1] + '\r\n'
                )

if __name__ == '__main__':
    if len(sys.argv) == 1:
        print('Usage: %s host port')
        sys.exit(0)
    print_function('Enter Password: ',end='')
    p = raw_input()
    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    s.connect((sys.argv[1], int(sys.argv[2])))
    l = thread.allocate_lock()
    l.acquire()
    thread.start_new_thread (reader, (l, s, p))
    writer (l, s)

########NEW FILE########
__FILENAME__ = m_syslog
# -*- Mode: Python -*-

# ======================================================================
# Copyright 1997 by Sam Rushing
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Sam
# Rushing not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================

"""socket interface to unix syslog.
On Unix, there are usually two ways of getting to syslog: via a
local unix-domain socket, or via the TCP service.

Usually "/dev/log" is the unix domain socket.  This may be different
for other systems.

>>> my_client = syslog_client ('/dev/log')

Otherwise, just use the UDP version, port 514.

>>> my_client = syslog_client (('my_log_host', 514))

On win32, you will have to use the UDP version.  Note that
you can use this to log to other hosts (and indeed, multiple
hosts).

This module is not a drop-in replacement for the python
<syslog> extension module - the interface is different.

Usage:

>>> c = syslog_client()
>>> c = syslog_client ('/strange/non_standard_log_location')
>>> c = syslog_client (('other_host.com', 514))
>>> c.log ('testing', facility='local0', priority='debug')

"""

# TODO: support named-pipe syslog.
# [see ftp://sunsite.unc.edu/pub/Linux/system/Daemons/syslog-fifo.tar.z]

# from <linux/sys/syslog.h>:
# ===========================================================================
# priorities/facilities are encoded into a single 32-bit quantity, where the
# bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
# (0-big number).  Both the priorities and the facilities map roughly
# one-to-one to strings in the syslogd(8) source code.  This mapping is
# included in this file.
#
# priorities (these are ordered)

LOG_EMERG               = 0             #  system is unusable
LOG_ALERT               = 1             #  action must be taken immediately
LOG_CRIT                = 2             #  critical conditions
LOG_ERR                 = 3             #  error conditions
LOG_WARNING             = 4             #  warning conditions
LOG_NOTICE              = 5             #  normal but significant condition
LOG_INFO                = 6             #  informational
LOG_DEBUG               = 7             #  debug-level messages

#  facility codes
LOG_KERN                = 0             #  kernel messages
LOG_USER                = 1             #  random user-level messages
LOG_MAIL                = 2             #  mail system
LOG_DAEMON              = 3             #  system daemons
LOG_AUTH                = 4             #  security/authorization messages
LOG_SYSLOG              = 5             #  messages generated internally by syslogd
LOG_LPR                 = 6             #  line printer subsystem
LOG_NEWS                = 7             #  network news subsystem
LOG_UUCP                = 8             #  UUCP subsystem
LOG_CRON                = 9             #  clock daemon
LOG_AUTHPRIV    = 10    #  security/authorization messages (private)

#  other codes through 15 reserved for system use
LOG_LOCAL0              = 16            #  reserved for local use
LOG_LOCAL1              = 17            #  reserved for local use
LOG_LOCAL2              = 18            #  reserved for local use
LOG_LOCAL3              = 19            #  reserved for local use
LOG_LOCAL4              = 20            #  reserved for local use
LOG_LOCAL5              = 21            #  reserved for local use
LOG_LOCAL6              = 22            #  reserved for local use
LOG_LOCAL7              = 23            #  reserved for local use

priority_names = {
        "alert":        LOG_ALERT,
        "crit":         LOG_CRIT,
        "debug":        LOG_DEBUG,
        "emerg":        LOG_EMERG,
        "err":          LOG_ERR,
        "error":        LOG_ERR,                #  DEPRECATED
        "info":         LOG_INFO,
        "notice":       LOG_NOTICE,
        "panic":        LOG_EMERG,              #  DEPRECATED
        "warn":         LOG_WARNING,            #  DEPRECATED
        "warning":      LOG_WARNING,
        }

facility_names = {
        "auth":         LOG_AUTH,
        "authpriv":     LOG_AUTHPRIV,
        "cron":         LOG_CRON,
        "daemon":       LOG_DAEMON,
        "kern":         LOG_KERN,
        "lpr":          LOG_LPR,
        "mail":         LOG_MAIL,
        "news":         LOG_NEWS,
        "security":     LOG_AUTH,               #  DEPRECATED
        "syslog":       LOG_SYSLOG,
        "user":         LOG_USER,
        "uucp":         LOG_UUCP,
        "local0":       LOG_LOCAL0,
        "local1":       LOG_LOCAL1,
        "local2":       LOG_LOCAL2,
        "local3":       LOG_LOCAL3,
        "local4":       LOG_LOCAL4,
        "local5":       LOG_LOCAL5,
        "local6":       LOG_LOCAL6,
        "local7":       LOG_LOCAL7,
        }

import supervisor.medusa.text_socket as socket

class syslog_client:
    def __init__ (self, address='/dev/log'):
        self.address = address
        self.stream = 0
        if isinstance(address, type('')):
            try:
                self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
                self.socket.connect(address)
            except socket.error:
                # Some Linux installations have /dev/log
                # a stream socket instead of a datagram socket.
                self.socket = socket.socket (socket.AF_UNIX,
                                             socket.SOCK_STREAM)
                self.stream = 1
        else:
            self.socket = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)

    # curious: when talking to the unix-domain '/dev/log' socket, a
    #   zero-terminator seems to be required.  this string is placed
    #   into a class variable so that it can be overridden if
    #   necessary.

    log_format_string = '<%d>%s\000'

    def log (self, message, facility=LOG_USER, priority=LOG_INFO):
        message = self.log_format_string % (
                self.encode_priority (facility, priority),
                message
                )
        if self.stream:
            self.socket.send (message)
        else:
            self.socket.sendto (message, self.address)

    def encode_priority (self, facility, priority):
        if type(facility) == type(''):
            facility = facility_names[facility]
        if type(priority) == type(''):
            priority = priority_names[priority]
        return (facility<<3) | priority

    def close (self):
        if self.stream:
            self.socket.close()

########NEW FILE########
__FILENAME__ = producers
# -*- Mode: Python -*-

RCS_ID = '$Id: producers.py,v 1.9 2004/04/21 13:56:28 akuchling Exp $'

"""
A collection of producers.
Each producer implements a particular feature:  They can be combined
in various ways to get interesting and useful behaviors.

For example, you can feed dynamically-produced output into the compressing
producer, then wrap this with the 'chunked' transfer-encoding producer.
"""

from asynchat import find_prefix_at_end

class simple_producer:
    """producer for a string"""
    def __init__ (self, data, buffer_size=1024):
        self.data = data
        self.buffer_size = buffer_size

    def more (self):
        if len (self.data) > self.buffer_size:
            result = self.data[:self.buffer_size]
            self.data = self.data[self.buffer_size:]
            return result
        else:
            result = self.data
            self.data = ''
            return result

class scanning_producer:
    """like simple_producer, but more efficient for large strings"""
    def __init__ (self, data, buffer_size=1024):
        self.data = data
        self.buffer_size = buffer_size
        self.pos = 0

    def more (self):
        if self.pos < len(self.data):
            lp = self.pos
            rp = min (
                    len(self.data),
                    self.pos + self.buffer_size
                    )
            result = self.data[lp:rp]
            self.pos += len(result)
            return result
        else:
            return ''

class lines_producer:
    """producer for a list of lines"""

    def __init__ (self, lines):
        self.lines = lines

    def more (self):
        if self.lines:
            chunk = self.lines[:50]
            self.lines = self.lines[50:]
            return '\r\n'.join(chunk) + '\r\n'
        else:
            return ''

class buffer_list_producer:
    """producer for a list of strings"""

    # i.e., data == ''.join(buffers)

    def __init__ (self, buffers):
        self.index = 0
        self.buffers = buffers

    def more (self):
        if self.index >= len(self.buffers):
            return ''
        else:
            data = self.buffers[self.index]
            self.index += 1
            return data

class file_producer:
    """producer wrapper for file[-like] objects"""

    # match http_channel's outgoing buffer size
    out_buffer_size = 1<<16

    def __init__ (self, file):
        self.done = 0
        self.file = file

    def more (self):
        if self.done:
            return ''
        else:
            data = self.file.read (self.out_buffer_size)
            if not data:
                self.file.close()
                del self.file
                self.done = 1
                return ''
            else:
                return data

# A simple output producer.  This one does not [yet] have
# the safety feature builtin to the monitor channel:  runaway
# output will not be caught.

# don't try to print from within any of the methods
# of this object.

class output_producer:
    """Acts like an output file; suitable for capturing sys.stdout"""
    def __init__ (self):
        self.data = ''

    def write (self, data):
        lines = data.split('\n')
        data = '\r\n'.join(lines)
        self.data += data

    def writeline (self, line):
        self.data = self.data + line + '\r\n'

    def writelines (self, lines):
        self.data = self.data + '\r\n'.join(lines) + '\r\n'

    def flush (self):
        pass

    def softspace (self, *args):
        pass

    def more (self):
        if self.data:
            result = self.data[:512]
            self.data = self.data[512:]
            return result
        else:
            return ''

class composite_producer:
    """combine a fifo of producers into one"""
    def __init__ (self, producers):
        self.producers = producers

    def more (self):
        while len(self.producers):
            p = self.producers[0]
            d = p.more()
            if d:
                return d
            else:
                self.producers.pop(0)
        else:
            return ''


class globbing_producer:
    """
    'glob' the output from a producer into a particular buffer size.
    helps reduce the number of calls to send().  [this appears to
    gain about 30% performance on requests to a single channel]
    """

    def __init__ (self, producer, buffer_size=1<<16):
        self.producer = producer
        self.buffer = ''
        self.buffer_size = buffer_size

    def more (self):
        while len(self.buffer) < self.buffer_size:
            data = self.producer.more()
            if data:
                self.buffer = self.buffer + data
            else:
                break
        r = self.buffer
        self.buffer = ''
        return r


class hooked_producer:
    """
    A producer that will call <function> when it empties,.
    with an argument of the number of bytes produced.  Useful
    for logging/instrumentation purposes.
    """

    def __init__ (self, producer, function):
        self.producer = producer
        self.function = function
        self.bytes = 0

    def more (self):
        if self.producer:
            result = self.producer.more()
            if not result:
                self.producer = None
                self.function (self.bytes)
            else:
                self.bytes += len(result)
            return result
        else:
            return ''

# HTTP 1.1 emphasizes that an advertised Content-Length header MUST be
# correct.  In the face of Strange Files, it is conceivable that
# reading a 'file' may produce an amount of data not matching that
# reported by os.stat() [text/binary mode issues, perhaps the file is
# being appended to, etc..]  This makes the chunked encoding a True
# Blessing, and it really ought to be used even with normal files.
# How beautifully it blends with the concept of the producer.

class chunked_producer:
    """A producer that implements the 'chunked' transfer coding for HTTP/1.1.
    Here is a sample usage:
            request['Transfer-Encoding'] = 'chunked'
            request.push (
                    producers.chunked_producer (your_producer)
                    )
            request.done()
    """

    def __init__ (self, producer, footers=None):
        self.producer = producer
        self.footers = footers

    def more (self):
        if self.producer:
            data = self.producer.more()
            if data:
                return '%x\r\n%s\r\n' % (len(data), data)
            else:
                self.producer = None
                if self.footers:
                    return '\r\n'.join(['0'] + self.footers) + '\r\n\r\n'
                else:
                    return '0\r\n\r\n'
        else:
            return ''

try:
    import zlib
except ImportError:
    zlib = None

class compressed_producer:
    """
    Compress another producer on-the-fly, using ZLIB
    """

    # Note: It's not very efficient to have the server repeatedly
    # compressing your outgoing files: compress them ahead of time, or
    # use a compress-once-and-store scheme.  However, if you have low
    # bandwidth and low traffic, this may make more sense than
    # maintaining your source files compressed.
    #
    # Can also be used for compressing dynamically-produced output.

    def __init__ (self, producer, level=5):
        self.producer = producer
        self.compressor = zlib.compressobj (level)

    def more (self):
        if self.producer:
            cdata = ''
            # feed until we get some output
            while not cdata:
                data = self.producer.more()
                if not data:
                    self.producer = None
                    return self.compressor.flush()
                else:
                    cdata = self.compressor.compress (data)
            return cdata
        else:
            return ''

class escaping_producer:

    """A producer that escapes a sequence of characters"""
    # Common usage: escaping the CRLF.CRLF sequence in SMTP, NNTP, etc...

    def __init__ (self, producer, esc_from='\r\n.', esc_to='\r\n..'):
        self.producer = producer
        self.esc_from = esc_from
        self.esc_to = esc_to
        self.buffer = ''
        self.find_prefix_at_end = find_prefix_at_end

    def more (self):
        esc_from = self.esc_from
        esc_to   = self.esc_to

        buffer = self.buffer + self.producer.more()

        if buffer:
            buffer = buffer.replace(esc_from, esc_to)
            i = self.find_prefix_at_end (buffer, esc_from)
            if i:
                # we found a prefix
                self.buffer = buffer[-i:]
                return buffer[:-i]
            else:
                # no prefix, return it all
                self.buffer = ''
                return buffer
        else:
            return buffer

########NEW FILE########
__FILENAME__ = put_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: put_handler.py,v 1.4 2002/08/01 18:15:45 akuchling Exp $'

import re

import supervisor.medusa.default_handler as default_handler
unquote         = default_handler.unquote
get_header      = default_handler.get_header

last_request = None

class put_handler:
    def __init__ (self, filesystem, uri_regex):
        self.filesystem = filesystem
        if type (uri_regex) == type(''):
            self.uri_regex = re.compile (uri_regex)
        else:
            self.uri_regex = uri_regex

    def match (self, request):
        uri = request.uri
        if request.command == 'PUT':
            m = self.uri_regex.match (uri)
            if m and m.end() == len(uri):
                return 1
        return 0

    def handle_request (self, request):

        path, params, query, fragment = request.split_uri()

        # strip off leading slashes
        while path and path[0] == '/':
            path = path[1:]

        if '%' in path:
            path = unquote (path)

        # make sure there's a content-length header
        cl = get_header (CONTENT_LENGTH, request.header)
        if not cl:
            request.error (411)
            return
        else:
            cl = int(cl)

        # don't let the try to overwrite a directory
        if self.filesystem.isdir (path):
            request.error (405)
            return

        is_update = self.filesystem.isfile (path)

        try:
            output_file = self.filesystem.open (path, 'wb')
        except:
            request.error (405)
            return

        request.collector = put_collector (output_file, cl, request, is_update)

        # no terminator while receiving PUT data
        request.channel.set_terminator (None)

        # don't respond yet, wait until we've received the data...

class put_collector:
    def __init__ (self, file, length, request, is_update):
        self.file               = file
        self.length             = length
        self.request    = request
        self.is_update  = is_update
        self.bytes_in   = 0

    def collect_incoming_data (self, data):
        ld = len(data)
        bi = self.bytes_in
        if (bi + ld) >= self.length:
            # last bit of data
            chunk = self.length - bi
            self.file.write (data[:chunk])
            self.file.close()

            if chunk != ld:
                print('orphaned %d bytes: <%s>' % (ld - chunk, repr(data[chunk:])))

            # do some housekeeping
            r = self.request
            ch = r.channel
            ch.current_request = None
            # set the terminator back to the default
            ch.set_terminator ('\r\n\r\n')
            if self.is_update:
                r.reply_code = 204 # No content
                r.done()
            else:
                r.reply_now (201) # Created
            # avoid circular reference
            del self.request
        else:
            self.file.write (data)
            self.bytes_in += ld

    def found_terminator (self):
        # shouldn't be called
        pass

CONTENT_LENGTH = re.compile ('Content-Length: ([0-9]+)', re.IGNORECASE)

########NEW FILE########
__FILENAME__ = redirecting_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996-2000 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: redirecting_handler.py,v 1.4 2002/03/20 17:37:48 amk Exp $'

import re
import supervisor.medusa.counter as counter

class redirecting_handler:

    def __init__ (self, pattern, redirect, regex_flag=re.IGNORECASE):
        self.pattern = pattern
        self.redirect = redirect
        self.patreg = re.compile (pattern, regex_flag)
        self.hits = counter.counter()

    def match (self, request):
        m = self.patreg.match (request.uri)
        return m and (m.end() == len(request.uri))

    def handle_request (self, request):
        self.hits.increment()
        m = self.patreg.match (request.uri)
        part = m.group(1)

        request['Location'] = self.redirect % part
        request.error (302) # moved temporarily

    def __repr__ (self):
        return '<Redirecting Handler at %08x [%s => %s]>' % (
                id(self),
                repr(self.pattern),
                repr(self.redirect)
                )

    def status (self):
        import supervisor.medusa.producers as producers
        return producers.simple_producer (
                '<li> Redirecting Handler %s => %s <b>Hits</b>: %s' % (
                        self.pattern, self.redirect, self.hits
                        )
                )

########NEW FILE########
__FILENAME__ = resolver
# -*- Mode: Python -*-

#
#       Author: Sam Rushing <rushing@nightmare.com>
#
RCS_ID =  '$Id: resolver.py,v 1.4 2002/03/20 17:37:48 amk Exp $'


# Fast, low-overhead asynchronous name resolver.  uses 'pre-cooked'
# DNS requests, unpacks only as much as it needs of the reply.

# see rfc1035 for details

from supervisor.compat import reduce

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.text_socket as socket
import sys
import time
from supervisor.medusa.counter import counter

VERSION = RCS_ID.split()[2]

# header
#                                    1  1  1  1  1  1
#      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                      ID                       |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                    QDCOUNT                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                    ANCOUNT                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                    NSCOUNT                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                    ARCOUNT                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+


# question
#                                    1  1  1  1  1  1
#      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                                               |
#    /                     QNAME                     /
#    /                                               /
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                     QTYPE                     |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                     QCLASS                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

# build a DNS address request, _quickly_
def fast_address_request (host, id=0):
    return (
            '%c%c' % (chr((id>>8)&0xff),chr(id&0xff))
            + '\001\000\000\001\000\000\000\000\000\000%s\000\000\001\000\001' % (
                    ''.join (['%c%s' % (chr(len(part)),part) for part in host.split('.')])
                    )
            )

def fast_ptr_request (host, id=0):
    return (
            '%c%c' % (chr((id>>8)&0xff),chr(id&0xff))
            + '\001\000\000\001\000\000\000\000\000\000%s\000\000\014\000\001' %
            ''.join(['%c%s' % (chr(len(part)),part) for part in host.split('.')])
            )

def unpack_name (r,pos):
    n = []
    while 1:
        ll = ord(r[pos])
        if ll&0xc0:
            # compression
            pos = (ll&0x3f << 8) + (ord(r[pos+1]))
        elif ll == 0:
            break
        else:
            pos += 1
            n.append (r[pos:pos+ll])
            pos += ll
    return '.'.join (n)

def skip_name (r,pos):
    while 1:
        ll = ord(r[pos])
        if ll&0xc0:
            # compression
            return pos + 2
        elif ll == 0:
            pos += 1
            break
        else:
            pos = pos + ll + 1
    return pos

def unpack_ttl (r,pos):
    return reduce (
            lambda x,y: (x<<8)|y,
            map (ord, r[pos:pos+4])
            )

# resource record
#                                    1  1  1  1  1  1
#      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                                               |
#    /                                               /
#    /                      NAME                     /
#    |                                               |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                      TYPE                     |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                     CLASS                     |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                      TTL                      |
#    |                                               |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#    |                   RDLENGTH                    |
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
#    /                     RDATA                     /
#    /                                               /
#    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

def unpack_address_reply (r):
    ancount = (ord(r[6])<<8) + (ord(r[7]))
    # skip question, first name starts at 12,
    # this is followed by QTYPE and QCLASS
    pos = skip_name (r, 12) + 4
    if ancount:
        # we are looking very specifically for
        # an answer with TYPE=A, CLASS=IN (\000\001\000\001)
        for an in range(ancount):
            pos = skip_name (r, pos)
            if r[pos:pos+4] == '\000\001\000\001':
                return (
                        unpack_ttl (r,pos+4),
                        '%d.%d.%d.%d' % tuple(map(ord,r[pos+10:pos+14]))
                        )
            # skip over TYPE, CLASS, TTL, RDLENGTH, RDATA
            pos += 8
            rdlength = (ord(r[pos])<<8) + (ord(r[pos+1]))
            pos = pos + 2 + rdlength
        return 0, None
    else:
        return 0, None

def unpack_ptr_reply (r):
    ancount = (ord(r[6])<<8) + (ord(r[7]))
    # skip question, first name starts at 12,
    # this is followed by QTYPE and QCLASS
    pos = skip_name (r, 12) + 4
    if ancount:
        # we are looking very specifically for
        # an answer with TYPE=PTR, CLASS=IN (\000\014\000\001)
        for an in range(ancount):
            pos = skip_name (r, pos)
            if r[pos:pos+4] == '\000\014\000\001':
                return (
                        unpack_ttl (r,pos+4),
                        unpack_name (r, pos+10)
                        )
            # skip over TYPE, CLASS, TTL, RDLENGTH, RDATA
            pos += 8
            rdlength = (ord(r[pos])<<8) + (ord(r[pos+1]))
            pos = pos + 2 + rdlength
        return 0, None
    else:
        return 0, None


# This is a UDP (datagram) resolver.

#
# It may be useful to implement a TCP resolver.  This would presumably
# give us more reliable behavior when things get too busy.  A TCP
# client would have to manage the connection carefully, since the
# server is allowed to close it at will (the RFC recommends closing
# after 2 minutes of idle time).
#
# Note also that the TCP client will have to prepend each request
# with a 2-byte length indicator (see rfc1035).
#

class resolver (asyncore.dispatcher):
    id = counter()
    def __init__ (self, server='127.0.0.1'):
        asyncore.dispatcher.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_DGRAM)
        self.server = server
        self.request_map = {}
        self.last_reap_time = int(time.time())      # reap every few minutes

    def writable (self):
        return 0

    def log (self, *args):
        pass

    def handle_close (self):
        self.log_info('closing!')
        self.close()

    def handle_error (self):      # don't close the connection on error
        file_fun_line, t, v, tbinfo = asyncore.compact_traceback()
        self.log_info(
                        'Problem with DNS lookup (%s:%s %s)' % (t, v, tbinfo),
                        'error')

    def get_id (self):
        return self.id.as_long() % (1<<16)

    def reap (self):          # find DNS requests that have timed out
        now = int(time.time())
        if now - self.last_reap_time > 180:        # reap every 3 minutes
            self.last_reap_time = now              # update before we forget
            for k,(host,unpack,callback,when) in list(self.request_map.items()):
                if now - when > 180:               # over 3 minutes old
                    del self.request_map[k]
                    try:                           # same code as in handle_read
                        callback (host, 0, None)   # timeout val is (0,None)
                    except:
                        file_fun_line, t, v, tbinfo = asyncore.compact_traceback()
                        self.log_info('%s %s %s' % (t,v,tbinfo), 'error')

    def resolve (self, host, callback):
        self.reap()                                # first, get rid of old guys
        self.socket.sendto (
                fast_address_request (host, self.get_id()),
                (self.server, 53)
                )
        self.request_map [self.get_id()] = (
                host, unpack_address_reply, callback, int(time.time()))
        self.id.increment()

    def resolve_ptr (self, host, callback):
        self.reap()                                # first, get rid of old guys
        ip = host.split('.')
        ip.reverse()
        ip = '.'.join(ip) + '.in-addr.arpa'
        self.socket.sendto (
                fast_ptr_request (ip, self.get_id()),
                (self.server, 53)
                )
        self.request_map [self.get_id()] = (
                host, unpack_ptr_reply, callback, int(time.time()))
        self.id.increment()

    def handle_read (self):
        reply, whence = self.socket.recvfrom (512)
        # for security reasons we may want to double-check
        # that <whence> is the server we sent the request to.
        id = (ord(reply[0])<<8) + ord(reply[1])
        if id in self.request_map:
            host, unpack, callback, when = self.request_map[id]
            del self.request_map[id]
            ttl, answer = unpack (reply)
            try:
                callback (host, ttl, answer)
            except:
                file_fun_line, t, v, tbinfo = asyncore.compact_traceback()
                self.log_info('%s %s %s' % ( t,v,tbinfo), 'error')

class rbl (resolver):

    def resolve_maps (self, host, callback):
        ip = host.split('.')
        ip.reverse()
        ip = '.'.join(ip) + '.rbl.maps.vix.com'
        self.socket.sendto (
                fast_ptr_request (ip, self.get_id()),
                (self.server, 53)
                )
        self.request_map [self.get_id()] = host, self.check_reply, callback
        self.id.increment()

    def check_reply (self, r):
        # we only need to check RCODE.
        rcode = (ord(r[3])&0xf)
        self.log_info('MAPS RBL; RCODE =%02x\n %s' % (rcode, repr(r)))
        return 0, rcode # (ttl, answer)


class hooked_callback:
    def __init__ (self, hook, callback):
        self.hook, self.callback = hook, callback

    def __call__ (self, *args):
        self.hook(*args)
        self.callback(*args)

class caching_resolver (resolver):
    """Cache DNS queries.  Will need to honor the TTL value in the replies"""

    def __init__ (self, server='127.0.0.1'):
        resolver.__init__(self, server)
        self.cache = {}
        self.forward_requests = counter()
        self.reverse_requests = counter()
        self.cache_hits = counter()

    def resolve (self, host, callback):
        self.forward_requests.increment()
        if host in self.cache:
            when, ttl, answer = self.cache[host]
            # ignore TTL for now
            callback (host, ttl, answer)
            self.cache_hits.increment()
        else:
            resolver.resolve (
                    self,
                    host,
                    hooked_callback (
                            self.callback_hook,
                            callback
                            )
                    )

    def resolve_ptr (self, host, callback):
        self.reverse_requests.increment()
        if host in self.cache:
            when, ttl, answer = self.cache[host]
            # ignore TTL for now
            callback (host, ttl, answer)
            self.cache_hits.increment()
        else:
            resolver.resolve_ptr (
                    self,
                    host,
                    hooked_callback (
                            self.callback_hook,
                            callback
                            )
                    )

    def callback_hook (self, host, ttl, answer):
        self.cache[host] = time.time(), ttl, answer

    SERVER_IDENT = 'Caching DNS Resolver (V%s)' % VERSION

    def status (self):
        import supervisor.medusa.producers as producers
        return producers.simple_producer (
                '<h2>%s</h2>'                                   % self.SERVER_IDENT
                + '<br>Server: %s'                              % self.server
                + '<br>Cache Entries: %d'               % len(self.cache)
                + '<br>Outstanding Requests: %d' % len(self.request_map)
                + '<br>Forward Requests: %s'    % self.forward_requests
                + '<br>Reverse Requests: %s'    % self.reverse_requests
                + '<br>Cache Hits: %s'                  % self.cache_hits
                )

#test_reply = """\000\000\205\200\000\001\000\001\000\002\000\002\006squirl\011nightmare\003com\000\000\001\000\001\300\014\000\001\000\001\000\001Q\200\000\004\315\240\260\005\011nightmare\003com\000\000\002\000\001\000\001Q\200\000\002\300\014\3006\000\002\000\001\000\001Q\200\000\015\003ns1\003iag\003net\000\300\014\000\001\000\001\000\001Q\200\000\004\315\240\260\005\300]\000\001\000\001\000\000\350\227\000\004\314\033\322\005"""
# def test_unpacker ():
#       print unpack_address_reply (test_reply)
#
# import time
# class timer:
#       def __init__ (self):
#               self.start = time.time()
#       def end (self):
#               return time.time() - self.start
#
# # I get ~290 unpacks per second for the typical case, compared to ~48
# # using dnslib directly.  also, that latter number does not include
# # picking the actual data out.
#
# def benchmark_unpacker():
#
#       r = range(1000)
#       t = timer()
#       for i in r:
#               unpack_address_reply (test_reply)
#       print '%.2f unpacks per second' % (1000.0 / t.end())

if __name__ == '__main__':
    if len(sys.argv) == 1:
        print('usage: %s [-r] [-s <server_IP>] host [host ...]' % sys.argv[0])
        sys.exit(0)
    elif '-s' in sys.argv:
        i = sys.argv.index('-s')
        server = sys.argv[i+1]
        del sys.argv[i:i+2]
    else:
        server = '127.0.0.1'

    if '-r' in sys.argv:
        reverse = 1
        i = sys.argv.index('-r')
        del sys.argv[i]
    else:
        reverse = 0

    if '-m' in sys.argv:
        maps = 1
        sys.argv.remove ('-m')
    else:
        maps = 0

    if maps:
        r = rbl (server)
    else:
        r = caching_resolver(server)

    count = len(sys.argv) - 1

    def print_it (host, ttl, answer):
        global count
        print('%s: %s' % (host, answer))
        count -= 1
        if not count:
            r.close()

    for host in sys.argv[1:]:
        if reverse:
            r.resolve_ptr (host, print_it)
        elif maps:
            r.resolve_maps (host, print_it)
        else:
            r.resolve (host, print_it)

    # hooked asyncore.loop()
    while asyncore.socket_map:
        asyncore.poll (30.0)
        print('requests outstanding: %d' % len(r.request_map))

########NEW FILE########
__FILENAME__ = rpc_client
# -*- Mode: Python -*-

# Copyright 1999, 2000 by eGroups, Inc.
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# eGroups not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# EGROUPS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL EGROUPS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

import marshal
import supervisor.medusa.text_socket as socket

try:
    from exceptions import StandardError
except ImportError:
    StandardError = Exception
import sys

#
# there are three clients in here.
#
# 1) rpc client
# 2) fastrpc client
# 3) async fastrpc client
#
# we hope that *whichever* choice you make, that you will enjoy the
# excellent hand-made construction, and return to do business with us
# again in the near future.
#

class RPC_Error(StandardError):
    pass

# ===========================================================================
#                                                         RPC Client
# ===========================================================================

# request types:
# 0 call
# 1 getattr
# 2 setattr
# 3 repr
# 4 del


class rpc_proxy:

    DEBUG = 0

    def __init__ (self, conn, oid):
        # route around __setattr__
        self.__dict__['conn'] = conn
        self.__dict__['oid'] = oid

    # Warning: be VERY CAREFUL with attribute references, keep
    #             this __getattr__ in mind!

    def __getattr__ (self, attr):
        # __getattr__ and __call__
        if attr == '__call__':
            # 0 == __call__
            return self.__remote_call__
        elif attr == '__repr__':
            # 3 == __repr__
            return self.__remote_repr__
        elif attr == '__getitem__':
            return self.__remote_getitem__
        elif attr == '__setitem__':
            return self.__remote_setitem__
        elif attr == '__len__':
            return self.__remote_len__
        else:
            # 1 == __getattr__
            return self.__send_request__ (1, attr)

    def __setattr__ (self, attr, value):
        return self.__send_request__ (2, (attr, value))

    def __del__ (self):
        self.__send_request__ (4, None)

    def __remote_repr__ (self):
        r = self.__send_request__ (3, None)
        return '<remote object [%s]>' % r[1:-1]

    def __remote_call__ (self, *args):
        return self.__send_request__ (0, args)

    def __remote_getitem__ (self, key):
        return self.__send_request__ (5, key)

    def __remote_setitem__ (self, key, value):
        return self.__send_request__ (6, (key, value))

    def __remote_len__ (self):
        return self.__send_request__ (7, None)

    _request_types_ = ['call', 'getattr', 'setattr', 'repr', 'del', 'getitem', 'setitem', 'len']

    def __send_request__ (self, *args):
        if self.DEBUG:
            kind = args[0]
            print (
                    'RPC: ==> %s:%08x:%s:%s' % (
                            self.conn.address,
                            self.oid,
                            self._request_types_[kind],
                            repr(args[1:])
                            )
                    )
        packet = marshal.dumps ((self.oid,)+args)
        # send request
        self.conn.send_packet (packet)
        # get response
        data = self.conn.receive_packet()
        # types of response:
        # 0: proxy
        # 1: error
        # 2: marshall'd data

        kind, value = marshal.loads (data)

        if kind == 0:
            # proxy (value == oid)
            if self.DEBUG:
                print('RPC: <== proxy(%08x)' % value)
            return rpc_proxy (self.conn, value)
        elif kind == 1:
            raise RPC_Error(value)
        else:
            if self.DEBUG:
                print('RPC: <== %s' % (repr(value)))
            return value

class rpc_connection:

    cache = {}

    def __init__ (self, address):
        self.address = address
        self.connect ()

    def connect (self):
        s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
        s.connect (self.address)
        self.socket = s

    def receive_packet (self):
        packet_len = int(self.socket.recv(8), 16)
        packet = []
        while packet_len:
            data = self.socket.recv (8192)
            packet.append (data)
            packet_len -= len(data)
        return ''.join(packet)

    def send_packet (self, packet):
        self.socket.send ('%08x%s' % (len(packet), packet))

def rpc_connect (address = ('localhost', 8746)):
    if address not in rpc_connection.cache:
        conn = rpc_connection (address)
        # get oid of remote object
        data = conn.receive_packet()
        (oid,) = marshal.loads (data)
        rpc_connection.cache[address] = rpc_proxy (conn, oid)
    return rpc_connection.cache[address]

# ===========================================================================
#                       fastrpc client
# ===========================================================================

class fastrpc_proxy:

    def __init__ (self, conn, path=()):
        self.conn = conn
        self.path = path

    def __getattr__ (self, attr):
        if attr == '__call__':
            return self.__method_caller__
        else:
            return fastrpc_proxy (self.conn, self.path + (attr,))

    def __method_caller__ (self, *args):
        # send request
        packet = marshal.dumps ((self.path, args))
        self.conn.send_packet (packet)
        # get response
        data = self.conn.receive_packet()
        error, result = marshal.loads (data)
        if error is None:
            return result
        else:
            raise RPC_Error(error)

    def __repr__ (self):
        return '<remote-method-%s at %x>' % ('.'.join(self.path), id (self))

def fastrpc_connect (address = ('localhost', 8748)):
    if address not in rpc_connection.cache:
        conn = rpc_connection (address)
        rpc_connection.cache[address] = fastrpc_proxy (conn)
    return rpc_connection.cache[address]

# ===========================================================================
#                                                async fastrpc client
# ===========================================================================

import supervisor.medusa.asynchat_25 as asynchat

class async_fastrpc_client (asynchat.async_chat):

    STATE_LENGTH = 'length state'
    STATE_PACKET = 'packet state'

    def __init__ (self, address=('idb', 3001)):

        asynchat.async_chat.__init__ (self)

        if type(address) is type(''):
            family = socket.AF_UNIX
        else:
            family = socket.AF_INET

        self.create_socket (family, socket.SOCK_STREAM)
        self.address = address
        self.request_fifo = []
        self.buffer = []
        self.pstate = self.STATE_LENGTH
        self.set_terminator (8)
        self._connected = 0
        self.connect (self.address)

    def log (self, *args):
        pass

    def handle_connect (self):
        self._connected = 1

    def close (self):
        self._connected = 0
        self.flush_pending_requests ('lost connection to rpc server')
        asynchat.async_chat.close(self)

    def flush_pending_requests (self, why):
        f = self.request_fifo
        while len(f):
            callback = f.pop(0)
            callback (why, None)

    def collect_incoming_data (self, data):
        self.buffer.append (data)

    def found_terminator (self):
        self.buffer, data = [], ''.join(self.buffer)

        if self.pstate is self.STATE_LENGTH:
            packet_length = int(data, 16)
            self.set_terminator (packet_length)
            self.pstate = self.STATE_PACKET
        else:
            # modified to fix socket leak in chat server, 2000-01-27, schiller@eGroups.net
            #self.set_terminator (8)
            #self.pstate = self.STATE_LENGTH
            error, result = marshal.loads (data)
            callback = self.request_fifo.pop(0)
            callback (error, result)
            self.close()    # for chat server

    def call_method (self, method, args, callback):
        if not self._connected:
            # might be a unix socket...
            family, type = self.family_and_type
            self.create_socket (family, type)
            self.connect (self.address)
        # push the request out the socket
        path = method.split('.')
        packet = marshal.dumps ((path, args))
        self.push ('%08x%s' % (len(packet), packet))
        self.request_fifo.append(callback)


if __name__ == '__main__':
    if '-f' in sys.argv:
        connect = fastrpc_connect
    else:
        connect = rpc_connect

    print('connecting...')
    c = connect()
    print('calling <remote>.calc.sum (1,2,3)')
    print(c.calc.sum(1, 2, 3))
    print('calling <remote>.calc.nonexistent(), expect an exception!')
    print(c.calc.nonexistent())

########NEW FILE########
__FILENAME__ = rpc_server
# -*- Mode: Python -*-

# Copyright 1999, 2000 by eGroups, Inc.
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# eGroups not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# EGROUPS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL EGROUPS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

# There are two RPC implementations here.

# The first ('rpc') attempts to be as transparent as possible, and
# passes along 'internal' methods like __getattr__, __getitem__, and
# __del__.  It is rather 'chatty', and may not be suitable for a
# high-performance system.

# The second ('fastrpc') is less flexible, but has much less overhead,
# and is easier to use from an asynchronous client.

import marshal
import supervisor.medusa.text_socket as socket
import sys

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

from supervisor.medusa.producers import scanning_producer
from supervisor.medusa.counter import counter

from supervisor.compat import reduce

MY_NAME = socket.gethostname().split('.')[0]

# ===========================================================================
#                                                         RPC server
# ===========================================================================

# marshal is good for low-level data structures.
# but when passing an 'object' (any non-marshallable object)
# we really want to pass a 'reference', which will act on
# the other side as a proxy.  How transparent can we make this?

class rpc_channel (asynchat.async_chat):

    """Simple RPC server."""

    # a 'packet': NNNNNNNNmmmmmmmmmmmmmmmm
    # (hex length in 8 bytes, followed by marshal'd packet data)
    # same protocol used in both directions.

    STATE_LENGTH = 'length state'
    STATE_PACKET = 'packet state'

    ac_out_buffer_size = 65536

    request_counter = counter()
    exception_counter = counter()
    client_counter = counter()

    def __init__ (self, root, conn, addr):
        self.root = root
        self.addr = addr
        asynchat.async_chat.__init__ (self, conn)
        self.pstate = self.STATE_LENGTH
        self.set_terminator (8)
        self.buffer = []
        self.proxies = {}
        rid = id(root)
        self.new_reference (root)
        p = marshal.dumps ((rid,))
        # send root oid to the other side
        self.push ('%08x%s' % (len(p), p))
        self.client_counter.increment()

    def new_reference (self, object):
        oid = id(object)
        ignore, refcnt = self.proxies.get (oid, (None, 0))
        self.proxies[oid] = (object, refcnt + 1)

    def forget_reference (self, oid):
        object, refcnt = self.proxies.get (oid, (None, 0))
        if refcnt > 1:
            self.proxies[oid] = (object, refcnt - 1)
        else:
            del self.proxies[oid]

    def log (self, *ignore):
        pass

    def collect_incoming_data (self, data):
        self.buffer.append (data)

    def found_terminator (self):
        self.buffer, data = [], ''.join(self.buffer)

        if self.pstate is self.STATE_LENGTH:
            packet_length = int(data, 16)
            self.set_terminator (packet_length)
            self.pstate = self.STATE_PACKET
        else:

            self.set_terminator (8)
            self.pstate = self.STATE_LENGTH

            oid, kind, arg = marshal.loads (data)

            obj, refcnt = self.proxies[oid]
            reply_kind = 2

            try:
                if kind == 0:
                    # __call__
                    result = obj(*arg)
                elif kind == 1:
                    # __getattr__
                    result = getattr (obj, arg)
                elif kind == 2:
                    # __setattr__
                    key, value = arg
                    setattr (obj, key, value)
                    result = None
                elif kind == 3:
                    # __repr__
                    result = repr(obj)
                elif kind == 4:
                    # __del__
                    self.forget_reference (oid)
                    result = None
                elif kind == 5:
                    # __getitem__
                    result = obj[arg]
                elif kind == 6:
                    # __setitem__
                    (key, value) = arg
                    obj[key] = value
                    result = None
                elif kind == 7:
                    # __len__
                    result = len(obj)
                else:
                    result = None

            except:
                reply_kind = 1
                (file,fun,line), t, v, tbinfo = asyncore.compact_traceback()
                result = '%s:%s:%s:%s (%s:%s)' % (MY_NAME, file, fun, line, t, str(v))
                self.log_info (result, 'error')
                self.exception_counter.increment()

            self.request_counter.increment()

            try:
                rb = marshal.dumps ((reply_kind, result))
            except ValueError:
                # unmarshallable object, return a reference
                rid = id(result)
                self.new_reference (result)
                rb = marshal.dumps ((0, rid))

            self.push_with_producer (
                    scanning_producer (
                            ('%08x' % len(rb)) + rb,
                            buffer_size = 65536
                            )
                    )

class rpc_server_root:
    pass

class rpc_server (asyncore.dispatcher):

    def __init__ (self, root, address = ('', 8746)):
        asyncore.dispatcher.__init__(self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind (address)
        self.listen (128)
        self.root = root

    def handle_accept (self):
        conn, addr = self.accept()
        rpc_channel (self.root, conn, addr)


# ===========================================================================
#                                                  Fast RPC server
# ===========================================================================

# no proxies, request consists
# of a 'chain' of getattrs terminated by a __call__.

# Protocol:
# <path>.<to>.<object> ( <param1>, <param2>, ... )
# => ( <value1>, <value2>, ... )
#
#
# (<path>, <params>)
# path: tuple of strings
# params: tuple of objects

class fastrpc_channel (asynchat.async_chat):

    """Simple RPC server"""

    # a 'packet': NNNNNNNNmmmmmmmmmmmmmmmm
    # (hex length in 8 bytes, followed by marshal'd packet data)
    # same protocol used in both directions.

    # A request consists of (<path-tuple>, <args-tuple>)
    # where <path-tuple> is a list of strings (eqv to 'a.b.c'.split('.'))

    STATE_LENGTH = 'length state'
    STATE_PACKET = 'packet state'

    def __init__ (self, root, conn, addr):
        self.root = root
        self.addr = addr
        asynchat.async_chat.__init__ (self, conn)
        self.pstate = self.STATE_LENGTH
        self.set_terminator (8)
        self.buffer = []

    def log (*ignore):
        pass

    def collect_incoming_data (self, data):
        self.buffer.append (data)

    def found_terminator (self):
        self.buffer, data = [], ''.join(self.buffer)

        if self.pstate is self.STATE_LENGTH:
            packet_length = int(data, 16)
            self.set_terminator (packet_length)
            self.pstate = self.STATE_PACKET
        else:
            self.set_terminator (8)
            self.pstate = self.STATE_LENGTH
            (path, params) = marshal.loads (data)
            o = self.root

            e = None

            try:
                for p in path:
                    o = getattr (o, p)
                result = o(*params)
            except:
                e = repr (asyncore.compact_traceback())
                result = None

            rb = marshal.dumps ((e,result))
            self.push (('%08x' % len(rb)) + rb)

class fastrpc_server (asyncore.dispatcher):

    def __init__ (self, root, address = ('', 8748)):
        asyncore.dispatcher.__init__(self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind (address)
        self.listen (128)
        self.root = root

    def handle_accept (self):
        conn, addr = self.accept()
        fastrpc_channel (self.root, conn, addr)

# ===========================================================================

if __name__ == '__main__':

    class thing:
        def __del__ (self):
            print('a thing has gone away %08x' % id(self))

    class sample_calc:

        def product (self, *values):
            return reduce (lambda a,b: a*b, values, 1)

        def sum (self, *values):
            return reduce (lambda a,b: a+b, values, 0)

        def eval (self, string):
            return eval (string)

        def make_a_thing (self):
            return thing()

    if '-f' in sys.argv:
        server_class = fastrpc_server
        address = ('', 8748)
    else:
        server_class = rpc_server
        address = ('', 8746)

    root = rpc_server_root()
    root.calc = sample_calc()
    root.sys = sys
    rs = server_class (root, address)
    asyncore.loop()

########NEW FILE########
__FILENAME__ = script_handler
# -*- Mode: Python -*-

# This is a simple python server-side script handler.

# A note about performance: This is really only suited for 'fast'
# scripts: The script should generate its output quickly, since the
# whole web server will stall otherwise.  This doesn't mean you have
# to write 'fast code' or anything, it simply means that you shouldn't
# call any long-running code, [like say something that opens up an
# internet connection, or a database query that will hold up the
# server].  If you need this sort of feature, you can support it using
# the asynchronous I/O 'api' that the rest of medusa is built on.  [or
# you could probably use threads]

# Put your script into your web docs directory (like a cgi-bin
# script), make sure it has the correct extension [see the overridable
# script_handler.extension member below].
#
# There's lots of things that can be done to tweak the restricted
# execution model.  Also, of course you could just use 'execfile'
# instead (this is now the default, see class variable
# script_handler.restricted)

import re
import sys

from supervisor.compat import StringIO

import supervisor.medusa.counter as counter
import supervisor.medusa.default_handler as default_handler
import supervisor.medusa.producers as producers

unquote    = default_handler.unquote

class script_handler:

    extension = 'mpy'
    restricted = 0

    script_regex = re.compile (
            r'.*/([^/]+\.%s)' % extension,
            re.IGNORECASE
            )

    def __init__ (self, filesystem):
        self.filesystem = filesystem
        self.hits = counter.counter()
        self.exceptions = counter.counter()

    def match (self, request):
        path = request.split_uri()[0]
        m = self.script_regex.match (path)
        return m and (m.end() == len(path))

    def handle_request (self, request):

        path = request.split_uri()[0]

        while path and path[0] == '/':
            path = path[1:]

        if '%' in path:
            path = unquote (path)

        if not self.filesystem.isfile (path):
            request.error (404)
            return
        else:

            self.hits.increment()

            request.script_filename = self.filesystem.translate (path)

            if request.command in ('PUT', 'POST'):
                # look for a Content-Length header.
                cl = request.get_header ('content-length')
                length = int(cl)
                if not cl:
                    request.error (411)
                else:
                    collector (self, length, request)
            else:
                self.continue_request (
                        request,
                        StringIO() # empty stdin
                        )

    def continue_request (self, request, stdin):
        temp_files = stdin, StringIO(), StringIO()
        old_files = sys.stdin, sys.stdout, sys.stderr

        try:
            sys.request = request
            sys.stdin, sys.stdout, sys.stderr = temp_files
            try:
                exec(compile(open(request.script_filename).read(), request.script_filename, 'exec'))
                request.reply_code = 200
            except:
                request.reply_code = 500
                self.exceptions.increment()
        finally:
            sys.stdin, sys.stdout, sys.stderr = old_files
            del sys.request

        i,o,e = temp_files

        if request.reply_code != 200:
            s = e.getvalue()
        else:
            s = o.getvalue()

        request['Content-Length'] = len(s)
        request.push (s)
        request.done()

    def status (self):
        return producers.simple_producer (
                '<li>Server-Side Script Handler'
                + '<ul>'
                + '  <li><b>Hits:</b> %s' % self.hits
                + '  <li><b>Exceptions:</b> %s' % self.exceptions
                + '</ul>'
                )


class persistent_script_handler:

    def __init__ (self):
        self.modules = {}
        self.hits = counter.counter()
        self.exceptions = counter.counter()

    def add_module (self, name, module):
        self.modules[name] = module

    def del_module (self, name):
        del self.modules[name]

    def match (self, request):
        path = request.split_uri()[0]
        parts = path.split ('/')
        if (len(parts)>1) and parts[1] in self.modules:
            module = self.modules[parts[1]]
            request.module = module
            return 1
        else:
            return 0

    def handle_request (self, request):
        if request.command in ('PUT', 'POST'):
            # look for a Content-Length header.
            cl = request.get_header ('content-length')
            length = int(cl)
            if not cl:
                request.error (411)
            else:
                collector (self, length, request)
        else:
            self.continue_request (request, StringIO())

    def continue_request (self, request, input_data):
        temp_files = input_data, StringIO(), StringIO()
        old_files = sys.stdin, sys.stdout, sys.stderr

        try:
            sys.stdin, sys.stdout, sys.stderr = temp_files
            # provide a default
            request['Content-Type'] = 'text/html'
            try:
                request.module.main (request)
                request.reply_code = 200
            except:
                request.reply_code = 500
                self.exceptions.increment()
        finally:
            sys.stdin, sys.stdout, sys.stderr = old_files

        i,o,e = temp_files

        if request.reply_code != 200:
            s = e.getvalue()
        else:
            s = o.getvalue()

        request['Content-Length'] = len(s)
        request.push (s)
        request.done()

class collector:

    def __init__ (self, handler, length, request):
        self.handler = handler
        self.request = request
        self.request.collector = self
        self.request.channel.set_terminator (length)
        self.buffer = StringIO()

    def collect_incoming_data (self, data):
        self.buffer.write (data)

    def found_terminator (self):
        self.buffer.seek(0)
        self.request.collector = None
        self.request.channel.set_terminator ('\r\n\r\n')
        self.handler.continue_request (
                self.request,
                self.buffer
                )

########NEW FILE########
__FILENAME__ = status_handler
# -*- Mode: Python -*-

VERSION_STRING = "$Id: status_handler.py,v 1.7 2003/12/24 16:08:16 akuchling Exp $"

#
# medusa status extension
#

import time
import re
from cgi import escape

from supervisor.compat import StringIO
from supervisor.compat import long

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.http_server as http_server
import supervisor.medusa.medusa_gif as medusa_gif
import supervisor.medusa.producers as producers
from supervisor.medusa.counter import counter

START_TIME = long(time.time())

class status_extension:
    hit_counter = counter()

    def __init__ (self, objects, statusdir='/status', allow_emergency_debug=0):
        self.objects = objects
        self.statusdir = statusdir
        self.allow_emergency_debug = allow_emergency_debug
        # We use /status instead of statusdir here because it's too
        # hard to pass statusdir to the logger, who makes the HREF
        # to the object dir.  We don't need the security-through-
        # obscurity here in any case, because the id is obscurity enough
        self.hyper_regex = re.compile('/status/object/([0-9]+)/.*')
        self.hyper_objects = []
        for object in objects:
            self.register_hyper_object (object)

    def __repr__ (self):
        return '<Status Extension (%s hits) at %x>' % (
                self.hit_counter,
                id(self)
                )

    def match (self, request):
        path, params, query, fragment = request.split_uri()
        # For reasons explained above, we don't use statusdir for /object
        return (path[:len(self.statusdir)] == self.statusdir or
                        path[:len("/status/object/")] == '/status/object/')

    # Possible Targets:
    # /status
    # /status/channel_list
    # /status/medusa.gif

    # can we have 'clickable' objects?
    # [yes, we can use id(x) and do a linear search]

    # Dynamic producers:
    # HTTP/1.0: we must close the channel, because it's dynamic output
    # HTTP/1.1: we can use the chunked transfer-encoding, and leave
    #   it open.

    def handle_request (self, request):
        path = request.split_uri()[0]
        self.hit_counter.increment()
        if path == self.statusdir:          # and not a subdirectory
            up_time = ''.join(english_time(long(time.time()) - START_TIME))
            request['Content-Type'] = 'text/html'
            request.push (
                    '<html>'
                    '<title>Medusa Status Reports</title>'
                    '<body bgcolor="#ffffff">'
                    '<h1>Medusa Status Reports</h1>'
                    '<b>Up:</b> %s' % up_time
                    )
            for i in range(len(self.objects)):
                try:
                    request.push (self.objects[i].status())
                except:
                    import traceback
                    stream = StringIO()
                    traceback.print_exc(None,stream)
                    request.push('<h2><font color="red">Error in Channel %3d: %s</font><pre>%s</pre>' % (i,escape(repr(self.objects[i])), escape(stream.getvalue())))
                request.push ('<hr>\r\n')
            request.push (
                    '<p><a href="%s/channel_list">Channel List</a>'
                    '<hr>'
                    '<img src="%s/medusa.gif" align=right width=%d height=%d>'
                    '</body></html>' % (
                            self.statusdir,
                            self.statusdir,
                            medusa_gif.width,
                            medusa_gif.height
                            )
                    )
            request.done()
        elif path == self.statusdir + '/channel_list':
            request['Content-Type'] = 'text/html'
            request.push ('<html><body>')
            request.push(channel_list_producer(self.statusdir))
            request.push (
                    '<hr>'
                    '<img src="%s/medusa.gif" align=right width=%d height=%d>' % (
                            self.statusdir,
                            medusa_gif.width,
                            medusa_gif.height
                            ) +
                    '</body></html>'
                    )
            request.done()

        elif path == self.statusdir + '/medusa.gif':
            request['Content-Type'] = 'image/gif'
            request['Content-Length'] = len(medusa_gif.data)
            request.push (medusa_gif.data)
            request.done()

        elif path == self.statusdir + '/close_zombies':
            message = (
                    '<h2>Closing all zombie http client connections...</h2>'
                    '<p><a href="%s">Back to the status page</a>' % self.statusdir
                    )
            request['Content-Type'] = 'text/html'
            request['Content-Length'] = len (message)
            request.push (message)
            now = int (time.time())
            for channel in asyncore.socket_map.keys():
                if channel.__class__ == http_server.http_channel:
                    if channel != request.channel:
                        if (now - channel.creation_time) > channel.zombie_timeout:
                            channel.close()
            request.done()

        # Emergency Debug Mode
        # If a server is running away from you, don't KILL it!
        # Move all the AF_INET server ports and perform an autopsy...
        # [disabled by default to protect the innocent]
        elif self.allow_emergency_debug and path == self.statusdir + '/emergency_debug':
            request.push ('<html>Moving All Servers...</html>')
            request.done()
            for channel in asyncore.socket_map.keys():
                if channel.accepting:
                    if type(channel.addr) is type(()):
                        ip, port = channel.addr
                        channel.socket.close()
                        channel.del_channel()
                        channel.addr = (ip, port+10000)
                        fam, typ = channel.family_and_type
                        channel.create_socket (fam, typ)
                        channel.set_reuse_addr()
                        channel.bind (channel.addr)
                        channel.listen(5)

        else:
            m = self.hyper_regex.match (path)
            if m:
                oid = int(m.group(1))
                for object in self.hyper_objects:
                    if id (object) == oid:
                        if hasattr (object, 'hyper_respond'):
                            object.hyper_respond (self, path, request)
            else:
                request.error (404)
                return

    def status (self):
        return producers.simple_producer (
                '<li>Status Extension <b>Hits</b> : %s' % self.hit_counter
                )

    def register_hyper_object (self, object):
        if not object in self.hyper_objects:
            self.hyper_objects.append (object)

import supervisor.medusa.logger as logger

class logger_for_status (logger.tail_logger):

    def status (self):
        return 'Last %d log entries for: %s' % (
                len (self.messages),
                html_repr (self)
                )

    def hyper_respond (self, sh, path, request):
        request['Content-Type'] = 'text/plain'
        messages = self.messages[:]
        messages.reverse()
        request.push (lines_producer (messages))
        request.done()

class lines_producer:
    def __init__ (self, lines):
        self.lines = lines

    def more (self):
        if self.lines:
            chunk = self.lines[:50]
            self.lines = self.lines[50:]
            return '\r\n'.join(chunk) + '\r\n'
        else:
            return ''

class channel_list_producer (lines_producer):
    def __init__ (self, statusdir):
        channel_reprs = ['&lt;' + repr(x)[1:-1] + '&gt;' for x in asyncore.socket_map.values()]
        channel_reprs.sort()
        lines_producer.__init__ (
                self,
                ['<h1>Active Channel List</h1>',
                 '<pre>'
                 ] + channel_reprs + [
                         '</pre>',
                         '<p><a href="%s">Status Report</a>' % statusdir
                         ]
                )


def html_repr (object):
    so = escape (repr (object))
    if hasattr (object, 'hyper_respond'):
        return '<a href="/status/object/%d/">%s</a>' % (id (object), so)
    else:
        return so

def html_reprs (list, front='', back=''):
    reprs = list(map (
            lambda x,f=front,b=back: '%s%s%s' % (f,x,b),
            [escape (html_repr(x)) for x in list]
            ))
    reprs.sort()
    return reprs

# for example, tera, giga, mega, kilo
# p_d (n, (1024, 1024, 1024, 1024))
# smallest divider goes first - for example
# minutes, hours, days
# p_d (n, (60, 60, 24))

def progressive_divide (n, parts):
    result = []
    for part in parts:
        n, rem = divmod (n, part)
        result.append (rem)
    result.append (n)
    return result

# b,k,m,g,t
def split_by_units (n, units, dividers, format_string):
    divs = progressive_divide (n, dividers)
    result = []
    for i in range(len(units)):
        if divs[i]:
            result.append (format_string % (divs[i], units[i]))
    result.reverse()
    if not result:
        return [format_string % (0, units[0])]
    else:
        return result

def english_bytes (n):
    return split_by_units (
            n,
            ('','K','M','G','T'),
            (1024, 1024, 1024, 1024, 1024),
            '%d %sB'
            )

def english_time (n):
    return split_by_units (
            n,
            ('secs', 'mins', 'hours', 'days', 'weeks', 'years'),
            (         60,     60,      24,     7,       52),
            '%d %s'
            )

########NEW FILE########
__FILENAME__ = asyn_http_bench
# -*- Mode: Python -*-

from supervisor.compat import as_bytes

import asyncore
import socket
import sys

def blurt (thing):
    sys.stdout.write (thing)
    sys.stdout.flush ()

total_sessions = 0

class http_client (asyncore.dispatcher_with_send):
    def __init__ (self, host='127.0.0.1', port=80, uri='/', num=10):
        asyncore.dispatcher_with_send.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.host = host
        self.port = port
        self.uri = uri
        self.num = num
        self.bytes = 0
        self.connect ((host, port))

    def log (self, *info):
        pass

    def handle_connect (self):
        self.connected = 1
#               blurt ('o')
        self.send (as_bytes('GET %s HTTP/1.0\r\n\r\n' % self.uri))

    def handle_read (self):
#               blurt ('.')
        d = self.recv (8192)
        self.bytes += len(d)

    def handle_close (self):
        global total_sessions
#               blurt ('(%d)' % (self.bytes))
        self.close()
        total_sessions += 1
        if self.num:
            http_client (self.host, self.port, self.uri, self.num-1)

import time
class timer:
    def __init__ (self):
        self.start = time.time()
    def end (self):
        return time.time() - self.start

from asyncore import socket_map, poll

MAX = 0

def loop (timeout=30.0):
    global MAX
    while socket_map:
        if len(socket_map) > MAX:
            MAX = len(socket_map)
        poll (timeout)

if __name__ == '__main__':
    if len(sys.argv) < 6:
        print('usage: %s <host> <port> <uri> <hits> <num_clients>' % sys.argv[0])
    else:
        [host, port, uri, hits, num] = sys.argv[1:]
        hits = int(hits)
        num = int(num)
        port = int(port)
        t = timer()
        clients = [http_client(host, port, uri, hits-1) for x in range(num)]
        #import profile
        #profile.run ('loop')
        loop()
        total_time = t.end()
        print (
                '\n%d clients\n%d hits/client\n'
                'total_hits:%d\n%.3f seconds\ntotal hits/sec:%.3f' % (
                        num,
                        hits,
                        total_sessions,
                        total_time,
                        total_sessions / total_time
                        )
                )
        print('Max. number of concurrent sessions: %d' % MAX)


# linux 2.x, talking to medusa
# 50 clients
# 1000 hits/client
# total_hits:50000
# 2255.858 seconds
# total hits/sec:22.165
# Max. number of concurrent sessions: 50

########NEW FILE########
__FILENAME__ = bench
# -*- Mode: Python -*-

# benchmark a single channel, pipelined

request = 'GET /index.html HTTP/1.0\r\nConnection: Keep-Alive\r\n\r\n'
last_request = 'GET /index.html HTTP/1.0\r\nConnection: close\r\n\r\n'

import supervisor.medusa.text_socket as socket
import time

class timer:
    def __init__ (self):
        self.start = time.time()
    def end (self):
        return time.time() - self.start

def bench (host, port=80, n=100):
    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    s.connect ((host, port))
    t = timer()
    s.send ((request * n) + last_request)
    while 1:
        d = s.recv(65536)
        if not d:
            break
    total = t.end()
    print('time: %.2f seconds  (%.2f hits/sec)' % (total, n / total))

if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3:
        print('usage: %s <host> <port> <count>' % (sys.argv[0]))
    else:
        bench(sys.argv[1], int(sys.argv[2]), int(sys.argv[3]))

########NEW FILE########
__FILENAME__ = max_sockets
# -*- Mode: Python -*-

import supervisor.medusa.text_socket as socket
import select

# several factors here we might want to test:
# 1) max we can create
# 2) max we can bind
# 3) max we can listen on
# 4) max we can connect

def max_server_sockets():
    sl = []
    while 1:
        try:
            s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
            s.bind (('',0))
            s.listen(5)
            sl.append (s)
        except:
            break
    num = len(sl)
    for s in sl:
        s.close()
    del sl
    return num

def max_client_sockets():
    # make a server socket
    server = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    server.bind (('', 9999))
    server.listen (5)
    sl = []
    while 1:
        try:
            s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
            s.connect (('', 9999))
            conn, addr = server.accept()
            sl.append ((s,conn))
        except:
            break
    num = len(sl)
    for s,c in sl:
        s.close()
        c.close()
    del sl
    return num

def max_select_sockets():
    sl = []
    while 1:
        try:
            s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
            s.bind (('',0))
            s.listen(5)
            sl.append (s)
            select.select(sl,[],[],0)
        except:
            break
    num = len(sl) - 1
    for s in sl:
        s.close()
    del sl
    return num

########NEW FILE########
__FILENAME__ = test_11
# -*- Mode: Python -*-

import supervisor.medusa.text_socket as socket
from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import asynchat_25 as asynchat

# get some performance figures for an HTTP/1.1 server.
# use pipelining.

class test_client (asynchat.async_chat):

    ac_in_buffer_size = 16384
    ac_out_buffer_size = 16384

    total_in = 0

    concurrent = 0
    max_concurrent = 0

    def __init__ (self, addr, chain):
        asynchat.async_chat.__init__ (self)
        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
        self.set_terminator ('\r\n\r\n')
        self.connect (addr)
        self.push (chain)

    def handle_connect (self):
        test_client.concurrent += 1
        if test_client.concurrent > test_client.max_concurrent:
            test_client.max_concurrent = test_client.concurrent

    def handle_expt (self):
        print('unexpected FD_EXPT thrown.  closing()')
        self.close()

    def close (self):
        test_client.concurrent -= 1
        asynchat.async_chat.close(self)

    def collect_incoming_data (self, data):
        test_client.total_in += len(data)

    def found_terminator (self):
        pass

    def log (self, *args):
        pass


import time

class timer:
    def __init__ (self):
        self.start = time.time()

    def end (self):
        return time.time() - self.start

def build_request_chain (num, host, request_size):
    s = 'GET /test%d.html HTTP/1.1\r\nHost: %s\r\n\r\n' % (request_size, host)
    sl = [s] * (num-1)
    sl.append (
            'GET /test%d.html HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n' % (
                    request_size, host
                    )
            )
    return ''.join(sl)

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 6:
        print('usage: %s <host> <port> <request-size> <num-requests> <num-connections>\n' % sys.argv[0])
    else:
        host = sys.argv[1]

        ip = socket.gethostbyname (host)

        port, request_size, num_requests, num_conns = [int(i) for i in sys.argv[2:]]

        chain = build_request_chain (num_requests, host, request_size)

        t = timer()
        for i in range (num_conns):
            test_client ((host,port), chain)
        asyncore.loop()
        total_time = t.end()

        # ok, now do some numbers
        total_bytes = test_client.total_in
        num_trans = num_requests * num_conns
        throughput = float (total_bytes) / total_time
        trans_per_sec = num_trans / total_time

        sys.stderr.write ('total time: %.2f\n' % total_time)
        sys.stderr.write ('number of transactions: %d\n' % num_trans)
        sys.stderr.write ('total bytes sent: %d\n' % total_bytes)
        sys.stderr.write ('total throughput (bytes/sec): %.2f\n' % throughput)
        sys.stderr.write ('transactions/second: %.2f\n' % trans_per_sec)
        sys.stderr.write ('max concurrent connections: %d\n' % test_client.max_concurrent)

        sys.stdout.write (
            ','.join (str(i) for i in (num_conns, num_requests, request_size, throughput, trans_per_sec)) + '\n'
        )

########NEW FILE########
__FILENAME__ = test_lb
# -*- Mode: Python -*-

# Get a lower bound for Medusa performance with a simple async
# client/server benchmark built on the async lib.  The idea is to test
# all the underlying machinery [select, asyncore, asynchat, etc...] in
# a context where there is virtually no processing of the data.

import supervisor.medusa.text_socket as socket
import sys

# ==================================================
# server
# ==================================================

from supervisor.medusa import asyncore_25 as asyncore
from supervisor.medusa import asynchat_25 as asynchat

class test_channel (asynchat.async_chat):

    ac_in_buffer_size = 16384
    ac_out_buffer_size = 16384

    total_in = 0

    def __init__ (self, conn, addr):
        asynchat.async_chat.__init__ (self, conn)
        self.set_terminator ('\r\n\r\n')
        self.buffer = ''

    def collect_incoming_data (self, data):
        self.buffer = self.buffer + data
        test_channel.total_in += len(data)

    def found_terminator (self):
        # we've gotten the data, now send it back
        data = self.buffer
        self.buffer = ''
        self.push (data+'\r\n\r\n')

    def handle_close (self):
        sys.stdout.write ('.'); sys.stdout.flush()
        self.close()

    def log (self, *args):
        pass

class test_server (asyncore.dispatcher):
    def __init__ (self, addr):
        asyncore.dispatcher.__init__(self)
        if type(addr) == type(''):
            f = socket.AF_UNIX
        else:
            f = socket.AF_INET

        self.create_socket (f, socket.SOCK_STREAM)
        self.bind (addr)
        self.listen (5)
        print('server started on %s' % str(addr))

    def handle_accept (self):
        conn, addr = self.accept()
        test_channel (conn, addr)

# ==================================================
# client
# ==================================================

# pretty much the same behavior, except that we kick
# off the exchange and decide when to quit

class test_client (test_channel):

    def __init__ (self, addr, packet, number):
        if type(addr) == type(''):
            f = socket.AF_UNIX
        else:
            f = socket.AF_INET

        asynchat.async_chat.__init__ (self)
        self.create_socket (f, socket.SOCK_STREAM)
        self.set_terminator ('\r\n\r\n')
        self.buffer = ''
        self.connect (addr)
        self.push (packet + '\r\n\r\n')
        self.number = number
        self.count = 0

    def handle_connect (self):
        pass

    def found_terminator (self):
        self.count += 1
        if self.count == self.number:
            sys.stdout.write('.'); sys.stdout.flush()
            self.close()
        else:
            test_channel.found_terminator (self)

import time

class timer:
    def __init__ (self):
        self.start = time.time()

    def end (self):
        return time.time() - self.start

if __name__ == '__main__':
    if '--poll' in sys.argv:
        sys.argv.remove ('--poll')
        use_poll=1
    else:
        use_poll=0

    if len(sys.argv) == 1:
        print('usage: %s\n'\
              '  (as a server) [--poll] -s <ip> <port>\n'\
              '  (as a client) [--poll] -c <ip> <port> <packet-size> <num-packets> <num-connections>\n' % sys.argv[0])
        sys.exit(0)
    if sys.argv[1] == '-s':
        s = test_server((sys.argv[2], int(sys.argv[3])))
        asyncore.loop(use_poll=use_poll)
    elif sys.argv[1] == '-c':
        # create the packet
        packet = int(sys.argv[4]) * 'B'
        host = sys.argv[2]
        port = int(sys.argv[3])
        num_packets = int(sys.argv[5])
        num_conns = int(sys.argv[6])

        t = timer()
        for i in range (num_conns):
            test_client ((host,port), packet, num_packets)
        asyncore.loop(use_poll=use_poll)
        total_time = t.end()

        # ok, now do some numbers
        bytes = test_client.total_in
        num_trans = num_packets * num_conns
        total_bytes = num_trans * len(packet)
        throughput = float (total_bytes) / total_time
        trans_per_sec = num_trans / total_time

        sys.stderr.write ('total time: %.2f\n' % total_time)
        sys.stderr.write ( 'number of transactions: %d\n' % num_trans)
        sys.stderr.write ( 'total bytes sent: %d\n' % total_bytes)
        sys.stderr.write ( 'total throughput (bytes/sec): %.2f\n' % throughput)
        sys.stderr.write ( ' [note, throughput is this amount in each direction]\n')
        sys.stderr.write ( 'transactions/second: %.2f\n' % trans_per_sec)

        sys.stdout.write (
            ','.join([str(i) for i in (num_conns, num_packets, len(packet), throughput, trans_per_sec)]) + '\n'
        )

########NEW FILE########
__FILENAME__ = test_medusa
# -*- Mode: Python -*-

import supervisor.medusa.text_socket as socket
import time
from supervisor.medusa import http_date

now = http_date.build_http_date (time.time())

cache_request = '\r\n'.join(
        ['GET / HTTP/1.0',
         'If-Modified-Since: %s' % now,
         ]) + '\r\n\r\n'

nocache_request = 'GET / HTTP/1.0\r\n\r\n'

def get (request, host='', port=8080):
    s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    s.send(request)
    while 1:
        d = s.recv (8192)
        if not d:
            break
    s.close()

class timer:
    def __init__ (self):
        self.start = time.time()
    def end (self):
        return time.time() - self.start

def test_cache (n=1000):
    t = timer()
    for i in range (n):
        get(cache_request)
    end = t.end()
    print('cache: %d requests, %.2f seconds, %.2f hits/sec' % (n, end, n / end))


def test_nocache (n=1000):
    t = timer()
    for i in range (n):
        get(nocache_request)
    end = t.end()
    print('nocache: %d requests, %.2f seconds, %.2f hits/sec' % (n, end, n / end))

if __name__ == '__main__':
    test_cache()
    test_nocache()

########NEW FILE########
__FILENAME__ = test_producers
##
## Test script for producers.py
##
#
#__revision__ = "$Id: test_producers.py,v 1.2 2002/09/18 20:16:40 akuchling Exp $"
#
#import StringIO, zlib
#from sancho.unittest import TestScenario, parse_args, run_scenarios
#
#tested_modules = ["supervisor.medusa.producers"]
#
#
#from supervisor.medusa import producers
#
#test_string = ''
#for i in range(16385):
#    test_string += chr(48 + (i%10))
#
#class ProducerTest (TestScenario):
#
#    def setup (self):
#        pass
#
#    def shutdown (self):
#        pass
#
#    def _check_all (self, p, expected_string):
#        # Check that a producer returns all of the string,
#        # and that it's the unchanged string.
#        count = 0
#        data = ""
#        while 1:
#            s = p.more()
#            if s == "":
#                break
#            count += len(s)
#            data += s
#        self.test_val('count', len(expected_string))
#        self.test_val('data', expected_string)
#        self.test_val('p.more()', '')
#        return data
#
#    def check_simple (self):
#        p = producers.simple_producer(test_string)
#        self.test_val('p.more()', test_string[:1024])
#
#        p = producers.simple_producer(test_string, buffer_size = 5)
#        self._check_all(p, test_string)
#
#    def check_scanning (self):
#        p = producers.scanning_producer(test_string)
#        self.test_val('p.more()', test_string[:1024])
#
#        p = producers.scanning_producer(test_string, buffer_size = 5)
#        self._check_all(p, test_string)
#
#    def check_lines (self):
#        p = producers.lines_producer(['a']* 65)
#        self._check_all(p, 'a\r\n'*65)
#
#    def check_buffer (self):
#        p = producers.buffer_list_producer(['a']* 1027)
#        self._check_all(p, 'a'*1027)
#
#    def check_file (self):
#        f = StringIO.StringIO(test_string)
#        p = producers.file_producer(f)
#        self._check_all(p, test_string)
#
#    def check_output (self):
#        p = producers.output_producer()
#        for i in range(0,66):
#            p.write('a')
#        for i in range(0,65):
#            p.write('b\n')
#        self._check_all(p, 'a'*66 + 'b\r\n'*65)
#
#    def check_composite (self):
#        p1 = producers.simple_producer('a'*66, buffer_size = 5)
#        p2 = producers.lines_producer(['b']*65)
#        p = producers.composite_producer([p1, p2])
#        self._check_all(p, 'a'*66 + 'b\r\n'*65)
#
#    def check_glob (self):
#        p1 = producers.simple_producer(test_string, buffer_size = 5)
#        p = producers.globbing_producer(p1, buffer_size = 1024)
#        self.test_true('1024 <= len(p.more())')
#
#    def check_hooked (self):
#        def f (num_bytes):
#            self.test_val('num_bytes', len(test_string))
#        p1 = producers.simple_producer(test_string, buffer_size = 5)
#        p = producers.hooked_producer(p1, f)
#        self._check_all(p, test_string)
#
#    def check_chunked (self):
#        p1 = producers.simple_producer('the quick brown fox', buffer_size = 5)
#        p = producers.chunked_producer(p1, footers=['FOOTER'])
#        self._check_all(p, """5\r
#the q\r
#5\r
#uick \r
#5\r
#brown\r
#4\r
# fox\r
#0\r
#FOOTER\r
#\r\n""")
#
#    def check_compressed (self):
#        p1 = producers.simple_producer(test_string, buffer_size = 5)
#        p = producers.compressed_producer(p1)
#        compr_data = self._check_all(p, zlib.compress(test_string, 5))
#        self.test_val('zlib.decompress(compr_data)', test_string)
#
#    def check_escaping (self):
#        p1 = producers.simple_producer('the quick brown fox', buffer_size = 5)
#        p = producers.escaping_producer(p1,
#                                        esc_from = ' ',
#                                        esc_to = '_')
#        self._check_all(p, 'the_quick_brown_fox')
#
## class ProducerTest
#
#
#if __name__ == "__main__":
#    (scenarios, options) = parse_args()
#    run_scenarios(scenarios, options)

########NEW FILE########
__FILENAME__ = test_single_11
# -*- Mode: Python -*-

# no-holds barred, test a single channel's pipelining speed

import supervisor.medusa.text_socket as socket

def build_request_chain (num, host, request_size):
    s = 'GET /test%d.html HTTP/1.1\r\nHost: %s\r\n\r\n' % (request_size, host)
    sl = [s] * (num-1)
    sl.append (
            'GET /test%d.html HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n' % (
                    request_size, host
                    )
            )
    return ''.join(sl)

import time

class timer:
    def __init__ (self):
        self.start = time.time()

    def end (self):
        return time.time() - self.start

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 5:
        print('usage: %s <host> <port> <request-size> <num-requests>' % (sys.argv[0]))
    else:
        host = sys.argv[1]
        port, request_size, num_requests = (int(i) for i in sys.argv[2:])
        chain = build_request_chain (num_requests, host, request_size)
        s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
        s.connect ((host,port))
        t = timer()
        s.send (chain)
        num_bytes = 0
        while 1:
            data = s.recv(16384)
            if not data:
                break
            else:
                num_bytes += len(data)
        total_time = t.end()
        print('total bytes received: %d' % num_bytes)
        print('total time: %.2f sec' % total_time)
        print('transactions/sec: %.2f' % (num_requests / total_time))

########NEW FILE########
__FILENAME__ = text_socket
# -*- Mode: Python -*-

__author__ = 'Scott Maxwell'

from supervisor.compat import PY3
from supervisor.compat import as_string, as_bytes

from socket import * # relied on to be imported from elsewhere

if PY3:
    bin_socket = socket
    class text_socket(socket):
        def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
            bin_socket.__init__(self, family, type, proto, fileno)

        def recv(self, *args, **kwargs):
            return as_string(bin_socket.recv(self, *args, **kwargs))

        def recvfrom(self, *args, **kwargs):
            reply, whence = bin_socket.recvfrom(self, *args, **kwargs)
            reply = as_string(reply)
            return reply, whence

        def send(self, data, *args, **kwargs):
            b = as_bytes(data)
            return bin_socket.send(self, b, *args, **kwargs)

        def sendall(self, data, *args, **kwargs):
            return bin_socket.sendall(self, as_bytes(data), *args, **kwargs)

        def sendto(self, data, *args, **kwargs):
            return bin_socket.sendto(self, as_bytes(data), *args, **kwargs)

        def accept(self):
#            sock, addr = bin_socket.accept(self)
#            sock = text_socket(self.family, self.type, self.proto, fileno=sock.fileno())
            fd, addr = self._accept()
            sock = text_socket(self.family, self.type, self.proto, fileno=fd)
            # Issue #7995: if no default timeout is set and the listening
            # socket had a (non-zero) timeout, force the new socket in blocking
            # mode to override platform-specific socket flags inheritance.
            if getdefaulttimeout() is None and self.gettimeout():
                sock.setblocking(True)
            return sock, addr

    text_socket.__init__.__doc__ = bin_socket.__init__.__doc__
    socket = text_socket

########NEW FILE########
__FILENAME__ = pi_module
# -*- Mode: Python -*-

# [reworking of the version in Python-1.5.1/Demo/scripts/pi.py]

# Print digits of pi forever.
#
# The algorithm, using Python's 'long' integers ("bignums"), works
# with continued fractions, and was conceived by Lambert Meertens.
#
# See also the ABC Programmer's Handbook, by Geurts, Meertens & Pemberton,
# published by Prentice-Hall (UK) Ltd., 1990.

StopException = "Stop!"

from supervisor.compat import long

def go (file):
    try:
        k, a, b, a1, b1 = long(2), long(4), long(1), long(12), long(4)
        while 1:
            # Next approximation
            p, q, k = k*k, long(2)*k+long(1), k+long(1)
            a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
            # Print common digits
            d, d1 = a/b, a1/b1
            while d == d1:
                if file.write (str(int(d))):
                    raise StopException
                a, a1 = long(10)*(a%b), long(10)*(a1%b1)
                d, d1 = a/b, a1/b1
    except StopException:
        return

class line_writer:
    """partition the endless line into 80-character ones"""

    def __init__ (self, file, digit_limit=10000):
        self.file = file
        self.buffer = ''
        self.count = 0
        self.digit_limit = digit_limit

    def write (self, data):
        self.buffer = self.buffer + data
        if len(self.buffer) > 80:
            line, self.buffer = self.buffer[:80], self.buffer[80:]
            self.file.write (line+'\r\n')
            self.count += 80
        if self.count > self.digit_limit:
            return 1
        else:
            return 0

def main (env, stdin, stdout):
    parts = env['REQUEST_URI'].split('/')
    if len(parts) >= 3:
        ndigits = int(parts[2])
    else:
        ndigits = 5000
    stdout.write ('Content-Type: text/plain\r\n\r\n')
    go (line_writer (stdout, ndigits))

########NEW FILE########
__FILENAME__ = select_trigger
# -*- Mode: 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
#
##############################################################################

__revision__ = "$Id: select_trigger.py,v 1.4 2003/01/09 15:49:15 akuchling Exp $"
from supervisor.compat import thread
from supervisor.compat import as_bytes

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

import os
import supervisor.medusa.text_socket as socket

if os.name == 'posix':

    class trigger (asyncore.file_dispatcher):

        """Wake up a call to select() running in the main thread"""

        # This is useful in a context where you are using Medusa's I/O
        # subsystem to deliver data, but the data is generated by another
        # thread.  Normally, if Medusa is in the middle of a call to
        # select(), new output data generated by another thread will have
        # to sit until the call to select() either times out or returns.
        # If the trigger is 'pulled' by another thread, it should immediately
        # generate a READ event on the trigger object, which will force the
        # select() invocation to return.

        # A common use for this facility: letting Medusa manage I/O for a
        # large number of connections; but routing each request through a
        # thread chosen from a fixed-size thread pool.  When a thread is
        # acquired, a transaction is performed, but output data is
        # accumulated into buffers that will be emptied more efficiently
        # by Medusa. [picture a server that can process database queries
        # rapidly, but doesn't want to tie up threads waiting to send data
        # to low-bandwidth connections]

        # The other major feature provided by this class is the ability to
        # move work back into the main thread: if you call pull_trigger()
        # with a thunk argument, when select() wakes up and receives the
        # event it will call your thunk from within that thread.  The main
        # purpose of this is to remove the need to wrap thread locks around
        # Medusa's data structures, which normally do not need them.  [To see
        # why this is true, imagine this scenario: A thread tries to push some
        # new data onto a channel's outgoing data queue at the same time that
        # the main thread is trying to remove some]

        def __init__ (self):
            r, w = self._fds = os.pipe()
            self.trigger = w
            asyncore.file_dispatcher.__init__(self, r)
            self.lock = thread.allocate_lock()
            self.thunks = []
            self._closed = 0

        # Override the asyncore close() method, because it seems that
        # it would only close the r file descriptor and not w.  The
        # constructor calls file_dispatcher.__init__ and passes r,
        # which would get stored in a file_wrapper and get closed by
        # the default close.  But that would leave w open...

        def close(self):
            if not self._closed:
                self._closed = 1
                self.del_channel()
                for fd in self._fds:
                    os.close(fd)
                self._fds = []

        def __repr__ (self):
            return '<select-trigger (pipe) at %x>' % id(self)

        def readable (self):
            return 1

        def writable (self):
            return 0

        def handle_connect (self):
            pass

        def handle_close(self):
            self.close()

        def pull_trigger (self, thunk=None):
            # print 'PULL_TRIGGER: ', len(self.thunks)
            if thunk:
                self.lock.acquire()
                try:
                    self.thunks.append(thunk)
                finally:
                    self.lock.release()
            os.write(self.trigger, as_bytes('x'))

        def handle_read (self):
            try:
                self.recv(8192)
            except socket.error:
                return
            self.lock.acquire()
            try:
                for thunk in self.thunks:
                    try:
                        thunk()
                    except:
                        nil, t, v, tbinfo = asyncore.compact_traceback()
                        print('exception in trigger thunk: (%s:%s %s)' % (t, v, tbinfo))
                self.thunks = []
            finally:
                self.lock.release()

else:

    # win32-safe version

    # XXX Should define a base class that has the common methods and
    # then put the platform-specific in a subclass named trigger.

    HOST = '127.0.0.1'
    MINPORT = 19950
    NPORTS = 50

    class trigger (asyncore.dispatcher):
        portoffset = 0

        def __init__ (self):
            a = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            w = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            # set TCP_NODELAY to true to avoid buffering
            w.setsockopt(socket.IPPROTO_TCP, 1, 1)

            # tricky: get a pair of connected sockets
            for i in range(NPORTS):
                trigger.portoffset = (trigger.portoffset + 1) % NPORTS
                port = MINPORT + trigger.portoffset
                address = (HOST, port)
                try:
                    a.bind(address)
                except socket.error:
                    continue
                else:
                    break
            else:
                raise RuntimeError('Cannot bind trigger!')

            a.listen(1)
            w.setblocking(0)
            try:
                w.connect(address)
            except:
                pass
            r, addr = a.accept()
            a.close()
            w.setblocking(1)
            self.trigger = w

            asyncore.dispatcher.__init__(self, r)
            self.lock = thread.allocate_lock()
            self.thunks = []
            self._trigger_connected = 0

        def __repr__ (self):
            return '<select-trigger (loopback) at %x>' % id(self)

        def readable (self):
            return 1

        def writable (self):
            return 0

        def handle_connect (self):
            pass

        def pull_trigger (self, thunk=None):
            if thunk:
                self.lock.acquire()
                try:
                    self.thunks.append(thunk)
                finally:
                    self.lock.release()
            self.trigger.send('x')

        def handle_read (self):
            try:
                self.recv(8192)
            except socket.error:
                return
            self.lock.acquire()
            try:
                for thunk in self.thunks:
                    try:
                        thunk()
                    except:
                        nil, t, v, tbinfo = asyncore.compact_traceback()
                        print ('exception in trigger thunk:'
                               ' (%s:%s %s)' % (t, v, tbinfo))
                self.thunks = []
            finally:
                self.lock.release()


the_trigger = None

class trigger_file:

    """A 'triggered' file object"""

    buffer_size = 4096

    def __init__ (self, parent):
        global the_trigger
        if the_trigger is None:
            the_trigger = trigger()
        self.parent = parent
        self.buffer = ''

    def write (self, data):
        self.buffer = self.buffer + data
        if len(self.buffer) > self.buffer_size:
            d, self.buffer = self.buffer, ''
            the_trigger.pull_trigger (
                    lambda d=d,p=self.parent: p.push (d)
                    )

    def writeline (self, line):
        self.write (line+'\r\n')

    def writelines(self, lines):
        self.write('\r\n'.join(lines) + '\r\n')

    def flush (self):
        if self.buffer:
            d, self.buffer = self.buffer, ''
            the_trigger.pull_trigger (
                    lambda p=self.parent,d=d: p.push (d)
                    )

    def softspace (self, *args):
        pass

    def close (self):
        # in a derived class, you may want to call trigger_close() instead.
        self.flush()
        self.parent = None

    def trigger_close (self):
        d, self.buffer = self.buffer, ''
        p, self.parent = self.parent, None
        the_trigger.pull_trigger (
                lambda p=p,d=d: (p.push(d), p.close_when_done())
                )

if __name__ == '__main__':

    import time

    def thread_function (output_file, i, n):
        print('entering thread_function')
        while n:
            time.sleep (5)
            output_file.write ('%2d.%2d %s\r\n' % (i, n, output_file))
            output_file.flush()
            n -= 1
        output_file.close()
        print('exiting thread_function')

    class thread_parent (asynchat.async_chat):

        def __init__ (self, conn, addr):
            self.addr = addr
            asynchat.async_chat.__init__ (self, conn)
            self.set_terminator ('\r\n')
            self.buffer = ''
            self.count = 0

        def collect_incoming_data (self, data):
            self.buffer = self.buffer + data

        def found_terminator (self):
            data, self.buffer = self.buffer, ''
            if not data:
                asyncore.close_all()
                print("done")
                return
            n = int(data.split()[0])
            tf = trigger_file (self)
            self.count += 1
            thread.start_new_thread (thread_function, (tf, self.count, n))

    class thread_server (asyncore.dispatcher):

        def __init__ (self, family=socket.AF_INET, address=('', 9003)):
            asyncore.dispatcher.__init__ (self)
            self.create_socket (family, socket.SOCK_STREAM)
            self.set_reuse_addr()
            self.bind (address)
            self.listen (5)

        def handle_accept (self):
            conn, addr = self.accept()
            thread_parent (conn, addr)

    thread_server()
    #asyncore.loop(1.0, use_poll=1)
    try:
        asyncore.loop ()
    except:
        asyncore.close_all()

########NEW FILE########
__FILENAME__ = test_module
# -*- Mode: Python -*-

import pprint

def main (env, stdin, stdout):

    stdout.write (
            '<html><body><h1>Test CGI Module</h1>\r\n'
            '<br>The Environment:<pre>\r\n'
            )
    pprint.pprint (env, stdout)
    stdout.write ('</pre></body></html>\r\n')

########NEW FILE########
__FILENAME__ = thread_channel
# -*- Mode: Python -*-

VERSION_STRING = "$Id: thread_channel.py,v 1.3 2002/03/19 22:49:40 amk Exp $"

# This will probably only work on Unix.

# The disadvantage to this technique is that it wastes file
# descriptors (especially when compared to select_trigger.py)

# May be possible to do it on Win32, using TCP localhost sockets.
# [does winsock support 'socketpair'?]

import supervisor.medusa.asyncore_25 as asyncore
import supervisor.medusa.asynchat_25 as asynchat

import fcntl
import os
import supervisor.medusa.text_socket as socket
try:
    import _thread as thread
except ImportError:
    import thread

# this channel slaves off of another one.  it starts a thread which
# pumps its output through the 'write' side of the pipe.  The 'read'
# side of the pipe will then notify us when data is ready.  We push
# this data on the owning data channel's output queue.

class thread_channel (asyncore.file_dispatcher):

    buffer_size = 8192

    def __init__ (self, channel, function, *args):
        self.parent = channel
        self.function = function
        self.args = args
        self.pipe = rfd, wfd = os.pipe()
        asyncore.file_dispatcher.__init__ (self, rfd)

    def start (self):
        rfd, wfd = self.pipe

        # The read side of the pipe is set to non-blocking I/O; it is
        # 'owned' by medusa.

        flags = fcntl.fcntl (rfd, fcntl.F_GETFL, 0)
        fcntl.fcntl (rfd, fcntl.F_SETFL, flags | os.O_NDELAY)

        # The write side of the pipe is left in blocking mode; it is
        # 'owned' by the thread.  However, we wrap it up as a file object.
        # [who wants to 'write()' to a number?]

        of = os.fdopen (wfd, 'w')

        thread.start_new_thread (
                self.function,
                # put the output file in front of the other arguments
                (of,) + self.args
                )

    def writable (self):
        return 0

    def readable (self):
        return 1

    def handle_read (self):
        data = self.recv (self.buffer_size)
        self.parent.push (data)

    def handle_close (self):
        # Depending on your intentions, you may want to close
        # the parent channel here.
        self.close()

# Yeah, it's bad when the test code is bigger than the library code.

if __name__ == '__main__':

    import time

    def thread_function (output_file, i, n):
        print('entering thread_function')
        while n:
            time.sleep (5)
            output_file.write ('%2d.%2d %s\r\n' % (i, n, output_file))
            output_file.flush()
            n -= 1
        output_file.close()
        print('exiting thread_function')

    class thread_parent (asynchat.async_chat):

        def __init__ (self, conn, addr):
            self.addr = addr
            asynchat.async_chat.__init__ (self, conn)
            self.set_terminator ('\r\n')
            self.buffer = ''
            self.count = 0

        def collect_incoming_data (self, data):
            self.buffer = self.buffer + data

        def found_terminator (self):
            data, self.buffer = self.buffer, ''
            n = int(data.split()[0])
            tc = thread_channel (self, thread_function, self.count, n)
            self.count += 1
            tc.start()

    class thread_server (asyncore.dispatcher):

        def __init__ (self, family=socket.AF_INET, address=('127.0.0.1', 9003)):
            asyncore.dispatcher.__init__ (self)
            self.create_socket (family, socket.SOCK_STREAM)
            self.set_reuse_addr()
            self.bind (address)
            self.listen (5)

        def handle_accept (self):
            conn, addr = self.accept()
            thread_parent(conn, addr)

    thread_server()
    #asyncore.loop(1.0, use_poll=1)
    asyncore.loop ()

########NEW FILE########
__FILENAME__ = thread_handler
# -*- Mode: Python -*-

import re
try:
    from StringIO import StringIO
except:
    from io import StringIO
import sys

import supervisor.medusa.thread.select_trigger as select_trigger
from supervisor.medusa import counter
from supervisor.medusa.default_handler import unquote

import threading

class request_queue:

    def __init__ (self):
        self.mon = threading.RLock()
        self.cv = threading.Condition (self.mon)
        self.queue = []

    def put (self, item):
        self.cv.acquire()
        self.queue.append(item)
        self.cv.notify()
        self.cv.release()

    def get(self):
        self.cv.acquire()
        while not self.queue:
            self.cv.wait()
        result = self.queue.pop(0)
        self.cv.release()
        return result

header2env= {
        'Content-Length'        : 'CONTENT_LENGTH',
        'Content-Type'          : 'CONTENT_TYPE',
        'Referer'                       : 'HTTP_REFERER',
        'User-Agent'            : 'HTTP_USER_AGENT',
        'Accept'                        : 'HTTP_ACCEPT',
        'Accept-Charset'        : 'HTTP_ACCEPT_CHARSET',
        'Accept-Language'       : 'HTTP_ACCEPT_LANGUAGE',
        'Host'                          : 'HTTP_HOST',
        'Connection'            : 'CONNECTION_TYPE',
        'Authorization'         : 'HTTP_AUTHORIZATION',
        'Cookie'                        : 'HTTP_COOKIE',
        }

# convert keys to lower case for case-insensitive matching
for (key,value) in list(header2env.items()):
    del header2env[key]
    key = key.lower()
    header2env[key]=value

class thread_output_file (select_trigger.trigger_file):

    def close (self):
        self.trigger_close()

class script_handler:

    def __init__ (self, queue, document_root=""):
        self.modules = {}
        self.document_root = document_root
        self.queue = queue

    def add_module (self, module, *names):
        if not names:
            names = ["/%s" % module.__name__]
        for name in names:
            self.modules['/'+name] = module

    def match (self, request):
        uri = request.uri

        i = uri.find("/", 1)
        if i != -1:
            uri = uri[:i]

        i = uri.find("?", 1)
        if i != -1:
            uri = uri[:i]

        if uri in self.modules:
            request.module = self.modules[uri]
            return 1
        else:
            return 0

    def handle_request (self, request):

        [path, params, query, fragment] = request.split_uri()

        while path and path[0] == '/':
            path = path[1:]

        if '%' in path:
            path = unquote (path)

        env = {}

        env['REQUEST_URI'] = "/" + path
        env['REQUEST_METHOD']   = request.command.upper()
        env['SERVER_PORT']       = str(request.channel.server.port)
        env['SERVER_NAME']       = request.channel.server.server_name
        env['SERVER_SOFTWARE'] = request['Server']
        env['DOCUMENT_ROOT']     = self.document_root

        parts = path.split("/")

        # are script_name and path_info ok?

        env['SCRIPT_NAME']      = "/" + parts[0]

        if query and query[0] == "?":
            query = query[1:]

        env['QUERY_STRING']     = query

        try:
            path_info = "/" + "/".join(parts[1:])
        except:
            path_info = ''

        env['PATH_INFO']                = path_info
        env['GATEWAY_INTERFACE']='CGI/1.1'                         # what should this really be?
        env['REMOTE_ADDR']              = request.channel.addr[0]
        env['REMOTE_HOST']              = request.channel.addr[0]  # TODO: connect to resolver

        for header in request.header:
            key, value = header.split(": ", 1)
            key = key.lower()

            if key in header2env:
                if header2env[key]:
                    env[header2env[key]]=value
            else:
                key = 'HTTP_' + "_".join(key.split ("-")).upper()
                env[key] = value

        ## remove empty environment variables
        for key in env.keys():
            if env[key]=="" or env[key] is None:
                del env[key]

        try:
            httphost = env['HTTP_HOST']
            parts = httphost.split(":")
            env['HTTP_HOST'] = parts[0]
        except KeyError:
            pass

        if request.command in ('put', 'post'):
            # PUT data requires a correct Content-Length: header
            # (though I bet with http/1.1 we can expect chunked encoding)
            request.collector = collector (self, request, env)
            request.channel.set_terminator (None)
        else:
            sin = StringIO ('')
            self.continue_request (sin, request, env)

    def continue_request (self, stdin, request, env):
        stdout = header_scanning_file (
                request,
                thread_output_file (request.channel)
                )
        self.queue.put (
                (request.module.main, (env, stdin, stdout))
                )

HEADER_LINE = re.compile ('([A-Za-z0-9-]+): ([^\r\n]+)')

# A file wrapper that handles the CGI 'Status:' header hack
# by scanning the output.

class header_scanning_file:

    def __init__ (self, request, file):
        self.buffer = ''
        self.request = request
        self.file = file
        self.got_header = 0
        self.bytes_out = counter.counter()

    def write (self, data):
        if self.got_header:
            self._write (data)
        else:
            # CGI scripts may optionally provide extra headers.
            #
            # If they do not, then the output is assumed to be
            # text/html, with an HTTP reply code of '200 OK'.
            #
            # If they do, we need to scan those headers for one in
            # particular: the 'Status:' header, which will tell us
            # to use a different HTTP reply code [like '302 Moved']
            #
            self.buffer = self.buffer + data
            lines = self.buffer.split('\n')
            # ignore the last piece, it is either empty, or a partial line
            lines = lines[:-1]
            # look for something un-header-like
            for i in range(len(lines)):
                li = lines[i]
                if (not li) or (HEADER_LINE.match (li) is None):
                    # this is either the header separator, or it
                    # is not a header line.
                    self.got_header = 1
                    h = self.build_header (lines[:i])
                    self._write (h)
                    # rejoin the rest of the data
                    d = '\n'.join(lines[i:])
                    self._write (d)
                    self.buffer = ''
                    break

    def build_header (self, lines):
        status = '200 OK'
        saw_content_type = 0
        hl = HEADER_LINE
        for line in lines:
            mo = hl.match (line)
            if mo is not None:
                h = mo.group(1).lower()
                if h == 'status':
                    status = mo.group(2)
                elif h == 'content-type':
                    saw_content_type = 1
        lines.insert (0, 'HTTP/1.0 %s' % status)
        lines.append ('Server: ' + self.request['Server'])
        lines.append ('Date: ' + self.request['Date'])
        if not saw_content_type:
            lines.append ('Content-Type: text/html')
        lines.append ('Connection: close')
        return '\r\n'.join(lines) + '\r\n\r\n'

    def _write (self, data):
        self.bytes_out.increment (len(data))
        self.file.write (data)

    def writelines(self, lst):
        self.write(''.join(lst))

    def flush(self):
        pass

    def close (self):
        if not self.got_header:
            # managed to slip through our header detectors
            self._write (self.build_header (['Status: 502', 'Content-Type: text/html']))
            self._write (
                    '<html><h1>Server Error</h1>\r\n'
                    '<b>Bad Gateway:</b> No Header from CGI Script\r\n'
                    '<pre>Data: %s</pre>'
                    '</html>\r\n' % (repr(self.buffer))
                    )
        self.request.log (int(self.bytes_out.as_long()))
        self.file.close()
        self.request.channel.current_request = None


class collector:

    """gathers input for PUT requests"""

    def __init__ (self, handler, request, env):
        self.handler    = handler
        self.env = env
        self.request    = request
        self.data = StringIO()

        # make sure there's a content-length header
        self.cl = request.get_header ('content-length')

        if not self.cl:
            request.error (411)
            return
        else:
            self.cl = int(self.cl)

    def collect_incoming_data (self, data):
        self.data.write (data)
        if self.data.tell() >= self.cl:
            self.data.seek(0)

            h=self.handler
            r=self.request

            # set the terminator back to the default
            self.request.channel.set_terminator ('\r\n\r\n')
            del self.handler
            del self.request

            h.continue_request (self.data, r, self.env)


class request_loop_thread (threading.Thread):

    def __init__ (self, queue):
        threading.Thread.__init__ (self)
        self.setDaemon(1)
        self.queue = queue

    def run (self):
        while 1:
            function, (env, stdin, stdout) = self.queue.get()
            function (env, stdin, stdout)
            stdout.close()

# ===========================================================================
#                                                          Testing
# ===========================================================================

if __name__ == '__main__':

    if len(sys.argv) < 2:
        print('Usage: %s <worker_threads>' % sys.argv[0])
    else:
        nthreads = int(sys.argv[1])

        import supervisor.medusa.asyncore_25 as asyncore
        from supervisor.medusa import http_server
        # create a generic web server
        hs = http_server.http_server ('', 7080)

        # create a request queue
        q = request_queue()

        # create a script handler
        sh = script_handler (q)

        # install the script handler on the web server
        hs.install_handler (sh)

        # get a couple of CGI modules
        import supervisor.medusa.thread.test_module as test_module
        import supervisor.medusa.thread.pi_module as pi_module

        # install the module on the script handler
        sh.add_module (test_module, 'test')
        sh.add_module (pi_module, 'pi')

        # fire up the worker threads
        for i in range (nthreads):
            rt = request_loop_thread (q)
            rt.start()

        # start the main event loop
        asyncore.loop()

########NEW FILE########
__FILENAME__ = unix_user_handler
# -*- Mode: Python -*-
#
#       Author: Sam Rushing <rushing@nightmare.com>
#       Copyright 1996, 1997 by Sam Rushing
#                                                All Rights Reserved.
#

RCS_ID =  '$Id: unix_user_handler.py,v 1.4 2002/11/25 00:09:23 akuchling Exp $'

# support for `~user/public_html'.

import re
import supervisor.medusa.default_handler as default_handler
import supervisor.medusa.filesys as filesys
import os
import pwd

get_header = default_handler.get_header

user_dir = re.compile ('/~([^/]+)(.*)')

class unix_user_handler (default_handler.default_handler):

    def __init__ (self, public_html = 'public_html'):
        self.public_html = public_html
        default_handler.default_handler.__init__ (self, None)

    # cache userdir-filesystem objects
    fs_cache = {}

    def match (self, request):
        m = user_dir.match (request.uri)
        return m and (m.end() == len (request.uri))

    def handle_request (self, request):
        # get the user name
        m = user_dir.match (request.uri)
        user = m.group(1)
        rest = m.group(2)

        # special hack to catch those lazy URL typers
        if not rest:
            request['Location'] = '/~%s/' % user
            request.error (301)
            return

        # have we already built a userdir fs for this user?
        if user in self.fs_cache:
            fs = self.fs_cache[user]
        else:
            # no, well then, let's build one.
            # first, find out where the user directory is
            try:
                info = pwd.getpwnam (user)
            except KeyError:
                request.error (404)
                return
            ud = info[5] + '/' + self.public_html
            if os.path.isdir (ud):
                fs = filesys.os_filesystem (ud)
                self.fs_cache[user] = fs
            else:
                request.error (404)
                return

        # fake out default_handler
        self.filesystem = fs
        # massage the request URI
        request.uri = '/' + rest
        return default_handler.default_handler.handle_request (self, request)

    def __repr__ (self):
        return '<Unix User Directory Handler at %08x [~user/%s, %d filesystems loaded]>' % (
                id(self),
                self.public_html,
                len(self.fs_cache)
                )

########NEW FILE########
__FILENAME__ = virtual_handler
# -*- Mode: Python -*-

import supervisor.medusa.text_socket as socket
import supervisor.medusa.default_handler as default_handler
import re

HOST = re.compile ('Host: ([^:/]+).*', re.IGNORECASE)

get_header = default_handler.get_header

class virtual_handler:

    """HTTP request handler for an HTTP/1.0-style virtual host.  Each
    Virtual host must have a different IP"""

    def __init__ (self, handler, hostname):
        self.handler = handler
        self.hostname = hostname
        try:
            self.ip = socket.gethostbyname (hostname)
        except socket.error:
            raise ValueError("Virtual Hostname %s does not appear to be registered in the DNS" % hostname)

    def match (self, request):
        if request.channel.addr[0] == self.ip:
            return 1
        else:
            return 0

    def handle_request (self, request):
        return self.handler.handle_request (request)

    def __repr__ (self):
        return '<virtual request handler for %s>' % self.hostname


class virtual_handler_with_host:

    """HTTP request handler for HTTP/1.1-style virtual hosts.  This
    matches by checking the value of the 'Host' header in the request.
    You actually don't _have_ to support HTTP/1.1 to use this, since
    many browsers now send the 'Host' header.  This is a Good Thing."""

    def __init__ (self, handler, hostname):
        self.handler = handler
        self.hostname = hostname

    def match (self, request):
        host = get_header (HOST, request.header)
        if host == self.hostname:
            return 1
        else:
            return 0

    def handle_request (self, request):
        return self.handler.handle_request (request)

    def __repr__ (self):
        return '<virtual request handler for %s>' % self.hostname

########NEW FILE########
__FILENAME__ = xmlrpc_handler
# -*- Mode: Python -*-

# See http://www.xml-rpc.com/
#     http://www.pythonware.com/products/xmlrpc/

# Based on "xmlrpcserver.py" by Fredrik Lundh (fredrik@pythonware.com)

VERSION = "$Id: xmlrpc_handler.py,v 1.6 2004/04/21 14:09:24 akuchling Exp $"

import supervisor.medusa.http_server as http_server
try:
    import xmlrpclib
except:
    import xmlrpc.client as xmlrpclib

import sys

class xmlrpc_handler:

    def match (self, request):
        # Note: /RPC2 is not required by the spec, so you may override this method.
        if request.uri[:5] == '/RPC2':
            return 1
        else:
            return 0

    def handle_request (self, request):
        if request.command == 'POST':
            request.collector = collector (self, request)
        else:
            request.error (400)

    def continue_request (self, data, request):
        params, method = xmlrpclib.loads (data)
        try:
            # generate response
            try:
                response = self.call (method, params)
                if type(response) != type(()):
                    response = (response,)
            except:
                # report exception back to server
                response = xmlrpclib.dumps (
                        xmlrpclib.Fault (1, "%s:%s" % (sys.exc_info()[0], sys.exc_info()[1]))
                        )
            else:
                response = xmlrpclib.dumps (response, methodresponse=1)
        except:
            # internal error, report as HTTP server error
            request.error (500)
        else:
            # got a valid XML RPC response
            request['Content-Type'] = 'text/xml'
            request.push (response)
            request.done()

    def call (self, method, params):
        # override this method to implement RPC methods
        raise Exception("NotYetImplemented")

class collector:

    """gathers input for POST and PUT requests"""

    def __init__ (self, handler, request):

        self.handler = handler
        self.request = request
        self.data = []

        # make sure there's a content-length header
        cl = request.get_header ('content-length')

        if not cl:
            request.error (411)
        else:
            cl = int(cl)
            # using a 'numeric' terminator
            self.request.channel.set_terminator (cl)

    def collect_incoming_data (self, data):
        self.data.append(data)

    def found_terminator (self):
        # set the terminator back to the default
        self.request.channel.set_terminator ('\r\n\r\n')
        self.handler.continue_request ("".join(self.data), self.request)

if __name__ == '__main__':

    class rpc_demo (xmlrpc_handler):

        def call (self, method, params):
            print('method="%s" params=%s' % (method, params))
            return "Sure, that works"

    import supervisor.medusa.asyncore_25 as asyncore

    hs = http_server.http_server ('', 8000)
    rpc = rpc_demo()
    hs.install_handler (rpc)

    asyncore.loop()

########NEW FILE########
__FILENAME__ = options
import supervisor.medusa.text_socket as socket
import getopt
import os
import sys
import tempfile
import errno
import signal
import re
import pwd
import grp
import resource
import stat
import pkg_resources
import select
import glob
import platform
import warnings

from fcntl import fcntl
from fcntl import F_SETFL, F_GETFL

from supervisor.compat import print_function
from supervisor.compat import PY3
from supervisor.compat import ConfigParser
from supervisor.compat import as_bytes, as_string
from supervisor.compat import xmlrpclib
from supervisor.compat import StringIO
from supervisor.compat import basestring

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.datatypes import process_or_group_name
from supervisor.datatypes import boolean
from supervisor.datatypes import integer
from supervisor.datatypes import name_to_uid
from supervisor.datatypes import gid_for_uid
from supervisor.datatypes import existing_dirpath
from supervisor.datatypes import byte_size
from supervisor.datatypes import signal_number
from supervisor.datatypes import list_of_exitcodes
from supervisor.datatypes import dict_of_key_value_pairs
from supervisor.datatypes import logfile_name
from supervisor.datatypes import list_of_strings
from supervisor.datatypes import octal_type
from supervisor.datatypes import existing_directory
from supervisor.datatypes import logging_level
from supervisor.datatypes import colon_separated_user_group
from supervisor.datatypes import inet_address
from supervisor.datatypes import InetStreamSocketConfig
from supervisor.datatypes import UnixStreamSocketConfig
from supervisor.datatypes import url
from supervisor.datatypes import Automatic
from supervisor.datatypes import auto_restart
from supervisor.datatypes import profile_options
from supervisor.datatypes import set_here

from supervisor import loggers
from supervisor import states
from supervisor import xmlrpc
from supervisor import read_file

mydir = os.path.abspath(os.path.dirname(__file__))
version_txt = os.path.join(mydir, 'version.txt')

VERSION = read_file(version_txt).strip()

def normalize_path(v):
    return os.path.normpath(os.path.abspath(os.path.expanduser(v)))

class Dummy:
    pass

class Options:
    stderr = sys.stderr
    stdout = sys.stdout
    exit = sys.exit
    warnings = warnings

    uid = gid = None

    progname = sys.argv[0]
    configfile = None
    schemadir = None
    configroot = None
    here = None

    # Class variable deciding whether positional arguments are allowed.
    # If you want positional arguments, set this to 1 in your subclass.
    positional_args_allowed = 0

    def __init__(self, require_configfile=True):
        """Constructor.

        Params:
        require_configfile -- whether we should fail on no config file.
        """
        self.names_list = []
        self.short_options = []
        self.long_options = []
        self.options_map = {}
        self.default_map = {}
        self.required_map = {}
        self.environ_map = {}
        self.attr_priorities = {}
        self.require_configfile = require_configfile
        self.add(None, None, "h", "help", self.help)
        self.add("configfile", None, "c:", "configuration=")

        here = os.path.dirname(os.path.dirname(sys.argv[0]))
        searchpaths = [os.path.join(here, 'etc', 'supervisord.conf'),
                       os.path.join(here, 'supervisord.conf'),
                       'supervisord.conf',
                       'etc/supervisord.conf',
                       '/etc/supervisord.conf']
        self.searchpaths = searchpaths

    def default_configfile(self):
        """Return the name of the found config file or print usage/exit."""
        config = None
        for path in self.searchpaths:
            if os.path.exists(path):
                config = path
                break
        if config is None and self.require_configfile:
            self.usage('No config file found at default paths (%s); '
                       'use the -c option to specify a config file '
                       'at a different path' % ', '.join(self.searchpaths))
        return config

    def help(self, dummy):
        """Print a long help message to stdout and exit(0).

        Occurrences of "%s" in are replaced by self.progname.
        """
        help = self.doc + "\n"
        if help.find("%s") > 0:
            help = help.replace("%s", self.progname)
        print_function(help, end='', file=self.stdout)
        self.exit(0)

    def usage(self, msg):
        """Print a brief error message to stderr and exit(2)."""
        self.stderr.write("Error: %s\n" % str(msg))
        self.stderr.write("For help, use %s -h\n" % self.progname)
        self.exit(2)

    def add(self,
            name=None,                  # attribute name on self
            confname=None,              # dotted config path name
            short=None,                 # short option name
            long=None,                  # long option name
            handler=None,               # handler (defaults to string)
            default=None,               # default value
            required=None,              # message if not provided
            flag=None,                  # if not None, flag value
            env=None,                   # if not None, environment variable
            ):
        """Add information about a configuration option.

        This can take several forms:

        add(name, confname)
            Configuration option 'confname' maps to attribute 'name'
        add(name, None, short, long)
            Command line option '-short' or '--long' maps to 'name'
        add(None, None, short, long, handler)
            Command line option calls handler
        add(name, None, short, long, handler)
            Assign handler return value to attribute 'name'

        In addition, one of the following keyword arguments may be given:

        default=...  -- if not None, the default value
        required=... -- if nonempty, an error message if no value provided
        flag=...     -- if not None, flag value for command line option
        env=...      -- if not None, name of environment variable that
                        overrides the configuration file or default
        """
        if flag is not None:
            if handler is not None:
                raise ValueError("use at most one of flag= and handler=")
            if not long and not short:
                raise ValueError("flag= requires a command line flag")
            if short and short.endswith(":"):
                raise ValueError("flag= requires a command line flag")
            if long and long.endswith("="):
                raise ValueError("flag= requires a command line flag")
            handler = lambda arg, flag=flag: flag

        if short and long:
            if short.endswith(":") != long.endswith("="):
                raise ValueError("inconsistent short/long options: %r %r" % (
                    short, long))

        if short:
            if short[0] == "-":
                raise ValueError("short option should not start with '-'")
            key, rest = short[:1], short[1:]
            if rest not in ("", ":"):
                raise ValueError("short option should be 'x' or 'x:'")
            key = "-" + key
            if key in self.options_map:
                raise ValueError("duplicate short option key '%s'" % key)
            self.options_map[key] = (name, handler)
            self.short_options.append(short)

        if long:
            if long[0] == "-":
                raise ValueError("long option should not start with '-'")
            key = long
            if key[-1] == "=":
                key = key[:-1]
            key = "--" + key
            if key in self.options_map:
                raise ValueError("duplicate long option key '%s'" % key)
            self.options_map[key] = (name, handler)
            self.long_options.append(long)

        if env:
            self.environ_map[env] = (name, handler)

        if name:
            if not hasattr(self, name):
                setattr(self, name, None)
            self.names_list.append((name, confname))
            if default is not None:
                self.default_map[name] = default
            if required:
                self.required_map[name] = required

    def _set(self, attr, value, prio):
        current = self.attr_priorities.get(attr, -1)
        if prio >= current:
            setattr(self, attr, value)
            self.attr_priorities[attr] = prio

    def realize(self, args=None, doc=None,
                progname=None, raise_getopt_errs=True):
        """Realize a configuration.

        Optional arguments:

        args     -- the command line arguments, less the program name
                    (default is sys.argv[1:])

        doc      -- usage message (default is __main__.__doc__)
        """
        # Provide dynamic default method arguments
        if args is None:
            args = sys.argv[1:]
        if progname is None:
            progname = sys.argv[0]
        if doc is None:
            try:
                import __main__
                doc = __main__.__doc__
            except Exception:
                pass
        self.progname = progname
        self.doc = doc

        self.options = []
        self.args = []

        # Call getopt
        try:
            self.options, self.args = getopt.getopt(
                args, "".join(self.short_options), self.long_options)
        except getopt.error as msg:
            if raise_getopt_errs:
                self.usage(msg)

        # Check for positional args
        if self.args and not self.positional_args_allowed:
            self.usage("positional arguments are not supported")

        # Process options returned by getopt
        for opt, arg in self.options:
            name, handler = self.options_map[opt]
            if handler is not None:
                try:
                    arg = handler(arg)
                except ValueError as msg:
                    self.usage("invalid value for %s %r: %s" % (opt, arg, msg))
            if name and arg is not None:
                if getattr(self, name) is not None:
                    self.usage("conflicting command line option %r" % opt)
                self._set(name, arg, 2)

        # Process environment variables
        for envvar in self.environ_map.keys():
            name, handler = self.environ_map[envvar]
            if envvar in os.environ:
                value = os.environ[envvar]
                if handler is not None:
                    try:
                        value = handler(value)
                    except ValueError as msg:
                        self.usage("invalid environment value for %s %r: %s"
                                   % (envvar, value, msg))
                if name and value is not None:
                    self._set(name, value, 1)

        if self.configfile is None:
            uid = os.getuid()
            if uid == 0 and "supervisord" in self.progname: # pragma: no cover
                self.warnings.warn(
                    'Supervisord is running as root and it is searching '
                    'for its configuration file in default locations '
                    '(including its current working directory); you '
                    'probably want to specify a "-c" argument specifying an '
                    'absolute path to a configuration file for improved '
                    'security.'
                    )

            self.configfile = self.default_configfile()

        self.process_config()

    def process_config(self, do_usage=True):
        """Process configuration data structure.

        This includes reading config file if necessary, setting defaults etc.
        """
        if self.configfile:
            self.process_config_file(do_usage)

        # Copy config options to attributes of self.  This only fills
        # in options that aren't already set from the command line.
        for name, confname in self.names_list:
            if confname:
                parts = confname.split(".")
                obj = self.configroot
                for part in parts:
                    if obj is None:
                        break
                    # Here AttributeError is not a user error!
                    obj = getattr(obj, part)
                self._set(name, obj, 0)

        # Process defaults
        for name, value in self.default_map.items():
            if getattr(self, name) is None:
                setattr(self, name, value)

        # Process required options
        for name, message in self.required_map.items():
            if getattr(self, name) is None:
                self.usage(message)

    def process_config_file(self, do_usage):
        # Process config file
        if not hasattr(self.configfile, 'read'):
            self.here = os.path.abspath(os.path.dirname(self.configfile))
            set_here(self.here)
        try:
            self.read_config(self.configfile)
        except ValueError as msg:
            if do_usage:
                # if this is not called from an RPC method, run usage and exit.
                self.usage(str(msg))
            else:
                # if this is called from an RPC method, raise an error
                raise ValueError(msg)

    def get_plugins(self, parser, factory_key, section_prefix):
        factories = []

        for section in parser.sections():
            if not section.startswith(section_prefix):
                continue
            name = section.split(':', 1)[1]
            factory_spec = parser.saneget(section, factory_key, None)
            if factory_spec is None:
                raise ValueError('section [%s] does not specify a %s'  %
                                 (section, factory_key))
            try:
                factory = self.import_spec(factory_spec)
            except ImportError:
                raise ValueError('%s cannot be resolved within [%s]' % (
                    factory_spec, section))
            items = parser.items(section)
            items.remove((factory_key, factory_spec))
            factories.append((name, factory, dict(items)))

        return factories

    def import_spec(self, spec):
        return pkg_resources.EntryPoint.parse("x="+spec).load(False)


class ServerOptions(Options):
    user = None
    sockchown = None
    sockchmod = None
    logfile = None
    loglevel = None
    pidfile = None
    passwdfile = None
    nodaemon = None
    environment = None
    httpservers = ()
    unlink_socketfiles = True
    mood = states.SupervisorStates.RUNNING

    def __init__(self):
        Options.__init__(self)
        self.configroot = Dummy()
        self.configroot.supervisord = Dummy()

        self.add(None, None, "v", "version", self.version)
        self.add("nodaemon", "supervisord.nodaemon", "n", "nodaemon", flag=1,
                 default=0)
        self.add("user", "supervisord.user", "u:", "user=")
        self.add("umask", "supervisord.umask", "m:", "umask=",
                 octal_type, default='022')
        self.add("directory", "supervisord.directory", "d:", "directory=",
                 existing_directory)
        self.add("logfile", "supervisord.logfile", "l:", "logfile=",
                 existing_dirpath, default="supervisord.log")
        self.add("logfile_maxbytes", "supervisord.logfile_maxbytes",
                 "y:", "logfile_maxbytes=", byte_size,
                 default=50 * 1024 * 1024) # 50MB
        self.add("logfile_backups", "supervisord.logfile_backups",
                 "z:", "logfile_backups=", integer, default=10)
        self.add("loglevel", "supervisord.loglevel", "e:", "loglevel=",
                 logging_level, default="info")
        self.add("pidfile", "supervisord.pidfile", "j:", "pidfile=",
                 existing_dirpath, default="supervisord.pid")
        self.add("identifier", "supervisord.identifier", "i:", "identifier=",
                 str, default="supervisor")
        self.add("childlogdir", "supervisord.childlogdir", "q:", "childlogdir=",
                 existing_directory, default=tempfile.gettempdir())
        self.add("minfds", "supervisord.minfds",
                 "a:", "minfds=", int, default=1024)
        self.add("minprocs", "supervisord.minprocs",
                 "", "minprocs=", int, default=200)
        self.add("nocleanup", "supervisord.nocleanup",
                 "k", "nocleanup", flag=1, default=0)
        self.add("strip_ansi", "supervisord.strip_ansi",
                 "t", "strip_ansi", flag=1, default=0)
        self.add("profile_options", "supervisord.profile_options",
                 "", "profile_options=", profile_options, default=None)
        self.pidhistory = {}
        self.process_group_configs = []
        self.parse_warnings = []
        self.signal_receiver = SignalReceiver()

    def version(self, dummy):
        """Print version to stdout and exit(0).
        """
        self.stdout.write('%s\n' % VERSION)
        self.exit(0)

    def getLogger(self, *args, **kwargs):
        """
        A proxy to loggers.getLogger so the options might customize log setup.
        Used by tests to mock log setup.
        """
        return loggers.getLogger(*args, **kwargs)

    def realize(self, *arg, **kw):
        Options.realize(self, *arg, **kw)
        section = self.configroot.supervisord

        # Additional checking of user option; set uid and gid
        if self.user is not None:
            try:
                uid = name_to_uid(self.user)
            except ValueError as msg:
                self.usage(msg) # invalid user
            self.uid = uid
            self.gid = gid_for_uid(uid)

        if not self.loglevel:
            self.loglevel = section.loglevel

        if self.logfile:
            logfile = self.logfile
        else:
            logfile = section.logfile

        self.logfile = normalize_path(logfile)

        if self.pidfile:
            pidfile = self.pidfile
        else:
            pidfile = section.pidfile

        self.pidfile = normalize_path(pidfile)

        self.rpcinterface_factories = section.rpcinterface_factories

        self.serverurl = None

        self.server_configs = sconfigs = section.server_configs

        # we need to set a fallback serverurl that process.spawn can use

        # prefer a unix domain socket
        for config in [ config for config in sconfigs if
                        config['family'] is socket.AF_UNIX ]:
            path = config['file']
            self.serverurl = 'unix://%s' % path
            break

        # fall back to an inet socket
        if self.serverurl is None:
            for config in [ config for config in sconfigs if
                            config['family'] is socket.AF_INET]:
                host = config['host']
                port = config['port']
                if not host:
                    host = 'localhost'
                self.serverurl = 'http://%s:%s' % (host, port)

        # self.serverurl may still be None if no servers at all are
        # configured in the config file

        self.identifier = section.identifier

    def process_config(self, do_usage=True):
        Options.process_config(self, do_usage=do_usage)

        new = self.configroot.supervisord.process_group_configs
        self.process_group_configs = new

    def read_config(self, fp):
        # Clear parse warnings, since we may be re-reading the
        # config a second time after a reload.
        self.parse_warnings = []

        section = self.configroot.supervisord
        need_close = False
        if not hasattr(fp, 'read'):
            if not os.path.exists(fp):
                raise ValueError("could not find config file %s" % fp)
            try:
                fp = open(fp, 'r')
                need_close = True
            except (IOError, OSError):
                raise ValueError("could not read config file %s" % fp)
        kwargs = {}
        if PY3:
            kwargs['inline_comment_prefixes'] = (';','#')
        parser = UnhosedConfigParser(**kwargs)
        try:
            try:
                parser.read_file(fp)
            except AttributeError:
                parser.readfp(fp)
        except ConfigParser.ParsingError as why:
            raise ValueError(str(why))
        finally:
            if need_close:
                fp.close()

        if parser.has_section('include'):
            if not parser.has_option('include', 'files'):
                raise ValueError(".ini file has [include] section, but no "
                "files setting")
            files = parser.get('include', 'files')
            files = files.split()
            if hasattr(fp, 'name'):
                base = os.path.dirname(os.path.abspath(fp.name))
            else:
                base = '.'
            for pattern in files:
                pattern = os.path.join(base, pattern)
                filenames = glob.glob(pattern)
                if not filenames:
                    self.parse_warnings.append(
                        'No file matches via include "%s"' % pattern)
                    continue
                for filename in filenames:
                    self.parse_warnings.append(
                        'Included extra file "%s" during parsing' % filename)
                    try:
                        parser.read(filename)
                    except ConfigParser.ParsingError as why:
                        raise ValueError(str(why))

        sections = parser.sections()
        if not 'supervisord' in sections:
            raise ValueError('.ini file does not include supervisord section')
        get = parser.getdefault
        section.minfds = integer(get('minfds', 1024))
        section.minprocs = integer(get('minprocs', 200))

        directory = get('directory', None)
        if directory is None:
            section.directory = None
        else:
            section.directory = existing_directory(directory)

        section.user = get('user', None)
        section.umask = octal_type(get('umask', '022'))
        section.logfile = existing_dirpath(get('logfile', 'supervisord.log'))
        section.logfile_maxbytes = byte_size(get('logfile_maxbytes', '50MB'))
        section.logfile_backups = integer(get('logfile_backups', 10))
        section.loglevel = logging_level(get('loglevel', 'info'))
        section.pidfile = existing_dirpath(get('pidfile', 'supervisord.pid'))
        section.identifier = get('identifier', 'supervisor')
        section.nodaemon = boolean(get('nodaemon', 'false'))

        tempdir = tempfile.gettempdir()
        section.childlogdir = existing_directory(get('childlogdir', tempdir))
        section.nocleanup = boolean(get('nocleanup', 'false'))
        section.strip_ansi = boolean(get('strip_ansi', 'false'))

        expansions = {'here':self.here}
        expansions.update(environ_expansions())
        environ_str = get('environment', '')
        environ_str = expand(environ_str, expansions, 'environment')
        section.environment = dict_of_key_value_pairs(environ_str)
        # Process rpcinterface plugins before groups to allow custom events to
        # be registered.
        section.rpcinterface_factories = self.get_plugins(
            parser,
            'supervisor.rpcinterface_factory',
            'rpcinterface:'
            )
        section.process_group_configs = self.process_groups_from_parser(parser)
        for group in section.process_group_configs:
            for proc in group.process_configs:
                env = section.environment.copy()
                env.update(proc.environment)
                proc.environment = env
        section.server_configs = self.server_configs_from_parser(parser)
        section.profile_options = None
        return section

    def process_groups_from_parser(self, parser):
        groups = []
        all_sections = parser.sections()
        homogeneous_exclude = []
        get = parser.saneget

        # process heterogeneous groups
        for section in all_sections:
            if not section.startswith('group:'):
                continue
            group_name = process_or_group_name(section.split(':', 1)[1])
            programs = list_of_strings(get(section, 'programs', None))
            priority = integer(get(section, 'priority', 999))
            group_processes = []
            for program in programs:
                program_section = "program:%s" % program
                if not program_section in all_sections:
                    raise ValueError(
                        '[%s] names unknown program %s' % (section, program))
                homogeneous_exclude.append(program_section)
                processes = self.processes_from_section(parser, program_section,
                                                        group_name,
                                                        ProcessConfig)
                group_processes.extend(processes)
            groups.append(
                ProcessGroupConfig(self, group_name, priority, group_processes)
                )

        # process "normal" homogeneous groups
        for section in all_sections:
            if ( (not section.startswith('program:') )
                 or section in homogeneous_exclude ):
                continue
            program_name = process_or_group_name(section.split(':', 1)[1])
            priority = integer(get(section, 'priority', 999))
            processes=self.processes_from_section(parser, section, program_name,
                                                  ProcessConfig)
            groups.append(
                ProcessGroupConfig(self, program_name, priority, processes)
                )

        # process "event listener" homogeneous groups
        for section in all_sections:
            if not section.startswith('eventlistener:'):
                continue
            pool_name = section.split(':', 1)[1]
            # give listeners a "high" default priority so they are started first
            # and stopped last at mainloop exit
            priority = integer(get(section, 'priority', -1))
            buffer_size = integer(get(section, 'buffer_size', 10))
            result_handler = get(section, 'result_handler',
                                       'supervisor.dispatchers:default_handler')
            try:
                result_handler = self.import_spec(result_handler)
            except ImportError:
                raise ValueError('%s cannot be resolved within [%s]' % (
                    result_handler, section))
            pool_event_names = [x.upper() for x in
                                list_of_strings(get(section, 'events', ''))]
            pool_event_names = set(pool_event_names)
            if not pool_event_names:
                raise ValueError('[%s] section requires an "events" line' %
                                 section)
            from supervisor.events import EventTypes
            pool_events = []
            for pool_event_name in pool_event_names:
                pool_event = getattr(EventTypes, pool_event_name, None)
                if pool_event is None:
                    raise ValueError('Unknown event type %s in [%s] events' %
                                     (pool_event_name, section))
                pool_events.append(pool_event)
            processes=self.processes_from_section(parser, section, pool_name,
                                                  EventListenerConfig)

            groups.append(
                EventListenerPoolConfig(self, pool_name, priority, processes,
                                        buffer_size, pool_events,
                                        result_handler)
                )

        # process fastcgi homogeneous groups
        for section in all_sections:
            if ( (not section.startswith('fcgi-program:') )
                 or section in homogeneous_exclude ):
                continue
            program_name = process_or_group_name(section.split(':', 1)[1])
            priority = integer(get(section, 'priority', 999))

            # find proc_uid from "user" option
            proc_user = get(section, 'user', None)
            if proc_user is None:
                proc_uid = None
            else:
                proc_uid = name_to_uid(proc_user)

            socket_owner = get(section, 'socket_owner', None)
            if socket_owner is not None:
                try:
                    socket_owner = colon_separated_user_group(socket_owner)
                except ValueError:
                    raise ValueError('Invalid socket_owner value %s'
                                                                % socket_owner)

            socket_mode = get(section, 'socket_mode', None)
            if socket_mode is not None:
                try:
                    socket_mode = octal_type(socket_mode)
                except (TypeError, ValueError):
                    raise ValueError('Invalid socket_mode value %s'
                                                                % socket_mode)

            socket = get(section, 'socket', None)
            if not socket:
                raise ValueError('[%s] section requires a "socket" line' %
                                 section)

            expansions = {'here':self.here,
                          'program_name':program_name}
            expansions.update(environ_expansions())
            socket = expand(socket, expansions, 'socket')
            try:
                socket_config = self.parse_fcgi_socket(socket, proc_uid,
                                                    socket_owner, socket_mode)
            except ValueError as e:
                raise ValueError('%s in [%s] socket' % (str(e), section))

            processes=self.processes_from_section(parser, section, program_name,
                                                  FastCGIProcessConfig)
            groups.append(
                FastCGIGroupConfig(self, program_name, priority, processes,
                                   socket_config)
                )

        groups.sort()
        return groups

    def parse_fcgi_socket(self, sock, proc_uid, socket_owner, socket_mode):
        if sock.startswith('unix://'):
            path = sock[7:]
            #Check it's an absolute path
            if not os.path.isabs(path):
                raise ValueError("Unix socket path %s is not an absolute path",
                                 path)
            path = normalize_path(path)

            if socket_owner is None:
                uid = os.getuid()
                if proc_uid is not None and proc_uid != uid:
                    socket_owner = (proc_uid, gid_for_uid(proc_uid))

            if socket_mode is None:
                socket_mode = 448 # 0700 in Py2, 0o700 Py3

            return UnixStreamSocketConfig(path, owner=socket_owner,
                                                mode=socket_mode)

        if socket_owner is not None or socket_mode is not None:
            raise ValueError("socket_owner and socket_mode params should"
                    + " only be used with a Unix domain socket")

        m = re.match(r'tcp://([^\s:]+):(\d+)$', sock)
        if m:
            host = m.group(1)
            port = int(m.group(2))
            return InetStreamSocketConfig(host, port)

        raise ValueError("Bad socket format %s", sock)

    def processes_from_section(self, parser, section, group_name,
                               klass=None):
        if klass is None:
            klass = ProcessConfig
        programs = []
        get = parser.saneget
        program_name = process_or_group_name(section.split(':', 1)[1])
        priority = integer(get(section, 'priority', 999))
        autostart = boolean(get(section, 'autostart', 'true'))
        autorestart = auto_restart(get(section, 'autorestart', 'unexpected'))
        startsecs = integer(get(section, 'startsecs', 1))
        startretries = integer(get(section, 'startretries', 3))
        stopsignal = signal_number(get(section, 'stopsignal', 'TERM'))
        stopwaitsecs = integer(get(section, 'stopwaitsecs', 10))
        stopasgroup = boolean(get(section, 'stopasgroup', 'false'))
        killasgroup = boolean(get(section, 'killasgroup', stopasgroup))
        exitcodes = list_of_exitcodes(get(section, 'exitcodes', '0,2'))
        redirect_stderr = boolean(get(section, 'redirect_stderr','false'))
        numprocs = integer(get(section, 'numprocs', 1))
        numprocs_start = integer(get(section, 'numprocs_start', 0))
        environment_str = get(section, 'environment', '')
        stdout_cmaxbytes = byte_size(get(section,'stdout_capture_maxbytes','0'))
        stdout_events = boolean(get(section, 'stdout_events_enabled','false'))
        stderr_cmaxbytes = byte_size(get(section,'stderr_capture_maxbytes','0'))
        stderr_events = boolean(get(section, 'stderr_events_enabled','false'))
        serverurl = get(section, 'serverurl', None)
        if serverurl and serverurl.strip().upper() == 'AUTO':
            serverurl = None

        # find uid from "user" option
        user = get(section, 'user', None)
        if user is None:
            uid = None
        else:
            uid = name_to_uid(user)

        umask = get(section, 'umask', None)
        if umask is not None:
            umask = octal_type(umask)

        command = get(section, 'command', None)
        if command is None:
            raise ValueError(
                'program section %s does not specify a command' % section)

        process_name = process_or_group_name(
            get(section, 'process_name', '%(program_name)s'))

        if numprocs > 1:
            if not '%(process_num)' in process_name:
                # process_name needs to include process_num when we
                # represent a group of processes
                raise ValueError(
                    '%(process_num) must be present within process_name when '
                    'numprocs > 1')

        if stopasgroup and not killasgroup:
            raise ValueError(
                "Cannot set stopasgroup=true and killasgroup=false"
                )

        host_node_name = platform.node()
        for process_num in range(numprocs_start, numprocs + numprocs_start):
            expansions = {'here':self.here,
                          'process_num':process_num,
                          'program_name':program_name,
                          'host_node_name':host_node_name,
                          'group_name':group_name}
            expansions.update(environ_expansions())

            environment = dict_of_key_value_pairs(
                expand(environment_str, expansions, 'environment'))

            directory = get(section, 'directory', None)
            if directory:
                directory = expand(directory, expansions, 'directory')

            logfiles = {}

            for k in ('stdout', 'stderr'):
                n = '%s_logfile' % k
                lf_val = get(section, n, Automatic)
                if isinstance(lf_val, basestring):
                    lf_val = expand(lf_val, expansions, n)
                lf_val = logfile_name(lf_val)
                logfiles[n] = lf_val

                bu_key = '%s_logfile_backups' % k
                backups = integer(get(section, bu_key, 10))
                logfiles[bu_key] = backups

                mb_key = '%s_logfile_maxbytes' % k
                maxbytes = byte_size(get(section, mb_key, '50MB'))
                logfiles[mb_key] = maxbytes

                sy_key = '%s_syslog' % k
                syslog = boolean(get(section, sy_key, False))
                logfiles[sy_key] = syslog

                if lf_val is Automatic and not maxbytes:
                    self.parse_warnings.append(
                        'For [%s], AUTO logging used for %s without '
                        'rollover, set maxbytes > 0 to avoid filling up '
                        'filesystem unintentionally' % (section, n))

            pconfig = klass(
                self,
                name=expand(process_name, expansions, 'process_name'),
                command=expand(command, expansions, 'command'),
                directory=directory,
                umask=umask,
                priority=priority,
                autostart=autostart,
                autorestart=autorestart,
                startsecs=startsecs,
                startretries=startretries,
                uid=uid,
                stdout_logfile=logfiles['stdout_logfile'],
                stdout_capture_maxbytes = stdout_cmaxbytes,
                stdout_events_enabled = stdout_events,
                stdout_logfile_backups=logfiles['stdout_logfile_backups'],
                stdout_logfile_maxbytes=logfiles['stdout_logfile_maxbytes'],
                stdout_syslog=logfiles['stdout_syslog'],
                stderr_logfile=logfiles['stderr_logfile'],
                stderr_capture_maxbytes = stderr_cmaxbytes,
                stderr_events_enabled = stderr_events,
                stderr_logfile_backups=logfiles['stderr_logfile_backups'],
                stderr_logfile_maxbytes=logfiles['stderr_logfile_maxbytes'],
                stderr_syslog=logfiles['stderr_syslog'],
                stopsignal=stopsignal,
                stopwaitsecs=stopwaitsecs,
                stopasgroup=stopasgroup,
                killasgroup=killasgroup,
                exitcodes=exitcodes,
                redirect_stderr=redirect_stderr,
                environment=environment,
                serverurl=serverurl)

            programs.append(pconfig)

        programs.sort() # asc by priority
        return programs

    def _parse_servernames(self, parser, stype):
        options = []
        for section in parser.sections():
            if section.startswith(stype):
                parts = section.split(':', 1)
                if len(parts) > 1:
                    name = parts[1]
                else:
                    name = None # default sentinel
                options.append((name, section))
        return options

    def _parse_username_and_password(self, parser, section):
        get = parser.saneget
        username = get(section, 'username', None)
        password = get(section, 'password', None)
        if username is None and password is not None:
            raise ValueError(
                'Must specify username if password is specified in [%s]'
                % section)
        return {'username':username, 'password':password}

    def server_configs_from_parser(self, parser):
        configs = []
        inet_serverdefs = self._parse_servernames(parser, 'inet_http_server')
        for name, section in inet_serverdefs:
            config = {}
            get = parser.saneget
            config.update(self._parse_username_and_password(parser, section))
            config['name'] = name
            config['family'] = socket.AF_INET
            port = get(section, 'port', None)
            if port is None:
                raise ValueError('section [%s] has no port value' % section)
            host, port = inet_address(port)
            config['host'] = host
            config['port'] = port
            config['section'] = section
            configs.append(config)

        unix_serverdefs = self._parse_servernames(parser, 'unix_http_server')
        for name, section in unix_serverdefs:
            config = {}
            get = parser.saneget
            sfile = get(section, 'file', None)
            if sfile is None:
                raise ValueError('section [%s] has no file value' % section)
            sfile = sfile.strip()
            config['name'] = name
            config['family'] = socket.AF_UNIX
            sfile = expand(sfile, {'here':self.here}, 'socket file')
            config['file'] = normalize_path(sfile)
            config.update(self._parse_username_and_password(parser, section))
            chown = get(section, 'chown', None)
            if chown is not None:
                try:
                    chown = colon_separated_user_group(chown)
                except ValueError:
                    raise ValueError('Invalid sockchown value %s' % chown)
            else:
                chown = (-1, -1)
            config['chown'] = chown
            chmod = get(section, 'chmod', None)
            if chmod is not None:
                try:
                    chmod = octal_type(chmod)
                except (TypeError, ValueError):
                    raise ValueError('Invalid chmod value %s' % chmod)
            else:
                chmod = 448 # 0700 on py2, 0o700 on py3
            config['chmod'] = chmod
            config['section'] = section
            configs.append(config)

        return configs

    def daemonize(self):
        # To daemonize, we need to become the leader of our own session
        # (process) group.  If we do not, signals sent to our
        # parent process will also be sent to us.   This might be bad because
        # signals such as SIGINT can be sent to our parent process during
        # normal (uninteresting) operations such as when we press Ctrl-C in the
        # parent terminal window to escape from a logtail command.
        # To disassociate ourselves from our parent's session group we use
        # os.setsid.  It means "set session id", which has the effect of
        # disassociating a process from is current session and process group
        # and setting itself up as a new session leader.
        #
        # Unfortunately we cannot call setsid if we're already a session group
        # leader, so we use "fork" to make a copy of ourselves that is
        # guaranteed to not be a session group leader.
        #
        # We also change directories, set stderr and stdout to null, and
        # change our umask.
        #
        # This explanation was (gratefully) garnered from
        # http://www.hawklord.uklinux.net/system/daemons/d3.htm

        pid = os.fork()
        if pid != 0:
            # Parent
            self.logger.blather("supervisord forked; parent exiting")
            os._exit(0)
        # Child
        self.logger.info("daemonizing the supervisord process")
        if self.directory:
            try:
                os.chdir(self.directory)
            except OSError as err:
                self.logger.critical("can't chdir into %r: %s"
                                     % (self.directory, err))
            else:
                self.logger.info("set current directory: %r"
                                 % self.directory)
        os.close(0)
        self.stdin = sys.stdin = sys.__stdin__ = open("/dev/null")
        os.close(1)
        self.stdout = sys.stdout = sys.__stdout__ = open("/dev/null", "w")
        os.close(2)
        self.stderr = sys.stderr = sys.__stderr__ = open("/dev/null", "w")
        os.setsid()
        os.umask(self.umask)
        # XXX Stevens, in his Advanced Unix book, section 13.3 (page
        # 417) recommends calling umask(0) and closing unused
        # file descriptors.  In his Network Programming book, he
        # additionally recommends ignoring SIGHUP and forking again
        # after the setsid() call, for obscure SVR4 reasons.

    def write_pidfile(self):
        pid = os.getpid()
        try:
            f = open(self.pidfile, 'w')
            f.write('%s\n' % pid)
            f.close()
        except (IOError, OSError):
            self.logger.critical('could not write pidfile %s' % self.pidfile)
        else:
            self.logger.info('supervisord started with pid %s' % pid)

    def cleanup(self):
        try:
            for config, server in self.httpservers:
                if config['family'] == socket.AF_UNIX:
                    if self.unlink_socketfiles:
                        socketname = config['file']
                        try:
                            os.unlink(socketname)
                        except OSError:
                            pass
        except OSError:
            pass
        try:
            os.unlink(self.pidfile)
        except OSError:
            pass

    def close_httpservers(self):
        for config, server in self.httpservers:
            server.close()
            map = self.get_socket_map()
            # server._map is a reference to the asyncore socket_map
            for dispatcher in map.values():
                # For unknown reasons, sometimes an http_channel
                # dispatcher in the socket map related to servers
                # remains open *during a reload*.  If one of these
                # exists at this point, we need to close it by hand
                # (thus removing it from the asyncore.socket_map).  If
                # we don't do this, 'cleanup_fds' will cause its file
                # descriptor to be closed, but it will still remain in
                # the socket_map, and eventually its file descriptor
                # will be passed to # select(), which will bomb.  See
                # also http://www.plope.com/software/collector/253
                dispatcher_server = getattr(dispatcher, 'server', None)
                if dispatcher_server is server:
                    dispatcher.close()

    def close_logger(self):
        self.logger.close()

    def setsignals(self):
        receive = self.signal_receiver.receive
        signal.signal(signal.SIGTERM, receive)
        signal.signal(signal.SIGINT, receive)
        signal.signal(signal.SIGQUIT, receive)
        signal.signal(signal.SIGHUP, receive)
        signal.signal(signal.SIGCHLD, receive)
        signal.signal(signal.SIGUSR2, receive)

    def get_signal(self):
        return self.signal_receiver.get_signal()

    def openhttpservers(self, supervisord):
        try:
            self.httpservers = self.make_http_servers(supervisord)
        except socket.error as why:
            if why.args[0] == errno.EADDRINUSE:
                self.usage('Another program is already listening on '
                           'a port that one of our HTTP servers is '
                           'configured to use.  Shut this program '
                           'down first before starting supervisord.')
            else:
                help = 'Cannot open an HTTP server: socket.error reported'
                errorname = errno.errorcode.get(why.args[0])
                if errorname is None:
                    self.usage('%s %s' % (help, why.args[0]))
                else:
                    self.usage('%s errno.%s (%d)' %
                               (help, errorname, why.args[0]))
            self.unlink_socketfiles = False
        except ValueError as why:
            self.usage(why.args[0])

    def get_autochildlog_name(self, name, identifier, channel):
        prefix='%s-%s---%s-' % (name, channel, identifier)
        logfile = self.mktempfile(
            suffix='.log',
            prefix=prefix,
            dir=self.childlogdir)
        return logfile

    def clear_autochildlogdir(self):
        # must be called after realize()
        childlogdir = self.childlogdir
        fnre = re.compile(r'.+?---%s-\S+\.log\.{0,1}\d{0,4}' % self.identifier)
        try:
            filenames = os.listdir(childlogdir)
        except (IOError, OSError):
            self.logger.warn('Could not clear childlog dir')
            return

        for filename in filenames:
            if fnre.match(filename):
                pathname = os.path.join(childlogdir, filename)
                try:
                    os.remove(pathname)
                except (OSError, IOError):
                    self.logger.warn('Failed to clean up %r' % pathname)

    def get_socket_map(self):
        return asyncore.socket_map

    def cleanup_fds(self):
        # try to close any leaked file descriptors (for reload)
        start = 5
        for x in range(start, self.minfds):
            try:
                os.close(x)
            except OSError:
                pass

    def select(self, r, w, x, timeout):
        return select.select(r, w, x, timeout)

    def kill(self, pid, signal):
        os.kill(pid, signal)

    def set_uid(self):
        if self.uid is None:
            if os.getuid() == 0:
                return 'Supervisor running as root (no user in config file)'
            return None
        msg = self.dropPrivileges(self.uid)
        if msg is None:
            return 'Set uid to user %s' % self.uid
        return msg

    def dropPrivileges(self, user):
        # Drop root privileges if we have them
        if user is None:
            return "No user specified to setuid to!"

        # get uid for user, which can be a number or username
        try:
            uid = int(user)
        except ValueError:
            try:
                pwrec = pwd.getpwnam(user)
            except KeyError:
                return "Can't find username %r" % user
            uid = pwrec[2]
        else:
            try:
                pwrec = pwd.getpwuid(uid)
            except KeyError:
                return "Can't find uid %r" % uid

        current_uid = os.getuid()

        if current_uid == uid:
            # do nothing and return successfully if the uid is already the
            # current one.  this allows a supervisord running as an
            # unprivileged user "foo" to start a process where the config
            # has "user=foo" (same user) in it.
            return

        if current_uid != 0:
            return "Can't drop privilege as nonroot user"

        gid = pwrec[3]
        if hasattr(os, 'setgroups'):
            user = pwrec[0]
            groups = [grprec[2] for grprec in grp.getgrall() if user in
                      grprec[3]]

            # always put our primary gid first in this list, otherwise we can
            # lose group info since sometimes the first group in the setgroups
            # list gets overwritten on the subsequent setgid call (at least on
            # freebsd 9 with python 2.7 - this will be safe though for all unix
            # /python version combos)
            groups.insert(0, gid)
            try:
                os.setgroups(groups)
            except OSError:
                return 'Could not set groups of effective user'
        try:
            os.setgid(gid)
        except OSError:
            return 'Could not set group id of effective user'
        os.setuid(uid)

    def waitpid(self):
        # need pthread_sigmask here to avoid concurrent sigchild, but
        # Python doesn't offer it as it's not standard across UNIX versions.
        # there is still a race condition here; we can get a sigchild while
        # we're sitting in the waitpid call.
        try:
            pid, sts = os.waitpid(-1, os.WNOHANG)
        except OSError as why:
            err = why.args[0]
            if err not in (errno.ECHILD, errno.EINTR):
                self.logger.critical(
                    'waitpid error; a process may not be cleaned up properly')
            if err == errno.EINTR:
                self.logger.blather('EINTR during reap')
            pid, sts = None, None
        return pid, sts

    def set_rlimits(self):
        limits = []
        if hasattr(resource, 'RLIMIT_NOFILE'):
            limits.append(
                {
                'msg':('The minimum number of file descriptors required '
                       'to run this process is %(min)s as per the "minfds" '
                       'command-line argument or config file setting. '
                       'The current environment will only allow you '
                       'to open %(hard)s file descriptors.  Either raise '
                       'the number of usable file descriptors in your '
                       'environment (see README.rst) or lower the '
                       'minfds setting in the config file to allow '
                       'the process to start.'),
                'min':self.minfds,
                'resource':resource.RLIMIT_NOFILE,
                'name':'RLIMIT_NOFILE',
                })
        if hasattr(resource, 'RLIMIT_NPROC'):
            limits.append(
                {
                'msg':('The minimum number of available processes required '
                       'to run this program is %(min)s as per the "minprocs" '
                       'command-line argument or config file setting. '
                       'The current environment will only allow you '
                       'to open %(hard)s processes.  Either raise '
                       'the number of usable processes in your '
                       'environment (see README.rst) or lower the '
                       'minprocs setting in the config file to allow '
                       'the program to start.'),
                'min':self.minprocs,
                'resource':resource.RLIMIT_NPROC,
                'name':'RLIMIT_NPROC',
                })

        msgs = []

        for limit in limits:

            lmin = limit['min']
            res = limit['resource']
            msg = limit['msg']
            name = limit['name']
            name = name # name is used below by locals()

            soft, hard = resource.getrlimit(res)

            if (soft < lmin) and (soft != -1): # -1 means unlimited
                if (hard < lmin) and (hard != -1):
                    # setrlimit should increase the hard limit if we are
                    # root, if not then setrlimit raises and we print usage
                    hard = lmin

                try:
                    resource.setrlimit(res, (lmin, hard))
                    msgs.append('Increased %(name)s limit to %(lmin)s' %
                                locals())
                except (resource.error, ValueError):
                    self.usage(msg % locals())
        return msgs

    def make_logger(self, critical_messages, warn_messages, info_messages):
        # must be called after realize() and after supervisor does setuid()
        format = '%(asctime)s %(levelname)s %(message)s\n'
        self.logger = loggers.getLogger(self.loglevel)
        if self.nodaemon:
            loggers.handle_stdout(self.logger, format)
        loggers.handle_file(
            self.logger,
            self.logfile,
            format,
            rotating=True,
            maxbytes=self.logfile_maxbytes,
            backups=self.logfile_backups,
        )
        for msg in critical_messages:
            self.logger.critical(msg)
        for msg in warn_messages:
            self.logger.warn(msg)
        for msg in info_messages:
            self.logger.info(msg)

    def make_http_servers(self, supervisord):
        from supervisor.http import make_http_servers
        return make_http_servers(self, supervisord)

    def close_fd(self, fd):
        try:
            os.close(fd)
        except OSError:
            pass

    def fork(self):
        return os.fork()

    def dup2(self, frm, to):
        return os.dup2(frm, to)

    def setpgrp(self):
        return os.setpgrp()

    def stat(self, filename):
        return os.stat(filename)

    def write(self, fd, data):
        return os.write(fd, as_bytes(data))

    def execve(self, filename, argv, env):
        return os.execve(filename, argv, env)

    def mktempfile(self, suffix, prefix, dir):
        # set os._urandomfd as a hack around bad file descriptor bug
        # seen in the wild, see
        # http://www.plope.com/software/collector/252
        os._urandomfd = None
        fd, filename = tempfile.mkstemp(suffix, prefix, dir)
        os.close(fd)
        return filename

    def remove(self, path):
        os.remove(path)

    def exists(self, path):
        return os.path.exists(path)

    def _exit(self, code):
        os._exit(code)

    def setumask(self, mask):
        os.umask(mask)

    def get_path(self):
        """Return a list corresponding to $PATH, or a default."""
        path = ["/bin", "/usr/bin", "/usr/local/bin"]
        if "PATH" in os.environ:
            p = os.environ["PATH"]
            if p:
                path = p.split(os.pathsep)
        return path

    def get_pid(self):
        return os.getpid()

    def check_execv_args(self, filename, argv, st):
        if st is None:
            raise NotFound("can't find command %r" % filename)

        elif stat.S_ISDIR(st[stat.ST_MODE]):
            raise NotExecutable("command at %r is a directory" % filename)

        elif not (stat.S_IMODE(st[stat.ST_MODE]) & 73):
            # 73 is spelled 0111 in py2, 0o111 in py3
            raise NotExecutable("command at %r is not executable" % filename)

        elif not os.access(filename, os.X_OK):
            raise NoPermission("no permission to run command %r" % filename)

    def reopenlogs(self):
        self.logger.info('supervisord logreopen')
        for handler in self.logger.handlers:
            if hasattr(handler, 'reopen'):
                handler.reopen()

    def readfd(self, fd):
        try:
            data = os.read(fd, 2 << 16) # 128K
        except OSError as why:
            if why.args[0] not in (errno.EWOULDBLOCK, errno.EBADF, errno.EINTR):
                raise
            data = ''
        return as_string(data)

    def process_environment(self):
        os.environ.update(self.environment or {})

    def open(self, fn, mode='r'):
        return open(fn, mode)

    def chdir(self, dir):
        os.chdir(dir)

    def make_pipes(self, stderr=True):
        """ Create pipes for parent to child stdin/stdout/stderr
        communications.  Open fd in non-blocking mode so we can read them
        in the mainloop without blocking.  If stderr is False, don't
        create a pipe for stderr. """

        pipes = {'child_stdin':None,
                 'stdin':None,
                 'stdout':None,
                 'child_stdout':None,
                 'stderr':None,
                 'child_stderr':None}
        try:
            stdin, child_stdin = os.pipe()
            pipes['child_stdin'], pipes['stdin'] = stdin, child_stdin
            stdout, child_stdout = os.pipe()
            pipes['stdout'], pipes['child_stdout'] = stdout, child_stdout
            if stderr:
                stderr, child_stderr = os.pipe()
                pipes['stderr'], pipes['child_stderr'] = stderr, child_stderr
            for fd in (pipes['stdout'], pipes['stderr'], pipes['stdin']):
                if fd is not None:
                    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | os.O_NDELAY)
            return pipes
        except OSError:
            for fd in pipes.values():
                if fd is not None:
                    self.close_fd(fd)

    def close_parent_pipes(self, pipes):
        for fdname in ('stdin', 'stdout', 'stderr'):
            fd = pipes[fdname]
            if fd is not None:
                self.close_fd(fd)

    def close_child_pipes(self, pipes):
        for fdname in ('child_stdin', 'child_stdout', 'child_stderr'):
            fd = pipes[fdname]
            if fd is not None:
                self.close_fd(fd)

class ClientOptions(Options):
    positional_args_allowed = 1

    interactive = None
    prompt = None
    serverurl = None
    username = None
    password = None
    history_file = None

    def __init__(self):
        Options.__init__(self, require_configfile=False)
        self.configroot = Dummy()
        self.configroot.supervisorctl = Dummy()
        self.configroot.supervisorctl.interactive = None
        self.configroot.supervisorctl.prompt = 'supervisor'
        self.configroot.supervisorctl.serverurl = None
        self.configroot.supervisorctl.username = None
        self.configroot.supervisorctl.password = None
        self.configroot.supervisorctl.history_file = None

        from supervisor.supervisorctl import DefaultControllerPlugin
        default_factory = ('default', DefaultControllerPlugin, {})
        # we always add the default factory. If you want to a supervisorctl
        # without the default plugin, please write your own supervisorctl.
        self.plugin_factories = [default_factory]

        self.add("interactive", "supervisorctl.interactive", "i",
                 "interactive", flag=1, default=0)
        self.add("prompt", "supervisorctl.prompt", default="supervisor")
        self.add("serverurl", "supervisorctl.serverurl", "s:", "serverurl=",
                 url, default="http://localhost:9001")
        self.add("username", "supervisorctl.username", "u:", "username=")
        self.add("password", "supervisorctl.password", "p:", "password=")
        self.add("history", "supervisorctl.history_file", "r:", "history_file=")

    def realize(self, *arg, **kw):
        Options.realize(self, *arg, **kw)
        if not self.args:
            self.interactive = 1

    def read_config(self, fp):
        section = self.configroot.supervisorctl
        need_close = False
        if not hasattr(fp, 'read'):
            self.here = os.path.dirname(normalize_path(fp))
            if not os.path.exists(fp):
                raise ValueError("could not find config file %s" % fp)
            try:
                fp = open(fp, 'r')
                need_close = True
            except (IOError, OSError):
                raise ValueError("could not read config file %s" % fp)
        config = UnhosedConfigParser()
        config.mysection = 'supervisorctl'
        try:
            config.read_file(fp)
        except AttributeError:
            config.readfp(fp)
        if need_close:
            fp.close()
        sections = config.sections()
        if not 'supervisorctl' in sections:
            raise ValueError('.ini file does not include supervisorctl section')
        serverurl = config.getdefault('serverurl', 'http://localhost:9001')
        if serverurl.startswith('unix://'):
            sf = serverurl[7:]
            path = expand(sf, {'here':self.here}, 'serverurl')
            path = normalize_path(path)
            serverurl = 'unix://%s' % path
        section.serverurl = serverurl

        # The defaults used below are really set in __init__ (since
        # section==self.configroot.supervisorctl)
        section.prompt = config.getdefault('prompt', section.prompt)
        section.username = config.getdefault('username', section.username)
        section.password = config.getdefault('password', section.password)
        history_file = config.getdefault('history_file', section.history_file)

        if history_file:
            history_file = normalize_path(history_file)
            section.history_file = history_file
            self.history_file = history_file
        else:
            section.history_file = None
            self.history_file = None

        self.plugin_factories += self.get_plugins(
            config,
            'supervisor.ctl_factory',
            'ctlplugin:'
            )

        return section

    def getServerProxy(self):
        # mostly put here for unit testing
        return xmlrpclib.ServerProxy(
            # dumbass ServerProxy won't allow us to pass in a non-HTTP url,
            # so we fake the url we pass into it and always use the transport's
            # 'serverurl' to figure out what to attach to
            'http://127.0.0.1',
            transport = xmlrpc.SupervisorTransport(self.username,
                                                   self.password,
                                                   self.serverurl)
            )

_marker = []

class UnhosedConfigParser(ConfigParser.RawConfigParser):
    mysection = 'supervisord'
    def read_string(self, s):
        if not PY3 and isinstance(s, str):
            s = unicode(s)
        s = StringIO(s)
        try:
            return self.read_file(s)
        except AttributeError:
            return self.readfp(s)

    def getdefault(self, option, default=_marker):
        try:
            return self.get(self.mysection, option)
        except ConfigParser.NoOptionError:
            if default is _marker:
                raise
            else:
                return default

    def saneget(self, section, option, default=_marker):
        try:
            return self.get(section, option)
        except ConfigParser.NoOptionError:
            if default is _marker:
                raise
            else:
                return default

class Config(object):
    def __ne__(self, other):
        return not self.__eq__(other)

    def __lt__(self, other):
        if self.priority == other.priority:
            return self.name < other.name

        return self.priority < other.priority

    def __le__(self, other):
        if self.priority == other.priority:
            return self.name <= other.name

        return self.priority <= other.priority

    def __gt__(self, other):
        if self.priority == other.priority:
            return self.name > other.name

        return self.priority > other.priority

    def __ge__(self, other):
        if self.priority == other.priority:
            return self.name >= other.name

        return self.priority >= other.priority

    def __repr__(self):
        return '<%s instance at %s named %s>' % (self.__class__, id(self),
                                                 self.name)

class ProcessConfig(Config):
    req_param_names = [
        'name', 'uid', 'command', 'directory', 'umask', 'priority',
        'autostart', 'autorestart', 'startsecs', 'startretries',
        'stdout_logfile', 'stdout_capture_maxbytes',
        'stdout_events_enabled', 'stdout_syslog',
        'stdout_logfile_backups', 'stdout_logfile_maxbytes',
        'stderr_logfile', 'stderr_capture_maxbytes',
        'stderr_logfile_backups', 'stderr_logfile_maxbytes',
        'stderr_events_enabled', 'stderr_syslog',
        'stopsignal', 'stopwaitsecs', 'stopasgroup', 'killasgroup',
        'exitcodes', 'redirect_stderr' ]
    optional_param_names = [ 'environment', 'serverurl' ]

    def __init__(self, options, **params):
        self.options = options
        for name in self.req_param_names:
            setattr(self, name, params[name])
        for name in self.optional_param_names:
            setattr(self, name, params.get(name, None))

    def __eq__(self, other):
        if not isinstance(other, ProcessConfig):
            return False

        for name in self.req_param_names + self.optional_param_names:
            if Automatic in [getattr(self, name), getattr(other, name)] :
                continue
            if getattr(self, name) != getattr(other, name):
                return False

        return True

    def create_autochildlogs(self):
        # temporary logfiles which are erased at start time
        get_autoname = self.options.get_autochildlog_name
        sid = self.options.identifier
        name = self.name
        if self.stdout_logfile is Automatic:
            self.stdout_logfile = get_autoname(name, sid, 'stdout')
        if self.stderr_logfile is Automatic:
            self.stderr_logfile = get_autoname(name, sid, 'stderr')

    def make_process(self, group=None):
        from supervisor.process import Subprocess
        process = Subprocess(self)
        process.group = group
        return process

    def make_dispatchers(self, proc):
        use_stderr = not self.redirect_stderr
        p = self.options.make_pipes(use_stderr)
        stdout_fd,stderr_fd,stdin_fd = p['stdout'],p['stderr'],p['stdin']
        dispatchers = {}
        from supervisor.dispatchers import POutputDispatcher
        from supervisor.dispatchers import PInputDispatcher
        from supervisor import events
        if stdout_fd is not None:
            etype = events.ProcessCommunicationStdoutEvent
            dispatchers[stdout_fd] = POutputDispatcher(proc, etype, stdout_fd)
        if stderr_fd is not None:
            etype = events.ProcessCommunicationStderrEvent
            dispatchers[stderr_fd] = POutputDispatcher(proc,etype, stderr_fd)
        if stdin_fd is not None:
            dispatchers[stdin_fd] = PInputDispatcher(proc, 'stdin', stdin_fd)
        return dispatchers, p

class EventListenerConfig(ProcessConfig):
    def make_dispatchers(self, proc):
        use_stderr = not self.redirect_stderr
        p = self.options.make_pipes(use_stderr)
        stdout_fd,stderr_fd,stdin_fd = p['stdout'],p['stderr'],p['stdin']
        dispatchers = {}
        from supervisor.dispatchers import PEventListenerDispatcher
        from supervisor.dispatchers import PInputDispatcher
        from supervisor.dispatchers import POutputDispatcher
        from supervisor import events
        if stdout_fd is not None:
            dispatchers[stdout_fd] = PEventListenerDispatcher(proc, 'stdout',
                                                              stdout_fd)
        if stderr_fd is not None:
            etype = events.ProcessCommunicationStderrEvent
            dispatchers[stderr_fd] = POutputDispatcher(proc, etype, stderr_fd)
        if stdin_fd is not None:
            dispatchers[stdin_fd] = PInputDispatcher(proc, 'stdin', stdin_fd)
        return dispatchers, p

class FastCGIProcessConfig(ProcessConfig):

    def make_process(self, group=None):
        if group is None:
            raise NotImplementedError('FastCGI programs require a group')
        from supervisor.process import FastCGISubprocess
        process = FastCGISubprocess(self)
        process.group = group
        return process

    def make_dispatchers(self, proc):
        dispatchers, p = ProcessConfig.make_dispatchers(self, proc)
        #FastCGI child processes expect the FastCGI socket set to
        #file descriptor 0, so supervisord cannot use stdin
        #to communicate with the child process
        stdin_fd = p['stdin']
        if stdin_fd is not None:
            dispatchers[stdin_fd].close()
        return dispatchers, p

class ProcessGroupConfig(Config):
    def __init__(self, options, name, priority, process_configs):
        self.options = options
        self.name = name
        self.priority = priority
        self.process_configs = process_configs

    def __eq__(self, other):
        if not isinstance(other, ProcessGroupConfig):
            return False

        if self.name != other.name:
            return False
        if self.priority != other.priority:
            return False
        if self.process_configs != other.process_configs:
            return False

        return True

    def after_setuid(self):
        for config in self.process_configs:
            config.create_autochildlogs()

    def make_group(self):
        from supervisor.process import ProcessGroup
        return ProcessGroup(self)

class EventListenerPoolConfig(Config):
    def __init__(self, options, name, priority, process_configs, buffer_size,
                 pool_events, result_handler):
        self.options = options
        self.name = name
        self.priority = priority
        self.process_configs = process_configs
        self.buffer_size = buffer_size
        self.pool_events = pool_events
        self.result_handler = result_handler

    def __eq__(self, other):
        if not isinstance(other, EventListenerPoolConfig):
            return False

        if (self.name == other.name) and (self.priority == other.priority):
            return True

        return False

    def after_setuid(self):
        for config in self.process_configs:
            config.create_autochildlogs()

    def make_group(self):
        from supervisor.process import EventListenerPool
        return EventListenerPool(self)

class FastCGIGroupConfig(ProcessGroupConfig):
    def __init__(self, options, name, priority, process_configs, socket_config):
        ProcessGroupConfig.__init__(
            self,
            options,
            name,
            priority,
            process_configs,
            )
        self.socket_config = socket_config

    def __eq__(self, other):
        if not isinstance(other, FastCGIGroupConfig):
            return False

        if self.socket_config != other.socket_config:
            return False

        return ProcessGroupConfig.__eq__(self, other)

    def make_group(self):
        from supervisor.process import FastCGIProcessGroup
        return FastCGIProcessGroup(self)

def readFile(filename, offset, length):
    """ Read length bytes from the file named by filename starting at
    offset """

    absoffset = abs(offset)
    abslength = abs(length)

    f = None
    try:
        f = open(filename, 'rb')
        if absoffset != offset:
            # negative offset returns offset bytes from tail of the file
            if length:
                raise ValueError('BAD_ARGUMENTS')
            f.seek(0, 2)
            sz = f.tell()
            pos = int(sz - absoffset)
            if pos < 0:
                pos = 0
            f.seek(pos)
            data = f.read(absoffset)
        else:
            if abslength != length:
                raise ValueError('BAD_ARGUMENTS')
            if length == 0:
                f.seek(offset)
                data = f.read()
            else:
                f.seek(offset)
                data = f.read(length)
    except (OSError, IOError):
        raise ValueError('FAILED')

    finally:
        if f:
            f.close()
    return data

def tailFile(filename, offset, length):
    """
    Read length bytes from the file named by filename starting at
    offset, automatically increasing offset and setting overflow
    flag if log size has grown beyond (offset + length).  If length
    bytes are not available, as many bytes as are available are returned.
    """

    overflow = False
    f = None
    try:
        f = open(filename, 'rb')
        f.seek(0, 2)
        sz = f.tell()

        if sz > (offset + length):
            overflow = True
            offset   = sz - 1

        if (offset + length) > sz:
            if offset > (sz - 1):
                length = 0
            offset = sz - length

        if offset < 0: offset = 0
        if length < 0: length = 0

        if length == 0:
            data = ''
        else:
            f.seek(offset)
            data = f.read(length)

        offset = sz
        return [as_string(data), offset, overflow]

    except (OSError, IOError):
        return ['', offset, False]
    finally:
        if f:
            f.close()

# Helpers for dealing with signals and exit status

def decode_wait_status(sts):
    """Decode the status returned by wait() or waitpid().

    Return a tuple (exitstatus, message) where exitstatus is the exit
    status, or -1 if the process was killed by a signal; and message
    is a message telling what happened.  It is the caller's
    responsibility to display the message.
    """
    if os.WIFEXITED(sts):
        es = os.WEXITSTATUS(sts) & 0xffff
        msg = "exit status %s" % es
        return es, msg
    elif os.WIFSIGNALED(sts):
        sig = os.WTERMSIG(sts)
        msg = "terminated by %s" % signame(sig)
        if hasattr(os, "WCOREDUMP"):
            iscore = os.WCOREDUMP(sts)
        else:
            iscore = sts & 0x80
        if iscore:
            msg += " (core dumped)"
        return -1, msg
    else:
        msg = "unknown termination cause 0x%04x" % sts
        return -1, msg

_signames = None

def signame(sig):
    """Return a symbolic name for a signal.

    Return "signal NNN" if there is no corresponding SIG name in the
    signal module.
    """

    if _signames is None:
        _init_signames()
    return _signames.get(sig) or "signal %d" % sig

def _init_signames():
    global _signames
    d = {}
    for k, v in signal.__dict__.items():
        k_startswith = getattr(k, "startswith", None)
        if k_startswith is None:
            continue
        if k_startswith("SIG") and not k_startswith("SIG_"):
            d[v] = k
    _signames = d

class SignalReceiver:
    def __init__(self):
        self._signals_recvd = []

    def receive(self, sig, frame):
        if sig not in self._signals_recvd:
            self._signals_recvd.append(sig)

    def get_signal(self):
        if self._signals_recvd:
            sig = self._signals_recvd.pop(0)
        else:
            sig = None
        return sig

# miscellaneous utility functions

def expand(s, expansions, name):
    try:
        return s % expansions
    except KeyError:
        raise ValueError(
            'Format string %r for %r contains names which cannot be '
            'expanded' % (s, name))
    except:
        raise ValueError(
            'Format string %r for %r is badly formatted' % (s, name)
            )

_environ_expansions = None

def environ_expansions():
    """Return dict of environment variables, suitable for use in string
    expansions.

    Every environment variable is prefixed by 'ENV_'.
    """
    global _environ_expansions

    if _environ_expansions:
        return _environ_expansions

    _environ_expansions = {}
    for key, value in os.environ.items():
        _environ_expansions['ENV_%s' % key] = value

    return _environ_expansions

def make_namespec(group_name, process_name):
    # we want to refer to the process by its "short name" (a process named
    # process1 in the group process1 has a name "process1").  This is for
    # backwards compatibility
    if group_name == process_name:
        name = process_name
    else:
        name = '%s:%s' % (group_name, process_name)
    return name

def split_namespec(namespec):
    names = namespec.split(':', 1)
    if len(names) == 2:
        # group and process name differ
        group_name, process_name = names
        if not process_name or process_name == '*':
            process_name = None
    else:
        # group name is same as process name
        group_name, process_name = namespec, namespec
    return group_name, process_name

# exceptions

class ProcessException(Exception):
    """ Specialized exceptions used when attempting to start a process """

class BadCommand(ProcessException):
    """ Indicates the command could not be parsed properly. """

class NotExecutable(ProcessException):
    """ Indicates that the filespec cannot be executed because its path
    resolves to a file which is not executable, or which is a directory. """

class NotFound(ProcessException):
    """ Indicates that the filespec cannot be executed because it could not
    be found """

class NoPermission(ProcessException):
    """ Indicates that the file cannot be executed because the supervisor
    process does not possess the appropriate UNIX filesystem permission
    to execute the file. """


########NEW FILE########
__FILENAME__ = pidproxy
#!/usr/bin/env python

""" An executable which proxies for a subprocess; upon a signal, it sends that
signal to the process identified by a pidfile. """

import os
import sys
import signal
import time

class PidProxy:
    pid = None
    def __init__(self, args):
        self.setsignals()
        try:
            self.pidfile, cmdargs = args[1], args[2:]
            self.command = os.path.abspath(cmdargs[0])
            self.cmdargs = cmdargs
        except (ValueError, IndexError):
            self.usage()
            sys.exit(1)

    def go(self):
        self.pid = os.spawnv(os.P_NOWAIT, self.command, self.cmdargs)
        while 1:
            time.sleep(5)
            try:
                pid, sts = os.waitpid(-1, os.WNOHANG)
            except OSError:
                pid, sts = None, None
            if pid:
                break

    def usage(self):
        print("pidproxy.py <pidfile name> <command> [<cmdarg1> ...]")

    def setsignals(self):
        signal.signal(signal.SIGTERM, self.passtochild)
        signal.signal(signal.SIGHUP, self.passtochild)
        signal.signal(signal.SIGINT, self.passtochild)
        signal.signal(signal.SIGUSR1, self.passtochild)
        signal.signal(signal.SIGUSR2, self.passtochild)
        signal.signal(signal.SIGQUIT, self.passtochild)
        signal.signal(signal.SIGCHLD, self.reap)

    def reap(self, sig, frame):
        # do nothing, we reap our child synchronously
        pass

    def passtochild(self, sig, frame):
        try:
            pid = int(open(self.pidfile, 'r').read().strip())
        except:
            print("Can't read child pidfile %s!" % self.pidfile)
            return
        os.kill(pid, sig)
        if sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
            sys.exit(0)

def main():
    pp = PidProxy(sys.argv)
    pp.go()

if __name__ == '__main__':
    main()
    
    
    

########NEW FILE########
__FILENAME__ = process
import os
import time
import errno
import shlex
import traceback
import signal

from supervisor.compat import maxint
from supervisor.compat import StringIO
from supervisor.compat import total_ordering

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.states import ProcessStates
from supervisor.states import SupervisorStates
from supervisor.states import getProcessStateDescription
from supervisor.states import STOPPED_STATES

from supervisor.options import decode_wait_status
from supervisor.options import signame
from supervisor.options import ProcessException, BadCommand

from supervisor.dispatchers import EventListenerStates

from supervisor import events

from supervisor.datatypes import RestartUnconditionally

from supervisor.socket_manager import SocketManager

class Subprocess(object):

    """A class to manage a subprocess."""

    # Initial state; overridden by instance variables

    pid = 0 # Subprocess pid; 0 when not running
    config = None # ProcessConfig instance
    state = None # process state code
    listener_state = None # listener state code (if we're an event listener)
    event = None # event currently being processed (if we're an event listener)
    laststart = 0 # Last time the subprocess was started; 0 if never
    laststop = 0  # Last time the subprocess was stopped; 0 if never
    delay = 0 # If nonzero, delay starting or killing until this time
    administrative_stop = 0 # true if the process has been stopped by an admin
    system_stop = 0 # true if the process has been stopped by the system
    killing = 0 # flag determining whether we are trying to kill this proc
    backoff = 0 # backoff counter (to startretries)
    dispatchers = None # asyncore output dispatchers (keyed by fd)
    pipes = None # map of channel name to file descriptor #
    exitstatus = None # status attached to dead process by finish()
    spawnerr = None # error message attached by spawn() if any
    group = None # ProcessGroup instance if process is in the group

    def __init__(self, config):
        """Constructor.

        Argument is a ProcessConfig instance.
        """
        self.config = config
        self.dispatchers = {}
        self.pipes = {}
        self.state = ProcessStates.STOPPED

    def removelogs(self):
        for dispatcher in self.dispatchers.values():
            if hasattr(dispatcher, 'removelogs'):
                dispatcher.removelogs()

    def reopenlogs(self):
        for dispatcher in self.dispatchers.values():
            if hasattr(dispatcher, 'reopenlogs'):
                dispatcher.reopenlogs()

    def drain(self):
        for dispatcher in self.dispatchers.values():
            # note that we *must* call readable() for every
            # dispatcher, as it may have side effects for a given
            # dispatcher (eg. call handle_listener_state_change for
            # event listener processes)
            if dispatcher.readable():
                dispatcher.handle_read_event()
            if dispatcher.writable():
                dispatcher.handle_write_event()

    def write(self, chars):
        if not self.pid or self.killing:
            raise OSError(errno.EPIPE, "Process already closed")

        stdin_fd = self.pipes['stdin']
        if stdin_fd is None:
            raise OSError(errno.EPIPE, "Process has no stdin channel")

        dispatcher = self.dispatchers[stdin_fd]
        if dispatcher.closed:
            raise OSError(errno.EPIPE, "Process' stdin channel is closed")

        dispatcher.input_buffer += chars
        dispatcher.flush() # this must raise EPIPE if the pipe is closed

    def get_execv_args(self):
        """Internal: turn a program name into a file name, using $PATH,
        make sure it exists / is executable, raising a ProcessException
        if not """
        try:
            commandargs = shlex.split(self.config.command)
        except ValueError as e:
            raise BadCommand("can't parse command %r: %s" % \
                (self.config.command, str(e)))

        if commandargs:
            program = commandargs[0]
        else:
            raise BadCommand("command is empty")

        if "/" in program:
            filename = program
            try:
                st = self.config.options.stat(filename)
            except OSError:
                st = None

        else:
            path = self.config.options.get_path()
            found = None
            st = None
            for dir in path:
                found = os.path.join(dir, program)
                try:
                    st = self.config.options.stat(found)
                except OSError:
                    pass
                else:
                    break
            if st is None:
                filename = program
            else:
                filename = found

        # check_execv_args will raise a ProcessException if the execv
        # args are bogus, we break it out into a separate options
        # method call here only to service unit tests
        self.config.options.check_execv_args(filename, commandargs, st)

        return filename, commandargs

    event_map = {
        ProcessStates.BACKOFF: events.ProcessStateBackoffEvent,
        ProcessStates.FATAL:   events.ProcessStateFatalEvent,
        ProcessStates.UNKNOWN: events.ProcessStateUnknownEvent,
        ProcessStates.STOPPED: events.ProcessStateStoppedEvent,
        ProcessStates.EXITED:  events.ProcessStateExitedEvent,
        ProcessStates.RUNNING: events.ProcessStateRunningEvent,
        ProcessStates.STARTING: events.ProcessStateStartingEvent,
        ProcessStates.STOPPING: events.ProcessStateStoppingEvent,
        }

    def change_state(self, new_state, expected=True):
        old_state = self.state
        if new_state is old_state:
            # exists for unit tests
            return False

        event_class = self.event_map.get(new_state)
        if event_class is not None:
            event = event_class(self, old_state, expected)
            events.notify(event)

        if new_state == ProcessStates.BACKOFF:
            now = time.time()
            self.backoff += 1
            self.delay = now + self.backoff

        self.state = new_state

    def _assertInState(self, *states):
        if self.state not in states:
            current_state = getProcessStateDescription(self.state)
            allowable_states = ' '.join(map(getProcessStateDescription, states))
            raise AssertionError('Assertion failed for %s: %s not in %s' %  (
                self.config.name, current_state, allowable_states))

    def record_spawnerr(self, msg):
        self.spawnerr = msg
        self.config.options.logger.info("spawnerr: %s" % msg)

    def spawn(self):
        """Start the subprocess.  It must not be running already.

        Return the process id.  If the fork() call fails, return None.
        """
        options = self.config.options

        if self.pid:
            msg = 'process %r already running' % self.config.name
            options.logger.warn(msg)
            return

        self.killing = 0
        self.spawnerr = None
        self.exitstatus = None
        self.system_stop = 0
        self.administrative_stop = 0

        self.laststart = time.time()

        self._assertInState(ProcessStates.EXITED, ProcessStates.FATAL,
                            ProcessStates.BACKOFF, ProcessStates.STOPPED)

        self.change_state(ProcessStates.STARTING)

        try:
            filename, argv = self.get_execv_args()
        except ProcessException as what:
            self.record_spawnerr(what.args[0])
            self._assertInState(ProcessStates.STARTING)
            self.change_state(ProcessStates.BACKOFF)
            return

        try:
            self.dispatchers, self.pipes = self.config.make_dispatchers(self)
        except OSError as why:
            code = why.args[0]
            if code == errno.EMFILE:
                # too many file descriptors open
                msg = 'too many open files to spawn %r' % self.config.name
            else:
                msg = 'unknown error: %s' % errno.errorcode.get(code, code)
            self.record_spawnerr(msg)
            self._assertInState(ProcessStates.STARTING)
            self.change_state(ProcessStates.BACKOFF)
            return

        try:
            pid = options.fork()
        except OSError as why:
            code = why.args[0]
            if code == errno.EAGAIN:
                # process table full
                msg  = ('Too many processes in process table to spawn %r' %
                        self.config.name)
            else:
                msg = 'unknown error: %s' % errno.errorcode.get(code, code)

            self.record_spawnerr(msg)
            self._assertInState(ProcessStates.STARTING)
            self.change_state(ProcessStates.BACKOFF)
            options.close_parent_pipes(self.pipes)
            options.close_child_pipes(self.pipes)
            return

        if pid != 0:
            return self._spawn_as_parent(pid)

        else:
            return self._spawn_as_child(filename, argv)

    def _spawn_as_parent(self, pid):
        # Parent
        self.pid = pid
        options = self.config.options
        options.close_child_pipes(self.pipes)
        options.logger.info('spawned: %r with pid %s' % (self.config.name, pid))
        self.spawnerr = None
        self.delay = time.time() + self.config.startsecs
        options.pidhistory[pid] = self
        return pid

    def _prepare_child_fds(self):
        options = self.config.options
        options.dup2(self.pipes['child_stdin'], 0)
        options.dup2(self.pipes['child_stdout'], 1)
        if self.config.redirect_stderr:
            options.dup2(self.pipes['child_stdout'], 2)
        else:
            options.dup2(self.pipes['child_stderr'], 2)
        for i in range(3, options.minfds):
            options.close_fd(i)

    def _spawn_as_child(self, filename, argv):
        options = self.config.options
        try:
            # prevent child from receiving signals sent to the
            # parent by calling os.setpgrp to create a new process
            # group for the child; this prevents, for instance,
            # the case of child processes being sent a SIGINT when
            # running supervisor in foreground mode and Ctrl-C in
            # the terminal window running supervisord is pressed.
            # Presumably it also prevents HUP, etc received by
            # supervisord from being sent to children.
            options.setpgrp()

            self._prepare_child_fds()
            # sending to fd 2 will put this output in the stderr log

            # set user
            setuid_msg = self.set_uid()
            if setuid_msg:
                uid = self.config.uid
                msg = "couldn't setuid to %s: %s\n" % (uid, setuid_msg)
                options.write(2, "supervisor: " + msg)
                return # finally clause will exit the child process

            # set environment
            env = os.environ.copy()
            env['SUPERVISOR_ENABLED'] = '1'
            serverurl = self.config.serverurl
            if serverurl is None: # unset
                serverurl = self.config.options.serverurl # might still be None
            if serverurl:
                env['SUPERVISOR_SERVER_URL'] = serverurl
            env['SUPERVISOR_PROCESS_NAME'] = self.config.name
            if self.group:
                env['SUPERVISOR_GROUP_NAME'] = self.group.config.name
            if self.config.environment is not None:
                env.update(self.config.environment)
            # change directory
            cwd = self.config.directory
            try:
                if cwd is not None:
                    options.chdir(cwd)
            except OSError as why:
                code = errno.errorcode.get(why.args[0], why.args[0])
                msg = "couldn't chdir to %s: %s\n" % (cwd, code)
                options.write(2, "supervisor: " + msg)
                return # finally clause will exit the child process

            # set umask, then execve
            try:
                if self.config.umask is not None:
                    options.setumask(self.config.umask)
                options.execve(filename, argv, env)
            except OSError as why:
                code = errno.errorcode.get(why.args[0], why.args[0])
                msg = "couldn't exec %s: %s\n" % (argv[0], code)
                options.write(2, "supervisor: " + msg)
            except:
                (file, fun, line), t,v,tbinfo = asyncore.compact_traceback()
                error = '%s, %s: file: %s line: %s' % (t, v, file, line)
                msg = "couldn't exec %s: %s\n" % (filename, error)
                options.write(2, "supervisor: " + msg)

            # this point should only be reached if execve failed.
            # the finally clause will exit the child process.

        finally:
            options.write(2, "supervisor: child process was not spawned\n")
            options._exit(127) # exit process with code for spawn failure

    def stop(self):
        """ Administrative stop """
        self.administrative_stop = 1
        return self.kill(self.config.stopsignal)

    def give_up(self):
        self.delay = 0
        self.backoff = 0
        self.system_stop = 1
        self._assertInState(ProcessStates.BACKOFF)
        self.change_state(ProcessStates.FATAL)

    def kill(self, sig):
        """Send a signal to the subprocess.  This may or may not kill it.

        Return None if the signal was sent, or an error message string
        if an error occurred or if the subprocess is not running.
        """
        now = time.time()
        options = self.config.options

        # Properly stop processes in BACKOFF state.
        if self.state == ProcessStates.BACKOFF:
            msg = ("Attempted to kill %s, which is in BACKOFF state." %
                   (self.config.name))
            options.logger.debug(msg)
            self.change_state(ProcessStates.STOPPED)
            return None

        if not self.pid:
            msg = ("attempted to kill %s with sig %s but it wasn't running" %
                   (self.config.name, signame(sig)))
            options.logger.debug(msg)
            return msg

        #If we're in the stopping state, then we've already sent the stop
        #signal and this is the kill signal
        if self.state == ProcessStates.STOPPING:
            killasgroup = self.config.killasgroup
        else:
            killasgroup = self.config.stopasgroup

        as_group = ""
        if killasgroup:
            as_group = "process group "

        options.logger.debug('killing %s (pid %s) %swith signal %s'
                             % (self.config.name,
                                self.pid,
                                as_group,
                                signame(sig))
                             )

        # RUNNING/STARTING/STOPPING -> STOPPING
        self.killing = 1
        self.delay = now + self.config.stopwaitsecs
        # we will already be in the STOPPING state if we're doing a
        # SIGKILL as a result of overrunning stopwaitsecs
        self._assertInState(ProcessStates.RUNNING,ProcessStates.STARTING,
                            ProcessStates.STOPPING)
        self.change_state(ProcessStates.STOPPING)

        pid = self.pid
        if killasgroup:
            # send to the whole process group instead
            pid = -self.pid

        try:
            options.kill(pid, sig)
        except:
            io = StringIO()
            traceback.print_exc(file=io)
            tb = io.getvalue()
            msg = 'unknown problem killing %s (%s):%s' % (self.config.name,
                                                          self.pid, tb)
            options.logger.critical(msg)
            self.change_state(ProcessStates.UNKNOWN)
            self.pid = 0
            self.killing = 0
            self.delay = 0
            return msg

        return None

    def finish(self, pid, sts):
        """ The process was reaped and we need to report and manage its state
        """
        self.drain()

        es, msg = decode_wait_status(sts)

        now = time.time()
        self.laststop = now
        processname = self.config.name

        tooquickly = now - self.laststart < self.config.startsecs
        exit_expected = es in self.config.exitcodes

        if self.killing:
            # likely the result of a stop request
            # implies STOPPING -> STOPPED
            self.killing = 0
            self.delay = 0
            self.exitstatus = es

            msg = "stopped: %s (%s)" % (processname, msg)
            self._assertInState(ProcessStates.STOPPING)
            self.change_state(ProcessStates.STOPPED)

        elif tooquickly:
            # the program did not stay up long enough to make it to RUNNING
            # implies STARTING -> BACKOFF
            self.exitstatus = None
            self.spawnerr = 'Exited too quickly (process log may have details)'
            msg = "exited: %s (%s)" % (processname, msg + "; not expected")
            self._assertInState(ProcessStates.STARTING)
            self.change_state(ProcessStates.BACKOFF)

        else:
            # this finish was not the result of a stop request, the
            # program was in the RUNNING state but exited implies
            # RUNNING -> EXITED
            self.delay = 0
            self.backoff = 0
            self.exitstatus = es

            if self.state == ProcessStates.STARTING:
                # XXX I don't know under which circumstances this
                # happens, but in the wild, there is a transition that
                # subverts the RUNNING state (directly from STARTING
                # to EXITED), so we perform the correct transition
                # here.
                self.change_state(ProcessStates.RUNNING)

            self._assertInState(ProcessStates.RUNNING)

            if exit_expected:
                # expected exit code
                msg = "exited: %s (%s)" % (processname, msg + "; expected")
                self.change_state(ProcessStates.EXITED, expected=True)
            else:
                # unexpected exit code
                self.spawnerr = 'Bad exit code %s' % es
                msg = "exited: %s (%s)" % (processname, msg + "; not expected")
                self.change_state(ProcessStates.EXITED, expected=False)

        self.config.options.logger.info(msg)

        self.pid = 0
        self.config.options.close_parent_pipes(self.pipes)
        self.pipes = {}
        self.dispatchers = {}

        # if we died before we processed the current event (only happens
        # if we're an event listener), notify the event system that this
        # event was rejected so it can be processed again.
        if self.event is not None:
            # Note: this should only be true if we were in the BUSY
            # state when finish() was called.
            events.notify(events.EventRejectedEvent(self, self.event))
            self.event = None

    def set_uid(self):
        if self.config.uid is None:
            return
        msg = self.config.options.dropPrivileges(self.config.uid)
        return msg

    def __lt__(self, other):
        return self.config.priority < other.config.priority

    def __eq__(self, other):
        # sort by priority
        return self.config.priority == other.config.priority

    def __repr__(self):
        return '<Subprocess at %s with name %s in state %s>' % (
            id(self),
            self.config.name,
            getProcessStateDescription(self.get_state()))

    def get_state(self):
        return self.state

    def transition(self):
        now = time.time()
        state = self.state

        logger = self.config.options.logger

        if self.config.options.mood > SupervisorStates.RESTARTING:
            # dont start any processes if supervisor is shutting down
            if state == ProcessStates.EXITED:
                if self.config.autorestart:
                    if self.config.autorestart is RestartUnconditionally:
                        # EXITED -> STARTING
                        self.spawn()
                    else: # autorestart is RestartWhenExitUnexpected
                        if self.exitstatus not in self.config.exitcodes:
                            # EXITED -> STARTING
                            self.spawn()
            elif state == ProcessStates.STOPPED and not self.laststart:
                if self.config.autostart:
                    # STOPPED -> STARTING
                    self.spawn()
            elif state == ProcessStates.BACKOFF:
                if self.backoff <= self.config.startretries:
                    if now > self.delay:
                        # BACKOFF -> STARTING
                        self.spawn()

        if state == ProcessStates.STARTING:
            if now - self.laststart > self.config.startsecs:
                # STARTING -> RUNNING if the proc has started
                # successfully and it has stayed up for at least
                # proc.config.startsecs,
                self.delay = 0
                self.backoff = 0
                self._assertInState(ProcessStates.STARTING)
                self.change_state(ProcessStates.RUNNING)
                msg = (
                    'entered RUNNING state, process has stayed up for '
                    '> than %s seconds (startsecs)' % self.config.startsecs)
                logger.info('success: %s %s' % (self.config.name, msg))

        if state == ProcessStates.BACKOFF:
            if self.backoff > self.config.startretries:
                # BACKOFF -> FATAL if the proc has exceeded its number
                # of retries
                self.give_up()
                msg = ('entered FATAL state, too many start retries too '
                       'quickly')
                logger.info('gave up: %s %s' % (self.config.name, msg))

        elif state == ProcessStates.STOPPING:
            time_left = self.delay - now
            if time_left <= 0:
                # kill processes which are taking too long to stop with a final
                # sigkill.  if this doesn't kill it, the process will be stuck
                # in the STOPPING state forever.
                self.config.options.logger.warn(
                    'killing %r (%s) with SIGKILL' % (self.config.name,
                                                      self.pid))
                self.kill(signal.SIGKILL)

Subprocess = total_ordering(Subprocess)

class FastCGISubprocess(Subprocess):
    """Extends Subprocess class to handle FastCGI subprocesses"""

    def __init__(self, config):
        Subprocess.__init__(self, config)
        self.fcgi_sock = None

    def before_spawn(self):
        """
        The FastCGI socket needs to be created by the parent before we fork
        """
        if self.group is None:
            raise NotImplementedError('No group set for FastCGISubprocess')
        if not hasattr(self.group, 'socket_manager'):
            raise NotImplementedError('No SocketManager set for '
                                      '%s:%s' % (self.group, dir(self.group)))
        self.fcgi_sock = self.group.socket_manager.get_socket()

    def spawn(self):
        """
        Overrides Subprocess.spawn() so we can hook in before it happens
        """
        self.before_spawn()
        pid = Subprocess.spawn(self)
        if pid is None:
            #Remove object reference to decrement the reference count on error
            self.fcgi_sock = None
        return pid

    def after_finish(self):
        """
        Releases reference to FastCGI socket when process is reaped
        """
        #Remove object reference to decrement the reference count
        self.fcgi_sock = None

    def finish(self, pid, sts):
        """
        Overrides Subprocess.finish() so we can hook in after it happens
        """
        retval = Subprocess.finish(self, pid, sts)
        self.after_finish()
        return retval

    def _prepare_child_fds(self):
        """
        Overrides Subprocess._prepare_child_fds()
        The FastCGI socket needs to be set to file descriptor 0 in the child
        """
        sock_fd = self.fcgi_sock.fileno()

        options = self.config.options
        options.dup2(sock_fd, 0)
        options.dup2(self.pipes['child_stdout'], 1)
        if self.config.redirect_stderr:
            options.dup2(self.pipes['child_stdout'], 2)
        else:
            options.dup2(self.pipes['child_stderr'], 2)
        for i in range(3, options.minfds):
            options.close_fd(i)

class ProcessGroupBase(object):
    def __init__(self, config):
        self.config = config
        self.processes = {}
        for pconfig in self.config.process_configs:
            self.processes[pconfig.name] = pconfig.make_process(self)


    def __lt__(self, other):
        return self.config.priority < other.config.priority

    def __eq__(self, other):
        return self.config.priority == other.config.priority

    def __repr__(self):
        return '<%s instance at %s named %s>' % (self.__class__, id(self),
                                                 self.config.name)

    def removelogs(self):
        for process in self.processes.values():
            process.removelogs()

    def reopenlogs(self):
        for process in self.processes.values():
            process.reopenlogs()

    def stop_all(self):
        processes = list(self.processes.values())
        processes.sort()
        processes.reverse() # stop in desc priority order

        for proc in processes:
            state = proc.get_state()
            if state == ProcessStates.RUNNING:
                # RUNNING -> STOPPING
                proc.stop()
            elif state == ProcessStates.STARTING:
                # STARTING -> STOPPING
                proc.stop()
            elif state == ProcessStates.BACKOFF:
                # BACKOFF -> FATAL
                proc.give_up()

    def get_unstopped_processes(self):
        """ Processes which aren't in a state that is considered 'stopped' """
        return [ x for x in self.processes.values() if x.get_state() not in
                 STOPPED_STATES ]

    def get_dispatchers(self):
        dispatchers = {}
        for process in self.processes.values():
            dispatchers.update(process.dispatchers)
        return dispatchers

ProcessGroupBase = total_ordering(ProcessGroupBase)

class ProcessGroup(ProcessGroupBase):
    def transition(self):
        for proc in self.processes.values():
            proc.transition()

class FastCGIProcessGroup(ProcessGroup):

    def __init__(self, config, **kwargs):
        ProcessGroup.__init__(self, config)
        sockManagerKlass = kwargs.get('socketManager', SocketManager)
        self.socket_manager = sockManagerKlass(config.socket_config,
                                               logger=config.options.logger)
        # It's not required to call get_socket() here but we want
        # to fail early during start up if there is a config error
        try:
            self.socket_manager.get_socket()
        except Exception as e:
            raise ValueError('Could not create FastCGI socket %s: %s' % (self.socket_manager.config(), e))

class EventListenerPool(ProcessGroupBase):
    def __init__(self, config):
        ProcessGroupBase.__init__(self, config)
        self.event_buffer = []
        for event_type in self.config.pool_events:
            events.subscribe(event_type, self._acceptEvent)
        events.subscribe(events.EventRejectedEvent, self.handle_rejected)
        self.serial = -1
        self.last_dispatch = 0
        self.dispatch_throttle = 0 # in seconds: .00195 is an interesting one

    def handle_rejected(self, event):
        process = event.process
        procs = self.processes.values()
        if process in procs: # this is one of our processes
            # rebuffer the event
            self._acceptEvent(event.event, head=True)

    def transition(self):
        processes = self.processes.values()
        dispatch_capable = False
        for process in processes:
            process.transition()
            # this is redundant, we do it in _dispatchEvent too, but we
            # want to reduce function call overhead
            if process.state == ProcessStates.RUNNING:
                if process.listener_state == EventListenerStates.READY:
                    dispatch_capable = True
        if dispatch_capable:
            if self.dispatch_throttle:
                now = time.time()
                if now - self.last_dispatch < self.dispatch_throttle:
                    return
            self.dispatch()

    def dispatch(self):
        while self.event_buffer:
            # dispatch the oldest event
            event = self.event_buffer.pop(0)
            ok = self._dispatchEvent(event)
            if not ok:
                # if we can't dispatch an event, rebuffer it and stop trying
                # to process any further events in the buffer
                self._acceptEvent(event, head=True)
                break
        self.last_dispatch = time.time()

    def _acceptEvent(self, event, head=False):
        # events are required to be instances
        # this has a side effect to fail with an attribute error on 'old style' classes
        if not hasattr(event, 'serial'):
            event.serial = new_serial(GlobalSerial)
        if not hasattr(event, 'pool_serials'):
            event.pool_serials = {}
        if self.config.name not in event.pool_serials:
            event.pool_serials[self.config.name] = new_serial(self)
        else:
            self.config.options.logger.debug(
                'rebuffering event %s for pool %s (bufsize %s)' % (
                (event.serial, self.config.name, len(self.event_buffer))))

        if len(self.event_buffer) >= self.config.buffer_size:
            if self.event_buffer:
                # discard the oldest event
                discarded_event = self.event_buffer.pop(0)
                self.config.options.logger.error(
                    'pool %s event buffer overflowed, discarding event %s' % (
                    (self.config.name, discarded_event.serial)))
        if head:
            self.event_buffer.insert(0, event)
        else:
            self.event_buffer.append(event)

    def _dispatchEvent(self, event):
        pool_serial = event.pool_serials[self.config.name]

        for process in self.processes.values():
            if process.state != ProcessStates.RUNNING:
                continue
            if process.listener_state == EventListenerStates.READY:
                payload = str(event)
                try:
                    event_type = event.__class__
                    serial = event.serial
                    envelope = self._eventEnvelope(event_type, serial,
                                                   pool_serial, payload)
                    process.write(envelope)
                except OSError as why:
                    if why.args[0] != errno.EPIPE:
                        raise
                    continue

                process.listener_state = EventListenerStates.BUSY
                process.event = event
                self.config.options.logger.debug(
                    'event %s sent to listener %s' % (
                    event.serial, process.config.name))
                return True

        return False

    def _eventEnvelope(self, event_type, serial, pool_serial, payload):
        event_name = events.getEventNameByType(event_type)
        payload_len = len(payload)
        D = {
            'ver':'3.0',
            'sid':self.config.options.identifier,
            'serial':serial,
            'pool_name':self.config.name,
            'pool_serial':pool_serial,
            'event_name':event_name,
            'len':payload_len,
            'payload':payload,
             }
        return ('ver:%(ver)s server:%(sid)s serial:%(serial)s '
                'pool:%(pool_name)s poolserial:%(pool_serial)s '
                'eventname:%(event_name)s len:%(len)s\n%(payload)s' % D)

class GlobalSerial(object):
    def __init__(self):
        self.serial = -1

GlobalSerial = GlobalSerial() # singleton

def new_serial(inst):
    if inst.serial == maxint:
        inst.serial = -1
    inst.serial += 1
    return inst.serial




########NEW FILE########
__FILENAME__ = rpcinterface
import os
import time
import datetime
import errno

from supervisor.compat import as_string
from supervisor.compat import unicode
from supervisor.compat import basestring

from supervisor.options import readFile
from supervisor.options import tailFile
from supervisor.options import NotExecutable
from supervisor.options import NotFound
from supervisor.options import NoPermission
from supervisor.options import make_namespec
from supervisor.options import split_namespec
from supervisor.options import VERSION

from supervisor.events import notify
from supervisor.events import RemoteCommunicationEvent

from supervisor.http import NOT_DONE_YET
from supervisor.xmlrpc import Faults
from supervisor.xmlrpc import RPCError

from supervisor.states import SupervisorStates
from supervisor.states import getSupervisorStateDescription
from supervisor.states import ProcessStates
from supervisor.states import getProcessStateDescription
from supervisor.states import RUNNING_STATES

API_VERSION  = '3.0'

class SupervisorNamespaceRPCInterface:
    def __init__(self, supervisord):
        self.supervisord = supervisord

    def _update(self, text):
        self.update_text = text # for unit tests, mainly
        if isinstance(self.supervisord.options.mood, int) and self.supervisord.options.mood < SupervisorStates.RUNNING:
            raise RPCError(Faults.SHUTDOWN_STATE)

    # RPC API methods

    def getAPIVersion(self):
        """ Return the version of the RPC API used by supervisord

        @return string version version id
        """
        self._update('getAPIVersion')
        return API_VERSION

    getVersion = getAPIVersion # b/w compatibility with releases before 3.0

    def getSupervisorVersion(self):
        """ Return the version of the supervisor package in use by supervisord

        @return string version version id
        """
        self._update('getSupervisorVersion')
        return VERSION

    def getIdentification(self):
        """ Return identifying string of supervisord

        @return string identifier identifying string
        """
        self._update('getIdentification')
        return self.supervisord.options.identifier

    def getState(self):
        """ Return current state of supervisord as a struct

        @return struct A struct with keys int statecode, string statename
        """
        self._update('getState')

        state = self.supervisord.options.mood
        statename = getSupervisorStateDescription(state)
        data =  {
            'statecode':state,
            'statename':statename,
            }
        return data

    def getPID(self):
        """ Return the PID of supervisord

        @return int PID
        """
        self._update('getPID')
        return self.supervisord.options.get_pid()

    def readLog(self, offset, length):
        """ Read length bytes from the main log starting at offset

        @param int offset         offset to start reading from.
        @param int length         number of bytes to read from the log.
        @return string result     Bytes of log
        """
        self._update('readLog')

        logfile = self.supervisord.options.logfile

        if logfile is None or not os.path.exists(logfile):
            raise RPCError(Faults.NO_FILE, logfile)

        try:
            return as_string(readFile(logfile, int(offset), int(length)))
        except ValueError as inst:
            why = inst.args[0]
            raise RPCError(getattr(Faults, why))

    readMainLog = readLog # b/w compatibility with releases before 2.1

    def clearLog(self):
        """ Clear the main log.

        @return boolean result always returns True unless error
        """
        self._update('clearLog')

        logfile = self.supervisord.options.logfile
        if logfile is None or not self.supervisord.options.exists(logfile):
            raise RPCError(Faults.NO_FILE)

        # there is a race condition here, but ignore it.
        try:
            self.supervisord.options.remove(logfile)
        except (OSError, IOError):
            raise RPCError(Faults.FAILED)

        for handler in self.supervisord.options.logger.handlers:
            if hasattr(handler, 'reopen'):
                self.supervisord.options.logger.info('reopening log file')
                handler.reopen()
        return True

    def shutdown(self):
        """ Shut down the supervisor process

        @return boolean result always returns True unless error
        """
        self._update('shutdown')
        self.supervisord.options.mood = SupervisorStates.SHUTDOWN
        return True

    def restart(self):
        """ Restart the supervisor process

        @return boolean result  always return True unless error
        """
        self._update('restart')

        self.supervisord.options.mood = SupervisorStates.RESTARTING
        return True

    def reloadConfig(self):
        """
        Reload configuration

        @return boolean result  always return True unless error
        """
        self._update('reloadConfig')
        try:
            self.supervisord.options.process_config(do_usage=False)
        except ValueError as msg:
            raise RPCError(Faults.CANT_REREAD, msg)

        added, changed, removed = self.supervisord.diff_to_active()

        added = [group.name for group in added]
        changed = [group.name for group in changed]
        removed = [group.name for group in removed]
        return [[added, changed, removed]] # cannot return len > 1, apparently

    def addProcessGroup(self, name):
        """ Update the config for a running process from config file.

        @param string name         name of process group to add
        @return boolean result     true if successful
        """
        self._update('addProcessGroup')

        for config in self.supervisord.options.process_group_configs:
            if config.name == name:
                result = self.supervisord.add_process_group(config)
                if not result:
                    raise RPCError(Faults.ALREADY_ADDED, name)
                return True
        raise RPCError(Faults.BAD_NAME, name)

    def removeProcessGroup(self, name):
        """ Remove a stopped process from the active configuration.

        @param string name         name of process group to remove
        @return boolean result     Indicates whether the removal was successful
        """
        self._update('removeProcessGroup')
        if name not in self.supervisord.process_groups:
            raise RPCError(Faults.BAD_NAME, name)

        result = self.supervisord.remove_process_group(name)
        if not result:
            raise RPCError(Faults.STILL_RUNNING)
        return True

    def _getAllProcesses(self, lexical=False):
        # if lexical is true, return processes sorted in lexical order,
        # otherwise, sort in priority order
        all_processes = []

        if lexical:
            group_names = list(self.supervisord.process_groups.keys())
            group_names.sort()
            for group_name in group_names:
                group = self.supervisord.process_groups[group_name]
                process_names = list(group.processes.keys())
                process_names.sort()
                for process_name in process_names:
                    process = group.processes[process_name]
                    all_processes.append((group, process))
        else:
            groups = list(self.supervisord.process_groups.values())
            groups.sort() # asc by priority

            for group in groups:
                processes = list(group.processes.values())
                processes.sort() # asc by priority
                for process in processes:
                    all_processes.append((group, process))

        return all_processes

    def _getGroupAndProcess(self, name):
        # get process to start from name
        group_name, process_name = split_namespec(name)

        group = self.supervisord.process_groups.get(group_name)
        if group is None:
            raise RPCError(Faults.BAD_NAME, name)

        if process_name is None:
            return group, None

        process = group.processes.get(process_name)
        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        return group, process

    def startProcess(self, name, wait=True):
        """ Start a process

        @param string name Process name (or ``group:name``, or ``group:*``)
        @param boolean wait Wait for process to be fully started
        @return boolean result     Always true unless error

        """
        self._update('startProcess')
        group, process = self._getGroupAndProcess(name)
        if process is None:
            group_name, process_name = split_namespec(name)
            return self.startProcessGroup(group_name, wait)

        # test filespec, don't bother trying to spawn if we know it will
        # eventually fail
        try:
            filename, argv = process.get_execv_args()
        except NotFound as why:
            raise RPCError(Faults.NO_FILE, why.args[0])
        except (NotExecutable, NoPermission) as why:
            raise RPCError(Faults.NOT_EXECUTABLE, why.args[0])

        started = []

        startsecs = process.config.startsecs

        def startit():
            if not started:

                if process.get_state() in RUNNING_STATES:
                    raise RPCError(Faults.ALREADY_STARTED, name)

                process.spawn()

                if process.spawnerr:
                    raise RPCError(Faults.SPAWN_ERROR, name)

                # we use a list here to fake out lexical scoping;
                # using a direct assignment to 'started' in the
                # function appears to not work (symptom: 2nd or 3rd
                # call through, it forgets about 'started', claiming
                # it's undeclared).
                started.append(time.time())

            if not wait or not startsecs:
                return True

            t = time.time()
            runtime = (t - started[0])
            state = process.get_state()

            if state not in (ProcessStates.STARTING, ProcessStates.RUNNING):
                raise RPCError(Faults.ABNORMAL_TERMINATION, name)

            if runtime < startsecs:
                return NOT_DONE_YET

            if state == ProcessStates.RUNNING:
                return True

            raise RPCError(Faults.ABNORMAL_TERMINATION, name)

        startit.delay = 0.05
        startit.rpcinterface = self
        return startit # deferred

    def startProcessGroup(self, name, wait=True):
        """ Start all processes in the group named 'name'

        @param string name     The group name
        @param boolean wait    Wait for each process to be fully started
        @return array result   An array of process status info structs
        """
        self._update('startProcessGroup')

        group = self.supervisord.process_groups.get(name)

        if group is None:
            raise RPCError(Faults.BAD_NAME, name)

        processes = list(group.processes.values())
        processes.sort()
        processes = [ (group, process) for process in processes ]

        startall = make_allfunc(processes, isNotRunning, self.startProcess,
                                wait=wait)

        startall.delay = 0.05
        startall.rpcinterface = self
        return startall # deferred

    def startAllProcesses(self, wait=True):
        """ Start all processes listed in the configuration file

        @param boolean wait    Wait for each process to be fully started
        @return array result   An array of process status info structs
        """
        self._update('startAllProcesses')

        processes = self._getAllProcesses()
        startall = make_allfunc(processes, isNotRunning, self.startProcess,
                                wait=wait)

        startall.delay = 0.05
        startall.rpcinterface = self
        return startall # deferred

    def stopProcess(self, name, wait=True):
        """ Stop a process named by name

        @param string name  The name of the process to stop (or 'group:name')
        @param boolean wait        Wait for the process to be fully stopped
        @return boolean result     Always return True unless error
        """
        self._update('stopProcess')

        group, process = self._getGroupAndProcess(name)

        if process is None:
            group_name, process_name = split_namespec(name)
            return self.stopProcessGroup(group_name, wait)

        stopped = []
        called  = []

        def killit():
            if not called:
                if process.get_state() not in RUNNING_STATES:
                    raise RPCError(Faults.NOT_RUNNING)
                # use a mutable for lexical scoping; see startProcess
                called.append(1)

            if not stopped:
                msg = process.stop()
                if msg is not None:
                    raise RPCError(Faults.FAILED, msg)
                stopped.append(1)

                if wait:
                    return NOT_DONE_YET
                else:
                    return True

            if process.get_state() not in (ProcessStates.STOPPED,
                                           ProcessStates.EXITED):
                return NOT_DONE_YET
            else:
                return True

        killit.delay = 0.2
        killit.rpcinterface = self
        return killit # deferred

    def stopProcessGroup(self, name, wait=True):
        """ Stop all processes in the process group named 'name'

        @param string name     The group name
        @param boolean wait    Wait for each process to be fully stopped
        @return array result   An array of process status info structs
        """
        self._update('stopProcessGroup')

        group = self.supervisord.process_groups.get(name)

        if group is None:
            raise RPCError(Faults.BAD_NAME, name)

        processes = list(group.processes.values())
        processes.sort()
        processes = [ (group, process) for process in processes ]

        killall = make_allfunc(processes, isRunning, self.stopProcess,
                               wait=wait)

        killall.delay = 0.05
        killall.rpcinterface = self
        return killall # deferred

    def stopAllProcesses(self, wait=True):
        """ Stop all processes in the process list

        @param  boolean wait   Wait for each process to be fully stopped
        @return array result   An array of process status info structs
        """
        self._update('stopAllProcesses')

        processes = self._getAllProcesses()

        killall = make_allfunc(processes, isRunning, self.stopProcess,
                               wait=wait)

        killall.delay = 0.05
        killall.rpcinterface = self
        return killall # deferred

    def getAllConfigInfo(self):
        """ Get info about all available process configurations. Each struct
        represents a single process (i.e. groups get flattened).

        @return array result  An array of process config info structs
        """
        self._update('getAllConfigInfo')

        configinfo = []
        for gconfig in self.supervisord.options.process_group_configs:
            inuse = gconfig.name in self.supervisord.process_groups
            for pconfig in gconfig.process_configs:
                configinfo.append(
                    { 'name': pconfig.name,
                      'group': gconfig.name,
                      'inuse': inuse,
                      'autostart': pconfig.autostart,
                      'group_prio': gconfig.priority,
                      'process_prio': pconfig.priority })

        configinfo.sort(key=lambda r: r['name'])
        return configinfo

    def _interpretProcessInfo(self, info):
        state = info['state']

        if state == ProcessStates.RUNNING:
            start = info['start']
            now = info['now']
            start_dt = datetime.datetime(*time.gmtime(start)[:6])
            now_dt = datetime.datetime(*time.gmtime(now)[:6])
            uptime = now_dt - start_dt
            desc = 'pid %s, uptime %s' % (info['pid'], uptime)

        elif state in (ProcessStates.FATAL, ProcessStates.BACKOFF):
            desc = info['spawnerr']
            if not desc:
                desc = 'unknown error (try "tail %s")' % info['name']

        elif state in (ProcessStates.STOPPED, ProcessStates.EXITED):
            if info['start']:
                stop = info['stop']
                stop_dt = datetime.datetime(*time.localtime(stop)[:7])
                desc = stop_dt.strftime('%b %d %I:%M %p')
            else:
                desc = 'Not started'

        else:
            desc = ''

        return desc

    def getProcessInfo(self, name):
        """ Get info about a process named name

        @param string name The name of the process (or 'group:name')
        @return struct result     A structure containing data about the process
        """
        self._update('getProcessInfo')

        group, process = self._getGroupAndProcess(name)

        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        start = int(process.laststart)
        stop = int(process.laststop)
        now = int(time.time())

        state = process.get_state()
        spawnerr = process.spawnerr or ''
        exitstatus = process.exitstatus or 0
        stdout_logfile = process.config.stdout_logfile or ''
        stderr_logfile = process.config.stderr_logfile or ''

        info = {
            'name':process.config.name,
            'group':group.config.name,
            'start':start,
            'stop':stop,
            'now':now,
            'state':state,
            'statename':getProcessStateDescription(state),
            'spawnerr':spawnerr,
            'exitstatus':exitstatus,
            'logfile':stdout_logfile, # b/c alias
            'stdout_logfile':stdout_logfile,
            'stderr_logfile':stderr_logfile,
            'pid':process.pid,
            }

        description = self._interpretProcessInfo(info)
        info['description'] = description
        return info

    def getAllProcessInfo(self):
        """ Get info about all processes

        @return array result  An array of process status results
        """
        self._update('getAllProcessInfo')

        all_processes = self._getAllProcesses(lexical=True)

        output = []
        for group, process in all_processes:
            name = make_namespec(group.config.name, process.config.name)
            output.append(self.getProcessInfo(name))
        return output

    def _readProcessLog(self, name, offset, length, channel):
        group, process = self._getGroupAndProcess(name)

        logfile = getattr(process.config, '%s_logfile' % channel)

        if logfile is None or not os.path.exists(logfile):
            raise RPCError(Faults.NO_FILE, logfile)

        try:
            return as_string(readFile(logfile, int(offset), int(length)))
        except ValueError as inst:
            why = inst.args[0]
            raise RPCError(getattr(Faults, why))

    def readProcessStdoutLog(self, name, offset, length):
        """ Read length bytes from name's stdout log starting at offset

        @param string name        the name of the process (or 'group:name')
        @param int offset         offset to start reading from.
        @param int length         number of bytes to read from the log.
        @return string result     Bytes of log
        """
        self._update('readProcessStdoutLog')
        return self._readProcessLog(name, offset, length, 'stdout')

    readProcessLog = readProcessStdoutLog # b/c alias

    def readProcessStderrLog(self, name, offset, length):
        """ Read length bytes from name's stderr log starting at offset

        @param string name        the name of the process (or 'group:name')
        @param int offset         offset to start reading from.
        @param int length         number of bytes to read from the log.
        @return string result     Bytes of log
        """
        self._update('readProcessStderrLog')
        return self._readProcessLog(name, offset, length, 'stderr')

    def _tailProcessLog(self, name, offset, length, channel):
        group, process = self._getGroupAndProcess(name)

        logfile = getattr(process.config, '%s_logfile' % channel)

        if logfile is None or not os.path.exists(logfile):
            return ['', 0, False]

        return tailFile(logfile, int(offset), int(length))

    def tailProcessStdoutLog(self, name, offset, length):
        """
        Provides a more efficient way to tail the (stdout) log than
        readProcessStdoutLog().  Use readProcessStdoutLog() to read
        chunks and tailProcessStdoutLog() to tail.

        Requests (length) bytes from the (name)'s log, starting at
        (offset).  If the total log size is greater than (offset +
        length), the overflow flag is set and the (offset) is
        automatically increased to position the buffer at the end of
        the log.  If less than (length) bytes are available, the
        maximum number of available bytes will be returned.  (offset)
        returned is always the last offset in the log +1.

        @param string name         the name of the process (or 'group:name')
        @param int offset          offset to start reading from
        @param int length          maximum number of bytes to return
        @return array result       [string bytes, int offset, bool overflow]
        """
        self._update('tailProcessStdoutLog')
        return self._tailProcessLog(name, offset, length, 'stdout')

    tailProcessLog = tailProcessStdoutLog # b/c alias

    def tailProcessStderrLog(self, name, offset, length):
        """
        Provides a more efficient way to tail the (stderr) log than
        readProcessStderrLog().  Use readProcessStderrLog() to read
        chunks and tailProcessStderrLog() to tail.

        Requests (length) bytes from the (name)'s log, starting at
        (offset).  If the total log size is greater than (offset +
        length), the overflow flag is set and the (offset) is
        automatically increased to position the buffer at the end of
        the log.  If less than (length) bytes are available, the
        maximum number of available bytes will be returned.  (offset)
        returned is always the last offset in the log +1.

        @param string name         the name of the process (or 'group:name')
        @param int offset          offset to start reading from
        @param int length          maximum number of bytes to return
        @return array result       [string bytes, int offset, bool overflow]
        """
        self._update('tailProcessStderrLog')
        return self._tailProcessLog(name, offset, length, 'stderr')

    def clearProcessLogs(self, name):
        """ Clear the stdout and stderr logs for the named process and
        reopen them.

        @param string name   The name of the process (or 'group:name')
        @return boolean result      Always True unless error
        """
        self._update('clearProcessLogs')

        group, process = self._getGroupAndProcess(name)

        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        try:
            # implies a reopen
            process.removelogs()
        except (IOError, OSError):
            raise RPCError(Faults.FAILED, name)

        return True

    clearProcessLog = clearProcessLogs # b/c alias

    def clearAllProcessLogs(self):
        """ Clear all process log files

        @return array result   An array of process status info structs
        """
        self._update('clearAllProcessLogs')
        results  = []
        callbacks = []

        all_processes = self._getAllProcesses()

        for group, process in all_processes:
            callbacks.append((group, process, self.clearProcessLog))

        def clearall():
            if not callbacks:
                return results

            group, process, callback = callbacks.pop(0)
            name = make_namespec(group.config.name, process.config.name)
            try:
                callback(name)
            except RPCError as e:
                results.append(
                    {'name':process.config.name,
                     'group':group.config.name,
                     'status':e.code,
                     'description':e.text})
            else:
                results.append(
                    {'name':process.config.name,
                     'group':group.config.name,
                     'status':Faults.SUCCESS,
                     'description':'OK'}
                    )

            if callbacks:
                return NOT_DONE_YET

            return results

        clearall.delay = 0.05
        clearall.rpcinterface = self
        return clearall # deferred

    def sendProcessStdin(self, name, chars):
        """ Send a string of chars to the stdin of the process name.
        If non-7-bit data is sent (unicode), it is encoded to utf-8
        before being sent to the process' stdin.  If chars is not a
        string or is not unicode, raise INCORRECT_PARAMETERS.  If the
        process is not running, raise NOT_RUNNING.  If the process'
        stdin cannot accept input (e.g. it was closed by the child
        process), raise NO_FILE.

        @param string name        The process name to send to (or 'group:name')
        @param string chars       The character data to send to the process
        @return boolean result    Always return True unless error
        """
        self._update('sendProcessStdin')

        if isinstance(chars, unicode):
            chars = chars.encode('utf-8')

        if not isinstance(chars, basestring):
            raise RPCError(Faults.INCORRECT_PARAMETERS, chars)

        group, process = self._getGroupAndProcess(name)

        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        if not process.pid or process.killing:
            raise RPCError(Faults.NOT_RUNNING, name)

        try:
            process.write(chars)
        except OSError as why:
            if why.args[0] == errno.EPIPE:
                raise RPCError(Faults.NO_FILE, name)
            else:
                raise

        return True

    def sendRemoteCommEvent(self, type, data):
        """ Send an event that will be received by event listener
        subprocesses subscribing to the RemoteCommunicationEvent.

        @param  string  type  String for the "type" key in the event header
        @param  string  data  Data for the event body
        @return boolean       Always return True unless error
        """
        if isinstance(type, unicode):
            type = type.encode('utf-8')
        if isinstance(data, unicode):
            data = data.encode('utf-8')

        notify(
            RemoteCommunicationEvent(type, data)
        )

        return True

def make_allfunc(processes, predicate, func, **extra_kwargs):
    """ Return a closure representing a function that calls a
    function for every process, and returns a result """

    callbacks = []
    results = []

    def allfunc(processes=processes, predicate=predicate, func=func,
                extra_kwargs=extra_kwargs, callbacks=callbacks,
                results=results):
        if not callbacks:

            for group, process in processes:
                name = make_namespec(group.config.name, process.config.name)
                if predicate(process):
                    try:
                        callback = func(name, **extra_kwargs)
                        callbacks.append((group, process, callback))
                    except RPCError as e:
                        results.append({'name':process.config.name,
                                        'group':group.config.name,
                                        'status':e.code,
                                        'description':e.text})
                        continue

        if not callbacks:
            return results

        group, process, callback = callbacks.pop(0)

        try:
            value = callback()
        except RPCError as e:
            results.append(
                {'name':process.config.name,
                 'group':group.config.name,
                 'status':e.code,
                 'description':e.text})
            return NOT_DONE_YET

        if value is NOT_DONE_YET:
            # push it back into the queue; it will finish eventually
            callbacks.append((group, process, callback))
        else:
            results.append(
                {'name':process.config.name,
                 'group':group.config.name,
                 'status':Faults.SUCCESS,
                 'description':'OK'}
                )

        if callbacks:
            return NOT_DONE_YET

        return results

    # XXX the above implementation has a weakness inasmuch as the
    # first call into each individual process callback will always
    # return NOT_DONE_YET, so they need to be called twice.  The
    # symptom of this is that calling this method causes the
    # client to block for much longer than it actually requires to
    # kill all of the running processes.  After the first call to
    # the killit callback, the process is actually dead, but the
    # above killall method processes the callbacks one at a time
    # during the select loop, which, because there is no output
    # from child processes after e.g. stopAllProcesses is called,
    # is not busy, so hits the timeout for each callback.  I
    # attempted to make this better, but the only way to make it
    # better assumes totally synchronous reaping of child
    # processes, which requires infrastructure changes to
    # supervisord that are scary at the moment as it could take a
    # while to pin down all of the platform differences and might
    # require a C extension to the Python signal module to allow
    # the setting of ignore flags to signals.
    return allfunc

def isRunning(process):
    if process.get_state() in RUNNING_STATES:
        return True

def isNotRunning(process):
    return not isRunning(process)

# this is not used in code but referenced via an entry point in the conf file
def make_main_rpcinterface(supervisord):
    return SupervisorNamespaceRPCInterface(supervisord)


########NEW FILE########
__FILENAME__ = loop_eventgen
#!/usr/bin/env python

# A process which emits a process communications event on its stdout,
# and subsequently waits for a line to be sent back to its stdin by
# loop_listener.py.

import sys
import time
from supervisor import childutils

def main(max):
    start = time.time()
    report = open('/tmp/report', 'w')
    i = 0
    while 1:
        childutils.pcomm.stdout('the_data')
        sys.stdin.readline()
        report.write(str(i) + ' @ %s\n' % childutils.get_asctime())
        report.flush()
        i+=1
        if max and i >= max:
            end = time.time()
            report.write('%s per second\n' % (i / (end - start)))
            sys.exit(0)

if __name__ == '__main__':
    max = 0
    if len(sys.argv) > 1:
        max = int(sys.argv[1])
    main(max)



########NEW FILE########
__FILENAME__ = loop_listener
#!/usr/bin/env python -u

# An event listener that listens for process communications events
# from loop_eventgen.py and uses RPC to write data to the event
# generator's stdin.

import os
from supervisor import childutils

def main():
    rpcinterface = childutils.getRPCInterface(os.environ)
    while 1:
        headers, payload = childutils.listener.wait()
        if headers['eventname'].startswith('PROCESS_COMMUNICATION'):
            pheaders, pdata = childutils.eventdata(payload)
            pname = '%s:%s' % (pheaders['processname'], pheaders['groupname'])
            rpcinterface.supervisor.sendProcessStdin(pname, 'Got it yo\n')
        childutils.listener.ok()

if __name__ == '__main__':
    main()

########NEW FILE########
__FILENAME__ = sample_commevent
#!/usr/bin/env python

# An example process which emits a stdout process communication event every
# second (or every number of seconds specified as a single argument).

import sys
import time

def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()

def main(sleep):
    while 1:
        write_stdout('<!--XSUPERVISOR:BEGIN-->')
        write_stdout('the data')
        write_stdout('<!--XSUPERVISOR:END-->')
        time.sleep(sleep)

if __name__ == '__main__':
    if len(sys.argv) > 1:
        main(float(sys.argv[1]))
    else:
        main(1)


########NEW FILE########
__FILENAME__ = sample_eventlistener
#!/usr/bin/env python -u

# A sample long-running supervisor event listener which demonstrates
# how to accept event notifications from supervisor and how to respond
# properly.  This demonstration does *not* use the
# supervisor.childutils module, which wraps the specifics of
# communications in higher-level API functions.  If your listeners are
# implemented using Python, it is recommended that you use the
# childutils module API instead of modeling your scripts on the
# lower-level protocol example below.

import sys

def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()

def write_stderr(s):
    sys.stderr.write(s)
    sys.stderr.flush()

def main():
    while 1:
        write_stdout('READY\n') # transition from ACKNOWLEDGED to READY
        line = sys.stdin.readline()  # read header line from stdin 
        write_stderr(line) # print it out to stderr (testing only)
        headers = dict([ x.split(':') for x in line.split() ])
        data = sys.stdin.read(int(headers['len'])) # read the event payload
        write_stderr(data) # print the event payload to stderr (testing only)
        write_stdout('RESULT 2\nOK') # transition from BUSY to ACKNOWLEDGED
        #write_stdout('RESULT 4\nFAIL') # transition from BUSY TO ACKNOWLEDGED

if __name__ == '__main__':
    main()

########NEW FILE########
__FILENAME__ = sample_exiting_eventlistener
#!/usr/bin/env python

# A sample long-running supervisor event listener which demonstrates
# how to accept event notifications from supervisor and how to respond
# properly.  It is the same as the sample_eventlistener.py script
# except it exits after each request (presumably to be restarted by
# supervisor).  This demonstration does *not* use the
# supervisor.childutils module, which wraps the specifics of
# communications in higher-level API functions.  If your listeners are
# implemented using Python, it is recommended that you use the
# childutils module API instead of modeling your scripts on the
# lower-level protocol example below.

import sys

def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()

def write_stderr(s):
    sys.stderr.write(s)
    sys.stderr.flush()

def main():
    write_stdout('READY\n') # transition from ACKNOWLEDGED to READY
    line = sys.stdin.readline()  # read a line from stdin from supervisord
    write_stderr(line) # print it out to stderr (testing only)
    headers = dict([ x.split(':') for x in line.split() ])
    data = sys.stdin.read(int(headers['len'])) # read the event payload
    write_stderr(data) # print the event payload to stderr (testing only)
    write_stdout('RESULT 2\nOK') # transition from READY to ACKNOWLEDGED
    # exit, if the eventlistener process config has autorestart=true,
    # it will be restarted by supervisord.

if __name__ == '__main__':
    main()
    

########NEW FILE########
__FILENAME__ = socket_manager
import supervisor.medusa.text_socket as socket

class Proxy:
    """ Class for wrapping a shared resource object and getting
        notified when it's deleted
    """
    
    def __init__(self, object, **kwargs):
        self.object = object
        self.on_delete = kwargs.get('on_delete', None)

    def __del__(self):
        if self.on_delete:
            self.on_delete()
    
    def __getattr__(self, name):
        return getattr(self.object, name)
        
    def _get(self):
        return self.object
        
class ReferenceCounter:
    """ Class for tracking references to a shared resource
    """
    
    def __init__(self, **kwargs):
        self.on_non_zero = kwargs['on_non_zero']
        self.on_zero = kwargs['on_zero']
        self.ref_count = 0
    
    def get_count(self):
        return self.ref_count
    
    def increment(self):
        if self.ref_count == 0:
            self.on_non_zero()
        self.ref_count += 1
        
    def decrement(self):
        if self.ref_count <= 0:
            raise Exception('Illegal operation: cannot decrement below zero')
        self.ref_count -= 1
        if self.ref_count == 0:
            self.on_zero()

class SocketManager:
    """ Class for managing sockets in servers that create/bind/listen
        before forking multiple child processes to accept() 
        Sockets are managed at the process group level and referenced counted
        at the process level b/c that's really the only place to hook in
    """
    
    def __init__(self, socket_config, **kwargs):
        self.logger = kwargs.get('logger', None)
        self.socket = None
        self.prepared = False
        self.socket_config = socket_config
        self.ref_ctr = ReferenceCounter(on_zero=self._close, on_non_zero=self._prepare_socket)
        
    def __repr__(self):
        return '<%s at %s for %s>' % (self.__class__,
                                      id(self),
                                      self.socket_config.url)

    def config(self):
        return self.socket_config
        
    def is_prepared(self):
        return self.prepared

    def get_socket(self):
        self.ref_ctr.increment()
        self._require_prepared()
        return Proxy(self.socket, on_delete=self.ref_ctr.decrement)
        
    def get_socket_ref_count(self):
        self._require_prepared()
        return self.ref_ctr.get_count()
        
    def _require_prepared(self):
        if not self.prepared:
            raise Exception('Socket has not been prepared')
    
    def _prepare_socket(self):
        if not self.prepared:
            if self.logger:
                self.logger.info('Creating socket %s' % self.socket_config)
            self.socket = self.socket_config.create_and_bind()
            self.socket.listen(socket.SOMAXCONN)
            self.prepared = True

    def _close(self):
        self._require_prepared()
        if self.logger:
            self.logger.info('Closing socket %s' % self.socket_config)
        self.socket.close()
        self.prepared = False

########NEW FILE########
__FILENAME__ = states
# This module must not depend on any other non-stdlib module to prevent
# circular import problems.

class ProcessStates:
    STOPPED = 0
    STARTING = 10
    RUNNING = 20
    BACKOFF = 30
    STOPPING = 40
    EXITED = 100
    FATAL = 200
    UNKNOWN = 1000

STOPPED_STATES = (ProcessStates.STOPPED,
                  ProcessStates.EXITED,
                  ProcessStates.FATAL,
                  ProcessStates.UNKNOWN)

RUNNING_STATES = (ProcessStates.RUNNING,
                  ProcessStates.BACKOFF,
                  ProcessStates.STARTING)



def getProcessStateDescription(code):
    for statename in ProcessStates.__dict__:
        if getattr(ProcessStates, statename) == code:
            return statename

class SupervisorStates:
    FATAL = 2
    RUNNING = 1
    RESTARTING = 0
    SHUTDOWN = -1

def getSupervisorStateDescription(code):
    for statename in SupervisorStates.__dict__:
        if getattr(SupervisorStates, statename) == code:
            return statename


class EventListenerStates:
    READY = 10 # the process ready to be sent an event from supervisor
    BUSY = 20 # event listener is processing an event sent to it by supervisor
    ACKNOWLEDGED = 30 # the event listener processed an event
    UNKNOWN = 40 # the event listener is in an unknown state

def getEventListenerStateDescription(code):
    for statename in EventListenerStates.__dict__:
        if getattr(EventListenerStates, statename) == code:
            return statename


########NEW FILE########
__FILENAME__ = supervisorctl
#!/usr/bin/env python -u

"""supervisorctl -- control applications run by supervisord from the cmd line.

Usage: %s [options] [action [arguments]]

Options:
-c/--configuration -- configuration file path (default /etc/supervisord.conf)
-h/--help -- print usage message and exit
-i/--interactive -- start an interactive shell after executing commands
-s/--serverurl URL -- URL on which supervisord server is listening
     (default "http://localhost:9001").
-u/--username -- username to use for authentication with server
-p/--password -- password to use for authentication with server
-r/--history-file -- keep a readline history (if readline is available)

action [arguments] -- see below

Actions are commands like "tail" or "stop".  If -i is specified or 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.
"""

import cmd
import sys
import getpass

import supervisor.medusa.text_socket as socket
import errno
import threading

from supervisor.compat import xmlrpclib
from supervisor.compat import urlparse
from supervisor.compat import unicode

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.options import ClientOptions
from supervisor.options import make_namespec
from supervisor.options import split_namespec
from supervisor import xmlrpc
from supervisor import states

class fgthread(threading.Thread):
    """ A subclass of threading.Thread, with a kill() method.
    To be used for foreground output/error streaming.
    http://mail.python.org/pipermail/python-list/2004-May/260937.html
    """

    def __init__(self, program, ctl):
        threading.Thread.__init__(self)
        import supervisor.http_client as http_client
        self.killed = False
        self.program = program
        self.ctl = ctl
        self.listener = http_client.Listener()
        self.output_handler = http_client.HTTPHandler(self.listener,
                                                      self.ctl.options.username,
                                                      self.ctl.options.password)
        self.error_handler = http_client.HTTPHandler(self.listener,
                                                     self.ctl.options.username,
                                                     self.ctl.options.password)

    def start(self):
        # Start the thread
        self.__run_backup = self.run
        self.run = self.__run
        threading.Thread.start(self)

    def run(self):
        self.output_handler.get(self.ctl.options.serverurl,
                                '/logtail/%s/stdout'%self.program)
        self.error_handler.get(self.ctl.options.serverurl,
                               '/logtail/%s/stderr'%self.program)
        asyncore.loop()

    def __run(self):
        # Hacked run function, which installs the trace
        sys.settrace(self.globaltrace)
        self.__run_backup()
        self.run = self.__run_backup

    def globaltrace(self, frame, why, arg):
        if why == 'call':
            return self.localtrace
        else:
            return None

    def localtrace(self, frame, why, arg):
        if self.killed:
            if why == 'line':
                raise SystemExit()
        return self.localtrace

    def kill(self):
        self.output_handler.close()
        self.error_handler.close()
        self.killed = True

class Controller(cmd.Cmd):

    def __init__(self, options, completekey='tab', stdin=None,
                 stdout=None):
        self.options = options
        self.prompt = self.options.prompt + '> '
        self.options.plugins = []
        self.vocab = ['help']
        self._complete_info = None
        cmd.Cmd.__init__(self, completekey, stdin, stdout)
        for name, factory, kwargs in self.options.plugin_factories:
            plugin = factory(self, **kwargs)
            for a in dir(plugin):
                if a.startswith('do_') and callable(getattr(plugin, a)):
                    self.vocab.append(a[3:])
            self.options.plugins.append(plugin)
            plugin.name = name

    def emptyline(self):
        # We don't want a blank line to repeat the last command.
        return

    def onecmd(self, line):
        """ Override the onecmd method to:
          - catch and print all exceptions
          - allow for composite commands in interactive mode (foo; bar)
          - call 'do_foo' on plugins rather than ourself
        """
        origline = line
        lines = line.split(';') # don't filter(None, line.split), as we pop
        line = lines.pop(0)
        # stuffing the remainder into cmdqueue will cause cmdloop to
        # call us again for each command.
        self.cmdqueue.extend(lines)
        cmd, arg, line = self.parseline(line)
        if not line:
            return self.emptyline()
        if cmd is None:
            return self.default(line)
        self._complete_info = None
        self.lastcmd = line
        if cmd == '':
            return self.default(line)
        else:
            do_func = self._get_do_func(cmd)
            if do_func is None:
                return self.default(line)
            try:
                try:
                    return do_func(arg)
                except xmlrpclib.ProtocolError as e:
                    if e.errcode == 401:
                        if self.options.interactive:
                            self.output('Server requires authentication')
                            username = raw_input('Username:')
                            password = getpass.getpass(prompt='Password:')
                            self.output('')
                            self.options.username = username
                            self.options.password = password
                            return self.onecmd(origline)
                        else:
                            self.options.usage('Server requires authentication')
                    else:
                        raise
                do_func(arg)
            except SystemExit:
                raise
            except Exception:
                (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
                error = 'error: %s, %s: file: %s line: %s' % (t, v, file, line)
                self.output(error)
                if not self.options.interactive:
                    sys.exit(2)

    def _get_do_func(self, cmd):
        func_name = 'do_' + cmd
        func = getattr(self, func_name, None)
        if not func:
            for plugin in self.options.plugins:
                func = getattr(plugin, func_name, None)
                if func is not None:
                    break
        return func

    def output(self, stuff):
        if stuff is not None:
            if isinstance(stuff, unicode):
                stuff = stuff.encode('utf-8')
            self.stdout.write(stuff + '\n')

    def get_supervisor(self):
        return self.get_server_proxy('supervisor')

    def get_server_proxy(self, namespace=None):
        proxy = self.options.getServerProxy()
        if namespace is None:
            return proxy
        else:
            return getattr(proxy, namespace)

    def upcheck(self):
        try:
            supervisor = self.get_supervisor()
            api = supervisor.getVersion() # deprecated
            from supervisor import rpcinterface
            if api != rpcinterface.API_VERSION:
                self.output(
                    'Sorry, this version of supervisorctl expects to '
                    'talk to a server with API version %s, but the '
                    'remote version is %s.' % (rpcinterface.API_VERSION, api))
                return False
        except xmlrpclib.Fault as e:
            if e.faultCode == xmlrpc.Faults.UNKNOWN_METHOD:
                self.output(
                    'Sorry, supervisord responded but did not recognize '
                    'the supervisor namespace commands that supervisorctl '
                    'uses to control it.  Please check that the '
                    '[rpcinterface:supervisor] section is enabled in the '
                    'configuration file (see sample.conf).')
                return False
            raise
        except socket.error as why:
            if why.args[0] == errno.ECONNREFUSED:
                self.output('%s refused connection' % self.options.serverurl)
                return False
            elif why.args[0] == errno.ENOENT:
                self.output('%s no such file' % self.options.serverurl)
                return False
            raise
        return True

    def complete(self, text, state, line=None):
        """Completer function that Cmd will register with readline using
        readline.set_completer().  This function will be called by readline
        as complete(text, state) where text is a fragment to complete and
        state is an integer (0..n).  Each call returns a string with a new
        completion.  When no more are available, None is returned."""
        if line is None: # line is only set in tests
            import readline
            line = readline.get_line_buffer()

        # take the last phrase from a line like "stop foo; start bar"
        phrase = line.split(';')[-1]

        matches = []
        # blank phrase completes to action list
        if not phrase.strip():
            matches = self._complete_actions(text)
        else:
            words = phrase.split()
            action = words[0]
            # incomplete action completes to action list
            if len(words) == 1 and not phrase.endswith(' '):
                matches = self._complete_actions(text)
            # actions that accept an action name
            elif action in ('help'):
                matches = self._complete_actions(text)
            # actions that accept a group name
            elif action in ('add', 'remove', 'update'):
                matches = self._complete_groups(text)
            # actions that accept a process name
            elif action in ('clear', 'fg', 'pid', 'restart', 'start',
                            'stop', 'status', 'tail'):
                matches = self._complete_processes(text)
        if len(matches) > state:
            return matches[state]

    def _complete_actions(self, text):
        """Build a completion list of action names matching text"""
        return [ a + ' ' for a in self.vocab if a.startswith(text)]

    def _complete_groups(self, text):
        """Build a completion list of group names matching text"""
        groups = []
        for info in self._get_complete_info():
            if info['group'] not in groups:
                groups.append(info['group'])
        return [ g + ' ' for g in groups if g.startswith(text) ]

    def _complete_processes(self, text):
        """Build a completion list of process names matching text"""
        processes = []
        for info in self._get_complete_info():
            if ':' in text or info['name'] != info['group']:
                processes.append('%s:%s' % (info['group'], info['name']))
                if '%s:*' % info['group'] not in processes:
                    processes.append('%s:*' % info['group'])
            else:
                processes.append(info['name'])
        return [ p + ' ' for p in processes if p.startswith(text) ]

    def _get_complete_info(self):
        """Get all process info used for completion.  We cache this between
        commands to reduce XML-RPC calls because readline may call
        complete() many times if the user hits tab only once."""
        if self._complete_info is None:
            self._complete_info = self.get_supervisor().getAllProcessInfo()
        return self._complete_info

    def do_help(self, arg):
        if arg.strip() == 'help':
            self.help_help()
        else:
            for plugin in self.options.plugins:
                plugin.do_help(arg)

    def help_help(self):
        self.output("help\t\tPrint a list of available actions")
        self.output("help <action>\tPrint help for <action>")

    def do_EOF(self, arg):
        self.output('')
        return 1

    def help_EOF(self):
        self.output("To quit, type ^D or use the quit command")

def get_names(inst):
    names = []
    classes = [inst.__class__]
    while classes:
        aclass = classes.pop(0)
        if aclass.__bases__:
            classes = classes + list(aclass.__bases__)
        names = names + dir(aclass)
    return names

class ControllerPluginBase:
    name = 'unnamed'

    def __init__(self, controller):
        self.ctl = controller

    def _doc_header(self):
        return "%s commands (type help <topic>):" % self.name
    doc_header = property(_doc_header)

    def do_help(self, arg):
        if arg:
            # XXX check arg syntax
            try:
                func = getattr(self, 'help_' + arg)
            except AttributeError:
                try:
                    doc = getattr(self, 'do_' + arg).__doc__
                    if doc:
                        self.ctl.output(doc)
                        return
                except AttributeError:
                    pass
                self.ctl.output(self.ctl.nohelp % (arg,))
                return
            func()
        else:
            names = get_names(self)
            cmds_doc = []
            cmds_undoc = []
            help = {}
            for name in names:
                if name[:5] == 'help_':
                    help[name[5:]]=1
            names.sort()
            # There can be duplicates if routines overridden
            prevname = ''
            for name in names:
                if name[:3] == 'do_':
                    if name == prevname:
                        continue
                    prevname = name
                    cmd=name[3:]
                    if cmd in help:
                        cmds_doc.append(cmd)
                        del help[cmd]
                    elif getattr(self, name).__doc__:
                        cmds_doc.append(cmd)
                    else:
                        cmds_undoc.append(cmd)
            self.ctl.output('')
            self.ctl.print_topics(self.doc_header, cmds_doc, 15, 80)

class DefaultControllerPlugin(ControllerPluginBase):
    name = 'default'
    listener = None # for unit tests
    def _tailf(self, path):
        self.ctl.output('==> Press Ctrl-C to exit <==')

        username = self.ctl.options.username
        password = self.ctl.options.password
        handler = None
        try:
            # Python's urllib2 (at least as of Python 2.4.2) isn't up
            # to this task; it doesn't actually implement a proper
            # HTTP/1.1 client that deals with chunked responses (it
            # always sends a Connection: close header).  We use a
            # homegrown client based on asyncore instead.  This makes
            # me sad.
            import supervisor.http_client as http_client
            if self.listener is None:
                listener = http_client.Listener()
            else:
                listener = self.listener # for unit tests
            handler = http_client.HTTPHandler(listener, username, password)
            handler.get(self.ctl.options.serverurl, path)
            asyncore.loop()
        except KeyboardInterrupt:
            if handler:
                handler.close()
            self.ctl.output('')
            return

    def do_tail(self, arg):
        if not self.ctl.upcheck():
            return

        args = arg.split()

        if len(args) < 1:
            self.ctl.output('Error: too few arguments')
            self.help_tail()
            return

        elif len(args) > 3:
            self.ctl.output('Error: too many arguments')
            self.help_tail()
            return

        modifier = None

        if args[0].startswith('-'):
            modifier = args.pop(0)

        if len(args) == 1:
            name = args[-1]
            channel = 'stdout'
        else:
            if args:
                name = args[0]
                channel = args[-1].lower()
                if channel not in ('stderr', 'stdout'):
                    self.ctl.output('Error: bad channel %r' % channel)
                    return
            else:
                self.ctl.output('Error: tail requires process name')
                return

        bytes = 1600

        if modifier is not None:
            what = modifier[1:]
            if what == 'f':
                bytes = None
            else:
                try:
                    bytes = int(what)
                except:
                    self.ctl.output('Error: bad argument %s' % modifier)
                    return

        supervisor = self.ctl.get_supervisor()

        if bytes is None:
            return self._tailf('/logtail/%s/%s' % (name, channel))

        else:
            try:
                if channel is 'stdout':
                    output = supervisor.readProcessStdoutLog(name,
                                                             -bytes, 0)
                else: # if channel is 'stderr'
                    output = supervisor.readProcessStderrLog(name,
                                                             -bytes, 0)
            except xmlrpclib.Fault as e:
                template = '%s: ERROR (%s)'
                if e.faultCode == xmlrpc.Faults.NO_FILE:
                    self.ctl.output(template % (name, 'no log file'))
                elif e.faultCode == xmlrpc.Faults.FAILED:
                    self.ctl.output(template % (name,
                                             'unknown error reading log'))
                elif e.faultCode == xmlrpc.Faults.BAD_NAME:
                    self.ctl.output(template % (name,
                                             'no such process name'))
                else:
                    raise
            else:
                self.ctl.output(output)

    def help_tail(self):
        self.ctl.output(
            "tail [-f] <name> [stdout|stderr] (default stdout)\n"
            "Ex:\n"
            "tail -f <name>\t\tContinuous tail of named process stdout\n"
            "\t\t\tCtrl-C to exit.\n"
            "tail -100 <name>\tlast 100 *bytes* of process stdout\n"
            "tail <name> stderr\tlast 1600 *bytes* of process stderr"
            )

    def do_maintail(self, arg):
        if not self.ctl.upcheck():
            return

        args = arg.split()

        if len(args) > 1:
            self.ctl.output('Error: too many arguments')
            self.help_maintail()
            return

        elif len(args) == 1:
            if args[0].startswith('-'):
                what = args[0][1:]
                if what == 'f':
                    path = '/mainlogtail'
                    return self._tailf(path)
                try:
                    what = int(what)
                except:
                    self.ctl.output('Error: bad argument %s' % args[0])
                    return
                else:
                    bytes = what
            else:
                self.ctl.output('Error: bad argument %s' % args[0])
                return

        else:
            bytes = 1600

        supervisor = self.ctl.get_supervisor()

        try:
            output = supervisor.readLog(-bytes, 0)
        except xmlrpclib.Fault as e:
            template = '%s: ERROR (%s)'
            if e.faultCode == xmlrpc.Faults.NO_FILE:
                self.ctl.output(template % ('supervisord', 'no log file'))
            elif e.faultCode == xmlrpc.Faults.FAILED:
                self.ctl.output(template % ('supervisord',
                                         'unknown error reading log'))
            else:
                raise
        else:
            self.ctl.output(output)

    def help_maintail(self):
        self.ctl.output(
            "maintail -f \tContinuous tail of supervisor main log file"
            " (Ctrl-C to exit)\n"
            "maintail -100\tlast 100 *bytes* of supervisord main log file\n"
            "maintail\tlast 1600 *bytes* of supervisor main log file\n"
            )

    def do_quit(self, arg):
        sys.exit(0)

    def help_quit(self):
        self.ctl.output("quit\tExit the supervisor shell.")

    do_exit = do_quit

    def help_exit(self):
        self.ctl.output("exit\tExit the supervisor shell.")

    def _show_statuses(self, process_infos):
        namespecs, maxlen = [], 30
        for i, info in enumerate(process_infos):
            namespecs.append(make_namespec(info['group'], info['name']))
            if len(namespecs[i]) > maxlen:
                maxlen = len(namespecs[i])

        template = '%(namespec)-' + str(maxlen+3) + 's%(state)-10s%(desc)s'
        for i, info in enumerate(process_infos):
            line = template % {'namespec': namespecs[i],
                               'state': info['statename'],
                               'desc': info['description']}
            self.ctl.output(line)

    def do_status(self, arg):
        if not self.ctl.upcheck():
            return

        supervisor = self.ctl.get_supervisor()
        all_infos = supervisor.getAllProcessInfo()

        names = arg.split()
        if not names or "all" in names:
            matching_infos = all_infos
        else:
            matching_infos = []

            for name in names:
                bad_name = True
                group_name, process_name = split_namespec(name)

                for info in all_infos:
                    matched = info['group'] == group_name
                    if process_name is not None:
                        matched = matched and info['name'] == process_name

                    if matched:
                        bad_name = False
                        matching_infos.append(info)

                if bad_name:
                    if process_name is None:
                        msg = "%s: ERROR (no such group)" % group_name
                    else:
                        msg = "%s: ERROR (no such process)" % name
                    self.ctl.output(msg)
        self._show_statuses(matching_infos)

    def help_status(self):
        self.ctl.output("status <name>\t\tGet status for a single process")
        self.ctl.output("status <gname>:*\tGet status for all "
                        "processes in a group")
        self.ctl.output("status <name> <name>\tGet status for multiple named "
                        "processes")
        self.ctl.output("status\t\t\tGet all process status info")

    def do_pid(self, arg):
        supervisor = self.ctl.get_supervisor()
        if not self.ctl.upcheck():
            return
        names = arg.split()
        if not names:
            pid = supervisor.getPID()
            self.ctl.output(str(pid))
        elif 'all' in names:
            for info in supervisor.getAllProcessInfo():
                self.ctl.output(str(info['pid']))
        else:
            for name in names:
                try:
                    info = supervisor.getProcessInfo(name)
                except xmlrpclib.Fault as e:
                    if e.faultCode == xmlrpc.Faults.BAD_NAME:
                        self.ctl.output('No such process %s' % name)
                    else:
                        raise
                else:
                    self.ctl.output(str(info['pid']))

    def help_pid(self):
        self.ctl.output("pid\t\t\tGet the PID of supervisord.")
        self.ctl.output("pid <name>\t\tGet the PID of a single "
            "child process by name.")
        self.ctl.output("pid all\t\t\tGet the PID of every child "
            "process, one per line.")

    def _startresult(self, result):
        name = make_namespec(result['group'], result['name'])
        code = result['status']
        template = '%s: ERROR (%s)'
        if code == xmlrpc.Faults.BAD_NAME:
            return template % (name, 'no such process')
        elif code == xmlrpc.Faults.NO_FILE:
            return template % (name, 'no such file')
        elif code == xmlrpc.Faults.NOT_EXECUTABLE:
            return template % (name, 'file is not executable')
        elif code == xmlrpc.Faults.ALREADY_STARTED:
            return template % (name, 'already started')
        elif code == xmlrpc.Faults.SPAWN_ERROR:
            return template % (name, 'spawn error')
        elif code == xmlrpc.Faults.ABNORMAL_TERMINATION:
            return template % (name, 'abnormal termination')
        elif code == xmlrpc.Faults.SUCCESS:
            return '%s: started' % name
        # assertion
        raise ValueError('Unknown result code %s for %s' % (code, name))

    def do_start(self, arg):
        if not self.ctl.upcheck():
            return

        names = arg.split()
        supervisor = self.ctl.get_supervisor()

        if not names:
            self.ctl.output("Error: start requires a process name")
            self.help_start()
            return

        if 'all' in names:
            results = supervisor.startAllProcesses()
            for result in results:
                result = self._startresult(result)
                self.ctl.output(result)

        else:
            for name in names:
                group_name, process_name = split_namespec(name)
                if process_name is None:
                    try:
                        results = supervisor.startProcessGroup(group_name)
                        for result in results:
                            result = self._startresult(result)
                            self.ctl.output(result)
                    except xmlrpclib.Fault as e:
                        if e.faultCode == xmlrpc.Faults.BAD_NAME:
                            error = "%s: ERROR (no such group)" % group_name
                            self.ctl.output(error)
                        else:
                            raise
                else:
                    try:
                        result = supervisor.startProcess(name)
                    except xmlrpclib.Fault as e:
                        error = self._startresult({'status': e.faultCode,
                                                   'name': process_name,
                                                   'group': group_name,
                                                   'description': e.faultString})
                        self.ctl.output(error)
                    else:
                        name = make_namespec(group_name, process_name)
                        self.ctl.output('%s: started' % name)

    def help_start(self):
        self.ctl.output("start <name>\t\tStart a process")
        self.ctl.output("start <gname>:*\t\tStart all processes in a group")
        self.ctl.output(
            "start <name> <name>\tStart multiple processes or groups")
        self.ctl.output("start all\t\tStart all processes")

    def _stopresult(self, result):
        name = make_namespec(result['group'], result['name'])
        code = result['status']
        fault_string = result['description']
        template = '%s: ERROR (%s)'
        if code == xmlrpc.Faults.BAD_NAME:
            return template % (name, 'no such process')
        elif code == xmlrpc.Faults.NOT_RUNNING:
            return template % (name, 'not running')
        elif code == xmlrpc.Faults.SUCCESS:
            return '%s: stopped' % name
        elif code == xmlrpc.Faults.FAILED:
            return fault_string
        # assertion
        raise ValueError('Unknown result code %s for %s' % (code, name))

    def do_stop(self, arg):
        if not self.ctl.upcheck():
            return

        names = arg.split()
        supervisor = self.ctl.get_supervisor()

        if not names:
            self.ctl.output('Error: stop requires a process name')
            self.help_stop()
            return

        if 'all' in names:
            results = supervisor.stopAllProcesses()
            for result in results:
                result = self._stopresult(result)
                self.ctl.output(result)

        else:
            for name in names:
                group_name, process_name = split_namespec(name)
                if process_name is None:
                    try:
                        results = supervisor.stopProcessGroup(group_name)
                        for result in results:
                            result = self._stopresult(result)
                            self.ctl.output(result)
                    except xmlrpclib.Fault as e:
                        if e.faultCode == xmlrpc.Faults.BAD_NAME:
                            error = "%s: ERROR (no such group)" % group_name
                            self.ctl.output(error)
                        else:
                            raise
                else:
                    try:
                        supervisor.stopProcess(name)
                    except xmlrpclib.Fault as e:
                        error = self._stopresult({'status': e.faultCode,
                                                  'name': process_name,
                                                  'group': group_name,
                                                  'description':e.faultString})
                        self.ctl.output(error)
                    else:
                        name = make_namespec(group_name, process_name)
                        self.ctl.output('%s: stopped' % name)

    def help_stop(self):
        self.ctl.output("stop <name>\t\tStop a process")
        self.ctl.output("stop <gname>:*\t\tStop all processes in a group")
        self.ctl.output("stop <name> <name>\tStop multiple processes or groups")
        self.ctl.output("stop all\t\tStop all processes")

    def do_restart(self, arg):
        if not self.ctl.upcheck():
            return

        names = arg.split()

        if not names:
            self.ctl.output('Error: restart requires a process name')
            self.help_restart()
            return

        self.do_stop(arg)
        self.do_start(arg)

    def help_restart(self):
        self.ctl.output("restart <name>\t\tRestart a process")
        self.ctl.output("restart <gname>:*\tRestart all processes in a group")
        self.ctl.output("restart <name> <name>\tRestart multiple processes or "
                     "groups")
        self.ctl.output("restart all\t\tRestart all processes")
        self.ctl.output("Note: restart does not reread config files. For that,"
                        " see reread and update.")

    def do_shutdown(self, arg):
        if self.ctl.options.interactive:
            yesno = raw_input('Really shut the remote supervisord process '
                              'down y/N? ')
            really = yesno.lower().startswith('y')
        else:
            really = 1

        if really:
            supervisor = self.ctl.get_supervisor()
            try:
                supervisor.shutdown()
            except xmlrpclib.Fault as e:
                if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                    self.ctl.output('ERROR: already shutting down')
                else:
                    raise
            except socket.error as e:
                if e.args[0] == errno.ECONNREFUSED:
                    msg = 'ERROR: %s refused connection (already shut down?)'
                    self.ctl.output(msg % self.ctl.options.serverurl)
                elif e.args[0] == errno.ENOENT:
                    msg = 'ERROR: %s no such file (already shut down?)'
                    self.ctl.output(msg % self.ctl.options.serverurl)
                else:
                    raise
            else:
                self.ctl.output('Shut down')

    def help_shutdown(self):
        self.ctl.output("shutdown \tShut the remote supervisord down.")

    def do_reload(self, arg):
        if self.ctl.options.interactive:
            yesno = raw_input('Really restart the remote supervisord process '
                              'y/N? ')
            really = yesno.lower().startswith('y')
        else:
            really = 1
        if really:
            supervisor = self.ctl.get_supervisor()
            try:
                supervisor.restart()
            except xmlrpclib.Fault as e:
                if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                    self.ctl.output('ERROR: already shutting down')
                else:
                    raise
            else:
                self.ctl.output('Restarted supervisord')

    def help_reload(self):
        self.ctl.output("reload \t\tRestart the remote supervisord.")

    def _formatChanges(self, added_changed_dropped_tuple):
        added, changed, dropped = added_changed_dropped_tuple
        changedict = {}
        for n, t in [(added, 'available'),
                     (changed, 'changed'),
                     (dropped, 'disappeared')]:
            changedict.update(dict(zip(n, [t] * len(n))))

        if changedict:
            names = list(changedict.keys())
            names.sort()
            for name in names:
                self.ctl.output("%s: %s" % (name, changedict[name]))
        else:
            self.ctl.output("No config updates to processes")

    def _formatConfigInfo(self, configinfo):
        name = make_namespec(configinfo['group'], configinfo['name'])
        formatted = { 'name': name }
        if configinfo['inuse']:
            formatted['inuse'] = 'in use'
        else:
            formatted['inuse'] = 'avail'
        if configinfo['autostart']:
            formatted['autostart'] = 'auto'
        else:
            formatted['autostart'] = 'manual'
        formatted['priority'] = "%s:%s" % (configinfo['group_prio'],
                                           configinfo['process_prio'])

        template = '%(name)-32s %(inuse)-9s %(autostart)-9s %(priority)s'
        return template % formatted

    def do_avail(self, arg):
        supervisor = self.ctl.get_supervisor()
        try:
            configinfo = supervisor.getAllConfigInfo()
        except xmlrpclib.Fault as e:
            if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                self.ctl.output('ERROR: supervisor shutting down')
            else:
                raise
        else:
            for pinfo in configinfo:
                self.ctl.output(self._formatConfigInfo(pinfo))

    def help_avail(self):
        self.ctl.output("avail\t\t\tDisplay all configured processes")

    def do_reread(self, arg):
        supervisor = self.ctl.get_supervisor()
        try:
            result = supervisor.reloadConfig()
        except xmlrpclib.Fault as e:
            if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                self.ctl.output('ERROR: supervisor shutting down')
            elif e.faultCode == xmlrpc.Faults.CANT_REREAD:
                self.ctl.output('ERROR: %s' % e.faultString)
            else:
                raise
        else:
            self._formatChanges(result[0])

    def help_reread(self):
        self.ctl.output("reread \t\t\tReload the daemon's configuration files")

    def do_add(self, arg):
        names = arg.split()

        supervisor = self.ctl.get_supervisor()
        for name in names:
            try:
                supervisor.addProcessGroup(name)
            except xmlrpclib.Fault as e:
                if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                    self.ctl.output('ERROR: shutting down')
                elif e.faultCode == xmlrpc.Faults.ALREADY_ADDED:
                    self.ctl.output('ERROR: process group already active')
                elif e.faultCode == xmlrpc.Faults.BAD_NAME:
                    self.ctl.output(
                        "ERROR: no such process/group: %s" % name)
                else:
                    raise
            else:
                self.ctl.output("%s: added process group" % name)

    def help_add(self):
        self.ctl.output("add <name> [...]\tActivates any updates in config "
                        "for process/group")

    def do_remove(self, arg):
        names = arg.split()

        supervisor = self.ctl.get_supervisor()
        for name in names:
            try:
                supervisor.removeProcessGroup(name)
            except xmlrpclib.Fault as e:
                if e.faultCode == xmlrpc.Faults.STILL_RUNNING:
                    self.ctl.output('ERROR: process/group still running: %s'
                                    % name)
                elif e.faultCode == xmlrpc.Faults.BAD_NAME:
                    self.ctl.output(
                        "ERROR: no such process/group: %s" % name)
                else:
                    raise
            else:
                self.ctl.output("%s: removed process group" % name)

    def help_remove(self):
        self.ctl.output("remove <name> [...]\tRemoves process/group from "
                        "active config")

    def do_update(self, arg):
        def log(name, message):
            self.ctl.output("%s: %s" % (name, message))

        supervisor = self.ctl.get_supervisor()
        try:
            result = supervisor.reloadConfig()
        except xmlrpclib.Fault as e:
            if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
                self.ctl.output('ERROR: already shutting down')
                return
            else:
                raise

        added, changed, removed = result[0]
        valid_gnames = set(arg.split())

        # If all is specified treat it as if nothing was specified.
        if "all" in valid_gnames:
            valid_gnames = set()

        # If any gnames are specified we need to verify that they are
        # valid in order to print a useful error message.
        if valid_gnames:
            groups = set()
            for info in supervisor.getAllProcessInfo():
                groups.add(info['group'])
            # New gnames would not currently exist in this set so
            # add those as well.
            groups.update(added)

            for gname in valid_gnames:
                if gname not in groups:
                    self.ctl.output('ERROR: no such group: %s' % gname)

        for gname in removed:
            if valid_gnames and gname not in valid_gnames:
                continue
            results = supervisor.stopProcessGroup(gname)
            log(gname, "stopped")

            fails = [res for res in results
                     if res['status'] == xmlrpc.Faults.FAILED]
            if fails:
                log(gname, "has problems; not removing")
                continue
            supervisor.removeProcessGroup(gname)
            log(gname, "removed process group")

        for gname in changed:
            if valid_gnames and gname not in valid_gnames:
                continue
            supervisor.stopProcessGroup(gname)
            log(gname, "stopped")

            supervisor.removeProcessGroup(gname)
            supervisor.addProcessGroup(gname)
            log(gname, "updated process group")

        for gname in added:
            if valid_gnames and gname not in valid_gnames:
                continue
            supervisor.addProcessGroup(gname)
            log(gname, "added process group")

    def help_update(self):
        self.ctl.output("update\t\t\tReload config and add/remove as necessary")
        self.ctl.output("update all\t\tReload config and add/remove as necessary")
        self.ctl.output("update <gname> [...]\tUpdate specific groups")

    def _clearresult(self, result):
        name = make_namespec(result['group'], result['name'])
        code = result['status']
        template = '%s: ERROR (%s)'
        if code == xmlrpc.Faults.BAD_NAME:
            return template % (name, 'no such process')
        elif code == xmlrpc.Faults.FAILED:
            return template % (name, 'failed')
        elif code == xmlrpc.Faults.SUCCESS:
            return '%s: cleared' % name
        raise ValueError('Unknown result code %s for %s' % (code, name))

    def do_clear(self, arg):
        if not self.ctl.upcheck():
            return

        names = arg.split()

        if not names:
            self.ctl.output('Error: clear requires a process name')
            self.help_clear()
            return

        supervisor = self.ctl.get_supervisor()

        if 'all' in names:
            results = supervisor.clearAllProcessLogs()
            for result in results:
                result = self._clearresult(result)
                self.ctl.output(result)
        else:
            for name in names:
                group_name, process_name = split_namespec(name)
                try:
                    supervisor.clearProcessLogs(name)
                except xmlrpclib.Fault as e:
                    error = self._clearresult({'status': e.faultCode,
                                               'name': process_name,
                                               'group': group_name,
                                               'description': e.faultString})
                    self.ctl.output(error)
                else:
                    name = make_namespec(group_name, process_name)
                    self.ctl.output('%s: cleared' % name)

    def help_clear(self):
        self.ctl.output("clear <name>\t\tClear a process' log files.")
        self.ctl.output(
            "clear <name> <name>\tClear multiple process' log files")
        self.ctl.output("clear all\t\tClear all process' log files")

    def do_open(self, arg):
        url = arg.strip()
        parts = urlparse.urlparse(url)
        if parts[0] not in ('unix', 'http'):
            self.ctl.output('ERROR: url must be http:// or unix://')
            return
        self.ctl.options.serverurl = url
        self.do_status('')

    def help_open(self):
        self.ctl.output("open <url>\tConnect to a remote supervisord process.")
        self.ctl.output("\t\t(for UNIX domain socket, use unix:///socket/path)")

    def do_version(self, arg):
        if not self.ctl.upcheck():
            return
        supervisor = self.ctl.get_supervisor()
        self.ctl.output(supervisor.getSupervisorVersion())

    def help_version(self):
        self.ctl.output(
            "version\t\t\tShow the version of the remote supervisord "
            "process")

    def do_fg(self,args=None):
        if not self.ctl.upcheck():
            return
        if not args:
            self.ctl.output('Error: no process name supplied')
            self.help_fg()
            return
        args = args.split()
        if len(args) > 1:
            self.ctl.output('Error: too many process names supplied')
            return
        program = args[0]
        supervisor = self.ctl.get_supervisor()
        try:
            info = supervisor.getProcessInfo(program)
        except xmlrpclib.Fault as msg:
            if msg.faultCode == xmlrpc.Faults.BAD_NAME:
                self.ctl.output('Error: bad process name supplied')
                return
            # for any other fault
            self.ctl.output(str(msg))
            return
        if not info['state'] == states.ProcessStates.RUNNING:
            self.ctl.output('Error: process not running')
            return
        # everything good; continue
        a = None
        try:
            a = fgthread(program,self.ctl)
            # this thread takes care of
            # the output/error messages
            a.start()
            while True:
                # this takes care of the user input
                inp = raw_input() + '\n'
                try:
                    supervisor.sendProcessStdin(program, inp)
                except xmlrpclib.Fault as msg:
                    if msg.faultCode == xmlrpc.Faults.NOT_RUNNING:
                        self.ctl.output('Process got killed')
                        self.ctl.output('Exiting foreground')
                        a.kill()
                        return
                info = supervisor.getProcessInfo(program)
                if not info['state'] == states.ProcessStates.RUNNING:
                    self.ctl.output('Process got killed')
                    self.ctl.output('Exiting foreground')
                    a.kill()
                    return
                continue
        except (KeyboardInterrupt, EOFError):
            if a:
                a.kill()
            self.ctl.output('Exiting foreground')
        return

    def help_fg(self,args=None):
        self.ctl.output('fg <process>\tConnect to a process in foreground mode')
        self.ctl.output('Press Ctrl+C to exit foreground')

def main(args=None, options=None):
    if options is None:
        options = ClientOptions()
    options.realize(args, doc=__doc__)
    c = Controller(options)
    if options.args:
        c.onecmd(" ".join(options.args))
    if options.interactive:
        try:
            import readline
            delims = readline.get_completer_delims()
            delims = delims.replace(':', '') # "group:process" as one word
            delims = delims.replace('*', '') # "group:*" as one word
            delims = delims.replace('-', '') # names with "-" as one word
            readline.set_completer_delims(delims)

            if options.history_file:
                try:
                    readline.read_history_file(options.history_file)
                except IOError:
                    pass
                def save():
                    try:
                        readline.write_history_file(options.history_file)
                    except IOError:
                        pass

                import atexit
                atexit.register(save)
        except ImportError:
            pass
        try:
            c.cmdqueue.append('status')
            c.cmdloop()
        except KeyboardInterrupt:
            c.output('')
            pass

if __name__ == "__main__":
    main()

########NEW FILE########
__FILENAME__ = supervisord
#!/usr/bin/env python

"""supervisord -- run a set of applications as daemons.

Usage: %s [options]

Options:
-c/--configuration FILENAME -- configuration file
-n/--nodaemon -- run in the foreground (same as 'nodaemon true' in config file)
-h/--help -- print this usage message and exit
-v/--version -- print supervisord version number and exit
-u/--user USER -- run supervisord as this user (or numeric uid)
-m/--umask UMASK -- use this umask for daemon subprocess (default is 022)
-d/--directory DIRECTORY -- directory to chdir to when daemonized
-l/--logfile FILENAME -- use FILENAME as logfile path
-y/--logfile_maxbytes BYTES -- use BYTES to limit the max size of logfile
-z/--logfile_backups NUM -- number of backups to keep when max bytes reached
-e/--loglevel LEVEL -- use LEVEL as log level (debug,info,warn,error,critical)
-j/--pidfile FILENAME -- write a pid file for the daemon process to FILENAME
-i/--identifier STR -- identifier used for this instance of supervisord
-q/--childlogdir DIRECTORY -- the log directory for child process logs
-k/--nocleanup --  prevent the process from performing cleanup (removal of
                   old automatic child log files) at startup.
-a/--minfds NUM -- the minimum number of file descriptors for start success
-t/--strip_ansi -- strip ansi escape codes from process output
--minprocs NUM  -- the minimum number of processes available for start success
--profile_options OPTIONS -- run supervisord under profiler and output
                             results based on OPTIONS, which  is a comma-sep'd
                             list of 'cumulative', 'calls', and/or 'callers',
                             e.g. 'cumulative,callers')
"""

import os
import time
import errno
import select
import signal

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.options import ServerOptions
from supervisor.options import signame
from supervisor import events
from supervisor.states import SupervisorStates
from supervisor.states import getProcessStateDescription

class Supervisor:
    stopping = False # set after we detect that we are handling a stop request
    lastshutdownreport = 0 # throttle for delayed process error reports at stop
    process_groups = None # map of process group name to process group object
    stop_groups = None # list used for priority ordered shutdown

    def __init__(self, options):
        self.options = options
        self.process_groups = {}
        self.ticks = {}

    def main(self):
        if not self.options.first:
            # prevent crash on libdispatch-based systems, at least for the
            # first request
            self.options.cleanup_fds()
        info_messages = []
        critical_messages = []
        warn_messages = []
        setuid_msg = self.options.set_uid()
        if setuid_msg:
            critical_messages.append(setuid_msg)
        if self.options.first:
            rlimit_messages = self.options.set_rlimits()
            info_messages.extend(rlimit_messages)
        warn_messages.extend(self.options.parse_warnings)

        # this sets the options.logger object
        # delay logger instantiation until after setuid
        self.options.make_logger(critical_messages, warn_messages,
                                 info_messages)

        if not self.options.nocleanup:
            # clean up old automatic logs
            self.options.clear_autochildlogdir()

        self.run()

    def run(self):
        self.process_groups = {} # clear
        self.stop_groups = None # clear
        events.clear()
        try:
            for config in self.options.process_group_configs:
                self.add_process_group(config)
            self.options.process_environment()
            self.options.openhttpservers(self)
            self.options.setsignals()
            if (not self.options.nodaemon) and self.options.first:
                self.options.daemonize()
            # writing pid file needs to come *after* daemonizing or pid
            # will be wrong
            self.options.write_pidfile()
            self.runforever()
        finally:
            self.options.cleanup()

    def diff_to_active(self, new=None):
        if not new:
            new = self.options.process_group_configs
        cur = [group.config for group in self.process_groups.values()]

        curdict = dict(zip([cfg.name for cfg in cur], cur))
        newdict = dict(zip([cfg.name for cfg in new], new))

        added   = [cand for cand in new if cand.name not in curdict]
        removed = [cand for cand in cur if cand.name not in newdict]

        changed = [cand for cand in new
                   if cand != curdict.get(cand.name, cand)]

        return added, changed, removed

    def add_process_group(self, config):
        name = config.name
        if name not in self.process_groups:
            config.after_setuid()
            self.process_groups[name] = config.make_group()
            events.notify(events.ProcessGroupAddedEvent(name))
            return True
        return False

    def remove_process_group(self, name):
        if self.process_groups[name].get_unstopped_processes():
            return False
        del self.process_groups[name]
        events.notify(events.ProcessGroupRemovedEvent(name))
        return True

    def get_process_map(self):
        process_map = {}
        for group in self.process_groups.values():
            process_map.update(group.get_dispatchers())
        return process_map

    def shutdown_report(self):
        unstopped = []

        for group in self.process_groups.values():
            unstopped.extend(group.get_unstopped_processes())

        if unstopped:
            # throttle 'waiting for x to die' reports
            now = time.time()
            if now > (self.lastshutdownreport + 3): # every 3 secs
                names = [ p.config.name for p in unstopped ]
                namestr = ', '.join(names)
                self.options.logger.info('waiting for %s to die' % namestr)
                self.lastshutdownreport = now
                for proc in unstopped:
                    state = getProcessStateDescription(proc.get_state())
                    self.options.logger.blather(
                        '%s state: %s' % (proc.config.name, state))
        return unstopped

    def ordered_stop_groups_phase_1(self):
        if self.stop_groups:
            # stop the last group (the one with the "highest" priority)
            self.stop_groups[-1].stop_all()

    def ordered_stop_groups_phase_2(self):
        # after phase 1 we've transitioned and reaped, let's see if we
        # can remove the group we stopped from the stop_groups queue.
        if self.stop_groups:
            # pop the last group (the one with the "highest" priority)
            group = self.stop_groups.pop()
            if group.get_unstopped_processes():
                # if any processes in the group aren't yet in a
                # stopped state, we're not yet done shutting this
                # group down, so push it back on to the end of the
                # stop group queue
                self.stop_groups.append(group)

    def runforever(self):
        events.notify(events.SupervisorRunningEvent())
        timeout = 1 # this cannot be fewer than the smallest TickEvent (5)

        socket_map = self.options.get_socket_map()

        while 1:
            combined_map = {}
            combined_map.update(socket_map)
            combined_map.update(self.get_process_map())

            pgroups = list(self.process_groups.values())
            pgroups.sort()

            if self.options.mood < SupervisorStates.RUNNING:
                if not self.stopping:
                    # first time, set the stopping flag, do a
                    # notification and set stop_groups
                    self.stopping = True
                    self.stop_groups = pgroups[:]
                    events.notify(events.SupervisorStoppingEvent())

                self.ordered_stop_groups_phase_1()

                if not self.shutdown_report():
                    # if there are no unstopped processes (we're done
                    # killing everything), it's OK to swtop or reload
                    raise asyncore.ExitNow

            r, w, x = [], [], []

            for fd, dispatcher in combined_map.items():
                if dispatcher.readable():
                    r.append(fd)
                if dispatcher.writable():
                    w.append(fd)

            try:
                r, w, x = self.options.select(r, w, x, timeout)
            except select.error as err:
                r = w = x = []
                if err.args[0] == errno.EINTR:
                    self.options.logger.blather('EINTR encountered in select')
                else:
                    raise

            for fd in r:
                if fd in combined_map:
                    try:
                        dispatcher = combined_map[fd]
                        self.options.logger.blather(
                            'read event caused by %(dispatcher)s',
                            dispatcher=dispatcher)
                        dispatcher.handle_read_event()
                    except asyncore.ExitNow:
                        raise
                    except:
                        combined_map[fd].handle_error()

            for fd in w:
                if fd in combined_map:
                    try:
                        dispatcher = combined_map[fd]
                        self.options.logger.blather(
                            'write event caused by %(dispatcher)s',
                            dispatcher=dispatcher)
                        dispatcher.handle_write_event()
                    except asyncore.ExitNow:
                        raise
                    except:
                        combined_map[fd].handle_error()

            [ group.transition() for group  in pgroups ]

            self.reap()
            self.handle_signal()
            self.tick()

            if self.options.mood < SupervisorStates.RUNNING:
                self.ordered_stop_groups_phase_2()

            if self.options.test:
                break

    def tick(self, now=None):
        """ Send one or more 'tick' events when the timeslice related to
        the period for the event type rolls over """
        if now is None:
            # now won't be None in unit tests
            now = time.time()
        for event in events.TICK_EVENTS:
            period = event.period
            last_tick = self.ticks.get(period)
            if last_tick is None:
                # we just started up
                last_tick = self.ticks[period] = timeslice(period, now)
            this_tick = timeslice(period, now)
            if this_tick != last_tick:
                self.ticks[period] = this_tick
                events.notify(event(this_tick, self))

    def reap(self, once=False):
        pid, sts = self.options.waitpid()
        if pid:
            process = self.options.pidhistory.get(pid, None)
            if process is None:
                self.options.logger.critical('reaped unknown pid %s' % pid)
            else:
                process.finish(pid, sts)
                del self.options.pidhistory[pid]
            if not once:
                self.reap() # keep reaping until no more kids to reap

    def handle_signal(self):
        sig = self.options.get_signal()
        if sig:
            if sig in (signal.SIGTERM, signal.SIGINT, signal.SIGQUIT):
                self.options.logger.warn(
                    'received %s indicating exit request' % signame(sig))
                self.options.mood = SupervisorStates.SHUTDOWN
            elif sig == signal.SIGHUP:
                self.options.logger.warn(
                    'received %s indicating restart request' % signame(sig))
                self.options.mood = SupervisorStates.RESTARTING
            elif sig == signal.SIGCHLD:
                self.options.logger.debug(
                    'received %s indicating a child quit' % signame(sig))
            elif sig == signal.SIGUSR2:
                self.options.logger.info(
                    'received %s indicating log reopen request' % signame(sig))
                self.options.reopenlogs()
                for group in self.process_groups.values():
                    group.reopenlogs()
            else:
                self.options.logger.blather(
                    'received %s indicating nothing' % signame(sig))

    def get_state(self):
        return self.options.mood

def timeslice(period, when):
    return int(when - (when % period))

# profile entry point
def profile(cmd, globals, locals, sort_order, callers):
    try:
        import cProfile as profile
    except ImportError:
        import profile
    import pstats
    import tempfile
    fd, fn = tempfile.mkstemp()
    try:
        profile.runctx(cmd, globals, locals, fn)
        stats = pstats.Stats(fn)
        stats.strip_dirs()
        # calls,time,cumulative and cumulative,calls,time are useful
        stats.sort_stats(*sort_order or ('cumulative', 'calls', 'time'))
        if callers:
            stats.print_callers(.3)
        else:
            stats.print_stats(.3)
    finally:
        os.remove(fn)


# Main program
def main(args=None, test=False):
    assert os.name == "posix", "This code makes Unix-specific assumptions"
    # if we hup, restart by making a new Supervisor()
    first = True
    while 1:
        options = ServerOptions()
        options.realize(args, doc=__doc__)
        options.first = first
        options.test = test
        if options.profile_options:
            sort_order, callers = options.profile_options
            profile('go(options)', globals(), locals(), sort_order, callers)
        else:
            go(options)
        if test or (options.mood < SupervisorStates.RESTARTING):
            break
        options.close_httpservers()
        options.close_logger()
        first = False

def go(options):
    d = Supervisor(options)
    try:
        d.main()
    except asyncore.ExitNow:
        pass

if __name__ == "__main__":
    main()

########NEW FILE########
__FILENAME__ = base
_NOW = 1151365354
_TIMEFORMAT = '%b %d %I:%M %p'

from supervisor.compat import total_ordering
from supervisor.compat import Fault
from supervisor.compat import as_string

# mock is imported here for py2/3 compat.  we only declare mock as a dependency
# via tests_require so it is not available on all supervisor installs.  the
# modules imported in supervisor.compat must always be available.

try: # pragma: no cover
    from unittest.mock import Mock, patch, sentinel
except ImportError: # pragma: no cover
    from mock import Mock, patch, sentinel

try: # pragma: no cover
    import unittest.mock as mock
except ImportError: # pragma: no cover
    import mock

class DummyOptions:

    make_pipes_error = None
    fork_error = None
    execv_error = None
    kill_error = None
    minfds = 5
    loglevel = 20

    def __init__(self):
        self.identifier = 'supervisor'
        self.childlogdir = '/tmp'
        self.uid = 999
        self.logger = self.getLogger()
        self.backofflimit = 10
        self.logfile = '/tmp/logfile'
        self.nocleanup = False
        self.strip_ansi = False
        self.pidhistory = {}
        self.process_group_configs = []
        self.nodaemon = False
        self.socket_map = {}
        self.mood = 1
        self.mustreopen = False
        self.realizeargs = None
        self.fds_cleaned_up = False
        self.rlimit_set = False
        self.setuid_called = False
        self.httpservers_opened = False
        self.signals_set = False
        self.daemonized = False
        self.make_logger_messages = None
        self.autochildlogdir_cleared = False
        self.cleaned_up = False
        self.pidfile_written = False
        self.directory = None
        self.waitpid_return = None, None
        self.kills = {}
        self._signal = None
        self.parent_pipes_closed = None
        self.child_pipes_closed = None
        self.forkpid = 0
        self.pgrp_set = None
        self.duped = {}
        self.written = {}
        self.fds_closed = []
        self._exitcode = None
        self.execve_called = False
        self.execv_args = None
        self.setuid_msg = None
        self.privsdropped = None
        self.logs_reopened = False
        self.environment_processed = False
        self.select_result = [], [], []
        self.select_error = None
        self.write_accept = None
        self.write_error = None
        self.tempfile_name = '/foo/bar'
        self.remove_error = None
        self.removed = []
        self.existing = []
        self.openreturn = None
        self.readfd_result = ''
        self.parse_warnings = []
        self.serverurl = 'http://localhost:9001'
        self.changed_directory = False
        self.chdir_error = None
        self.umaskset = None

    def getLogger(self, *args, **kw):
        logger = DummyLogger()
        logger.handlers = [DummyLogger()]
        logger.args = args, kw
        return logger

    def realize(self, args, **kw):
        self.realizeargs = args
        self.realizekw = kw

    def process_config(self, do_usage=True):
        pass

    def cleanup_fds(self):
        self.fds_cleaned_up = True

    def set_rlimits(self):
        self.rlimits_set = True
        return ['rlimits_set']

    def set_uid(self):
        self.setuid_called = True
        return 'setuid_called'

    def openhttpservers(self, supervisord):
        self.httpservers_opened = True

    def daemonize(self):
        self.daemonized = True

    def setsignals(self):
        self.signals_set = True

    def get_signal(self):
        return self._signal

    def get_socket_map(self):
        return self.socket_map

    def make_logger(self, critical_msgs, warn_msgs, info_msgs):
        self.make_logger_messages = critical_msgs, warn_msgs, info_msgs

    def clear_autochildlogdir(self):
        self.autochildlogdir_cleared = True

    def get_autochildlog_name(self, *ignored):
        return self.tempfile_name

    def cleanup(self):
        self.cleaned_up = True

    def write_pidfile(self):
        self.pidfile_written = True

    def waitpid(self):
        return self.waitpid_return

    def kill(self, pid, sig):
        if self.kill_error:
            raise OSError(self.kill_error)
        self.kills[pid] = sig

    def stat(self, filename):
        import os
        return os.stat(filename)

    def get_path(self):
        return ["/bin", "/usr/bin", "/usr/local/bin"]

    def get_pid(self):
        import os
        return os.getpid()

    def check_execv_args(self, filename, argv, st):
        if filename == '/bad/filename':
            from supervisor.options import NotFound
            raise NotFound('bad filename')

    def make_pipes(self, stderr=True):
        if self.make_pipes_error:
            raise OSError(self.make_pipes_error)
        pipes = {'child_stdin': 3, 'stdin': 4, 'stdout': 5, 'child_stdout': 6}
        if stderr:
            pipes['stderr'], pipes['child_stderr'] = (7, 8)
        else:
            pipes['stderr'], pipes['child_stderr'] = None, None
        return pipes

    def write(self, fd, chars):
        if self.write_error:
            raise OSError(self.write_error)
        if self.write_accept:
            chars = chars[self.write_accept]
        data = self.written.setdefault(fd, '')
        data += chars
        self.written[fd] = data
        return len(chars)

    def fork(self):
        if self.fork_error:
            raise OSError(self.fork_error)
        return self.forkpid

    def close_fd(self, fd):
        self.fds_closed.append(fd)

    def close_parent_pipes(self, pipes):
        self.parent_pipes_closed = pipes

    def close_child_pipes(self, pipes):
        self.child_pipes_closed = pipes

    def setpgrp(self):
        self.pgrp_set = True

    def dup2(self, frm, to):
        self.duped[frm] = to

    def _exit(self, code):
        self._exitcode = code

    def execve(self, filename, argv, environment):
        self.execve_called = True
        if self.execv_error:
            if self.execv_error == 1:
                raise OSError(self.execv_error)
            else:
                raise RuntimeError(self.execv_error)
        self.execv_args = (filename, argv)
        self.execv_environment = environment

    def dropPrivileges(self, uid):
        if self.setuid_msg:
            return self.setuid_msg
        self.privsdropped = uid

    def readfd(self, fd):
        return self.readfd_result

    def reopenlogs(self):
        self.logs_reopened = True

    def process_environment(self):
        self.environment_processed = True

    def mktempfile(self, prefix, suffix, dir):
        return self.tempfile_name

    def select(self, r, w, x, timeout):
        import select
        if self.select_error:
            raise select.error(self.select_error)
        return self.select_result

    def remove(self, path):
        import os
        if self.remove_error:
            raise os.error(self.remove_error)
        self.removed.append(path)

    def exists(self, path):
        if path in self.existing:
            return True
        return False

    def open(self, name, mode='r'):
        if self.openreturn:
            return self.openreturn
        return open(name, mode)

    def chdir(self, dir):
        if self.chdir_error:
            raise OSError(self.chdir_error)
        self.changed_directory = True

    def setumask(self, mask):
        self.umaskset = mask

class DummyLogger:
    level = None

    def __init__(self):
        self.reopened = False
        self.removed = False
        self.closed = False
        self.data = []

    def info(self, msg, **kw):
        if kw:
            msg = msg % kw
        self.data.append(msg)
    warn = debug = critical = trace = error = blather = info

    def log(self, level, msg, **kw):
        if kw:
            msg = msg % kw
        self.data.append(msg)

    def addHandler(self, handler):
        pass

    def reopen(self):
        self.reopened = True
    def close(self):
        self.closed = True
    def remove(self):
        self.removed = True
    def flush(self):
        self.flushed = True
    def getvalue(self):
        return ''.join(self.data)

class DummySupervisor:
    def __init__(self, options=None, state=None, process_groups=None):
        if options is None:
            self.options = DummyOptions()
        else:
            self.options = options
        if state is None:
            from supervisor.supervisord import SupervisorStates
            self.options.mood = SupervisorStates.RUNNING
        else:
            self.options.mood = state
        if process_groups is None:
            self.process_groups = {}
        else:
            self.process_groups = process_groups

    def get_state(self):
        return self.options.mood

class DummySocket:
    bind_called = False
    bind_addr = None
    listen_called = False
    listen_backlog = None
    close_called = False

    def __init__(self, fd):
        self.fd = fd

    def fileno(self):
        return self.fd

    def bind(self, addr):
        self.bind_called = True
        self.bind_addr = addr

    def listen(self, backlog):
        self.listen_called = True
        self.listen_backlog = backlog

    def close(self):
        self.close_called = True

    def __str__(self):
        return 'dummy socket'

class DummySocketConfig:
    def __init__(self, fd):
        self.fd = fd
        self.url = 'unix:///sock'

    def addr(self):
        return 'dummy addr'

    def __eq__(self, other):
        return self.fd == other.fd

    def __ne__(self, other):
        return not self.__eq__(other)

    def create_and_bind(self):
        return DummySocket(self.fd)

class DummySocketManager:
    def __init__(self, config, **kwargs):
        self._config = config

    def config(self):
        return self._config

    def get_socket(self):
        return DummySocket(self._config.fd)

class DummyProcess(object):
    # Initial state; overridden by instance variables
    pid = 0 # Subprocess pid; 0 when not running
    laststart = 0 # Last time the subprocess was started; 0 if never
    laststop = 0  # Last time the subprocess was stopped; 0 if never
    delay = 0 # If nonzero, delay starting or killing until this time
    administrative_stop = 0 # true if the process has been stopped by an admin
    system_stop = 0 # true if the process has been stopped by the system
    killing = 0 # flag determining whether we are trying to kill this proc
    backoff = 0 # backoff counter (to backofflimit)
    waitstatus = None
    exitstatus = None
    pipes = None
    rpipes = None
    dispatchers = None
    stdout_logged = ''
    stderr_logged = ''
    spawnerr = None
    stdout_buffer = '' # buffer of characters from child stdout output to log
    stderr_buffer = '' # buffer of characters from child stderr output to log
    stdin_buffer = '' # buffer of characters to send to child process' stdin
    listener_state = None
    group = None

    def __init__(self, config, state=None):
        self.config = config
        self.logsremoved = False
        self.stop_called = False
        self.backoff_secs = None
        self.spawned = False
        if state is None:
            from supervisor.process import ProcessStates
            state = ProcessStates.RUNNING
        self.state = state
        self.error_at_clear = False
        self.killed_with = None
        self.drained = False
        self.stdout_buffer = ''
        self.stderr_buffer = ''
        self.stdout_logged = ''
        self.stderr_logged = ''
        self.stdin_buffer = ''
        self.pipes = {}
        self.rpipes = {}
        self.dispatchers = {}
        self.finished = None
        self.logs_reopened = False
        self.execv_arg_exception = None
        self.input_fd_drained = None
        self.output_fd_drained = None
        self.transitioned = False
        self.write_error = None

    def reopenlogs(self):
        self.logs_reopened = True

    def removelogs(self):
        if self.error_at_clear:
            raise IOError('whatever')
        self.logsremoved = True

    def get_state(self):
        return self.state

    def stop(self):
        self.stop_called = True
        self.killing = False
        from supervisor.process import ProcessStates
        self.state = ProcessStates.STOPPED

    def kill(self, signal):
        self.killed_with = signal

    def spawn(self):
        self.spawned = True
        from supervisor.process import ProcessStates
        self.state = ProcessStates.RUNNING

    def drain(self):
        self.drained = True

    def readable_fds(self):
        return []

    def record_output(self):
        self.stdout_logged += self.stdout_buffer
        self.stdout_buffer = ''

        self.stderr_logged += self.stderr_buffer
        self.stderr_buffer = ''

    def finish(self, pid, sts):
        self.finished = pid, sts

    def give_up(self):
        from supervisor.process import ProcessStates
        self.state = ProcessStates.FATAL

    def get_execv_args(self):
        if self.execv_arg_exception:
            raise self.execv_arg_exception('whatever')
        import shlex
        commandargs = shlex.split(self.config.command)
        program = commandargs[0]
        return program, commandargs

    def drain_output_fd(self, fd):
        self.output_fd_drained = fd

    def drain_input_fd(self, fd):
        self.input_fd_drained = fd

    def write(self, chars):
        if self.write_error:
            raise OSError(self.write_error)
        self.stdin_buffer += chars

    def transition(self):
        self.transitioned = True

    def __eq__(self, other):
        return self.config.priority == other.config.priority

    def __lt__(self, other):
        return self.config.priority < other.config.priority

DummyProcess = total_ordering(DummyProcess)

class DummyPConfig:
    def __init__(self, options, name, command, directory=None, umask=None,
                 priority=999, autostart=True,
                 autorestart=True, startsecs=10, startretries=999,
                 uid=None, stdout_logfile=None, stdout_capture_maxbytes=0,
                 stdout_events_enabled=False,
                 stdout_logfile_backups=0, stdout_logfile_maxbytes=0,
                 stderr_logfile=None, stderr_capture_maxbytes=0,
                 stderr_events_enabled=False,
                 stderr_logfile_backups=0, stderr_logfile_maxbytes=0,
                 redirect_stderr=False,
                 stopsignal=None, stopwaitsecs=10, stopasgroup=False, killasgroup=False,
                 exitcodes=(0,2), environment=None, serverurl=None):
        self.options = options
        self.name = name
        self.command = command
        self.priority = priority
        self.autostart = autostart
        self.autorestart = autorestart
        self.startsecs = startsecs
        self.startretries = startretries
        self.uid = uid
        self.stdout_logfile = stdout_logfile
        self.stdout_capture_maxbytes = stdout_capture_maxbytes
        self.stdout_events_enabled = stdout_events_enabled
        self.stdout_logfile_backups = stdout_logfile_backups
        self.stdout_logfile_maxbytes = stdout_logfile_maxbytes
        self.stderr_logfile = stderr_logfile
        self.stderr_capture_maxbytes = stderr_capture_maxbytes
        self.stderr_events_enabled = stderr_events_enabled
        self.stderr_logfile_backups = stderr_logfile_backups
        self.stderr_logfile_maxbytes = stderr_logfile_maxbytes
        self.redirect_stderr = redirect_stderr
        if stopsignal is None:
            import signal
            stopsignal = signal.SIGTERM
        self.stopsignal = stopsignal
        self.stopwaitsecs = stopwaitsecs
        self.stopasgroup = stopasgroup
        self.killasgroup = killasgroup
        self.exitcodes = exitcodes
        self.environment = environment
        self.directory = directory
        self.umask = umask
        self.autochildlogs_created = False
        self.serverurl = serverurl

    def create_autochildlogs(self):
        self.autochildlogs_created = True

    def make_process(self, group=None):
        process = DummyProcess(self)
        process.group = group
        return process

    def make_dispatchers(self, proc):
        use_stderr = not self.redirect_stderr
        pipes = self.options.make_pipes(use_stderr)
        stdout_fd,stderr_fd,stdin_fd = (pipes['stdout'],pipes['stderr'],
                                        pipes['stdin'])
        dispatchers = {}
        if stdout_fd is not None:
            dispatchers[stdout_fd] = DummyDispatcher(readable=True)
        if stderr_fd is not None:
            dispatchers[stderr_fd] = DummyDispatcher(readable=True)
        if stdin_fd is not None:
            dispatchers[stdin_fd] = DummyDispatcher(writable=True)
        return dispatchers, pipes

def makeExecutable(file, substitutions=None):
    import os
    import sys
    import tempfile

    if substitutions is None:
        substitutions = {}
    data = open(file).read()
    last = os.path.split(file)[1]

    substitutions['PYTHON'] = sys.executable
    for key in substitutions.keys():
        data = data.replace('<<%s>>' % key.upper(), substitutions[key])

    tmpnam = tempfile.mktemp(prefix=last)
    f = open(tmpnam, 'w')
    f.write(data)
    f.close()
    os.chmod(tmpnam, 493) # 0755 on Py2, 0o755 on Py3
    return tmpnam

def makeSpew(unkillable=False):
    import os
    here = os.path.dirname(__file__)
    if not unkillable:
        return makeExecutable(os.path.join(here, 'fixtures/spew.py'))
    return makeExecutable(os.path.join(here, 'fixtures/unkillable_spew.py'))

class DummyMedusaServerLogger:
    def __init__(self):
        self.logged = []
    def log(self, category, msg):
        self.logged.append((category, msg))

class DummyMedusaServer:
    def __init__(self):
        self.logger = DummyMedusaServerLogger()

class DummyMedusaChannel:
    def __init__(self):
        self.server = DummyMedusaServer()
        self.producer = None

    def push_with_producer(self, producer):
        self.producer = producer

    def close_when_done(self):
        pass

    def set_terminator(self, terminator):
        pass

class DummyRequest(dict):
    command = 'GET'
    _error = None
    _done = False
    version = '1.0'
    def __init__(self, path, params, query, fragment, env=None):
        self.args = path, params, query, fragment
        self.producers = []
        self.headers = {}
        self.header = []
        self.outgoing = []
        self.channel = DummyMedusaChannel()
        if env is None:
            self.env = {}
        else:
            self.env = env

    def split_uri(self):
        return self.args

    def error(self, code):
        self._error = code

    def push(self, producer):
        self.producers.append(producer)

    def __setitem__(self, header, value):
        self.headers[header] = value

    def has_key(self, header):
        return header in self.headers

    def __contains__(self, item):
        return item in self.headers

    def done(self):
        self._done = True

    def build_reply_header(self):
        return ''

    def log(self, *arg, **kw):
        pass

    def cgi_environment(self):
        return self.env

    def get_server_url(self):
        return 'http://example.com'


class DummyRPCInterfaceFactory:
    def __init__(self, supervisord, **config):
        self.supervisord = supervisord
        self.config = config

class DummyRPCServer:
    def __init__(self):
        self.supervisor = DummySupervisorRPCNamespace()
        self.system = DummySystemRPCNamespace()

class DummySystemRPCNamespace:
    pass

class DummySupervisorRPCNamespace:
    _restartable = True
    _restarted = False
    _shutdown = False
    _readlog_error = False


    from supervisor.process import ProcessStates
    all_process_info = [
        {
        'name':'foo',
        'group':'foo',
        'pid':11,
        'state':ProcessStates.RUNNING,
        'statename':'RUNNING',
        'start':_NOW - 100,
        'stop':0,
        'spawnerr':'',
        'now':_NOW,
        'description':'foo description',
        },
        {
        'name':'bar',
        'group':'bar',
        'pid':12,
        'state':ProcessStates.FATAL,
        'statename':'FATAL',
        'start':_NOW - 100,
        'stop':_NOW - 50,
        'spawnerr':'screwed',
        'now':_NOW,
        'description':'bar description',
        },
        {
        'name':'baz_01',
        'group':'baz',
        'pid':13,
        'state':ProcessStates.STOPPED,
        'statename':'STOPPED',
        'start':_NOW - 100,
        'stop':_NOW - 25,
        'spawnerr':'',
        'now':_NOW,
        'description':'baz description',
        },
        ]

    def getAPIVersion(self):
        return '3.0'

    getVersion = getAPIVersion # deprecated

    def getPID(self):
        return 42

    def readProcessStdoutLog(self, name, offset, length):
        from supervisor import xmlrpc
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        elif name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, 'FAILED')
        elif name == 'NO_FILE':
            raise Fault(xmlrpc.Faults.NO_FILE, 'NO_FILE')
        a = 'output line\n' * 10
        return a[offset:]

    readProcessLog = readProcessStdoutLog
    readProcessStderrLog = readProcessStdoutLog

    def getAllProcessInfo(self):
        return self.all_process_info

    def getProcessInfo(self, name):
        from supervisor import xmlrpc
        for i in self.all_process_info:
            if i['name']==name:
                info=i
                return info
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        if name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, 'FAILED')
        if name == 'NO_FILE':
            raise Fault(xmlrpc.Faults.NO_FILE, 'NO_FILE')

    def startProcess(self, name):
        from supervisor import xmlrpc
        if name == 'BAD_NAME:BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME:BAD_NAME')
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        if name == 'NO_FILE':
            raise Fault(xmlrpc.Faults.NO_FILE, 'NO_FILE')
        if name == 'NOT_EXECUTABLE':
            raise Fault(xmlrpc.Faults.NOT_EXECUTABLE, 'NOT_EXECUTABLE')
        if name == 'ALREADY_STARTED':
            raise Fault(xmlrpc.Faults.ALREADY_STARTED, 'ALREADY_STARTED')
        if name == 'SPAWN_ERROR':
            raise Fault(xmlrpc.Faults.SPAWN_ERROR, 'SPAWN_ERROR')
        if name == 'ABNORMAL_TERMINATION':
            raise Fault(xmlrpc.Faults.ABNORMAL_TERMINATION,
                        'ABNORMAL_TERMINATION')
        return True

    def startProcessGroup(self, name):
        from supervisor import xmlrpc
        from supervisor.compat import Fault
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        return [
            {'name':'foo_00', 'group':'foo',
             'status': xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo_01', 'group':'foo',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            ]

    def startAllProcesses(self):
        from supervisor import xmlrpc
        return [
            {'name':'foo', 'group':'foo',
             'status': xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo2', 'group':'foo2',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'failed', 'group':'failed_group',
             'status':xmlrpc.Faults.SPAWN_ERROR,
             'description':'SPAWN_ERROR'}
            ]

    def stopProcessGroup(self, name):
        from supervisor import xmlrpc
        from supervisor.compat import Fault
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        return [
            {'name':'foo_00', 'group':'foo',
             'status': xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo_01', 'group':'foo',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            ]

    def stopProcess(self, name):
        from supervisor import xmlrpc
        if name == 'BAD_NAME:BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME:BAD_NAME')
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        if name == 'NOT_RUNNING':
            raise Fault(xmlrpc.Faults.NOT_RUNNING, 'NOT_RUNNING')
        if name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, 'FAILED')

        return True

    def stopAllProcesses(self):
        from supervisor import xmlrpc
        return [
            {'name':'foo','group':'foo',
             'status': xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo2', 'group':'foo2',
             'status':xmlrpc.Faults.SUCCESS,'description': 'OK'},
            {'name':'failed', 'group':'failed_group',
             'status':xmlrpc.Faults.BAD_NAME,
             'description':'FAILED'}
            ]

    def restart(self):
        if self._restartable:
            self._restarted = True
            return
        from supervisor import xmlrpc
        raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')

    def shutdown(self):
        if self._restartable:
            self._shutdown = True
            return
        from supervisor import xmlrpc
        raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')

    def reloadConfig(self):
        return [[['added'], ['changed'], ['removed']]]

    def addProcessGroup(self, name):
        from supervisor import xmlrpc
        if name == 'ALREADY_ADDED':
            raise Fault(xmlrpc.Faults.ALREADY_ADDED, '')
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, '')
        if name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, '')
        if name == 'SHUTDOWN_STATE':
            raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
        if hasattr(self, 'processes'):
            self.processes.append(name)
        else:
            self.processes = [name]

    def removeProcessGroup(self, name):
        from supervisor import xmlrpc
        if name == 'STILL_RUNNING':
            raise Fault(xmlrpc.Faults.STILL_RUNNING, '')
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, '')
        if name == 'FAILED':
            raise Fault(xmlrpc.Faults.FAILED, '')
        self.processes.remove(name)

    def clearProcessStdoutLog(self, name):
        from supervisor import xmlrpc
        if name == 'BAD_NAME':
            raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
        return True

    clearProcessLog = clearProcessStdoutLog
    clearProcessStderrLog = clearProcessStdoutLog
    clearProcessLogs = clearProcessStdoutLog

    def clearAllProcessLogs(self):
        from supervisor import xmlrpc
        return [
            {'name':'foo', 'group':'foo',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'foo2', 'group':'foo2',
             'status':xmlrpc.Faults.SUCCESS,
             'description': 'OK'},
            {'name':'failed', 'group':'failed_group',
             'status':xmlrpc.Faults.FAILED,
             'description':'FAILED'}
            ]

    def raiseError(self):
        raise ValueError('error')

    def getSupervisorVersion(self):
        return '3000'

    def readLog(self, whence, offset):
        if self._readlog_error:
            raise Fault(self._readlog_error, '')
        return 'mainlogdata'

class DummyPGroupConfig:
    def __init__(self, options, name='whatever', priority=999, pconfigs=None):
        self.options = options
        self.name = name
        self.priority = priority
        if pconfigs is None:
            pconfigs = []
        self.process_configs = pconfigs
        self.after_setuid_called = False
        self.pool_events = []
        self.buffer_size = 10

    def after_setuid(self):
        self.after_setuid_called = True

    def make_group(self):
        return DummyProcessGroup(self)

    def __repr__(self):
        return '<%s instance at %s named %s>' % (self.__class__, id(self),
                                                 self.name)

class DummyFCGIGroupConfig(DummyPGroupConfig):
    def __init__(self, options, name='whatever', priority=999, pconfigs=None, socket_config=DummySocketConfig(1)):
        DummyPGroupConfig.__init__(self, options, name, priority, pconfigs)
        self.socket_config = socket_config

class DummyProcessGroup(object):
    def __init__(self, config):
        self.config = config
        self.transitioned = False
        self.all_stopped = False
        self.dispatchers = {}
        self.unstopped_processes = []

    def transition(self):
        self.transitioned = True

    def stop_all(self):
        self.all_stopped = True

    def get_unstopped_processes(self):
        return self.unstopped_processes

    def get_dispatchers(self):
        return self.dispatchers

    def __lt__(self, other):
        return self.config.priority < other.config.priority

    def __eq__(self, other):
        return self.config.priority == other.config.priority

DummyProcessGroup = total_ordering(DummyProcessGroup)

class DummyFCGIProcessGroup(DummyProcessGroup):

    def __init__(self, config):
        DummyProcessGroup.__init__(self, config)
        self.socket_manager = DummySocketManager(config.socket_config)

class PopulatedDummySupervisor(DummySupervisor):
    def __init__(self, options, group_name, *pconfigs):
        DummySupervisor.__init__(self, options)
        self.process_groups = {}
        processes = {}
        self.group_name = group_name
        gconfig = DummyPGroupConfig(options, group_name, pconfigs=pconfigs)
        pgroup = DummyProcessGroup(gconfig)
        self.process_groups[group_name] = pgroup
        for pconfig in pconfigs:
            process = DummyProcess(pconfig)
            processes[pconfig.name] = process
        pgroup.processes = processes

    def set_procattr(self, process_name, attr_name, val, group_name=None):
        if group_name is None:
            group_name = self.group_name
        process = self.process_groups[group_name].processes[process_name]
        setattr(process, attr_name, val)

class DummyDispatcher:
    write_event_handled = False
    read_event_handled = False
    error_handled = False
    logs_reopened = False
    logs_removed = False
    closed = False
    flush_error = None
    flushed = False
    def __init__(self, readable=False, writable=False, error=False):
        self._readable = readable
        self._writable = writable
        self._error = error
        self.input_buffer = ''
        if readable:
            # only readable dispatchers should have these methods
            def reopenlogs():
                self.logs_reopened = True
            self.reopenlogs = reopenlogs
            def removelogs():
                self.logs_removed = True
            self.removelogs = removelogs

    def readable(self):
        return self._readable
    def writable(self):
        return self._writable
    def handle_write_event(self):
        if self._error:
            raise self._error
        self.write_event_handled = True
    def handle_read_event(self):
        if self._error:
            raise self._error
        self.read_event_handled = True
    def handle_error(self):
        self.error_handled = True
    def close(self):
        self.closed = True
    def flush(self):
        if self.flush_error:
            raise OSError(self.flush_error)
        self.flushed = True

class DummyStream:
    def __init__(self, error=None, fileno=20):
        self.error = error
        self.closed = False
        self.flushed = False
        self.written = ''
        self._fileno = fileno
    def close(self):
        if self.error:
            raise self.error
        self.closed = True
    def flush(self):
        if self.error:
            raise self.error
        self.flushed = True
    def write(self, msg):
        if self.error:
            error = self.error
            self.error = None
            raise error
        self.written += as_string(msg)
    def seek(self, num, whence=0):
        pass
    def tell(self):
        return len(self.written)
    def fileno(self):
        return self._fileno

class DummyEvent:
    def __init__(self, serial='abc'):
        if serial is not None:
            self.serial = serial

    def __str__(self):
        return 'dummy event'

def dummy_handler(event, result):
    pass

def rejecting_handler(event, result):
    from supervisor.dispatchers import RejectEvent
    raise RejectEvent(result)

def exception_handler(event, result):
    raise ValueError(result)

def lstrip(s):
    strings = [x.strip() for x in s.split('\n')]
    return '\n'.join(strings)

########NEW FILE########
__FILENAME__ = fakeos
from os import *
from os import _exit
import os

class FakeOS:
    def __init__(self):
        self.orig_uid = os.getuid()
        self.orig_gid = os.getgid()

    def setgroups(*args):
        return

    def getuid():
        return 0

    def setuid(arg):
        self.uid = arg
        self.setuid_called = 1

    def setgid(arg):
        self.gid = arg
        self.setgid_called = 1

    def clear():
        self.uid = orig_uid
        self.gid = orig_gid
        self.setuid_called = 0
        self.setgid_called = 0

fake = FakeOS()

setgroups = fake.setgroups
getuid = fake.getuid
setuid = fake.setuid
setgid = fake.setgid
clear = fake.clear

########NEW FILE########
__FILENAME__ = spew
#!<<PYTHON>>
import time

counter = 0

while 1:
   time.sleep(0.01)
   print("more spewage %s" % counter)
   counter += 1
   

########NEW FILE########
__FILENAME__ = unkillable_spew
#!<<PYTHON>>
import time
import signal
signal.signal(signal.SIGTERM, signal.SIG_IGN)

counter = 0

while 1:
   time.sleep(0.01)
   print("more spewage %s" % counter)
   counter += 1
   

########NEW FILE########
__FILENAME__ = test_childutils
import sys
import time
import unittest
from supervisor.compat import StringIO

class ChildUtilsTests(unittest.TestCase):
    def test_getRPCInterface(self):
        from supervisor.childutils import getRPCInterface
        rpc = getRPCInterface({'SUPERVISOR_SERVER_URL':'http://localhost:9001'})
        # we can't really test this thing; its a magic object
        self.assertTrue(rpc is not None)

    def test_getRPCTransport_no_uname_pass(self):
        from supervisor.childutils import getRPCTransport
        t = getRPCTransport({'SUPERVISOR_SERVER_URL':'http://localhost:9001'})
        self.assertEqual(t.username, '')
        self.assertEqual(t.password, '')
        self.assertEqual(t.serverurl, 'http://localhost:9001')

    def test_getRPCTransport_with_uname_pass(self):
        from supervisor.childutils import getRPCTransport
        env = {'SUPERVISOR_SERVER_URL':'http://localhost:9001',
               'SUPERVISOR_USERNAME':'chrism',
               'SUPERVISOR_PASSWORD':'abc123'}
        t = getRPCTransport(env)
        self.assertEqual(t.username, 'chrism')
        self.assertEqual(t.password, 'abc123')
        self.assertEqual(t.serverurl, 'http://localhost:9001')

    def test_get_headers(self):
        from supervisor.childutils import get_headers
        line = 'a:1 b:2'
        result = get_headers(line)
        self.assertEqual(result, {'a':'1', 'b':'2'})

    def test_eventdata(self):
        from supervisor.childutils import eventdata
        payload = 'a:1 b:2\nthedata\n'
        headers, data = eventdata(payload)
        self.assertEqual(headers, {'a':'1', 'b':'2'})
        self.assertEqual(data, 'thedata\n')

    def test_get_asctime(self):
        from supervisor.childutils import get_asctime
        timestamp = time.mktime((2009, 1, 18, 22, 14, 7, 0, 0, -1))
        result = get_asctime(timestamp)
        self.assertEqual(result, '2009-01-18 22:14:07,000')

class TestProcessCommunicationsProtocol(unittest.TestCase):
    def test_send(self):
        from supervisor.childutils import pcomm
        stdout = StringIO()
        pcomm.send('hello', stdout)
        from supervisor.events import ProcessCommunicationEvent
        begin = ProcessCommunicationEvent.BEGIN_TOKEN
        end = ProcessCommunicationEvent.END_TOKEN
        self.assertEqual(stdout.getvalue(), '%s%s%s' % (begin, 'hello', end))

    def test_stdout(self):
        from supervisor.childutils import pcomm
        old = sys.stdout
        try:
            io = sys.stdout = StringIO()
            pcomm.stdout('hello')
            from supervisor.events import ProcessCommunicationEvent
            begin = ProcessCommunicationEvent.BEGIN_TOKEN
            end = ProcessCommunicationEvent.END_TOKEN
            self.assertEqual(io.getvalue(), '%s%s%s' % (begin, 'hello', end))
        finally:
            sys.stdout = old

    def test_stderr(self):
        from supervisor.childutils import pcomm
        old = sys.stderr
        try:
            io = sys.stderr = StringIO()
            pcomm.stderr('hello')
            from supervisor.events import ProcessCommunicationEvent
            begin = ProcessCommunicationEvent.BEGIN_TOKEN
            end = ProcessCommunicationEvent.END_TOKEN
            self.assertEqual(io.getvalue(), '%s%s%s' % (begin, 'hello', end))
        finally:
            sys.stderr = old

class TestEventListenerProtocol(unittest.TestCase):
    def test_wait(self):
        from supervisor.childutils import listener
        class Dummy:
            def readline(self):
                return 'len:5'
            def read(self, *ignored):
                return 'hello'
        stdin = Dummy()
        stdout = StringIO()
        headers, payload = listener.wait(stdin, stdout)
        self.assertEqual(headers, {'len':'5'})
        self.assertEqual(payload, 'hello')
        self.assertEqual(stdout.getvalue(), 'READY\n')

    def test_token(self):
        from supervisor.childutils import listener
        from supervisor.dispatchers import PEventListenerDispatcher
        token = PEventListenerDispatcher.READY_FOR_EVENTS_TOKEN
        stdout = StringIO()
        listener.ready(stdout)
        self.assertEqual(stdout.getvalue(), token)

    def test_ok(self):
        from supervisor.childutils import listener
        from supervisor.dispatchers import PEventListenerDispatcher
        begin = PEventListenerDispatcher.RESULT_TOKEN_START
        stdout = StringIO()
        listener.ok(stdout)
        self.assertEqual(stdout.getvalue(), begin + '2\nOK')

    def test_fail(self):
        from supervisor.childutils import listener
        from supervisor.dispatchers import PEventListenerDispatcher
        begin = PEventListenerDispatcher.RESULT_TOKEN_START
        stdout = StringIO()
        listener.fail(stdout)
        self.assertEqual(stdout.getvalue(), begin + '4\nFAIL')

    def test_send(self):
        from supervisor.childutils import listener
        from supervisor.dispatchers import PEventListenerDispatcher
        begin = PEventListenerDispatcher.RESULT_TOKEN_START
        stdout = StringIO()
        msg = 'the body data ya fool\n'
        listener.send(msg, stdout)
        expected = '%s%s\n%s' % (begin, len(msg), msg)
        self.assertEqual(stdout.getvalue(), expected)


def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_confecho
"""Test suite for supervisor.confecho"""

import sys
import unittest
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO
from supervisor import confecho

class TopLevelFunctionTests(unittest.TestCase):
    def test_main_writes_data_out_that_looks_like_a_config_file(self):
        sio = StringIO()
        confecho.main(out=sio)

        output = sio.getvalue()
        self.assertTrue("[supervisord]" in output)


def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_datatypes
"""Test suite for supervisor.datatypes"""

import os
import signal
import tempfile
import unittest

from supervisor.tests.base import Mock, patch, sentinel
from supervisor.compat import maxint

from supervisor import datatypes
import supervisor.medusa.text_socket as socket

class ProcessOrGroupName(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.process_or_group_name(arg)

    def test_strips_surrounding_whitespace(self):
        name = " foo\t"
        self.assertEqual(self._callFUT(name), "foo")

    def test_disallows_inner_spaces(self):
        name = "foo bar"
        self.assertRaises(ValueError, self._callFUT, name)

    def test_disallows_colons(self):
        name = "foo:bar"
        self.assertRaises(ValueError, self._callFUT, name)

class IntegerTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.integer(arg)

    def test_converts_numeric(self):
        self.assertEqual(self._callFUT('1'), 1)

    def test_converts_numeric_overflowing_int(self):
        self.assertEqual(self._callFUT(str(maxint+1)), maxint+1)

    def test_raises_for_non_numeric(self):
        self.assertRaises(ValueError, self._callFUT, 'abc')

class BooleanTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.boolean(arg)

    def test_returns_true_for_truthy_values(self):
        for s in datatypes.TRUTHY_STRINGS:
            self.assertEqual(self._callFUT(s), True)

    def test_returns_true_for_upper_truthy_values(self):
        for s in map(str.upper, datatypes.TRUTHY_STRINGS):
            self.assertEqual(self._callFUT(s), True)

    def test_returns_false_for_falsy_values(self):
        for s in datatypes.FALSY_STRINGS:
            self.assertEqual(self._callFUT(s), False)

    def test_returns_false_for_upper_falsy_values(self):
        for s in map(str.upper, datatypes.FALSY_STRINGS):
            self.assertEqual(self._callFUT(s), False)

    def test_braises_value_error_for_bad_value(self):
        self.assertRaises(ValueError,
                          self._callFUT, 'not-a-value')

class ListOfStringsTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.list_of_strings(arg)

    def test_returns_empty_list_for_empty_string(self):
        self.assertEqual(self._callFUT(''), [])

    def test_returns_list_of_strings_by_comma_split(self):
        self.assertEqual(self._callFUT('foo,bar'), ['foo', 'bar'])

    def test_returns_strings_with_whitespace_stripped(self):
        self.assertEqual(self._callFUT(' foo , bar '), ['foo', 'bar'])

    def test_raises_value_error_when_comma_split_fails(self):
        self.assertRaises(ValueError,
                          self._callFUT, 42)

class ListOfIntsTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.list_of_ints(arg)

    def test_returns_empty_list_for_empty_string(self):
        self.assertEqual(self._callFUT(''), [])

    def test_returns_list_of_ints_by_comma_split(self):
        self.assertEqual(self._callFUT('1,42'), [1,42])

    def test_returns_ints_even_if_whitespace_in_string(self):
        self.assertEqual(self._callFUT(' 1 , 42 '), [1,42])

    def test_raises_value_error_when_comma_split_fails(self):
        self.assertRaises(ValueError,
                          self._callFUT, 42)

    def test_raises_value_error_when_one_value_is_bad(self):
        self.assertRaises(ValueError,
                          self._callFUT, '1, bad, 42')

class ListOfExitcodesTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.list_of_exitcodes(arg)

    def test_returns_list_of_ints_from_csv(self):
        self.assertEqual(self._callFUT('1,2,3'), [1,2,3])

    def test_returns_list_of_ints_from_one(self):
        self.assertEqual(self._callFUT('1'), [1])

    def test_raises_for_invalid_exitcode_values(self):
        self.assertRaises(ValueError, self._callFUT, 'a,b,c')
        self.assertRaises(ValueError, self._callFUT, '1024')
        self.assertRaises(ValueError, self._callFUT, '-1,1')

class DictOfKeyValuePairsTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.dict_of_key_value_pairs(arg)

    def test_returns_empty_dict_for_empty_str(self):
        actual = self._callFUT('')
        self.assertEqual({}, actual)

    def test_returns_dict_from_single_pair_str(self):
        actual = self._callFUT('foo=bar')
        expected = {'foo': 'bar'}
        self.assertEqual(actual, expected)

    def test_returns_dict_from_multi_pair_str(self):
        actual = self._callFUT('foo=bar,baz=qux')
        expected = {'foo': 'bar', 'baz': 'qux'}
        self.assertEqual(actual, expected)

    def test_returns_dict_even_if_whitespace(self):
        actual = self._callFUT(' foo = bar , baz = qux ')
        expected = {'foo': 'bar', 'baz': 'qux'}
        self.assertEqual(actual, expected)

    def test_returns_dict_even_if_newlines(self):
        actual = self._callFUT('foo\n=\nbar\n,\nbaz\n=\nqux')
        expected = {'foo': 'bar', 'baz': 'qux'}
        self.assertEqual(actual, expected)

    def test_handles_commas_inside_apostrophes(self):
        actual = self._callFUT("foo='bar,baz',baz='q,ux'")
        expected = {'foo': 'bar,baz', 'baz': 'q,ux'}
        self.assertEqual(actual, expected)

    def test_handles_commas_inside_quotes(self):
        actual = self._callFUT('foo="bar,baz",baz="q,ux"')
        expected = {'foo': 'bar,baz', 'baz': 'q,ux'}
        self.assertEqual(actual, expected)

    def test_handles_newlines_inside_quotes(self):
        actual = datatypes.dict_of_key_value_pairs('foo="a\nb\nc"')
        expected = {'foo': 'a\nb\nc'}
        self.assertEqual(actual, expected)

    def test_handles_quotes_inside_quotes(self):
        actual = datatypes.dict_of_key_value_pairs('foo="\'\\""')
        expected = {'foo': '\'"'}
        self.assertEqual(actual, expected)

    def test_handles_empty_inside_quotes(self):
        actual = datatypes.dict_of_key_value_pairs('foo=""')
        expected = {'foo': ''}
        self.assertEqual(actual, expected)

    def test_handles_unquoted_non_alphanum(self):
        actual = self._callFUT(
            'HOME=/home/auser,FOO=/.foo+(1.2)-_/,'
            'SUPERVISOR_SERVER_URL=http://127.0.0.1:9001')
        expected = {'HOME': '/home/auser', 'FOO': '/.foo+(1.2)-_/',
                    'SUPERVISOR_SERVER_URL': 'http://127.0.0.1:9001'}
        self.assertEqual(actual, expected)

    def test_allows_trailing_comma(self):
        actual = self._callFUT('foo=bar,')
        expected = {'foo': 'bar'}
        self.assertEqual(actual, expected)

    def test_raises_value_error_on_too_short(self):
        self.assertRaises(ValueError,
                          self._callFUT, 'foo')
        self.assertRaises(ValueError,
                          self._callFUT, 'foo=')
        self.assertRaises(ValueError,
                          self._callFUT, 'foo=bar,baz')
        self.assertRaises(ValueError,
                          self._callFUT, 'foo=bar,baz=')

    def test_raises_when_comma_is_missing(self):
        kvp = 'KEY1=no-comma KEY2=ends-with-comma,'
        self.assertRaises(ValueError,
                          self._callFUT, kvp)

class LogfileNameTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.logfile_name(arg)

    def test_returns_none_for_none_values(self):
        for thing in datatypes.LOGFILE_NONES:
            actual = self._callFUT(thing)
            self.assertEqual(actual, None)

    def test_returns_none_for_uppered_none_values(self):
        for thing in datatypes.LOGFILE_NONES:
            if hasattr(thing, 'upper'):
                thing = thing.upper()
            actual = self._callFUT(thing)
            self.assertEqual(actual, None)

    def test_returns_automatic_for_auto_values(self):
        for thing in datatypes.LOGFILE_AUTOS:
            actual = self._callFUT(thing)
            self.assertEqual(actual, datatypes.Automatic)

    def test_returns_automatic_for_uppered_auto_values(self):
        for thing in datatypes.LOGFILE_AUTOS:
            if hasattr(thing, 'upper'):
                thing = thing.upper()
            actual = self._callFUT(thing)
            self.assertEqual(actual, datatypes.Automatic)

    def test_returns_existing_dirpath_for_other_values(self):
        func = datatypes.existing_dirpath
        datatypes.existing_dirpath = lambda path: path
        try:
            path = '/path/to/logfile/With/Case/Preserved'
            actual = self._callFUT(path)
            self.assertEqual(actual, path)
        finally:
            datatypes.existing_dirpath = func

class RangeCheckedConversionTests(unittest.TestCase):
    def _getTargetClass(self):
        return datatypes.RangeCheckedConversion

    def _makeOne(self, conversion, rmin=None, rmax=None):
        return self._getTargetClass()(conversion, rmin, rmax)

    def test_below_lower_bound(self):
        conversion = self._makeOne(lambda *arg: -1, 0)
        self.assertRaises(ValueError, conversion, None)

    def test_above_upper_lower_bound(self):
        conversion = self._makeOne(lambda *arg: 1, 0, 0)
        self.assertRaises(ValueError, conversion, None)

    def test_passes(self):
        conversion = self._makeOne(lambda *arg: 0, 0, 0)
        self.assertEqual(conversion(0), 0)


class NameToGidTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.name_to_gid(arg)

    @patch("grp.getgrnam", Mock(return_value=[0,0,42]))
    def test_gets_gid_from_group_name(self):
        gid = self._callFUT("foo")
        self.assertEqual(gid, 42)

    @patch("grp.getgrgid", Mock(return_value=[0,0,42]))
    def test_gets_gid_from_group_id(self):
        gid = self._callFUT("42")
        self.assertEqual(gid, 42)

    @patch("grp.getgrnam", Mock(side_effect=KeyError("bad group name")))
    def test_raises_for_bad_group_name(self):
        self.assertRaises(ValueError, self._callFUT, "foo")

    @patch("grp.getgrgid", Mock(side_effect=KeyError("bad group id")))
    def test_raises_for_bad_group_id(self):
        self.assertRaises(ValueError, self._callFUT, "42")

class NameToUidTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.name_to_uid(arg)

    @patch("pwd.getpwnam", Mock(return_value=[0,0,42]))
    def test_gets_uid_from_username(self):
        uid = self._callFUT("foo")
        self.assertEqual(uid, 42)

    @patch("pwd.getpwuid", Mock(return_value=[0,0,42]))
    def test_gets_uid_from_user_id(self):
        uid = self._callFUT("42")
        self.assertEqual(uid, 42)

    @patch("pwd.getpwnam", Mock(side_effect=KeyError("bad username")))
    def test_raises_for_bad_username(self):
        self.assertRaises(ValueError, self._callFUT, "foo")

    @patch("pwd.getpwuid", Mock(side_effect=KeyError("bad user id")))
    def test_raises_for_bad_user_id(self):
        self.assertRaises(ValueError, self._callFUT, "42")

class OctalTypeTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.octal_type(arg)

    def test_success(self):
        self.assertEqual(self._callFUT('10'), 8)

    def test_raises_for_non_numeric(self):
        try:
            self._callFUT('bad')
            self.fail()
        except ValueError as e:
            expected = 'bad can not be converted to an octal type'
            self.assertEqual(e.args[0], expected)

    def test_raises_for_unconvertable_numeric(self):
        try:
            self._callFUT('1.2')
            self.fail()
        except ValueError as e:
            expected = '1.2 can not be converted to an octal type'
            self.assertEqual(e.args[0], expected)

class ExistingDirectoryTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.existing_directory(arg)

    def test_dir_exists(self):
        path = os.path.dirname(__file__)
        self.assertEqual(path, self._callFUT(path))

    def test_dir_does_not_exist(self):
        path = os.path.join(os.path.dirname(__file__), 'nonexistant')
        try:
            self._callFUT(path)
            self.fail()
        except ValueError as e:
            expected = "%s is not an existing directory" % path
            self.assertEqual(e.args[0], expected)

    def test_not_a_directory(self):
        path = __file__
        try:
            self._callFUT(path)
            self.fail()
        except ValueError as e:
            expected = "%s is not an existing directory" % path
            self.assertEqual(e.args[0], expected)

    def test_expands_home(self):
        home = os.path.expanduser('~')
        if os.path.exists(home):
            path = self._callFUT('~')
            self.assertEqual(home, path)

    def test_expands_here(self):
        datatypes.here = os.path.dirname(__file__)
        try:
            self.assertEqual(self._callFUT('%(here)s'), datatypes.here)
        finally:
            datatypes.here = None

class ExistingDirpathTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.existing_dirpath(arg)

    def test_returns_existing_dirpath(self):
        self.assertEqual(self._callFUT(__file__), __file__)

    def test_returns_dirpath_if_relative(self):
        self.assertEqual(self._callFUT('foo'), 'foo')

    def test_raises_if_dir_does_not_exist(self):
        path = os.path.join(os.path.dirname(__file__), 'nonexistant', 'foo')
        try:
            self._callFUT(path)
            self.fail()
        except ValueError as e:
            expected = ('The directory named as part of the path %s '
                        'does not exist.' % path)
            self.assertEqual(e.args[0], expected)

    def test_raises_if_exists_but_not_a_dir(self):
        path = os.path.join(os.path.dirname(__file__),
                            os.path.basename(__file__), 'foo')
        try:
            self._callFUT(path)
            self.fail()
        except ValueError as e:
            expected = ('The directory named as part of the path %s '
                        'does not exist.' % path)
            self.assertEqual(e.args[0], expected)

    def test_expands_home(self):
        home = os.path.expanduser('~')
        if os.path.exists(home):
            path = self._callFUT('~/foo')
            self.assertEqual(os.path.join(home, 'foo'), path)

    def test_expands_here(self):
        datatypes.here = os.path.dirname(__file__)
        try:
            expected = os.path.join(datatypes.here, 'foo')
            self.assertEqual(self._callFUT('%(here)s/foo'), expected)
        finally:
            datatypes.here = None

class LoggingLevelTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.logging_level(arg)

    def test_returns_level_from_name_case_insensitive(self):
        from supervisor.loggers import LevelsByName
        self.assertEqual(self._callFUT("wArN"), LevelsByName.WARN)

    def test_raises_for_bad_level_name(self):
        self.assertRaises(ValueError,
                          self._callFUT, "foo")

class UrlTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.url(arg)

    def test_accepts_urlparse_recognized_scheme_with_netloc(self):
        good_url = 'http://localhost:9001'
        self.assertEqual(self._callFUT(good_url), good_url)

    def test_rejects_urlparse_recognized_scheme_but_no_netloc(self):
        bad_url = 'http://'
        self.assertRaises(ValueError, self._callFUT, bad_url)

    def test_accepts_unix_scheme_with_path(self):
        good_url = "unix://somepath"
        self.assertEqual(good_url, self._callFUT(good_url))

    def test_rejects_unix_scheme_with_no_slashes_or_path(self):
        bad_url = "unix:"
        self.assertRaises(ValueError, self._callFUT, bad_url)

    def test_rejects_unix_scheme_with_slashes_but_no_path(self):
        bad_url = "unix://"
        self.assertRaises(ValueError, self._callFUT, bad_url)

class InetStreamSocketConfigTests(unittest.TestCase):
    def _getTargetClass(self):
        return datatypes.InetStreamSocketConfig

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_url(self):
        conf = self._makeOne('127.0.0.1', 8675)
        self.assertEqual(conf.url, 'tcp://127.0.0.1:8675')

    def test___str__(self):
        cfg = self._makeOne('localhost', 65531)
        self.assertEqual(str(cfg), 'tcp://localhost:65531')

    def test_repr(self):
        conf = self._makeOne('127.0.0.1', 8675)
        s = repr(conf)
        self.assertTrue('supervisor.datatypes.InetStreamSocketConfig' in s)
        self.assertTrue(s.endswith('for tcp://127.0.0.1:8675>'), s)

    def test_addr(self):
        conf = self._makeOne('127.0.0.1', 8675)
        addr = conf.addr()
        self.assertEqual(addr, ('127.0.0.1', 8675))

    def test_port_as_string(self):
        conf = self._makeOne('localhost', '5001')
        addr = conf.addr()
        self.assertEqual(addr, ('localhost', 5001))

    def test_create_and_bind(self):
        conf = self._makeOne('127.0.0.1', 8675)
        sock = conf.create_and_bind()
        reuse = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
        self.assertTrue(reuse)
        self.assertEqual(conf.addr(), sock.getsockname()) #verifies that bind was called
        sock.close()

    def test_same_urls_are_equal(self):
        conf1 = self._makeOne('localhost', 5001)
        conf2 = self._makeOne('localhost', 5001)
        self.assertTrue(conf1 == conf2)
        self.assertFalse(conf1 != conf2)

    def test_diff_urls_are_not_equal(self):
        conf1 = self._makeOne('localhost', 5001)
        conf2 = self._makeOne('localhost', 5002)
        self.assertTrue(conf1 != conf2)
        self.assertFalse(conf1 == conf2)

    def test_diff_objs_are_not_equal(self):
        conf1 = self._makeOne('localhost', 5001)
        conf2 = 'blah'
        self.assertTrue(conf1 != conf2)
        self.assertFalse(conf1 == conf2)

class UnixStreamSocketConfigTests(unittest.TestCase):
    def _getTargetClass(self):
        return datatypes.UnixStreamSocketConfig

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_url(self):
        conf = self._makeOne('/tmp/foo.sock')
        self.assertEqual(conf.url, 'unix:///tmp/foo.sock')

    def test___str__(self):
        cfg = self._makeOne('foo/bar')
        self.assertEqual(str(cfg), 'unix://foo/bar')

    def test_repr(self):
        conf = self._makeOne('/tmp/foo.sock')
        s = repr(conf)
        self.assertTrue('supervisor.datatypes.UnixStreamSocketConfig' in s)
        self.assertTrue(s.endswith('for unix:///tmp/foo.sock>'), s)

    def test_get_addr(self):
        conf = self._makeOne('/tmp/foo.sock')
        addr = conf.addr()
        self.assertEqual(addr, '/tmp/foo.sock')

    def test_create_and_bind(self):
        (tf_fd, tf_name) = tempfile.mkstemp()
        owner = (sentinel.uid, sentinel.gid)
        mode = sentinel.mode
        conf = self._makeOne(tf_name, owner=owner, mode=mode)

        # Patch os.chmod and os.chown functions with mocks
        # objects so that the test does not depend on
        # any specific system users or permissions
        chown_mock = Mock()
        chmod_mock = Mock()
        @patch('os.chown', chown_mock)
        @patch('os.chmod', chmod_mock)
        def call_create_and_bind(conf):
            return conf.create_and_bind()

        sock = call_create_and_bind(conf)
        self.assertTrue(os.path.exists(tf_name))
        # verifies that bind was called
        self.assertEqual(conf.addr(), sock.getsockname())
        sock.close()
        self.assertTrue(os.path.exists(tf_name))
        os.unlink(tf_name)
        # Verify that os.chown was called with correct args
        self.assertEqual(1, chown_mock.call_count)
        path_arg = chown_mock.call_args[0][0]
        uid_arg = chown_mock.call_args[0][1]
        gid_arg = chown_mock.call_args[0][2]
        self.assertEqual(tf_name, path_arg)
        self.assertEqual(owner[0], uid_arg)
        self.assertEqual(owner[1], gid_arg)
        # Verify that os.chmod was called with correct args
        self.assertEqual(1, chmod_mock.call_count)
        path_arg = chmod_mock.call_args[0][0]
        mode_arg = chmod_mock.call_args[0][1]
        self.assertEqual(tf_name, path_arg)
        self.assertEqual(mode, mode_arg)

    def test_create_and_bind_when_chown_fails(self):
        (tf_fd, tf_name) = tempfile.mkstemp()
        owner = (sentinel.uid, sentinel.gid)
        mode = sentinel.mode
        conf = self._makeOne(tf_name, owner=owner, mode=mode)

        @patch('os.chown', Mock(side_effect=OSError("msg")))
        @patch('os.chmod', Mock())
        def call_create_and_bind(conf):
            return conf.create_and_bind()

        try:
            call_create_and_bind(conf)
            self.fail()
        except ValueError as e:
            expected = "Could not change ownership of socket file: msg"
            self.assertEqual(e.args[0], expected)
            self.assertFalse(os.path.exists(tf_name))

    def test_create_and_bind_when_chmod_fails(self):
        (tf_fd, tf_name) = tempfile.mkstemp()
        owner = (sentinel.uid, sentinel.gid)
        mode = sentinel.mode
        conf = self._makeOne(tf_name, owner=owner, mode=mode)

        @patch('os.chown', Mock())
        @patch('os.chmod', Mock(side_effect=OSError("msg")))
        def call_create_and_bind(conf):
            return conf.create_and_bind()

        try:
            call_create_and_bind(conf)
            self.fail()
        except ValueError as e:
            expected = "Could not change permissions of socket file: msg"
            self.assertEqual(e.args[0], expected)
            self.assertFalse(os.path.exists(tf_name))

    def test_same_paths_are_equal(self):
        conf1 = self._makeOne('/tmp/foo.sock')
        conf2 = self._makeOne('/tmp/foo.sock')
        self.assertTrue(conf1 == conf2)
        self.assertFalse(conf1 != conf2)

    def test_diff_paths_are_not_equal(self):
        conf1 = self._makeOne('/tmp/foo.sock')
        conf2 = self._makeOne('/tmp/bar.sock')
        self.assertTrue(conf1 != conf2)
        self.assertFalse(conf1 == conf2)

    def test_diff_objs_are_not_equal(self):
        conf1 = self._makeOne('/tmp/foo.sock')
        conf2 = 'blah'
        self.assertTrue(conf1 != conf2)
        self.assertFalse(conf1 == conf2)

class InetAddressTests(unittest.TestCase):
    def _callFUT(self, s):
        return datatypes.inet_address(s)

    def test_no_port_number(self):
        self.assertRaises(ValueError, self._callFUT, 'a:')

    def test_bad_port_number(self):
        self.assertRaises(ValueError, self._callFUT, 'a')

    def test_default_host(self):
        host, port = self._callFUT('*:8080')
        self.assertEqual(host, '')
        self.assertEqual(port, 8080)

    def test_boring(self):
        host, port = self._callFUT('localhost:80')
        self.assertEqual(host, 'localhost')
        self.assertEqual(port, 80)

class SocketAddressTests(unittest.TestCase):
    def _getTargetClass(self):
        return datatypes.SocketAddress

    def _makeOne(self, s):
        return self._getTargetClass()(s)

    def test_unix_socket(self):
        addr = self._makeOne('/foo/bar')
        self.assertEqual(addr.family, socket.AF_UNIX)
        self.assertEqual(addr.address, '/foo/bar')

    def test_inet_socket(self):
        addr = self._makeOne('localhost:8080')
        self.assertEqual(addr.family, socket.AF_INET)
        self.assertEqual(addr.address, ('localhost', 8080))

class ColonSeparatedUserGroupTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.colon_separated_user_group(arg)

    def test_ok_username(self):
        self.assertEqual(self._callFUT('root')[0], 0)

    def test_missinguser_username(self):
        self.assertRaises(ValueError,
                          self._callFUT, 'godihopethisuserdoesntexist')

    def test_missinguser_username_and_groupname(self):
        self.assertRaises(ValueError,
                          self._callFUT, 'godihopethisuserdoesntexist:foo')

    def test_separated_user_group_returns_both(self):
        name_to_uid = Mock(return_value=12)
        name_to_gid = Mock(return_value=34)

        @patch("supervisor.datatypes.name_to_uid", name_to_uid)
        @patch("supervisor.datatypes.name_to_gid", name_to_gid)
        def colon_separated(value):
            return self._callFUT(value)

        uid, gid = colon_separated("foo:bar")
        name_to_uid.assert_called_with("foo")
        self.assertEqual(12, uid)
        name_to_gid.assert_called_with("bar")
        self.assertEqual(34, gid)

    def test_separated_user_group_returns_user_only(self):
        name_to_uid = Mock(return_value=42)

        @patch("supervisor.datatypes.name_to_uid", name_to_uid)
        def colon_separated(value):
            return self._callFUT(value)

        uid, gid = colon_separated("foo")
        name_to_uid.assert_called_with("foo")
        self.assertEqual(42, uid)
        self.assertEqual(-1, gid)

class SignalNumberTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.signal_number(arg)

    def test_converts_number(self):
        self.assertEqual(self._callFUT(signal.SIGTERM), signal.SIGTERM)

    def test_converts_name(self):
        self.assertEqual(self._callFUT(' term '), signal.SIGTERM)

    def test_converts_signame(self):
        self.assertEqual(self._callFUT('SIGTERM'), signal.SIGTERM)

    def test_raises_for_bad_number(self):
        try:
            self._callFUT('12345678')
            self.fail()
        except ValueError as e:
            expected = "value 12345678 is not a valid signal number"
            self.assertEqual(e.args[0], expected)

    def test_raises_for_bad_name(self):
        try:
            self._callFUT('BADSIG')
            self.fail()
        except ValueError as e:
            expected = "value BADSIG is not a valid signal name"
            self.assertEqual(e.args[0], expected)

class AutoRestartTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.auto_restart(arg)

    def test_converts_truthy(self):
        for s in datatypes.TRUTHY_STRINGS:
            result = self._callFUT(s)
            self.assertEqual(result, datatypes.RestartUnconditionally)

    def test_converts_falsy(self):
        for s in datatypes.FALSY_STRINGS:
            self.assertFalse(self._callFUT(s))

    def test_converts_unexpected(self):
        for s in ('unexpected', 'UNEXPECTED'):
            result = self._callFUT(s)
            self.assertEqual(result, datatypes.RestartWhenExitUnexpected)

    def test_raises_for_bad_value(self):
        try:
            self._callFUT('bad')
            self.fail()
        except ValueError as e:
            self.assertEqual(e.args[0], "invalid 'autorestart' value 'bad'")

class ProfileOptionsTests(unittest.TestCase):
    def _callFUT(self, arg):
        return datatypes.profile_options(arg)

    def test_empty(self):
        sort_options, callers = self._callFUT('')
        self.assertEqual([], sort_options)
        self.assertFalse(callers)

    def test_without_callers(self):
        sort_options, callers = self._callFUT('CUMULATIVE,calls')
        self.assertEqual(['cumulative', 'calls'], sort_options)
        self.assertFalse(callers)

    def test_with_callers(self):
        sort_options, callers = self._callFUT('cumulative, callers')
        self.assertEqual(['cumulative'], sort_options)
        self.assertTrue(callers)

########NEW FILE########
__FILENAME__ = test_dispatchers
import unittest
import os
import sys

from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyLogger
from supervisor.tests.base import DummyEvent
from supervisor import read_file

class PDispatcherTests(unittest.TestCase):
    def setUp(self):
        from supervisor.events import clear
        clear()

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.dispatchers import PDispatcher
        return PDispatcher

    def _makeOne(self, process=None, channel='stdout', fd=0):
        return self._getTargetClass()(process, channel, fd)
    
    def test_readable(self):
        inst = self._makeOne()
        self.assertRaises(NotImplementedError, inst.readable)
        
    def test_writable(self):
        inst = self._makeOne()
        self.assertRaises(NotImplementedError, inst.writable)

    def test_flush(self):
        inst = self._makeOne()
        self.assertEqual(inst.flush(), None)
        
class POutputDispatcherTests(unittest.TestCase):
    def setUp(self):
        from supervisor.events import clear
        clear()

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.dispatchers import POutputDispatcher
        return POutputDispatcher

    def _makeOne(self, process, channel='stdout'):
        from supervisor import events
        events = {'stdout': events.ProcessCommunicationStdoutEvent,
                  'stderr': events.ProcessCommunicationStderrEvent}
        # dispatcher derives its channel from event class
        return self._getTargetClass()(process, events[channel], 0)

    def test_writable(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.writable(), False)

    def test_readable_open(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.closed = False
        self.assertEqual(dispatcher.readable(), True)

    def test_readable_closed(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.closed = True
        self.assertEqual(dispatcher.readable(), False)

    def test_handle_write_event(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertRaises(NotImplementedError, dispatcher.handle_write_event)

    def test_handle_read_event(self):
        options = DummyOptions()
        options.readfd_result = 'abc'
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_capture_maxbytes=100)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(dispatcher.output_buffer, 'abc')

    def test_handle_read_event_no_data_closes(self):
        options = DummyOptions()
        options.readfd_result = ''
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_capture_maxbytes=100)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertFalse(dispatcher.closed)
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(dispatcher.output_buffer, '')
        self.assertTrue(dispatcher.closed)

    def test_handle_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        try:
            raise ValueError('foo')
        except:
            dispatcher.handle_error()
        result = options.logger.data[0]
        self.assertTrue(result.startswith(
            'uncaptured python exception, closing channel'),result)

    def test_toggle_capturemode_sends_event(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo',
                              stdout_capture_maxbytes=500)
        process = DummyProcess(config)
        process.pid = 4000
        dispatcher = self._makeOne(process)
        dispatcher.capturemode = True
        dispatcher.capturelog.getvalue = lambda: 'hallooo'
        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_COMMUNICATION, doit)
        dispatcher.toggle_capturemode()
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.process, process)
        self.assertEqual(event.pid, 4000)
        self.assertEqual(event.data, 'hallooo')

    def test_removelogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.removelogs()
        self.assertEqual(dispatcher.mainlog.handlers[0].reopened, True)
        self.assertEqual(dispatcher.mainlog.handlers[0].removed, True)
        self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)
        self.assertEqual(dispatcher.childlog.handlers[0].removed, True)

    def test_reopenlogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.reopenlogs()
        self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)
        self.assertEqual(dispatcher.mainlog.handlers[0].reopened, True)

    def test_record_output_log_non_capturemode(self):
        # stdout/stderr goes to the process log and the main log,
        # in non-capturemode, the data length doesn't matter
        options = DummyOptions()
        from supervisor import loggers
        options.loglevel = loggers.LevelsByName.TRAC
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.output_buffer = 'a'
        dispatcher.record_output()
        self.assertEqual(dispatcher.childlog.data, ['a'])
        self.assertEqual(options.logger.data[0],
             "'process1' stdout output:\na")
        self.assertEqual(dispatcher.output_buffer, '')

    def test_record_output_emits_stdout_event_when_enabled(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_events_enabled=True)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process, 'stdout')
        dispatcher.output_buffer = 'hello from stdout'

        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_LOG_STDOUT, doit)
        dispatcher.record_output()

        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.process, process)
        self.assertEqual(event.data, 'hello from stdout')

    def test_record_output_does_not_emit_stdout_event_when_disabled(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_events_enabled=False)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process, 'stdout')
        dispatcher.output_buffer = 'hello from stdout'

        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_LOG_STDOUT, doit)
        dispatcher.record_output()

        self.assertEqual(len(L), 0)

    def test_record_output_emits_stderr_event_when_enabled(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stderr_events_enabled=True)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process, 'stderr')
        dispatcher.output_buffer = 'hello from stderr'

        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_LOG_STDERR, doit)
        dispatcher.record_output()

        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.process, process)
        self.assertEqual(event.data, 'hello from stderr')

    def test_record_output_does_not_emit_stderr_event_when_disabled(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stderr_events_enabled=False)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process, 'stderr')
        dispatcher.output_buffer = 'hello from stderr'

        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.EventTypes.PROCESS_LOG_STDERR, doit)
        dispatcher.record_output()

        self.assertEqual(len(L), 0)

    def test_record_output_capturemode_string_longer_than_token(self):
        # stdout/stderr goes to the process log and the main log,
        # in capturemode, the length of the data needs to be longer
        # than the capture token to make it out.
        options = DummyOptions()
        from supervisor import loggers
        options.loglevel = loggers.LevelsByName.TRAC
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo',
                              stdout_capture_maxbytes=100)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.output_buffer = 'stdout string longer than a token'
        dispatcher.record_output()
        self.assertEqual(dispatcher.childlog.data,
                         ['stdout string longer than a token'])
        self.assertEqual(options.logger.data[0],
             "'process1' stdout output:\nstdout string longer than a token")

    def test_record_output_capturemode_string_not_longer_than_token(self):
        # stdout/stderr goes to the process log and the main log,
        # in capturemode, the length of the data needs to be longer
        # than the capture token to make it out.
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo',
                              stdout_capture_maxbytes=100)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.output_buffer = 'a'
        dispatcher.record_output()
        self.assertEqual(dispatcher.childlog.data, [])
        self.assertEqual(dispatcher.output_buffer, 'a')

    def test_stdout_capturemode_single_buffer(self):
        # mike reported that comm events that took place within a single
        # output buffer were broken 8/20/2007
        from supervisor.events import ProcessCommunicationEvent
        from supervisor.events import subscribe
        events = []
        def doit(event):
            events.append(event)
        subscribe(ProcessCommunicationEvent, doit)
        BEGIN_TOKEN = ProcessCommunicationEvent.BEGIN_TOKEN
        END_TOKEN = ProcessCommunicationEvent.END_TOKEN
        data = BEGIN_TOKEN + 'hello' + END_TOKEN
        options = DummyOptions()
        from supervisor.loggers import getLogger
        options.getLogger = getLogger # actually use real logger
        logfile = '/tmp/log'
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile=logfile,
                              stdout_capture_maxbytes=1000)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)

        try:
            dispatcher.output_buffer = data
            dispatcher.record_output()
            self.assertEqual(read_file(logfile), '')
            self.assertEqual(dispatcher.output_buffer, '')
            self.assertEqual(len(events), 1)

            event = events[0]
            from supervisor.events import ProcessCommunicationStdoutEvent
            self.assertEqual(event.__class__, ProcessCommunicationStdoutEvent)
            self.assertEqual(event.process, process)
            self.assertEqual(event.channel, 'stdout')
            self.assertEqual(event.data, 'hello')

        finally:
            try:
                dispatcher.capturelog.close()
                dispatcher.childlog.close()
                os.remove(logfile)
            except (OSError, IOError):
                pass

    def test_stdout_capturemode_multiple_buffers(self):
        from supervisor.events import ProcessCommunicationEvent
        from supervisor.events import subscribe
        events = []
        def doit(event):
            events.append(event)
        subscribe(ProcessCommunicationEvent, doit)
        import string
        # ascii_letters for python 3
        letters = getattr(string, "letters", string.ascii_letters)
        digits = string.digits * 4
        BEGIN_TOKEN = ProcessCommunicationEvent.BEGIN_TOKEN
        END_TOKEN = ProcessCommunicationEvent.END_TOKEN
        data = (letters +  BEGIN_TOKEN + digits + END_TOKEN + letters)

        # boundaries that split tokens
        broken = data.split(':')
        first = broken[0] + ':'
        second = broken[1] + ':'
        third = broken[2]

        options = DummyOptions()
        from supervisor.loggers import getLogger
        options.getLogger = getLogger # actually use real logger
        logfile = '/tmp/log'
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile=logfile,
                              stdout_capture_maxbytes=10000)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        try:
            dispatcher.output_buffer = first
            dispatcher.record_output()
            [ x.flush() for x in dispatcher.childlog.handlers]
            self.assertEqual(read_file(logfile), letters)
            self.assertEqual(dispatcher.output_buffer, first[len(letters):])
            self.assertEqual(len(events), 0)

            dispatcher.output_buffer += second
            dispatcher.record_output()
            self.assertEqual(len(events), 0)
            [ x.flush() for x in dispatcher.childlog.handlers]
            self.assertEqual(read_file(logfile), letters)
            self.assertEqual(dispatcher.output_buffer, first[len(letters):])
            self.assertEqual(len(events), 0)

            dispatcher.output_buffer += third
            dispatcher.record_output()
            [ x.flush() for x in dispatcher.childlog.handlers]
            self.assertEqual(read_file(logfile), letters *2)
            self.assertEqual(len(events), 1)
            event = events[0]
            from supervisor.events import ProcessCommunicationStdoutEvent
            self.assertEqual(event.__class__, ProcessCommunicationStdoutEvent)
            self.assertEqual(event.process, process)
            self.assertEqual(event.channel, 'stdout')
            self.assertEqual(event.data, digits)

        finally:
            try:
                dispatcher.capturelog.close()
                dispatcher.childlog.close()
                os.remove(logfile)
            except (OSError, IOError):
                pass

    def test_strip_ansi(self):
        options = DummyOptions()
        options.strip_ansi = True
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        ansi = '\x1b[34mHello world... this is longer than a token!\x1b[0m'
        noansi = 'Hello world... this is longer than a token!'

        dispatcher.output_buffer = ansi
        dispatcher.record_output()
        self.assertEqual(len(dispatcher.childlog.data), 1)
        self.assertEqual(dispatcher.childlog.data[0], noansi)

        options.strip_ansi = False

        dispatcher.output_buffer = ansi
        dispatcher.record_output()
        self.assertEqual(len(dispatcher.childlog.data), 2)
        self.assertEqual(dispatcher.childlog.data[1], ansi)

    def test_ctor_nologfiles(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.capturelog, None)
        self.assertEqual(dispatcher.mainlog, None)
        self.assertEqual(dispatcher.childlog, None)

    def test_ctor_logfile_only(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.capturelog, None)
        self.assertEqual(dispatcher.mainlog.__class__, DummyLogger)
        self.assertEqual(dispatcher.childlog, dispatcher.mainlog)

    def test_ctor_capturelog_only(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_capture_maxbytes=300)
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.capturelog.__class__,DummyLogger)
        self.assertEqual(dispatcher.mainlog, None)
        self.assertEqual(dispatcher.childlog, None)

    def test_ctor_nologs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.capturelog, None)
        self.assertEqual(dispatcher.mainlog, None)
        self.assertEqual(dispatcher.childlog, None)

    def test_repr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        drepr = repr(dispatcher)
        self.assertTrue('POutputDispatcher' in drepr)
        self.assertNotEqual(
            drepr.find('supervisor.tests.base.DummyProcess'),
            -1)
        self.assertTrue(drepr.endswith('(stdout)>'), drepr)

    def test_close(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.close()
        self.assertEqual(dispatcher.closed, True)
        dispatcher.close() # make sure we don't error if we try to close twice
        self.assertEqual(dispatcher.closed, True)


    def test_syslog_logfile_deprecated(self):
        import warnings
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        config.stdout_logfile = 'syslog'
        process = DummyProcess(config)
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            self._makeOne(process)
            self.assertEqual(len(w), 1)
        
        


class PInputDispatcherTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.dispatchers import PInputDispatcher
        return PInputDispatcher

    def _makeOne(self, process):
        channel = 'stdin'
        return self._getTargetClass()(process, channel, 0)

    def test_writable_open_nodata(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'a'
        dispatcher.closed = False
        self.assertEqual(dispatcher.writable(), True)

    def test_writable_open_withdata(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = ''
        dispatcher.closed = False
        self.assertEqual(dispatcher.writable(), False)

    def test_writable_closed_nodata(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'a'
        dispatcher.closed = True
        self.assertEqual(dispatcher.writable(), False)

    def test_writable_closed_withdata(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = ''
        dispatcher.closed = True
        self.assertEqual(dispatcher.writable(), False)

    def test_readable(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.readable(), False)

    def test_handle_write_event(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'halloooo'
        self.assertEqual(dispatcher.handle_write_event(), None)
        self.assertEqual(options.written[0], 'halloooo')

    def test_handle_write_event_nodata(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.input_buffer, '')
        dispatcher.handle_write_event
        self.assertEqual(dispatcher.input_buffer, '')
        self.assertEqual(options.written, {})

    def test_handle_write_event_epipe_raised(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'halloooo'
        import errno
        options.write_error = errno.EPIPE
        dispatcher.handle_write_event()
        self.assertEqual(dispatcher.input_buffer, '')
        self.assertTrue(options.logger.data[0].startswith(
            'fd 0 closed, stopped monitoring'))
        self.assertTrue(options.logger.data[0].endswith('(stdin)>'))

    def test_handle_write_event_uncaught_raised(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.input_buffer = 'halloooo'
        import errno
        options.write_error = errno.EBADF
        self.assertRaises(OSError, dispatcher.handle_write_event)

    def test_handle_write_event_over_os_limit(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        options.write_accept = 1
        dispatcher.input_buffer = 'a' * 50
        dispatcher.handle_write_event()
        self.assertEqual(len(dispatcher.input_buffer), 49)
        self.assertEqual(options.written[0], 'a')

    def test_handle_read_event(self):
        process = DummyProcess(None)
        dispatcher = self._makeOne(process)
        self.assertRaises(NotImplementedError, dispatcher.handle_read_event)

    def test_handle_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        try:
            raise ValueError('foo')
        except:
            dispatcher.handle_error()
        result = options.logger.data[0]
        self.assertTrue(result.startswith(
            'uncaptured python exception, closing channel'),result)

    def test_repr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        drepr = repr(dispatcher)
        self.assertTrue('PInputDispatcher' in drepr)
        self.assertNotEqual(
            drepr.find('supervisor.tests.base.DummyProcess'),
            -1)
        self.assertTrue(drepr.endswith('(stdin)>'), drepr)

    def test_close(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.close()
        self.assertEqual(dispatcher.closed, True)
        dispatcher.close() # make sure we don't error if we try to close twice
        self.assertEqual(dispatcher.closed, True)

class PEventListenerDispatcherTests(unittest.TestCase):
    def setUp(self):
        from supervisor.events import clear
        clear()

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.dispatchers import PEventListenerDispatcher
        return PEventListenerDispatcher

    def _makeOne(self, process):
        channel = 'stdout'
        return self._getTargetClass()(process, channel, 0)

    def test_writable(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.writable(), False)

    def test_readable_open(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.closed = False
        self.assertEqual(dispatcher.readable(), True)

    def test_readable_closed(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.closed = True
        self.assertEqual(dispatcher.readable(), False)

    def test_handle_write_event(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertRaises(NotImplementedError, dispatcher.handle_write_event)

    def test_handle_read_event_calls_handle_listener_state_change(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher = self._makeOne(process)
        options.readfd_result = dispatcher.READY_FOR_EVENTS_TOKEN
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(process.listener_state, EventListenerStates.READY)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(len(dispatcher.childlog.data), 1)
        self.assertEqual(dispatcher.childlog.data[0],
                         dispatcher.READY_FOR_EVENTS_TOKEN)

    def test_handle_read_event_nodata(self):
        options = DummyOptions()
        options.readfd_result = ''
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        from supervisor.dispatchers import EventListenerStates
        self.assertEqual(dispatcher.process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)

    def test_handle_read_event_logging_nologs(self):
        options = DummyOptions()
        options.readfd_result = 'supercalifragilisticexpialidocious'
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        # just make sure there are no errors if a child logger doesnt
        # exist
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(dispatcher.childlog, None)

    def test_handle_read_event_logging_childlog(self):
        options = DummyOptions()
        options.readfd_result = 'supercalifragilisticexpialidocious'
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.handle_read_event(), None)
        self.assertEqual(len(dispatcher.childlog.data), 1)
        self.assertEqual(dispatcher.childlog.data[0],
                         'supercalifragilisticexpialidocious')

    def test_handle_listener_state_change_from_unknown(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.UNKNOWN
        dispatcher.state_buffer = 'whatever'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data, [])
        self.assertEqual(process.listener_state, EventListenerStates.UNKNOWN)

    def test_handle_listener_state_change_acknowledged_to_ready(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher.state_buffer = 'READY\n'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: ACKNOWLEDGED -> READY')
        self.assertEqual(process.listener_state, EventListenerStates.READY)

    def test_handle_listener_state_change_acknowledged_gobbles(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher.state_buffer = 'READY\ngarbage\n'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: ACKNOWLEDGED -> READY')
        self.assertEqual(options.logger.data[1],
                         'process1: READY -> UNKNOWN')
        self.assertEqual(process.listener_state, EventListenerStates.UNKNOWN)

    def test_handle_listener_state_change_acknowledged_to_insufficient(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher.state_buffer = 'RE'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, 'RE')
        self.assertEqual(options.logger.data, [])
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)

    def test_handle_listener_state_change_acknowledged_to_unknown(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.ACKNOWLEDGED
        dispatcher.state_buffer = 'bogus data yo'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: ACKNOWLEDGED -> UNKNOWN')
        self.assertEqual(process.listener_state, EventListenerStates.UNKNOWN)

    def test_handle_listener_state_change_ready_to_unknown(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.READY
        dispatcher.state_buffer = 'bogus data yo'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: READY -> UNKNOWN')
        self.assertEqual(process.listener_state, EventListenerStates.UNKNOWN)

    def test_handle_listener_state_change_busy_to_insufficient(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        dispatcher.state_buffer = 'bogus data yo'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, 'bogus data yo')
        self.assertEqual(process.listener_state, EventListenerStates.BUSY)

    def test_handle_listener_state_change_busy_to_acknowledged_procd(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        from supervisor.dispatchers import default_handler
        process.group.config.result_handler = default_handler
        dispatcher.state_buffer = 'RESULT 2\nOKabc'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, 'abc')
        self.assertEqual(options.logger.data[0],
                         'process1: BUSY -> ACKNOWLEDGED (processed)')
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)

    def test_handle_listener_state_change_busy_to_acknowledged_rejected(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        from supervisor.dispatchers import default_handler
        process.group.config.result_handler = default_handler
        dispatcher.state_buffer = 'RESULT 4\nFAILabc'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, 'abc')
        self.assertEqual(options.logger.data[0],
                         'process1: BUSY -> ACKNOWLEDGED (rejected)')
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)

    def test_handle_listener_state_change_busy_to_unknown(self):
        from supervisor.events import EventRejectedEvent
        from supervisor.events import subscribe
        events = []
        def doit(event):
            events.append(event)
        subscribe(EventRejectedEvent, doit)
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        current_event = DummyEvent()
        process.event = current_event
        dispatcher.state_buffer = 'bogus data\n'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                'process1: BUSY -> UNKNOWN (bad result line \'bogus data\')')
        self.assertEqual(process.listener_state,
                         EventListenerStates.UNKNOWN)
        self.assertEqual(events[0].process, process)
        self.assertEqual(events[0].event, current_event)

    def test_handle_listener_state_busy_gobbles(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        process.listener_state = EventListenerStates.BUSY
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        from supervisor.dispatchers import default_handler
        process.group.config.result_handler = default_handler
        dispatcher.state_buffer = 'RESULT 2\nOKbogus data\n'
        self.assertEqual(dispatcher.handle_listener_state_change(), None)
        self.assertEqual(dispatcher.state_buffer, '')
        self.assertEqual(options.logger.data[0],
                         'process1: BUSY -> ACKNOWLEDGED (processed)')
        self.assertEqual(options.logger.data[1],
                         'process1: ACKNOWLEDGED -> UNKNOWN')
        self.assertEqual(process.listener_state,
                         EventListenerStates.UNKNOWN)

    def test_handle_result_accept(self):
        from supervisor.events import subscribe
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        subscribe(events.EventRejectedEvent, doit)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        def handle(event, result):
            pass
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        process.group.config.result_handler = handle
        process.listener_state = EventListenerStates.BUSY
        dispatcher.handle_result('foo')
        self.assertEqual(len(L), 0)
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)
        result = options.logger.data[0]
        self.assertTrue(result.endswith('BUSY -> ACKNOWLEDGED (processed)'))

    def test_handle_result_rejectevent(self):
        from supervisor.events import subscribe
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        subscribe(events.EventRejectedEvent, doit)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        def rejected(event, result):
            from supervisor.dispatchers import RejectEvent
            raise RejectEvent(result)
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        process.group.config.result_handler = rejected
        process.listener_state = EventListenerStates.BUSY
        dispatcher.handle_result('foo')
        self.assertEqual(len(L), 1)
        self.assertEqual(L[0].__class__, events.EventRejectedEvent)
        self.assertEqual(process.listener_state,
                         EventListenerStates.ACKNOWLEDGED)
        result = options.logger.data[0]
        self.assertTrue(result.endswith('BUSY -> ACKNOWLEDGED (rejected)'))

    def test_handle_result_exception(self):
        from supervisor.events import subscribe
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        L = []
        def doit(event):
            L.append(event)
        from supervisor import events
        subscribe(events.EventRejectedEvent, doit)
        from supervisor.dispatchers import EventListenerStates
        dispatcher = self._makeOne(process)
        def exception(event, result):
            raise ValueError
        class Dummy:
            pass
        process.group = Dummy()
        process.group.config = Dummy()
        process.group.config.result_handler = exception
        process.group.result_handler = exception
        process.listener_state = EventListenerStates.BUSY
        dispatcher.handle_result('foo')
        self.assertEqual(len(L), 1)
        self.assertEqual(L[0].__class__, events.EventRejectedEvent)
        self.assertEqual(process.listener_state,
                         EventListenerStates.UNKNOWN)
        result = options.logger.data[0]
        self.assertTrue(result.endswith('BUSY -> UNKNOWN'))

    def test_handle_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        try:
            raise ValueError('foo')
        except:
            dispatcher.handle_error()
        result = options.logger.data[0]
        self.assertTrue(result.startswith(
            'uncaptured python exception, closing channel'),result)

    def test_removelogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.removelogs()
        self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)
        self.assertEqual(dispatcher.childlog.handlers[0].removed, True)

    def test_reopenlogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.reopenlogs()
        self.assertEqual(dispatcher.childlog.handlers[0].reopened, True)

    def test_strip_ansi(self):
        options = DummyOptions()
        options.strip_ansi = True
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        ansi = '\x1b[34mHello world... this is longer than a token!\x1b[0m'
        noansi = 'Hello world... this is longer than a token!'

        options.readfd_result = ansi
        dispatcher.handle_read_event()
        self.assertEqual(len(dispatcher.childlog.data), 1)
        self.assertEqual(dispatcher.childlog.data[0], noansi)

        options.strip_ansi = False

        options.readfd_result = ansi
        dispatcher.handle_read_event()
        self.assertEqual(len(dispatcher.childlog.data), 2)
        self.assertEqual(dispatcher.childlog.data[1], ansi)

    def test_ctor_nologfiles(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.childlog, None)

    def test_ctor_logfile_only(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1',
                              stdout_logfile='/tmp/foo')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        self.assertEqual(dispatcher.process, process)
        self.assertEqual(dispatcher.channel, 'stdout')
        self.assertEqual(dispatcher.fd, 0)
        self.assertEqual(dispatcher.childlog.__class__, DummyLogger)

    def test_repr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        drepr = repr(dispatcher)
        self.assertTrue('PEventListenerDispatcher' in drepr)
        self.assertNotEqual(
            drepr.find('supervisor.tests.base.DummyProcess'),
            -1)
        self.assertTrue(drepr.endswith('(stdout)>'), drepr)

    def test_close(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'process1', '/bin/process1')
        process = DummyProcess(config)
        dispatcher = self._makeOne(process)
        dispatcher.close()
        self.assertEqual(dispatcher.closed, True)
        dispatcher.close() # make sure we don't error if we try to close twice
        self.assertEqual(dispatcher.closed, True)


class stripEscapeTests(unittest.TestCase):
    def _callFUT(self, s):
        from supervisor.dispatchers import stripEscapes
        return stripEscapes(s)

    def test_zero_length_string(self):
        self.assertEqual(self._callFUT(''), '')

    def test_ansi(self):
        ansi = '\x1b[34mHello world... this is longer than a token!\x1b[0m'
        noansi = 'Hello world... this is longer than a token!'
        self.assertEqual(self._callFUT(ansi), noansi)

    def test_noansi(self):
        noansi = 'Hello world... this is longer than a token!'
        self.assertEqual(self._callFUT(noansi), noansi)

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_events
import sys
import unittest

from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyEvent

class EventSubscriptionNotificationTests(unittest.TestCase):
    def setUp(self):
        from supervisor import events
        events.callbacks[:] = []

    def tearDown(self):
        from supervisor import events
        events.callbacks[:] = []

    def test_subscribe(self):
        from supervisor import events
        events.subscribe(None, None)
        self.assertEqual(events.callbacks, [(None, None)])

    def test_clear(self):
        from supervisor import events
        events.callbacks[:] = [(None, None)]
        events.clear()
        self.assertEqual(events.callbacks, [])

    def test_notify_true(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        class DummyEvent:
            pass
        events.callbacks[:] = [(DummyEvent, callback)]
        events.notify(DummyEvent())
        self.assertEqual(L, [1])

    def test_notify_false(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        class DummyEvent:
            pass
        class AnotherEvent:
            pass
        events.callbacks[:] = [(AnotherEvent, callback)]
        events.notify(DummyEvent())
        self.assertEqual(L, [])

    def test_notify_via_subclass(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        class DummyEvent:
            pass
        class ASubclassEvent(DummyEvent):
            pass
        events.callbacks[:] = [(DummyEvent, callback)]
        events.notify(ASubclassEvent())
        self.assertEqual(L, [1])


class TestEventTypes(unittest.TestCase):
    def test_ProcessLogEvent_attributes(self):
        from supervisor.events import ProcessLogEvent
        inst = ProcessLogEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)

    def test_ProcessLogEvent_inheritence(self):
        from supervisor.events import ProcessLogEvent
        from supervisor.events import Event
        self.assertTrue(
            issubclass(ProcessLogEvent, Event)
        )

    def test_ProcessLogStdoutEvent_attributes(self):
        from supervisor.events import ProcessLogStdoutEvent
        inst = ProcessLogStdoutEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)
        self.assertEqual(inst.channel, 'stdout')

    def test_ProcessLogStdoutEvent_inheritence(self):
        from supervisor.events import ProcessLogStdoutEvent
        from supervisor.events import ProcessLogEvent
        self.assertTrue(
            issubclass(ProcessLogStdoutEvent, ProcessLogEvent)
        )

    def test_ProcessLogStderrEvent_attributes(self):
        from supervisor.events import ProcessLogStderrEvent
        inst = ProcessLogStderrEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)
        self.assertEqual(inst.channel, 'stderr')

    def test_ProcessLogStderrEvent_inheritence(self):
        from supervisor.events import ProcessLogStderrEvent
        from supervisor.events import ProcessLogEvent
        self.assertTrue(
            issubclass(ProcessLogStderrEvent, ProcessLogEvent)
        )

    def test_ProcessCommunicationEvent_attributes(self):
        from supervisor.events import ProcessCommunicationEvent
        inst = ProcessCommunicationEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)

    def test_ProcessCommunicationEvent_inheritence(self):
        from supervisor.events import ProcessCommunicationEvent
        from supervisor.events import Event
        self.assertTrue(
            issubclass(ProcessCommunicationEvent, Event)
        )

    def test_ProcessCommunicationStdoutEvent_attributes(self):
        from supervisor.events import ProcessCommunicationStdoutEvent
        inst = ProcessCommunicationStdoutEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)
        self.assertEqual(inst.channel, 'stdout')

    def test_ProcessCommunicationStdoutEvent_inheritence(self):
        from supervisor.events import ProcessCommunicationStdoutEvent
        from supervisor.events import ProcessCommunicationEvent
        self.assertTrue(
            issubclass(ProcessCommunicationStdoutEvent,
                       ProcessCommunicationEvent)
        )

    def test_ProcessCommunicationStderrEvent_attributes(self):
        from supervisor.events import ProcessCommunicationStderrEvent
        inst = ProcessCommunicationStderrEvent(1, 2, 3)
        self.assertEqual(inst.process, 1)
        self.assertEqual(inst.pid, 2)
        self.assertEqual(inst.data, 3)
        self.assertEqual(inst.channel, 'stderr')

    def test_ProcessCommunicationStderrEvent_inheritence(self):
        from supervisor.events import ProcessCommunicationStderrEvent
        from supervisor.events import ProcessCommunicationEvent
        self.assertTrue(
            issubclass(ProcessCommunicationStderrEvent,
                       ProcessCommunicationEvent)
        )

    def test_RemoteCommunicationEvent_attributes(self):
        from supervisor.events import RemoteCommunicationEvent
        inst = RemoteCommunicationEvent(1, 2)
        self.assertEqual(inst.type, 1)
        self.assertEqual(inst.data, 2)

    def test_RemoteCommunicationEvent_inheritence(self):
        from supervisor.events import RemoteCommunicationEvent
        from supervisor.events import Event
        self.assertTrue(
            issubclass(RemoteCommunicationEvent, Event)
        )

    def test_EventRejectedEvent_attributes(self):
        from supervisor.events import EventRejectedEvent
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process = DummyProcess(pconfig1)
        rejected_event = DummyEvent()
        event = EventRejectedEvent(process, rejected_event)
        self.assertEqual(event.process, process)
        self.assertEqual(event.event, rejected_event)

    def test_EventRejectedEvent_does_not_inherit_from_event(self):
        from supervisor.events import EventRejectedEvent
        from supervisor.events import Event
        self.assertFalse(
            issubclass(EventRejectedEvent, Event)
        )

    def test_all_SupervisorStateChangeEvents(self):
        from supervisor import events
        for klass in (
            events.SupervisorStateChangeEvent,
            events.SupervisorRunningEvent,
            events.SupervisorStoppingEvent
            ):
            self._test_one_SupervisorStateChangeEvent(klass)

    def _test_one_SupervisorStateChangeEvent(self, klass):
        from supervisor.events import SupervisorStateChangeEvent
        self.assertTrue(issubclass(klass, SupervisorStateChangeEvent))

    def test_all_ProcessStateEvents(self):
        from supervisor import events
        for klass in (
            events.ProcessStateEvent,
            events.ProcessStateStoppedEvent,
            events.ProcessStateExitedEvent,
            events.ProcessStateFatalEvent,
            events.ProcessStateBackoffEvent,
            events.ProcessStateRunningEvent,
            events.ProcessStateUnknownEvent,
            events.ProcessStateStoppingEvent,
            events.ProcessStateStartingEvent,
            ):
            self._test_one_ProcessStateEvent(klass)

    def _test_one_ProcessStateEvent(self, klass):
        from supervisor.states import ProcessStates
        from supervisor.events import ProcessStateEvent
        self.assertTrue(issubclass(klass, ProcessStateEvent))
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process = DummyProcess(pconfig1)
        inst = klass(process, ProcessStates.STARTING)
        self.assertEqual(inst.process, process)
        self.assertEqual(inst.from_state, ProcessStates.STARTING)
        self.assertEqual(inst.expected, True)

    def test_all_TickEvents(self):
        from supervisor import events
        for klass in (
           events.TickEvent,
           events.Tick5Event,
           events.Tick60Event,
           events.Tick3600Event
           ):
           self._test_one_TickEvent(klass)

    def _test_one_TickEvent(self, klass):
        from supervisor.events import TickEvent
        self.assertTrue(issubclass(klass, TickEvent))

        inst = klass(1, 2)
        self.assertEqual(inst.when, 1)
        self.assertEqual(inst.supervisord, 2)

    def test_ProcessGroupAddedEvent_attributes(self):
        from supervisor.events import ProcessGroupAddedEvent
        inst = ProcessGroupAddedEvent('myprocess')
        self.assertEqual(inst.group, 'myprocess')

    def test_ProcessGroupRemovedEvent_attributes(self):
        from supervisor.events import ProcessGroupRemovedEvent
        inst = ProcessGroupRemovedEvent('myprocess')
        self.assertEqual(inst.group, 'myprocess')

class TestSerializations(unittest.TestCase):
    def _deserialize(self, serialization):
        data = serialization.split('\n')
        headerdata = data[0]
        payload = ''
        headers = {}
        if len(data) > 1:
            payload = data[1]
        if headerdata:
            try:
                headers = dict( [ x.split(':',1) for x in
                                  headerdata.split()] )
            except ValueError:
                raise AssertionError('headerdata %r could not be deserialized' %
                                     headerdata)
        return headers, payload

    def test_plog_stdout_event(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        from supervisor.events import ProcessLogStdoutEvent
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        event = ProcessLogStdoutEvent(process1, 1, 'yo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['processname'], 'process1', headers)
        self.assertEqual(headers['groupname'], 'process1', headers)
        self.assertEqual(headers['pid'], '1', headers)
        self.assertEqual(payload, 'yo')

    def test_plog_stderr_event(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        from supervisor.events import ProcessLogStderrEvent
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        event = ProcessLogStderrEvent(process1, 1, 'yo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['processname'], 'process1', headers)
        self.assertEqual(headers['groupname'], 'process1', headers)
        self.assertEqual(headers['pid'], '1', headers)
        self.assertEqual(payload, 'yo')

    def test_pcomm_stdout_event(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        from supervisor.events import ProcessCommunicationStdoutEvent
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        event = ProcessCommunicationStdoutEvent(process1, 1, 'yo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['processname'], 'process1', headers)
        self.assertEqual(headers['groupname'], 'process1', headers)
        self.assertEqual(headers['pid'], '1', headers)
        self.assertEqual(payload, 'yo')

    def test_pcomm_stderr_event(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        from supervisor.events import ProcessCommunicationStderrEvent
        event = ProcessCommunicationStderrEvent(process1, 1, 'yo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['processname'], 'process1', headers)
        self.assertEqual(headers['groupname'], 'process1', headers)
        self.assertEqual(headers['pid'], '1', headers)
        self.assertEqual(payload, 'yo')

    def test_remote_comm_event(self):
        from supervisor.events import RemoteCommunicationEvent
        event = RemoteCommunicationEvent('foo', 'bar')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['type'], 'foo', headers)
        self.assertEqual(payload, 'bar')

    def test_process_group_added_event(self):
        from supervisor.events import ProcessGroupAddedEvent
        event = ProcessGroupAddedEvent('foo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['groupname'], 'foo')
        self.assertEqual(payload, '')

    def test_process_group_removed_event(self):
        from supervisor.events import ProcessGroupRemovedEvent
        event = ProcessGroupRemovedEvent('foo')
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers['groupname'], 'foo')
        self.assertEqual(payload, '')

    def test_process_state_events_without_extra_values(self):
        from supervisor.states import ProcessStates
        from supervisor import events
        for klass in (
            events.ProcessStateFatalEvent,
            events.ProcessStateUnknownEvent,
            ):
            options = DummyOptions()
            pconfig1 = DummyPConfig(options, 'process1', 'process1',
                                    '/bin/process1')
            class DummyGroup:
                config = pconfig1
            process1 = DummyProcess(pconfig1)
            process1.group = DummyGroup
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(len(headers), 3)
            self.assertEqual(headers['processname'], 'process1')
            self.assertEqual(headers['groupname'], 'process1')
            self.assertEqual(headers['from_state'], 'STARTING')
            self.assertEqual(payload, '')

    def test_process_state_events_with_pid(self):
        from supervisor.states import ProcessStates
        from supervisor import events
        for klass in (
            events.ProcessStateRunningEvent,
            events.ProcessStateStoppedEvent,
            events.ProcessStateStoppingEvent,
            ):
            options = DummyOptions()
            pconfig1 = DummyPConfig(options, 'process1', 'process1',
                                    '/bin/process1')
            class DummyGroup:
                config = pconfig1
            process1 = DummyProcess(pconfig1)
            process1.group = DummyGroup
            process1.pid = 1
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(len(headers), 4)
            self.assertEqual(headers['processname'], 'process1')
            self.assertEqual(headers['groupname'], 'process1')
            self.assertEqual(headers['from_state'], 'STARTING')
            self.assertEqual(headers['pid'], '1')
            self.assertEqual(payload, '')

    def test_process_state_events_starting_and_backoff(self):
        from supervisor.states import ProcessStates
        from supervisor import events
        for klass in (
            events.ProcessStateStartingEvent,
            events.ProcessStateBackoffEvent,
            ):
            options = DummyOptions()
            pconfig1 = DummyPConfig(options, 'process1', 'process1',
                                    '/bin/process1')
            class DummyGroup:
                config = pconfig1
            process1 = DummyProcess(pconfig1)
            process1.group = DummyGroup
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(len(headers), 4)
            self.assertEqual(headers['processname'], 'process1')
            self.assertEqual(headers['groupname'], 'process1')
            self.assertEqual(headers['from_state'], 'STARTING')
            self.assertEqual(headers['tries'], '0')
            self.assertEqual(payload, '')
            process1.backoff = 1
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(headers['tries'], '1')
            process1.backoff = 2
            event = klass(process1, ProcessStates.STARTING)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(headers['tries'], '2')

    def test_process_state_exited_event_expected(self):
        from supervisor import events
        from supervisor.states import ProcessStates
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        process1.pid = 1
        event = events.ProcessStateExitedEvent(process1,
                                               ProcessStates.STARTING,
                                               expected=True)
        headers, payload = self._deserialize(str(event))
        self.assertEqual(len(headers), 5)
        self.assertEqual(headers['processname'], 'process1')
        self.assertEqual(headers['groupname'], 'process1')
        self.assertEqual(headers['pid'], '1')
        self.assertEqual(headers['from_state'], 'STARTING')
        self.assertEqual(headers['expected'], '1')
        self.assertEqual(payload, '')

    def test_process_state_exited_event_unexpected(self):
        from supervisor import events
        from supervisor.states import ProcessStates
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        class DummyGroup:
            config = pconfig1
        process1.group = DummyGroup
        process1.pid = 1
        event = events.ProcessStateExitedEvent(process1,
                                               ProcessStates.STARTING,
                                               expected=False)
        headers, payload = self._deserialize(str(event))
        self.assertEqual(len(headers), 5)
        self.assertEqual(headers['processname'], 'process1')
        self.assertEqual(headers['groupname'], 'process1')
        self.assertEqual(headers['pid'], '1')
        self.assertEqual(headers['from_state'], 'STARTING')
        self.assertEqual(headers['expected'], '0')
        self.assertEqual(payload, '')

    def test_supervisor_sc_event(self):
        from supervisor import events
        event = events.SupervisorRunningEvent()
        headers, payload = self._deserialize(str(event))
        self.assertEqual(headers, {})
        self.assertEqual(payload, '')

    def test_tick_events(self):
        from supervisor import events
        for klass in (
            events.Tick5Event,
            events.Tick60Event,
            events.Tick3600Event,
            ):
            event = klass(1, 2)
            headers, payload = self._deserialize(str(event))
            self.assertEqual(headers, {'when':'1'})
            self.assertEqual(payload, '')

class TestUtilityFunctions(unittest.TestCase):
    def test_getEventNameByType(self):
        from supervisor import events
        for name, value in events.EventTypes.__dict__.items():
            self.assertEqual(events.getEventNameByType(value), name)

    def test_register(self):
        from supervisor import events
        self.assertFalse(hasattr(events.EventTypes, 'FOO'))
        class FooEvent(events.Event):
            pass
        try:
            events.register('FOO', FooEvent)
            self.assertTrue(events.EventTypes.FOO is FooEvent)
        finally:
            del events.EventTypes.FOO

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_http
import base64
import os
import stat
import sys
import supervisor.medusa.text_socket as socket
import tempfile
import unittest

from supervisor.compat import as_bytes
from supervisor.compat import as_string
from supervisor.compat import sha1

from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import PopulatedDummySupervisor
from supervisor.tests.base import DummyRPCInterfaceFactory
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyRequest

from supervisor.http import NOT_DONE_YET

class HandlerTests:
    def _makeOne(self, supervisord):
        return self._getTargetClass()(supervisord)

    def test_match(self):
        class FakeRequest:
            def __init__(self, uri):
                self.uri = uri
        supervisor = DummySupervisor()
        handler = self._makeOne(supervisor)
        self.assertEqual(handler.match(FakeRequest(handler.path)), True)

class LogtailHandlerTests(HandlerTests, unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import logtail_handler
        return logtail_handler

    def test_handle_request_stdout_logfile_none(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                               stdout_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        handler = self._makeOne(supervisord)
        request = DummyRequest('/logtail/process1', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, 410)

    def test_handle_request_stdout_logfile_missing(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', 'it/is/missing')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        handler = self._makeOne(supervisord)
        request = DummyRequest('/logtail/foo', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, 410)

    def test_handle_request(self):
        f = tempfile.NamedTemporaryFile()
        t = f.name
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', stdout_logfile=t)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        handler = self._makeOne(supervisord)
        request = DummyRequest('/logtail/foo', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, None)
        from supervisor.medusa import http_date
        self.assertEqual(request.headers['Last-Modified'],
                         http_date.build_http_date(os.stat(t)[stat.ST_MTIME]))
        self.assertEqual(request.headers['Content-Type'], 'text/plain')
        self.assertEqual(len(request.producers), 1)
        self.assertEqual(request._done, True)

class MainLogTailHandlerTests(HandlerTests, unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import mainlogtail_handler
        return mainlogtail_handler

    def test_handle_request_stdout_logfile_none(self):
        supervisor = DummySupervisor()
        handler = self._makeOne(supervisor)
        request = DummyRequest('/mainlogtail', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, 410)

    def test_handle_request_stdout_logfile_missing(self):
        supervisor = DummySupervisor()
        supervisor.options.logfile = '/not/there'
        request = DummyRequest('/mainlogtail', None, None, None)
        handler = self._makeOne(supervisor)
        handler.handle_request(request)
        self.assertEqual(request._error, 410)

    def test_handle_request(self):
        supervisor = DummySupervisor()
        f = tempfile.NamedTemporaryFile()
        t = f.name
        supervisor.options.logfile = t
        handler = self._makeOne(supervisor)
        request = DummyRequest('/mainlogtail', None, None, None)
        handler.handle_request(request)
        self.assertEqual(request._error, None)
        from supervisor.medusa import http_date
        self.assertEqual(request.headers['Last-Modified'],
                         http_date.build_http_date(os.stat(t)[stat.ST_MTIME]))
        self.assertEqual(request.headers['Content-Type'], 'text/plain')
        self.assertEqual(len(request.producers), 1)
        self.assertEqual(request._done, True)


class TailFProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import tail_f_producer
        return tail_f_producer

    def _makeOne(self, request, filename, head):
        return self._getTargetClass()(request, filename, head)

    def test_handle_more(self):
        request = DummyRequest('/logtail/foo', None, None, None)
        from supervisor import http
        f = tempfile.NamedTemporaryFile()
        f.write(as_bytes('a' * 80))
        f.flush()
        t = f.name
        producer = self._makeOne(request, t, 80)
        result = producer.more()
        self.assertEqual(result, as_bytes('a' * 80))
        f.write(as_bytes('w' * 100))
        f.flush()
        result = producer.more()
        self.assertEqual(result, as_bytes('w' * 100))
        result = producer.more()
        self.assertEqual(result, http.NOT_DONE_YET)
        f.truncate(0)
        f.flush()
        result = producer.more()
        self.assertEqual(result, '==> File truncated <==\n')

class DeferringChunkedProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_chunked_producer
        return deferring_chunked_producer

    def _makeOne(self, producer, footers=None):
        return self._getTargetClass()(producer, footers)

    def test_more_not_done_yet(self):
        wrapped = DummyProducer(NOT_DONE_YET)
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_string(self):
        wrapped = DummyProducer('hello')
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), '5\r\nhello\r\n')

    def test_more_nodata(self):
        wrapped = DummyProducer()
        producer = self._makeOne(wrapped, footers=['a', 'b'])
        self.assertEqual(producer.more(), '0\r\na\r\nb\r\n\r\n')

    def test_more_nodata_footers(self):
        wrapped = DummyProducer('')
        producer = self._makeOne(wrapped, footers=['a', 'b'])
        self.assertEqual(producer.more(), '0\r\na\r\nb\r\n\r\n')

    def test_more_nodata_nofooters(self):
        wrapped = DummyProducer('')
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), '0\r\n\r\n')

    def test_more_noproducer(self):
        producer = self._makeOne(None)
        self.assertEqual(producer.more(), '')
        
class DeferringCompositeProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_composite_producer
        return deferring_composite_producer

    def _makeOne(self, producers):
        return self._getTargetClass()(producers)

    def test_more_not_done_yet(self):
        wrapped = DummyProducer(NOT_DONE_YET)
        producer = self._makeOne([wrapped])
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_string(self):
        wrapped1 = DummyProducer('hello')
        wrapped2 = DummyProducer('goodbye')
        producer = self._makeOne([wrapped1, wrapped2])
        self.assertEqual(producer.more(), 'hello')
        self.assertEqual(producer.more(), 'goodbye')
        self.assertEqual(producer.more(), '')

    def test_more_nodata(self):
        wrapped = DummyProducer()
        producer = self._makeOne([wrapped])
        self.assertEqual(producer.more(), '')

class DeferringGlobbingProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_globbing_producer
        return deferring_globbing_producer

    def _makeOne(self, producer, buffer_size=1<<16):
        return self._getTargetClass()(producer, buffer_size)

    def test_more_not_done_yet(self):
        wrapped = DummyProducer(NOT_DONE_YET)
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_string(self):
        wrapped = DummyProducer('hello', 'there', 'guy')
        producer = self._makeOne(wrapped, buffer_size=1)
        self.assertEqual(producer.more(), 'hello')

        wrapped = DummyProducer('hello', 'there', 'guy')
        producer = self._makeOne(wrapped, buffer_size=50)
        self.assertEqual(producer.more(), 'hellothereguy')

    def test_more_nodata(self):
        wrapped = DummyProducer()
        producer = self._makeOne(wrapped)
        self.assertEqual(producer.more(), '')

class DeferringHookedProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_hooked_producer
        return deferring_hooked_producer

    def _makeOne(self, producer, function):
        return self._getTargetClass()(producer, function)

    def test_more_not_done_yet(self):
        wrapped = DummyProducer(NOT_DONE_YET)
        producer = self._makeOne(wrapped, None)
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_string(self):
        wrapped = DummyProducer('hello')
        L = []
        def callback(bytes):
            L.append(bytes)
        producer = self._makeOne(wrapped, callback)
        self.assertEqual(producer.more(), 'hello')
        self.assertEqual(L, [])
        producer.more()
        self.assertEqual(L, [5])

    def test_more_nodata(self):
        wrapped = DummyProducer()
        L = []
        def callback(bytes):
            L.append(bytes)
        producer = self._makeOne(wrapped, callback)
        self.assertEqual(producer.more(), '')
        self.assertEqual(L, [0])

    def test_more_noproducer(self):
        producer = self._makeOne(None, None)
        self.assertEqual(producer.more(), '')

class Test_deferring_http_request(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import deferring_http_request
        return deferring_http_request

    def _makeOne(
        self,
        channel=None,
        req='GET / HTTP/1.0',
        command='GET',
        uri='/',
        version='1.0',
        header=(),
        ):
        return self._getTargetClass()(
            channel, req, command, uri, version, header
            )

    def _makeChannel(self):
        class Channel:
            closed = False
            def close_when_done(self):
                self.closed = True
            def push_with_producer(self, producer):
                self.producer = producer
        return Channel()
    
    def test_done_http_10_nokeepalive(self):
        channel = self._makeChannel()
        inst = self._makeOne(channel=channel, version='1.0')
        inst.done()
        self.assertTrue(channel.closed)

    def test_done_http_10_keepalive_no_content_length(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.0',
            header=['Connection: Keep-Alive'],
            )
        
        inst.done()
        self.assertTrue(channel.closed)
        
    def test_done_http_10_keepalive_and_content_length(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.0',
            header=['Connection: Keep-Alive'],
            )
        inst.reply_headers['Content-Length'] = 1
        inst.done()
        self.assertEqual(inst['Connection'], 'Keep-Alive')
        self.assertFalse(channel.closed)

    def test_done_http_11_connection_close(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            header=['Connection: close']
            )
        inst.done()
        self.assertTrue(channel.closed)

    def test_done_http_11_unknown_transfer_encoding(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            )
        inst.reply_headers['Transfer-Encoding'] = 'notchunked'
        inst.done()
        self.assertTrue(channel.closed)

    def test_done_http_11_chunked_transfer_encoding(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            )
        inst.reply_headers['Transfer-Encoding'] = 'chunked'
        inst.done()
        self.assertFalse(channel.closed)

    def test_done_http_11_use_chunked(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            )
        inst.use_chunked = True
        inst.done()
        self.assertTrue('Transfer-Encoding' in inst)
        self.assertFalse(channel.closed)

    def test_done_http_11_wo_content_length_no_te_no_use_chunked_close(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version='1.1',
            )
        inst.use_chunked = False
        inst.done()
        self.assertTrue(channel.closed)

    def test_done_http_09(self):
        channel = self._makeChannel()
        inst = self._makeOne(
            channel=channel,
            version=None,
            )
        inst.done()
        self.assertTrue(channel.closed)
        
class EncryptedDictionaryAuthorizedTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import encrypted_dictionary_authorizer
        return encrypted_dictionary_authorizer

    def _makeOne(self, dict):
        return self._getTargetClass()(dict)

    def test_authorize_baduser(self):
        authorizer = self._makeOne({})
        self.assertFalse(authorizer.authorize(('foo', 'bar')))

    def test_authorize_gooduser_badpassword(self):
        authorizer = self._makeOne({'foo':'password'})
        self.assertFalse(authorizer.authorize(('foo', 'bar')))

    def test_authorize_gooduser_goodpassword(self):
        authorizer = self._makeOne({'foo':'password'})
        self.assertTrue(authorizer.authorize(('foo', 'password')))

    def test_authorize_gooduser_goodpassword_with_colon(self):
        authorizer = self._makeOne({'foo':'pass:word'})
        self.assertTrue(authorizer.authorize(('foo', 'pass:word')))

    def test_authorize_gooduser_badpassword_sha(self):
        password = '{SHA}' + sha1(as_bytes('password')).hexdigest()
        authorizer = self._makeOne({'foo':password})
        self.assertFalse(authorizer.authorize(('foo', 'bar')))

    def test_authorize_gooduser_goodpassword_sha(self):
        password = '{SHA}' + sha1(as_bytes('password')).hexdigest()
        authorizer = self._makeOne({'foo':password})
        self.assertTrue(authorizer.authorize(('foo', 'password')))

class SupervisorAuthHandlerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http import supervisor_auth_handler
        return supervisor_auth_handler

    def _makeOne(self, dict, handler):
        return self._getTargetClass()(dict, handler)

    def test_ctor(self):
        handler = self._makeOne({'a':1}, None)
        from supervisor.http import encrypted_dictionary_authorizer
        self.assertEqual(handler.authorizer.__class__,
                         encrypted_dictionary_authorizer)

    def test_handle_request_authorizes_good_credentials(self):
        request = DummyRequest('/logtail/process1', None, None, None)
        encoded = base64.b64encode(as_bytes("user:password"))
        request.header = ["Authorization: Basic %s" % as_string(encoded)]
        handler = DummyHandler()
        auth_handler = self._makeOne({'user':'password'}, handler)
        auth_handler.handle_request(request)
        self.assertTrue(handler.handled_request)

    def test_handle_request_authorizes_good_password_with_colon(self):
        request = DummyRequest('/logtail/process1', None, None, None)
        # password contains colon
        encoded = base64.b64encode(as_bytes("user:pass:word"))
        request.header = ["Authorization: Basic %s" % as_string(encoded)]
        handler = DummyHandler()
        auth_handler = self._makeOne({'user':'pass:word'}, handler)
        auth_handler.handle_request(request)
        self.assertTrue(handler.handled_request)

    def test_handle_request_does_not_authorize_bad_credentials(self):
        request = DummyRequest('/logtail/process1', None, None, None)
        encoded = base64.b64encode(as_bytes("wrong:wrong"))
        request.header = ["Authorization: Basic %s" % as_string(encoded)]
        handler = DummyHandler()
        auth_handler = self._makeOne({'user':'password'}, handler)
        auth_handler.handle_request(request)
        self.assertFalse(handler.handled_request)


class TopLevelFunctionTests(unittest.TestCase):
    def _make_http_servers(self, sconfigs):
        options = DummyOptions()
        options.server_configs = sconfigs
        options.rpcinterface_factories = [('dummy',DummyRPCInterfaceFactory,{})]
        supervisord = DummySupervisor()
        from supervisor.http import make_http_servers
        servers = make_http_servers(options, supervisord)
        try:
            for config, s in servers:
                s.close()
                socketfile = config.get('file')
                if socketfile is not None:
                    os.unlink(socketfile)
        finally:
            from asyncore import socket_map
            socket_map.clear()
        return servers

    def test_make_http_servers_noauth(self):
        socketfile = tempfile.mktemp()
        inet = {'family':socket.AF_INET, 'host':'localhost', 'port':17735,
                'username':None, 'password':None, 'section':'inet_http_server'}
        unix = {'family':socket.AF_UNIX, 'file':socketfile, 'chmod':448, # 0700 in Py2, 0o700 in Py3
                'chown':(-1, -1), 'username':None, 'password':None,
                'section':'unix_http_server'}
        servers = self._make_http_servers([inet, unix])
        self.assertEqual(len(servers), 2)

        inetdata = servers[0]
        self.assertEqual(inetdata[0], inet)
        server = inetdata[1]
        idents = [
            'Supervisor XML-RPC Handler',
            'Logtail HTTP Request Handler',
            'Main Logtail HTTP Request Handler',
            'Supervisor Web UI HTTP Request Handler',
            'Default HTTP Request Handler'
            ]
        self.assertEqual([x.IDENT for x in server.handlers], idents)

        unixdata = servers[1]
        self.assertEqual(unixdata[0], unix)
        server = unixdata[1]
        self.assertEqual([x.IDENT for x in server.handlers], idents)

    def test_make_http_servers_withauth(self):
        socketfile = tempfile.mktemp()
        inet = {'family':socket.AF_INET, 'host':'localhost', 'port':17736,
                'username':'username', 'password':'password',
                'section':'inet_http_server'}
        unix = {'family':socket.AF_UNIX, 'file':socketfile, 'chmod':448, # 0700 in Py2, 0o700 in Py3
                'chown':(-1, -1), 'username':'username', 'password':'password',
                'section':'unix_http_server'}
        servers = self._make_http_servers([inet, unix])
        self.assertEqual(len(servers), 2)
        from supervisor.http import supervisor_auth_handler
        for config, server in servers:
            for handler in server.handlers:
                self.assertTrue(isinstance(handler, supervisor_auth_handler),
                                handler)

class DummyHandler:
    def __init__(self):
        self.handled_request = False

    def handle_request(self, request):
        self.handled_request = True

class DummyProducer:
    def __init__(self, *data):
        self.data = list(data)

    def more(self):
        if self.data:
            return self.data.pop(0)
        else:
            return ''

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_http_client
import socket
import sys
import unittest

class ListenerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http_client import Listener
        return Listener

    def _makeOne(self):
        return self._getTargetClass()()

    def _makeFakeStdout(self):
        class Stdout(object):
            def __init__(self):
                self.things = []
                self.flushed = False
            def write(self, thing):
                self.things.append(thing)
            def flush(self):
                self.flushed = True
        stdout = Stdout()
        return stdout

    def test_status(self):
        inst = self._makeOne()
        self.assertEqual(inst.status(None, None), None)
        
    def test_error(self):
        inst = self._makeOne()
        try:
            old_stdout = sys.stdout
            stdout = self._makeFakeStdout()
            sys.stdout = stdout
            self.assertEqual(inst.error('url', 'error'), None)
            self.assertEqual(stdout.things, ['url error\n'])
        finally:
            sys.stdout = old_stdout
        
    def test_response_header(self):
        inst = self._makeOne()
        self.assertEqual(inst.response_header(None, None, None), None)

    def test_done(self):
        inst = self._makeOne()
        self.assertEqual(inst.done(None), None)

    def test_feed(self):
        inst = self._makeOne()
        try:
            old_stdout = sys.stdout
            stdout = self._makeFakeStdout()
            sys.stdout = stdout
            inst.feed('url', 'data')
            self.assertEqual(stdout.things, ['data'])
        finally:
            sys.stdout = old_stdout

    def test_close(self):
        inst = self._makeOne()
        self.assertEqual(inst.close(None), None)

class HTTPHandlerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.http_client import HTTPHandler
        return HTTPHandler

    def _makeOne(self, listener=None, username='', password=None):
        if listener is None:
            listener = self._makeListener()
        socket_map = {}
        return self._getTargetClass()(
            listener,
            username,
            password,
            map=socket_map,
            )

    def _makeListener(self):
        listener = DummyListener()
        return listener

    def test_get_url_not_None(self):
        inst = self._makeOne()
        inst.url = 'abc'
        self.assertRaises(AssertionError, inst.get, 'abc')

    def test_get_bad_scheme(self):
        inst = self._makeOne()
        self.assertRaises(
            NotImplementedError,
            inst.get,
            'nothttp://localhost',
            '/abc'
            )
        
    def test_get_implied_port_80(self):
        inst = self._makeOne()
        sockets = []
        connects = []
        inst.create_socket = lambda *arg: sockets.append(arg)
        inst.connect = lambda tup: connects.append(tup)
        inst.get('http://localhost', '/abc/def')
        self.assertEqual(inst.port, 80)
        self.assertEqual(sockets, [(socket.AF_INET, socket.SOCK_STREAM)])
        self.assertEqual(connects, [('localhost', 80)])

    def test_get_explicit_port(self):
        inst = self._makeOne()
        sockets = []
        connects = []
        inst.create_socket = lambda *arg: sockets.append(arg)
        inst.connect = lambda tup: connects.append(tup)
        inst.get('http://localhost:8080', '/abc/def')
        self.assertEqual(inst.port, 8080)
        self.assertEqual(sockets, [(socket.AF_INET, socket.SOCK_STREAM)])
        self.assertEqual(connects, [('localhost', 8080)])

    def test_get_explicit_unix_domain_socket(self):
        inst = self._makeOne()
        sockets = []
        connects = []
        inst.create_socket = lambda *arg: sockets.append(arg)
        inst.connect = lambda tup: connects.append(tup)
        inst.get('unix:///a/b/c', '')
        self.assertEqual(sockets, [(socket.AF_UNIX, socket.SOCK_STREAM)])
        self.assertEqual(connects, ['/a/b/c'])

    def test_close(self):
        inst = self._makeOne()
        dels = []
        inst.del_channel = lambda: dels.append(True)
        inst.socket = DummySocket()
        inst.close()
        self.assertEqual(inst.listener.closed, None)
        self.assertEqual(inst.connected, 0)
        self.assertEqual(dels, [True])
        self.assertTrue(inst.socket.closed)
        self.assertEqual(inst.url, 'CLOSED')

    def test_header(self):
        from supervisor.http_client import CRLF
        inst = self._makeOne()
        pushes = []
        inst.push = lambda val: pushes.append(val)
        inst.header('name', 'val')
        self.assertEqual(pushes, ['name: val', CRLF])

    def test_handle_error_already_handled(self):
        inst = self._makeOne()
        inst.error_handled = True
        self.assertEqual(inst.handle_error(), None)
        
    def test_handle_error(self):
        inst = self._makeOne()
        closed = []
        inst.close = lambda: closed.append(True)
        inst.url = 'foo'
        self.assertEqual(inst.handle_error(), None)
        self.assertEqual(inst.listener.error_url, 'foo')
        self.assertEqual(
            inst.listener.error_msg,
            'Cannot connect, error: None (None)',
            )
        self.assertEqual(closed, [True])
        self.assertTrue(inst.error_handled)

    def test_handle_connect_no_password(self):
        inst = self._makeOne()
        pushed = []
        inst.push = lambda val: pushed.append(val)
        inst.path = '/'
        inst.host = 'localhost'
        inst.handle_connect()
        self.assertTrue(inst.connected)
        self.assertEqual(
            pushed,
            ['GET / HTTP/1.1',
             '\r\n',
             'Host: localhost',
             '\r\n',
             'Accept-Encoding: chunked',
             '\r\n',
             'Accept: */*',
             '\r\n',
             'User-agent: Supervisor HTTP Client',
             '\r\n',
             '\r\n',
             '\r\n']
            )

    def test_handle_connect_with_password(self):
        inst = self._makeOne()
        pushed = []
        inst.push = lambda val: pushed.append(val)
        inst.path = '/'
        inst.host = 'localhost'
        inst.password = 'password'
        inst.username = 'username'
        inst.handle_connect()
        self.assertTrue(inst.connected)
        self.assertEqual(
            pushed,
             ['GET / HTTP/1.1',
              '\r\n',
              'Host: localhost',
              '\r\n',
              'Accept-Encoding: chunked',
              '\r\n',
              'Accept: */*',
              '\r\n',
              'User-agent: Supervisor HTTP Client',
              '\r\n',
              'Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=',
              '\r\n',
              '\r\n',
              '\r\n'],
            )

    def test_feed(self):
        inst = self._makeOne()
        inst.feed('data')
        self.assertEqual(inst.listener.fed_data, ['data'])

    def test_collect_incoming_data_part_is_body(self):
        inst = self._makeOne()
        inst.part = inst.body
        inst.buffer = 'abc'
        inst.collect_incoming_data('foo')
        self.assertEqual(inst.listener.fed_data, ['abcfoo'])
        self.assertEqual(inst.buffer, '')

    def test_collect_incoming_data_part_is_not_body(self):
        inst = self._makeOne()
        inst.part = None
        inst.buffer = 'abc'
        inst.collect_incoming_data('foo')
        self.assertEqual(inst.listener.fed_data, [])
        self.assertEqual(inst.buffer, 'abcfoo')

    def test_found_terminator(self):
        inst = self._makeOne()
        parted = []
        inst.part = lambda: parted.append(True)
        inst.buffer = None
        inst.found_terminator()
        self.assertEqual(parted, [True])
        self.assertEqual(inst.buffer, '')

    def test_ignore(self):
        inst = self._makeOne()
        inst.buffer = None
        inst.ignore()
        self.assertEqual(inst.buffer, '')

    def test_status_line_not_startswith_http(self):
        inst = self._makeOne()
        inst.buffer = 'NOTHTTP/1.0 200 OK'
        self.assertRaises(ValueError, inst.status_line)

    def test_status_line_200(self):
        inst = self._makeOne()
        inst.buffer = 'HTTP/1.0 200 OK'
        version, status, reason = inst.status_line()
        self.assertEqual(version, 'HTTP/1.0')
        self.assertEqual(status, 200)
        self.assertEqual(reason, 'OK')
        self.assertEqual(inst.part, inst.headers)

    def test_status_line_not_200(self):
        inst = self._makeOne()
        inst.buffer = 'HTTP/1.0 201 OK'
        closed = []
        inst.close = lambda: closed.append(True)
        version, status, reason = inst.status_line()
        self.assertEqual(version, 'HTTP/1.0')
        self.assertEqual(status, 201)
        self.assertEqual(reason, 'OK')
        self.assertEqual(inst.part, inst.ignore)
        self.assertEqual(
            inst.listener.error_msg,
            'Cannot read, status code 201'
            )
        self.assertEqual(closed, [True])

    def test_headers_empty_line_nonchunked(self):
        inst = self._makeOne()
        inst.buffer = ''
        inst.encoding = 'not chunked'
        inst.length = 3
        terms = []
        inst.set_terminator = lambda L: terms.append(L)
        inst.headers()
        self.assertEqual(inst.part, inst.body)
        self.assertEqual(terms, [3])
        
    def test_headers_empty_line_chunked(self):
        inst = self._makeOne()
        inst.buffer = ''
        inst.encoding = 'chunked'
        inst.headers()
        self.assertEqual(inst.part, inst.chunked_size)

    def test_headers_nonempty_line_no_name_no_value(self):
        inst = self._makeOne()
        inst.buffer = ':'
        self.assertEqual(inst.headers(), None)

    def test_headers_nonempty_line_transfer_encoding(self):
        inst = self._makeOne()
        inst.buffer = 'Transfer-Encoding: chunked'
        responses = []
        inst.response_header = lambda n, v: responses.append((n, v))
        inst.headers()
        self.assertEqual(inst.encoding, 'chunked')
        self.assertEqual(responses, [('transfer-encoding', 'chunked')])

    def test_headers_nonempty_line_content_length(self):
        inst = self._makeOne()
        inst.buffer = 'Content-Length: 3'
        responses = []
        inst.response_header = lambda n, v: responses.append((n, v))
        inst.headers()
        self.assertEqual(inst.length, 3)
        self.assertEqual(responses, [('content-length', '3')])

    def test_headers_nonempty_line_arbitrary(self):
        inst = self._makeOne()
        inst.buffer = 'X-Test: abc'
        responses = []
        inst.response_header = lambda n, v: responses.append((n, v))
        inst.headers()
        self.assertEqual(responses, [('x-test', 'abc')])

    def test_response_header(self):
        inst = self._makeOne()
        inst.response_header('a', 'b')
        self.assertEqual(inst.listener.response_header_name, 'a')
        self.assertEqual(inst.listener.response_header_value, 'b')

    def test_body(self):
        inst = self._makeOne()
        closed = []
        inst.close = lambda: closed.append(True)
        inst.body()
        self.assertEqual(closed, [True])
        self.assertTrue(inst.listener.done)

    def test_done(self):
        inst = self._makeOne()
        inst.done()
        self.assertTrue(inst.listener.done)

    def test_chunked_size_empty_line(self):
        inst = self._makeOne()
        inst.buffer = ''
        inst.length = 1
        self.assertEqual(inst.chunked_size(), None)
        self.assertEqual(inst.length, 1)
        
    def test_chunked_size_zero_size(self):
        inst = self._makeOne()
        inst.buffer = '0'
        inst.length = 1
        self.assertEqual(inst.chunked_size(), None)
        self.assertEqual(inst.length, 1)
        self.assertEqual(inst.part, inst.trailer)

    def test_chunked_size_nonzero_size(self):
        inst = self._makeOne()
        inst.buffer = '10'
        inst.length = 1
        terms = []
        inst.set_terminator = lambda sz: terms.append(sz)
        self.assertEqual(inst.chunked_size(), None)
        self.assertEqual(inst.part, inst.chunked_body)
        self.assertEqual(inst.length, 17)
        self.assertEqual(terms, [16])

    def test_chunked_body(self):
        from supervisor.http_client import CRLF
        inst = self._makeOne()
        inst.buffer = 'buffer'
        terms = []
        lines = []
        inst.set_terminator = lambda v: terms.append(v)
        inst.feed = lambda v: lines.append(v)
        inst.chunked_body()
        self.assertEqual(terms, [CRLF])
        self.assertEqual(lines, ['buffer'])
        self.assertEqual(inst.part, inst.chunked_size)

    def test_trailer_line_not_crlf(self):
        inst = self._makeOne()
        inst.buffer = ''
        self.assertEqual(inst.trailer(), None)

    def test_trailer_line_crlf(self):
        from supervisor.http_client import CRLF
        inst = self._makeOne()
        inst.buffer = CRLF
        dones = []
        closes = []
        inst.done = lambda: dones.append(True)
        inst.close = lambda: closes.append(True)
        self.assertEqual(inst.trailer(), None)
        self.assertEqual(dones, [True])
        self.assertEqual(closes, [True])
        
class DummyListener(object):
    closed = None
    error_url = None
    error_msg = None
    done = False
    def __init__(self):
        self.fed_data = []
        
    def close(self, url):
        self.closed = url

    def error(self, url, msg):
        self.error_url = url
        self.error_msg = msg

    def feed(self, url, data):
        self.fed_data.append(data)

    def status(self, url, int):
        self.status_url = url
        self.status_int = int

    def response_header(self, url, name, value):
        self.response_header_name = name
        self.response_header_value = value

    def done(self, url):
        self.done = True

class DummySocket(object):
    closed = False
    def close(self):
        self.closed = True

########NEW FILE########
__FILENAME__ = test_loggers
# -*- coding: utf-8 -*-
import errno
import sys
import unittest
import tempfile
import shutil
import os
import syslog

from supervisor.compat import PY3
from supervisor.compat import as_string

from supervisor.tests.base import mock
from supervisor.tests.base import DummyStream
from supervisor import read_file

class LevelTests(unittest.TestCase):
    def test_LOG_LEVELS_BY_NUM_doesnt_include_builtins(self):
        from supervisor import loggers
        for level_name in loggers.LOG_LEVELS_BY_NUM.values():
            self.assertFalse(level_name.startswith('_'))

class HandlerTests:
    def setUp(self):
        self.basedir = tempfile.mkdtemp()
        self.filename = os.path.join(self.basedir, 'thelog')

    def tearDown(self):
        try:
            shutil.rmtree(self.basedir)
        except OSError:
            pass

    def _makeOne(self, *arg, **kw):
        klass = self._getTargetClass()
        return klass(*arg, **kw)

    def _makeLogRecord(self, msg):
        from supervisor import loggers
        record = loggers.LogRecord(
            level=loggers.LevelsByName.INFO,
            msg=msg,
            exc_info=None
            )
        return record

class BareHandlerTests(HandlerTests, unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.loggers import Handler
        return Handler

    def test_flush_stream_flush_raises_IOError_EPIPE(self):
        stream = DummyStream(error=IOError(errno.EPIPE))
        inst = self._makeOne(stream=stream)
        self.assertEqual(inst.flush(), None) # does not raise

    def test_flush_stream_flush_raises_IOError_not_EPIPE(self):
        stream = DummyStream(error=IOError(errno.EALREADY))
        inst = self._makeOne(stream=stream)
        self.assertRaises(IOError, inst.flush) # non-EPIPE IOError raises

    def test_close_already_closed(self):
        stream = DummyStream()
        inst = self._makeOne(stream=stream)
        inst.closed = True
        self.assertEqual(inst.close(), None)

    def test_close_stream_fileno_above_3(self):
        stream = DummyStream(fileno=50)
        inst = self._makeOne(stream=stream)
        self.assertEqual(inst.close(), None)
        self.assertTrue(inst.closed)
        self.assertTrue(inst.stream.closed)

    def test_close_stream_fileno_below_3(self):
        stream = DummyStream(fileno=0)
        inst = self._makeOne(stream=stream)
        self.assertEqual(inst.close(), None)
        self.assertFalse(inst.closed)
        self.assertFalse(inst.stream.closed)

    def test_emit_gardenpath(self):
        stream = DummyStream()
        inst = self._makeOne(stream=stream)
        record = self._makeLogRecord('foo')
        inst.emit(record)
        self.assertEqual(stream.flushed, True)
        self.assertEqual(stream.written, 'foo')

    def test_emit_unicode_error(self):
        stream = DummyStream(error=UnicodeError)
        inst = self._makeOne(stream=stream)
        record = self._makeLogRecord('foo')
        inst.emit(record)
        self.assertEqual(stream.flushed, True)
        self.assertEqual(stream.written, 'foo')

    def test_emit_other_error(self):
        stream = DummyStream(error=TypeError)
        inst = self._makeOne(stream=stream)
        handled = []
        inst.handleError = lambda: handled.append(True)
        record = self._makeLogRecord('foo')
        inst.emit(record)
        self.assertEqual(stream.flushed, False)
        self.assertEqual(stream.written, '')

class FileHandlerTests(HandlerTests, unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.loggers import FileHandler
        return FileHandler

    def test_ctor(self):
        handler = self._makeOne(self.filename)
        self.assertTrue(os.path.exists(self.filename), self.filename)
        self.assertEqual(handler.mode, 'a')
        self.assertEqual(handler.baseFilename, self.filename)
        self.assertEqual(handler.stream.name, self.filename)
        handler.close()

    def test_close(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        handler.stream = DummyStream()
        handler.close()
        self.assertEqual(handler.stream.closed, True)

    def test_close_raises(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        handler.stream = DummyStream(OSError)
        self.assertRaises(OSError, handler.close)
        self.assertEqual(handler.stream.closed, False)

    def test_reopen(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        stream = DummyStream()
        handler.stream = stream
        handler.reopen()
        self.assertEqual(stream.closed, True)
        self.assertEqual(handler.stream.name, self.filename)
        handler.close()

    def test_reopen_raises(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        stream = DummyStream()
        handler.stream = stream
        handler.baseFilename = os.path.join(self.basedir, 'notthere', 'a.log')
        self.assertRaises(IOError, handler.reopen)
        self.assertEqual(stream.closed, True)

    def test_remove_exists(self):
        handler = self._makeOne(self.filename)
        self.assertTrue(os.path.exists(self.filename), self.filename)
        handler.remove()
        self.assertFalse(os.path.exists(self.filename), self.filename)

    def test_remove_doesntexist(self):
        handler = self._makeOne(self.filename)
        os.remove(self.filename)
        self.assertFalse(os.path.exists(self.filename), self.filename)
        handler.remove() # should not raise
        self.assertFalse(os.path.exists(self.filename), self.filename)

    def test_remove_raises(self):
        handler = self._makeOne(self.filename)
        os.remove(self.filename)
        os.mkdir(self.filename)
        self.assertTrue(os.path.exists(self.filename), self.filename)
        self.assertRaises(OSError, handler.remove)

    def test_emit_ascii_noerror(self):
        handler = self._makeOne(self.filename)
        record = self._makeLogRecord('hello!')
        handler.emit(record)
        handler.close()
        self.assertEqual(read_file(self.filename,'r'), 'hello!')

#    def test_emit_unicode_noerror(self):
#        handler = self._makeOne(self.filename)
#        record = self._makeLogRecord(u'fi\xed')
#        handler.emit(record)
#        content = open(self.filename, 'r').read()
#        self.assertEqual(content, 'fi\xc3\xad')

    def test_emit_error(self):
        handler = self._makeOne(self.filename)
        handler.stream.close()
        handler.stream = DummyStream(error=OSError)
        record = self._makeLogRecord('hello!')
        try:
            old_stderr = sys.stderr
            dummy_stderr = DummyStream()
            sys.stderr = dummy_stderr
            handler.emit(record)
        finally:
            sys.stderr = old_stderr

        self.assertTrue(dummy_stderr.written.endswith('OSError\n'),
                        dummy_stderr.written)

class RotatingFileHandlerTests(FileHandlerTests):

    def _getTargetClass(self):
        from supervisor.loggers import RotatingFileHandler
        return RotatingFileHandler

    def test_ctor(self):
        handler = self._makeOne(self.filename)
        self.assertEqual(handler.mode, 'a')
        self.assertEqual(handler.maxBytes, 512*1024*1024)
        self.assertEqual(handler.backupCount, 10)

    def test_emit_does_rollover(self):
        handler = self._makeOne(self.filename, maxBytes=10, backupCount=2)
        record = self._makeLogRecord('a' * 4)

        handler.emit(record) # 4 bytes
        self.assertFalse(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 8 bytes
        self.assertFalse(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 12 bytes, do rollover
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 16 bytes
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 20 bytes
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertFalse(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 24 bytes, do rollover
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertTrue(os.path.exists(self.filename + '.2'))

        handler.emit(record) # 28 bytes
        self.assertTrue(os.path.exists(self.filename + '.1'))
        self.assertTrue(os.path.exists(self.filename + '.2'))

        self.assertEqual(read_file(self.filename), 'a' * 4)
        one = read_file(self.filename+'.1')
        self.assertEqual(one, 'a'*12)
        two = read_file(self.filename+'.2')
        self.assertEqual(two, 'a'*12)

    def test_current_logfile_removed(self):
        handler = self._makeOne(self.filename, maxBytes=6, backupCount=1)
        record = self._makeLogRecord('a' * 4)

        handler.emit(record) # 4 bytes
        self.assertTrue(os.path.exists(self.filename))
        self.assertFalse(os.path.exists(self.filename + '.1'))

        # Someone removes the active log file! :-(
        os.unlink(self.filename)
        self.assertFalse(os.path.exists(self.filename))

        handler.emit(record) # 8 bytes, do rollover
        self.assertTrue(os.path.exists(self.filename))
        self.assertFalse(os.path.exists(self.filename + '.1'))

    def test_removeAndRename_destination_does_not_exist(self):
        inst = self._makeOne(self.filename)
        renames = []
        removes = []
        inst._remove = lambda v: removes.append(v)
        inst._exists = lambda v: False
        inst._rename = lambda s, t: renames.append((s, t))
        inst.removeAndRename('foo', 'bar')
        self.assertEqual(renames, [('foo', 'bar')])
        self.assertEqual(removes, [])

    def test_removeAndRename_destination_exists(self):
        inst = self._makeOne(self.filename)
        renames = []
        removes = []
        inst._remove = lambda v: removes.append(v)
        inst._exists = lambda v: True
        inst._rename = lambda s, t: renames.append((s, t))
        inst.removeAndRename('foo', 'bar')
        self.assertEqual(renames, [('foo', 'bar')])
        self.assertEqual(removes, ['bar'])

    def test_removeAndRename_remove_raises_ENOENT(self):
        def remove(fn):
            raise OSError(errno.ENOENT)
        inst = self._makeOne(self.filename)
        renames = []
        inst._remove = remove
        inst._exists = lambda v: True
        inst._rename = lambda s, t: renames.append((s, t))
        inst.removeAndRename('foo', 'bar')
        self.assertEqual(renames, [('foo', 'bar')])

    def test_removeAndRename_remove_raises_other_than_ENOENT(self):
        def remove(fn):
            raise OSError(errno.EAGAIN)
        inst = self._makeOne(self.filename)
        inst._remove = remove
        inst._exists = lambda v: True
        self.assertRaises(OSError, inst.removeAndRename, 'foo', 'bar')

    def test_removeAndRename_rename_raises_ENOENT(self):
        def rename(s, d):
            raise OSError(errno.ENOENT)
        inst = self._makeOne(self.filename)
        inst._rename = rename
        inst._exists = lambda v: False
        self.assertEqual(inst.removeAndRename('foo', 'bar'), None)

    def test_removeAndRename_rename_raises_other_than_ENOENT(self):
        def rename(s, d):
            raise OSError(errno.EAGAIN)
        inst = self._makeOne(self.filename)
        inst._rename = rename
        inst._exists = lambda v: False
        self.assertRaises(OSError, inst.removeAndRename, 'foo', 'bar')

    def test_doRollover_maxbytes_lte_zero(self):
        inst = self._makeOne(self.filename)
        inst.maxBytes = 0
        self.assertEqual(inst.doRollover(), None)


class BoundIOTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.loggers import BoundIO
        return BoundIO

    def _makeOne(self, maxbytes, buf=''):
        klass = self._getTargetClass()
        return klass(maxbytes, buf)

    def test_write_overflow(self):
        io = self._makeOne(1, 'a')
        io.write('b')
        self.assertEqual(io.buf, 'b')

    def test_getvalue(self):
        io = self._makeOne(1, 'a')
        self.assertEqual(io.getvalue(), 'a')

    def test_clear(self):
        io = self._makeOne(1, 'a')
        io.clear()
        self.assertEqual(io.buf, '')

    def test_close(self):
        io = self._makeOne(1, 'a')
        io.close()
        self.assertEqual(io.buf, '')

class LoggerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.loggers import Logger
        return Logger

    def _makeOne(self, level=None, handlers=None):
        klass = self._getTargetClass()
        return klass(level, handlers)

    def test_blather(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.BLAT)
        logger = self._makeOne(LevelsByName.BLAT, (handler,))
        logger.blather('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.TRAC
        logger.blather('hello')
        self.assertEqual(len(handler.records), 1)

    def test_trace(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.TRAC)
        logger = self._makeOne(LevelsByName.TRAC, (handler,))
        logger.trace('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.DEBG
        logger.trace('hello')
        self.assertEqual(len(handler.records), 1)

    def test_debug(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.DEBG)
        logger = self._makeOne(LevelsByName.DEBG, (handler,))
        logger.debug('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.INFO
        logger.debug('hello')
        self.assertEqual(len(handler.records), 1)

    def test_info(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.INFO)
        logger = self._makeOne(LevelsByName.INFO, (handler,))
        logger.info('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.WARN
        logger.info('hello')
        self.assertEqual(len(handler.records), 1)

    def test_warn(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.WARN)
        logger = self._makeOne(LevelsByName.WARN, (handler,))
        logger.warn('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.ERRO
        logger.warn('hello')
        self.assertEqual(len(handler.records), 1)

    def test_error(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.ERRO)
        logger = self._makeOne(LevelsByName.ERRO, (handler,))
        logger.error('hello')
        self.assertEqual(len(handler.records), 1)
        logger.level = LevelsByName.CRIT
        logger.error('hello')
        self.assertEqual(len(handler.records), 1)

    def test_critical(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.CRIT)
        logger = self._makeOne(LevelsByName.CRIT, (handler,))
        logger.critical('hello')
        self.assertEqual(len(handler.records), 1)

    def test_close(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.CRIT)
        logger = self._makeOne(LevelsByName.CRIT, (handler,))
        logger.close()
        self.assertEqual(handler.closed, True)

    def test_getvalue(self):
        from supervisor.loggers import LevelsByName
        handler = DummyHandler(LevelsByName.CRIT)
        logger = self._makeOne(LevelsByName.CRIT, (handler,))
        self.assertRaises(NotImplementedError, logger.getvalue)


class MockSysLog(mock.Mock):
    def __call__(self, *args, **kwargs):
        message = args[-1]
        if sys.version_info < (3, 0) and isinstance(message, unicode):
            # Python 2.x raises a UnicodeEncodeError when attempting to
            #  transmit unicode characters that don't encode in the
            #  default encoding.
            message.encode()
        super(MockSysLog, self).__call__(*args, **kwargs)

class SyslogHandlerTests(HandlerTests, unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def _getTargetClass(self):
        return __import__('supervisor.loggers').loggers.SyslogHandler

    def _makeOne(self):
        return self._getTargetClass()()

    def test_emit_record_asdict_raises(self):
        class Record(object):
            def asdict(self):
                raise TypeError
        record = Record()
        handler = self._makeOne()
        handled = []
        handler.handleError = lambda: handled.append(True)
        handler.emit(record)
        self.assertEqual(handled, [True])


    @mock.patch('syslog.syslog', MockSysLog())
    def test_emit_ascii_noerror(self):
        handler = self._makeOne()
        record = self._makeLogRecord('hello!')
        handler.emit(record)
        syslog.syslog.assert_called_with('hello!')

    @mock.patch('syslog.syslog', MockSysLog())
    def test_close(self):
        handler = self._makeOne()
        handler.close()  # no-op for syslog

    @mock.patch('syslog.syslog', MockSysLog())
    def test_reopen(self):
        handler = self._makeOne()
        handler.reopen()  # no-op for syslog

    if PY3:
        @mock.patch('syslog.syslog', MockSysLog())
        def test_emit_unicode_noerror(self):
            handler = self._makeOne()
            record = self._makeLogRecord('fií')
            handler.emit(record)
            syslog.syslog.assert_called_with('fií')
        def test_emit_unicode_witherror(self):
            handler = self._makeOne()
            called = []
            def fake_syslog(msg):
                if not called:
                    called.append(msg)
                    raise UnicodeError
            handler._syslog = fake_syslog
            record = self._makeLogRecord('fií')
            handler.emit(record)
            self.assertEqual(called, ['fií'])
    else:
        @mock.patch('syslog.syslog', MockSysLog())
        def test_emit_unicode_noerror(self):
            handler = self._makeOne()
            inp = as_string('fií')
            record = self._makeLogRecord(inp)
            handler.emit(record)
            syslog.syslog.assert_called_with('fi\xc3\xad')
        def test_emit_unicode_witherror(self):
            handler = self._makeOne()
            called = []
            def fake_syslog(msg):
                if not called:
                    called.append(msg)
                    raise UnicodeError
            handler._syslog = fake_syslog
            record = self._makeLogRecord(as_string('fií'))
            handler.emit(record)
            self.assertEqual(called, [as_string('fi\xc3\xad')])

class DummyHandler:
    close = False
    def __init__(self, level):
        self.level = level
        self.records = []
    def emit(self, record):
        self.records.append(record)
    def close(self):
        self.closed = True

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_options
"""Test suite for supervisor.options"""

import logging
import os
import sys
import tempfile
import supervisor.medusa.text_socket as socket
import unittest
import signal
import shutil
import errno

from supervisor import read_file
from supervisor.compat import StringIO
from supervisor.compat import as_bytes

from supervisor.tests.base import Mock, sentinel, patch
from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import DummyLogger
from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummySocketConfig
from supervisor.tests.base import lstrip


def missing_but_potential_file():
    """Quick and dirty way of coming up with a decent filename that might exist"""
    tempf = tempfile.NamedTemporaryFile()
    fname = tempf.name
    tempf.close()
    return fname

class DummyExitException(Exception):
    def __init__(self, exitcode):
        self.exitcode = exitcode

def dummy_exit():
    """Dummy out exit so we don't actually exit"""
    def dummy_exit(_exitcode=0):
        raise DummyExitException(exitcode=_exitcode)
    return dummy_exit

def _getTempFile(name):
    prefix = 'supervisor.{0}.'.format(name)
    return tempfile.NamedTemporaryFile(prefix=prefix)

logger = logging.getLogger(__name__)


class OptionTests(unittest.TestCase):

    def _getTargetClass(self):
        from supervisor.options import Options
        return Options

    def _makeOptions(self, read_error=False):
        Options = self._getTargetClass()
        from supervisor.datatypes import integer

        class MyOptions(Options):
            master = {
                'other': 41 }
            def __init__(self, read_error=read_error):
                self.read_error = read_error
                Options.__init__(self)
                class Foo(object): pass
                self.configroot = Foo()

            def read_config(self, fp):
                if self.read_error:
                    raise ValueError(self.read_error)
                # Pretend we read it from file:
                self.configroot.__dict__.update(self.default_map)
                self.configroot.__dict__.update(self.master)

        options = MyOptions()
        options.configfile = StringIO()
        options.add(name='anoption', confname='anoption',
                    short='o', long='option', default='default')
        options.add(name='other', confname='other', env='OTHER',
                    short='p:', long='other=', handler=integer)
        return options

    def test_add_flag_not_None_handler_not_None(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(ValueError, inst.add, flag=True, handler=True)

    def test_add_flag_not_None_long_false_short_false(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            flag=True,
            long=False,
            short=False,
            )

    def test_add_flag_not_None_short_endswith_colon(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            flag=True,
            long=False,
            short=":",
            )

    def test_add_flag_not_None_long_endswith_equal(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            flag=True,
            long='=',
            short=False,
            )

    def test_add_inconsistent_short_long_options(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            long='=',
            short='abc',
            )

    def test_add_short_option_startswith_dash(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            long=False,
            short='-abc',
            )

    def test_add_short_option_too_long(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            long=False,
            short='abc',
            )

    def test_add_duplicate_short_option_key(self):
        cls = self._getTargetClass()
        inst = cls()
        inst.options_map = {'-a':True}
        self.assertRaises(
            ValueError,
            inst.add,
            long=False,
            short='a',
            )

    def test_add_long_option_startswith_dash(self):
        cls = self._getTargetClass()
        inst = cls()
        self.assertRaises(
            ValueError,
            inst.add,
            long='-abc',
            short=False,
            )

    def test_add_duplicate_long_option_key(self):
        cls = self._getTargetClass()
        inst = cls()
        inst.options_map = {'--abc':True}
        self.assertRaises(
            ValueError,
            inst.add,
            long='abc',
            short=False,
            )

    def test_searchpaths(self):
        options = self._makeOptions()
        self.assertEqual(len(options.searchpaths), 5)
        self.assertTrue('supervisord.conf' in options.searchpaths)
        self.assertTrue('etc/supervisord.conf' in options.searchpaths)
        self.assertTrue('/etc/supervisord.conf' in options.searchpaths)

    def test_options_and_args_order(self):
        # Only config file exists
        options = self._makeOptions()
        options.realize([])
        self.assertEqual(options.anoption, 'default')
        self.assertEqual(options.other, 41)

        # Env should trump config
        options = self._makeOptions()
        os.environ['OTHER'] = '42'
        options.realize([])
        self.assertEqual(options.other, 42)

        # Opt should trump both env (still set) and config
        options = self._makeOptions()
        options.realize(['-p', '43'])
        self.assertEqual(options.other, 43)
        del os.environ['OTHER']

    def test_config_reload(self):
        options = self._makeOptions()
        options.realize([])
        self.assertEqual(options.other, 41)
        options.master['other'] = 42
        options.process_config()
        self.assertEqual(options.other, 42)

    def test_config_reload_do_usage_false(self):
        options = self._makeOptions(read_error='error')
        self.assertRaises(ValueError, options.process_config,
                          False)

    def test_config_reload_do_usage_true(self):
        options = self._makeOptions(read_error='error')
        L = []
        def exit(num):
            L.append(num)
        options.stderr = options.stdout = StringIO()
        options.exit = exit
        options.configroot.anoption = 1
        options.configroot.other = 1
        options.process_config(True)
        self.assertEqual(L, [2])

    def test__set(self):
        from supervisor.options import Options
        options = Options()
        options._set('foo', 'bar', 0)
        self.assertEqual(options.foo, 'bar')
        self.assertEqual(options.attr_priorities['foo'], 0)
        options._set('foo', 'baz', 1)
        self.assertEqual(options.foo, 'baz')
        self.assertEqual(options.attr_priorities['foo'], 1)
        options._set('foo', 'gazonk', 0)
        self.assertEqual(options.foo, 'baz')
        self.assertEqual(options.attr_priorities['foo'], 1)
        options._set('foo', 'gazonk', 1)
        self.assertEqual(options.foo, 'gazonk')

    def test_missing_default_config(self):
        options = self._makeOptions()
        options.searchpaths = [missing_but_potential_file()]
        options.exit = dummy_exit()
        options.stderr = StringIO()
        try:
            options.default_configfile()
        except DummyExitException as e:
            self.assertEqual(e.exitcode, 2)
        else:
            self.fail("expected exception")
        self.assertTrue(options.stderr.getvalue().startswith("Error: No config file found at default paths"))

    def test_default_config(self):
        options = self._makeOptions()
        tempf = tempfile.NamedTemporaryFile()
        options.searchpaths = [tempf.name]
        config = options.default_configfile()
        self.assertEqual(config, tempf.name)
        tempf.close()

    def test_help(self):
        options = self._makeOptions()
        options.exit = dummy_exit()
        options.stdout = StringIO()
        options.progname = 'test_help'
        options.doc = 'A sample docstring for %s'
        try:
            options.help('Argument ignored?')
        except DummyExitException:
            self.assertEqual(options.stdout.getvalue(), 'A sample docstring for test_help\n')
        else:
            self.fail('help() did not try to exit.')

class ClientOptionsTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import ClientOptions
        return ClientOptions

    def _makeOne(self):
        return self._getTargetClass()()

    def test_no_config_file(self):
        """Making sure config file is not required."""
        instance = self._makeOne()

        # No default config file search in case they would exist
        self.assertTrue(len(instance.searchpaths) > 0)
        instance.searchpaths = []

        instance.exit = dummy_exit()

        instance.realize(args=['-s', 'http://localhost:9001', '-u', 'chris',
                               '-p', '123'])

        self.assertEqual(instance.interactive, 1)
        self.assertEqual(instance.serverurl, 'http://localhost:9001')
        self.assertEqual(instance.username, 'chris')
        self.assertEqual(instance.password, '123')

    def test_options(self):
        tempdir = tempfile.gettempdir()
        s = lstrip("""[supervisorctl]
        serverurl=http://localhost:9001
        username=chris
        password=123
        prompt=mysupervisor
        history_file=%s/sc_history
        """ % tempdir)

        fp = StringIO(s)
        instance = self._makeOne()
        instance.configfile = fp
        instance.realize(args=[])
        self.assertEqual(instance.interactive, True)
        history_file = os.path.join(tempdir, 'sc_history')
        self.assertEqual(instance.history_file, history_file)
        options = instance.configroot.supervisorctl
        self.assertEqual(options.prompt, 'mysupervisor')
        self.assertEqual(options.serverurl, 'http://localhost:9001')
        self.assertEqual(options.username, 'chris')
        self.assertEqual(options.password, '123')
        self.assertEqual(options.history_file, history_file)

    def test_unreadable_config_file(self):
        fname = missing_but_potential_file()
        self.assertFalse(os.path.exists(fname))

        instance = self._makeOne()
        instance.stderr = StringIO()

        instance.exit = dummy_exit()
        try:
            instance.realize(args=['-c', fname])
        except DummyExitException as e:
            self.assertEqual(e.exitcode, 2)
        else:
            self.fail("expected exception")

        try:
            instance.read_config(fname)
        except ValueError as e:
            self.assertTrue("could not find config file" in str(e))
        else:
            self.fail("expected exception")

        tempf = tempfile.NamedTemporaryFile()
        os.chmod(tempf.name, 0) # Removing read perms
        try:
            instance.read_config(tempf.name)
        except ValueError as e:
            self.assertTrue("could not read config file" in str(e))
        else:
            self.fail("expected exception")
        tempf.close()

    def test_options_unixsocket_cli(self):
        fp = StringIO('[supervisorctl]')
        instance = self._makeOne()
        instance.configfile = fp
        instance.realize(args=['--serverurl', 'unix:///dev/null'])
        self.assertEqual(instance.serverurl, 'unix:///dev/null')

class ServerOptionsTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import ServerOptions
        return ServerOptions

    def _makeOne(self):
        return self._getTargetClass()()

    def test_version(self):
        from supervisor.options import VERSION
        options = self._makeOne()
        options.stdout = StringIO()
        self.assertRaises(SystemExit, options.version, None)
        self.assertEqual(options.stdout.getvalue(), VERSION + '\n')

    def test_options(self):
        s = lstrip("""[inet_http_server]
        port=127.0.0.1:8999
        username=chrism
        password=foo

        [supervisord]
        directory=%(tempdir)s
        backofflimit=10
        user=root
        umask=022
        logfile=supervisord.log
        logfile_maxbytes=1000MB
        logfile_backups=5
        loglevel=error
        pidfile=supervisord.pid
        nodaemon=true
        identifier=fleeb
        childlogdir=%(tempdir)s
        nocleanup=true
        minfds=2048
        minprocs=300
        environment=FAKE_ENV_VAR=/some/path

        [program:cat1]
        command=/bin/cat
        priority=1
        autostart=true
        user=root
        stdout_logfile=/tmp/cat.log
        stopsignal=KILL
        stopwaitsecs=5
        startsecs=5
        startretries=10
        directory=/tmp
        umask=002

        [program:cat2]
        priority=2
        command=/bin/cat
        autostart=true
        autorestart=false
        stdout_logfile_maxbytes = 1024
        stdout_logfile_backups = 2
        stdout_logfile = /tmp/cat2.log

        [program:cat3]
        priority=3
        process_name = replaced
        command=/bin/cat
        autorestart=true
        exitcodes=0,1,127
        stopasgroup=true
        killasgroup=true

        [program:cat4]
        priority=4
        process_name = fleeb_%%(process_num)s
        numprocs = 2
        command = /bin/cat
        autorestart=unexpected

        [program:cat5]
        priority=5
        process_name = foo_%%(process_num)02d
        numprocs = 2
        numprocs_start = 1
        command = /bin/cat
        directory = /some/path/foo_%%(process_num)02d
        """ % {'tempdir':tempfile.gettempdir()})

        from supervisor import datatypes

        fp = StringIO(s)
        instance = self._makeOne()
        instance.configfile = fp
        instance.realize(args=[])
        options = instance.configroot.supervisord
        self.assertEqual(options.directory, tempfile.gettempdir())
        self.assertEqual(options.umask, 18) # 022 in Py2, 0o22 in Py3
        self.assertEqual(options.logfile, 'supervisord.log')
        self.assertEqual(options.logfile_maxbytes, 1000 * 1024 * 1024)
        self.assertEqual(options.logfile_backups, 5)
        self.assertEqual(options.loglevel, 40)
        self.assertEqual(options.pidfile, 'supervisord.pid')
        self.assertEqual(options.nodaemon, True)
        self.assertEqual(options.identifier, 'fleeb')
        self.assertEqual(options.childlogdir, tempfile.gettempdir())
        self.assertEqual(len(options.server_configs), 1)
        self.assertEqual(options.server_configs[0]['family'], socket.AF_INET)
        self.assertEqual(options.server_configs[0]['host'], '127.0.0.1')
        self.assertEqual(options.server_configs[0]['port'], 8999)
        self.assertEqual(options.server_configs[0]['username'], 'chrism')
        self.assertEqual(options.server_configs[0]['password'], 'foo')
        self.assertEqual(options.nocleanup, True)
        self.assertEqual(options.minfds, 2048)
        self.assertEqual(options.minprocs, 300)
        self.assertEqual(options.nocleanup, True)
        self.assertEqual(len(options.process_group_configs), 5)
        self.assertEqual(options.environment, dict(FAKE_ENV_VAR='/some/path'))

        cat1 = options.process_group_configs[0]
        self.assertEqual(cat1.name, 'cat1')
        self.assertEqual(cat1.priority, 1)
        self.assertEqual(len(cat1.process_configs), 1)

        proc1 = cat1.process_configs[0]
        self.assertEqual(proc1.name, 'cat1')
        self.assertEqual(proc1.command, '/bin/cat')
        self.assertEqual(proc1.priority, 1)
        self.assertEqual(proc1.autostart, True)
        self.assertEqual(proc1.autorestart, datatypes.RestartWhenExitUnexpected)
        self.assertEqual(proc1.startsecs, 5)
        self.assertEqual(proc1.startretries, 10)
        self.assertEqual(proc1.uid, 0)
        self.assertEqual(proc1.stdout_logfile, '/tmp/cat.log')
        self.assertEqual(proc1.stopsignal, signal.SIGKILL)
        self.assertEqual(proc1.stopwaitsecs, 5)
        self.assertEqual(proc1.stopasgroup, False)
        self.assertEqual(proc1.killasgroup, False)
        self.assertEqual(proc1.stdout_logfile_maxbytes,
                         datatypes.byte_size('50MB'))
        self.assertEqual(proc1.stdout_logfile_backups, 10)
        self.assertEqual(proc1.exitcodes, [0,2])
        self.assertEqual(proc1.directory, '/tmp')
        self.assertEqual(proc1.umask, 2)
        self.assertEqual(proc1.environment, dict(FAKE_ENV_VAR='/some/path'))

        cat2 = options.process_group_configs[1]
        self.assertEqual(cat2.name, 'cat2')
        self.assertEqual(cat2.priority, 2)
        self.assertEqual(len(cat2.process_configs), 1)

        proc2 = cat2.process_configs[0]
        self.assertEqual(proc2.name, 'cat2')
        self.assertEqual(proc2.command, '/bin/cat')
        self.assertEqual(proc2.priority, 2)
        self.assertEqual(proc2.autostart, True)
        self.assertEqual(proc2.autorestart, False)
        self.assertEqual(proc2.uid, None)
        self.assertEqual(proc2.stdout_logfile, '/tmp/cat2.log')
        self.assertEqual(proc2.stopsignal, signal.SIGTERM)
        self.assertEqual(proc2.stopasgroup, False)
        self.assertEqual(proc2.killasgroup, False)
        self.assertEqual(proc2.stdout_logfile_maxbytes, 1024)
        self.assertEqual(proc2.stdout_logfile_backups, 2)
        self.assertEqual(proc2.exitcodes, [0,2])
        self.assertEqual(proc2.directory, None)

        cat3 = options.process_group_configs[2]
        self.assertEqual(cat3.name, 'cat3')
        self.assertEqual(cat3.priority, 3)
        self.assertEqual(len(cat3.process_configs), 1)

        proc3 = cat3.process_configs[0]
        self.assertEqual(proc3.name, 'replaced')
        self.assertEqual(proc3.command, '/bin/cat')
        self.assertEqual(proc3.priority, 3)
        self.assertEqual(proc3.autostart, True)
        self.assertEqual(proc3.autorestart, datatypes.RestartUnconditionally)
        self.assertEqual(proc3.uid, None)
        self.assertEqual(proc3.stdout_logfile, datatypes.Automatic)
        self.assertEqual(proc3.stdout_logfile_maxbytes,
                         datatypes.byte_size('50MB'))
        self.assertEqual(proc3.stdout_logfile_backups, 10)
        self.assertEqual(proc3.exitcodes, [0,1,127])
        self.assertEqual(proc3.stopsignal, signal.SIGTERM)
        self.assertEqual(proc3.stopasgroup, True)
        self.assertEqual(proc3.killasgroup, True)

        cat4 = options.process_group_configs[3]
        self.assertEqual(cat4.name, 'cat4')
        self.assertEqual(cat4.priority, 4)
        self.assertEqual(len(cat4.process_configs), 2)

        proc4_a = cat4.process_configs[0]
        self.assertEqual(proc4_a.name, 'fleeb_0')
        self.assertEqual(proc4_a.command, '/bin/cat')
        self.assertEqual(proc4_a.priority, 4)
        self.assertEqual(proc4_a.autostart, True)
        self.assertEqual(proc4_a.autorestart,
                         datatypes.RestartWhenExitUnexpected)
        self.assertEqual(proc4_a.uid, None)
        self.assertEqual(proc4_a.stdout_logfile, datatypes.Automatic)
        self.assertEqual(proc4_a.stdout_logfile_maxbytes,
                         datatypes.byte_size('50MB'))
        self.assertEqual(proc4_a.stdout_logfile_backups, 10)
        self.assertEqual(proc4_a.exitcodes, [0,2])
        self.assertEqual(proc4_a.stopsignal, signal.SIGTERM)
        self.assertEqual(proc4_a.stopasgroup, False)
        self.assertEqual(proc4_a.killasgroup, False)
        self.assertEqual(proc4_a.directory, None)

        proc4_b = cat4.process_configs[1]
        self.assertEqual(proc4_b.name, 'fleeb_1')
        self.assertEqual(proc4_b.command, '/bin/cat')
        self.assertEqual(proc4_b.priority, 4)
        self.assertEqual(proc4_b.autostart, True)
        self.assertEqual(proc4_b.autorestart,
                         datatypes.RestartWhenExitUnexpected)
        self.assertEqual(proc4_b.uid, None)
        self.assertEqual(proc4_b.stdout_logfile, datatypes.Automatic)
        self.assertEqual(proc4_b.stdout_logfile_maxbytes,
                         datatypes.byte_size('50MB'))
        self.assertEqual(proc4_b.stdout_logfile_backups, 10)
        self.assertEqual(proc4_b.exitcodes, [0,2])
        self.assertEqual(proc4_b.stopsignal, signal.SIGTERM)
        self.assertEqual(proc4_b.stopasgroup, False)
        self.assertEqual(proc4_b.killasgroup, False)
        self.assertEqual(proc4_b.directory, None)

        cat5 = options.process_group_configs[4]
        self.assertEqual(cat5.name, 'cat5')
        self.assertEqual(cat5.priority, 5)
        self.assertEqual(len(cat5.process_configs), 2)

        proc5_a = cat5.process_configs[0]
        self.assertEqual(proc5_a.name, 'foo_01')
        self.assertEqual(proc5_a.directory, '/some/path/foo_01')

        proc5_b = cat5.process_configs[1]
        self.assertEqual(proc5_b.name, 'foo_02')
        self.assertEqual(proc5_b.directory, '/some/path/foo_02')

        here = os.path.abspath(os.getcwd())
        self.assertEqual(instance.uid, 0)
        self.assertEqual(instance.gid, 0)
        self.assertEqual(instance.directory, tempfile.gettempdir())
        self.assertEqual(instance.umask, 18) # 022 in Py2, 0o22 in Py3
        self.assertEqual(instance.logfile, os.path.join(here,'supervisord.log'))
        self.assertEqual(instance.logfile_maxbytes, 1000 * 1024 * 1024)
        self.assertEqual(instance.logfile_backups, 5)
        self.assertEqual(instance.loglevel, 40)
        self.assertEqual(instance.pidfile, os.path.join(here,'supervisord.pid'))
        self.assertEqual(instance.nodaemon, True)
        self.assertEqual(instance.passwdfile, None)
        self.assertEqual(instance.identifier, 'fleeb')
        self.assertEqual(instance.childlogdir, tempfile.gettempdir())

        self.assertEqual(len(instance.server_configs), 1)
        self.assertEqual(instance.server_configs[0]['family'], socket.AF_INET)
        self.assertEqual(instance.server_configs[0]['host'], '127.0.0.1')
        self.assertEqual(instance.server_configs[0]['port'], 8999)
        self.assertEqual(instance.server_configs[0]['username'], 'chrism')
        self.assertEqual(instance.server_configs[0]['password'], 'foo')

        self.assertEqual(instance.nocleanup, True)
        self.assertEqual(instance.minfds, 2048)
        self.assertEqual(instance.minprocs, 300)

    def test_no_config_file_exits(self):
        instance = self._makeOne()

        # No default config file search in case they would exist
        self.assertTrue(len(instance.searchpaths) > 0)
        instance.searchpaths = []

        instance.exit = dummy_exit()

        # Making sure we capture stdout and stderr
        instance.stderr = StringIO()

        try:
            instance.realize()
        except DummyExitException as e:
            # Caught expected exception
            import traceback
            self.assertEqual(
                e.exitcode, 2,
                "Wrong exitcode for: %s" % traceback.format_exc()
                )
        else:
            self.fail("Did not get a DummyExitException.")

    def test_reload(self):
        text = lstrip("""\
        [supervisord]
        user=root

        [program:one]
        command = /bin/cat

        [program:two]
        command = /bin/dog

        [program:four]
        command = /bin/sheep

        [group:thegroup]
        programs = one,two
        """)

        instance = self._makeOne()
        instance.configfile = StringIO(text)
        instance.realize(args=[])

        section = instance.configroot.supervisord

        self.assertEqual(len(section.process_group_configs), 2)

        cat = section.process_group_configs[0]
        self.assertEqual(len(cat.process_configs), 1)

        cat = section.process_group_configs[1]
        self.assertEqual(len(cat.process_configs), 2)
        self.assertTrue(section.process_group_configs is
                        instance.process_group_configs)

        text = lstrip("""\
        [supervisord]
        user=root

        [program:one]
        command = /bin/cat

        [program:three]
        command = /bin/pig

        [group:thegroup]
        programs = three
        """)
        instance.configfile = StringIO(text)
        instance.process_config()

        section = instance.configroot.supervisord

        self.assertEqual(len(section.process_group_configs), 2)

        cat = section.process_group_configs[0]
        self.assertEqual(len(cat.process_configs), 1)
        proc = cat.process_configs[0]
        self.assertEqual(proc.name, 'one')
        self.assertEqual(proc.command, '/bin/cat')
        self.assertTrue(section.process_group_configs is
                        instance.process_group_configs)

        cat = section.process_group_configs[1]
        self.assertEqual(len(cat.process_configs), 1)
        proc = cat.process_configs[0]
        self.assertEqual(proc.name, 'three')
        self.assertEqual(proc.command, '/bin/pig')

    def test_reload_clears_parse_warnings(self):
        instance = self._makeOne()
        old_warning = "Warning from a prior config read"
        instance.parse_warnings = [old_warning]

        text = lstrip("""\
        [supervisord]
        user=root

        [program:cat]
        command = /bin/cat
        """)
        instance.configfile = StringIO(text)
        instance.realize(args=[])
        self.assertFalse(old_warning in instance.parse_warnings)

    def test_unreadable_config_file(self):
        # Quick and dirty way of coming up with a decent filename
        tempf = tempfile.NamedTemporaryFile()
        fname = tempf.name
        tempf.close()
        self.assertFalse(os.path.exists(fname))

        instance = self._makeOne()
        instance.stderr = StringIO()

        instance.exit = dummy_exit()
        try:
            instance.realize(args=['-c', fname])
        except DummyExitException as e:
            self.assertEqual(e.exitcode, 2)
        else:
            self.fail("expected exception")

        try:
            instance.read_config(fname)
        except ValueError as e:
            self.assertTrue("could not find config file" in str(e))
        else:
            self.fail("expected exception")

        tempf = tempfile.NamedTemporaryFile()
        os.chmod(tempf.name, 0) # Removing read perms
        try:
            instance.read_config(tempf.name)
        except ValueError as e:
            self.assertTrue("could not read config file" in str(e))
        else:
            self.fail("expected exception")
        tempf.close()

    def test_readFile_failed(self):
        from supervisor.options import readFile
        try:
            readFile('/notthere', 0, 10)
        except ValueError as inst:
            self.assertEqual(inst.args[0], 'FAILED')
        else:
            raise AssertionError("Didn't raise")

    def test_include_with_no_matching_files_logs_warning(self):
        instance = self._makeOne()
        text = lstrip("""\
        [supervisord]
        user=root

        [include]
        files=nonexistant/*
        """)
        instance.configfile = StringIO(text)
        instance.realize(args=[])
        self.assertEqual(instance.parse_warnings,
                         ['No file matches via include "./nonexistant/*"'])

    def test_get_pid(self):
        instance = self._makeOne()
        self.assertEqual(os.getpid(), instance.get_pid())

    def test_get_signal_delegates_to_signal_receiver(self):
        instance = self._makeOne()
        instance.signal_receiver.receive(signal.SIGTERM, None)
        instance.signal_receiver.receive(signal.SIGCHLD, None)
        self.assertEqual(instance.get_signal(), signal.SIGTERM)
        self.assertEqual(instance.get_signal(), signal.SIGCHLD)
        self.assertEqual(instance.get_signal(), None)

    def test_check_execv_args_cant_find_command(self):
        instance = self._makeOne()
        from supervisor.options import NotFound
        self.assertRaises(NotFound, instance.check_execv_args,
                          '/not/there', None, None)

    def test_check_execv_args_notexecutable(self):
        instance = self._makeOne()
        from supervisor.options import NotExecutable
        self.assertRaises(NotExecutable,
                          instance.check_execv_args, '/etc/passwd',
                          ['etc/passwd'], os.stat('/etc/passwd'))

    def test_check_execv_args_isdir(self):
        instance = self._makeOne()
        from supervisor.options import NotExecutable
        self.assertRaises(NotExecutable,
                          instance.check_execv_args, '/',
                          ['/'], os.stat('/'))

    def test_cleanup_afunix_unlink(self):
        fn = tempfile.mktemp()
        f = open(fn, 'w')
        f.write('foo')
        f.close()
        instance = self._makeOne()
        class Port:
            family = socket.AF_UNIX
            address = fn
        class Server:
            pass
        instance.httpservers = [({'family':socket.AF_UNIX, 'file':fn},
                                 Server())]
        instance.pidfile = ''
        instance.cleanup()
        self.assertFalse(os.path.exists(fn))

    def test_cleanup_afunix_nounlink(self):
        fn = tempfile.mktemp()
        try:
            f = open(fn, 'w')
            f.write('foo')
            f.close()
            instance = self._makeOne()
            class Port:
                family = socket.AF_UNIX
                address = fn
            class Server:
                pass
            instance.httpservers = [({'family':socket.AF_UNIX, 'file':fn},
                                     Server())]
            instance.pidfile = ''
            instance.unlink_socketfiles = False
            instance.cleanup()
            self.assertTrue(os.path.exists(fn))
        finally:
            try:
                os.unlink(fn)
            except OSError:
                pass

    def test_close_httpservers(self):
        instance = self._makeOne()
        class Server:
            closed = False
            def close(self):
                self.closed = True
        server = Server()
        instance.httpservers = [({}, server)]
        instance.close_httpservers()
        self.assertEqual(server.closed, True)

    def test_close_logger(self):
        instance = self._makeOne()
        logger = DummyLogger()
        instance.logger = logger
        instance.close_logger()
        self.assertEqual(logger.closed, True)

    def test_write_pidfile_ok(self):
        fn = tempfile.mktemp()
        try:
            instance = self._makeOne()
            instance.logger = DummyLogger()
            instance.pidfile = fn
            instance.write_pidfile()
            self.assertTrue(os.path.exists(fn))
            pid = int(read_file(fn)[:-1])
            self.assertEqual(pid, os.getpid())
            msg = instance.logger.data[0]
            self.assertTrue(msg.startswith('supervisord started with pid'))
        finally:
            try:
                os.unlink(fn)
            except OSError:
                pass

    def test_write_pidfile_fail(self):
        fn = '/cannot/possibly/exist'
        instance = self._makeOne()
        instance.logger = DummyLogger()
        instance.pidfile = fn
        instance.write_pidfile()
        msg = instance.logger.data[0]
        self.assertTrue(msg.startswith('could not write pidfile'))

    def test_close_fd(self):
        instance = self._makeOne()
        innie, outie = os.pipe()
        os.read(innie, 0) # we can read it while its open
        os.write(outie, as_bytes('foo')) # we can write to it while its open
        instance.close_fd(innie)
        self.assertRaises(OSError, os.read, innie, 0)
        instance.close_fd(outie)
        self.assertRaises(OSError, os.write, outie, as_bytes('foo'))

    def test_processes_from_section(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        priority = 1
        autostart = false
        autorestart = false
        startsecs = 100
        startretries = 100
        user = root
        stdout_logfile = NONE
        stdout_logfile_backups = 1
        stdout_logfile_maxbytes = 100MB
        stdout_events_enabled = true
        stopsignal = KILL
        stopwaitsecs = 100
        killasgroup = true
        exitcodes = 1,4
        redirect_stderr = false
        environment = KEY1=val1,KEY2=val2,KEY3=%(process_num)s
        numprocs = 2
        process_name = %(group_name)s_%(program_name)s_%(process_num)02d
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
        self.assertEqual(len(pconfigs), 2)
        pconfig = pconfigs[0]
        self.assertEqual(pconfig.name, 'bar_foo_00')
        self.assertEqual(pconfig.command, '/bin/cat')
        self.assertEqual(pconfig.autostart, False)
        self.assertEqual(pconfig.autorestart, False)
        self.assertEqual(pconfig.startsecs, 100)
        self.assertEqual(pconfig.startretries, 100)
        self.assertEqual(pconfig.uid, 0)
        self.assertEqual(pconfig.stdout_logfile, None)
        self.assertEqual(pconfig.stdout_capture_maxbytes, 0)
        self.assertEqual(pconfig.stdout_logfile_maxbytes, 104857600)
        self.assertEqual(pconfig.stdout_events_enabled, True)
        self.assertEqual(pconfig.stopsignal, signal.SIGKILL)
        self.assertEqual(pconfig.stopasgroup, False)
        self.assertEqual(pconfig.killasgroup, True)
        self.assertEqual(pconfig.stopwaitsecs, 100)
        self.assertEqual(pconfig.exitcodes, [1,4])
        self.assertEqual(pconfig.redirect_stderr, False)
        self.assertEqual(pconfig.environment,
                         {'KEY1':'val1', 'KEY2':'val2', 'KEY3':'0'})

    def test_processes_from_section_host_node_name_expansion(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/foo --host=%(host_node_name)s
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
        import platform
        expected = "/bin/foo --host=" + platform.node()
        self.assertEqual(pconfigs[0].command, expected)

    def test_processes_from_section_environment_variables_expansion(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/foo --path='%(ENV_PATH)s'
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
        expected = "/bin/foo --path='%s'" % os.environ['PATH']
        self.assertEqual(pconfigs[0].command, expected)

    def test_processes_from_section_bad_program_name_spaces(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:spaces are bad]
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:spaces are bad', None)

    def test_processes_from_section_bad_program_name_colons(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:colons:are:bad]
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:colons:are:bad', None)

    def test_processes_from_section_no_procnum_in_processname(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        numprocs = 2
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_no_command(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        numprocs = 2
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_missing_replacement_in_process_name(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = %(not_there)s
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_bad_expression_in_process_name(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = %(program_name)
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_bad_chars_in_process_name(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = colons:are:bad
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_section_stopasgroup_implies_killasgroup(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = %(program_name)s
        stopasgroup = true
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
        self.assertEqual(len(pconfigs), 1)
        pconfig = pconfigs[0]
        self.assertEqual(pconfig.stopasgroup, True)
        self.assertEqual(pconfig.killasgroup, True)

    def test_processes_from_section_killasgroup_mismatch_w_stopasgroup(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/cat
        process_name = %(program_name)s
        stopasgroup = true
        killasgroup = false
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        self.assertRaises(ValueError, instance.processes_from_section,
                          config, 'program:foo', None)

    def test_processes_from_autolog_without_rollover(self):
        instance = self._makeOne()
        text = lstrip("""\
        [program:foo]
        command = /bin/foo
        stdout_logfile = AUTO
        stdout_logfile_maxbytes = 0
        stderr_logfile = AUTO
        stderr_logfile_maxbytes = 0
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        instance.logger = DummyLogger()
        config.read_string(text)
        instance.processes_from_section(config, 'program:foo', None)
        self.assertEqual(instance.parse_warnings[0],
             'For [program:foo], AUTO logging used for stdout_logfile '
             'without rollover, set maxbytes > 0 to avoid filling up '
              'filesystem unintentionally')
        self.assertEqual(instance.parse_warnings[1],
             'For [program:foo], AUTO logging used for stderr_logfile '
             'without rollover, set maxbytes > 0 to avoid filling up '
              'filesystem unintentionally')

    def test_homogeneous_process_groups_from_parser(self):
        text = lstrip("""\
        [program:many]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/cat
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 1)
        gconfig = gconfigs[0]
        self.assertEqual(gconfig.name, 'many')
        self.assertEqual(gconfig.priority, 1)
        self.assertEqual(len(gconfig.process_configs), 2)

    def test_event_listener_pools_from_parser(self):
        text = lstrip("""\
        [eventlistener:dog]
        events=PROCESS_COMMUNICATION
        process_name = %(program_name)s_%(process_num)s
        command = /bin/dog
        numprocs = 2
        priority = 1

        [eventlistener:cat]
        events=PROCESS_COMMUNICATION
        process_name = %(program_name)s_%(process_num)s
        command = /bin/cat
        numprocs = 3

        [eventlistener:biz]
        events=PROCESS_COMMUNICATION
        process_name = %(program_name)s_%(process_num)s
        command = /bin/biz
        numprocs = 2
        """)
        from supervisor.options import UnhosedConfigParser
        from supervisor.dispatchers import default_handler
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 3)

        gconfig1 = gconfigs[0]
        self.assertEqual(gconfig1.name, 'biz')
        self.assertEqual(gconfig1.result_handler, default_handler)
        self.assertEqual(len(gconfig1.process_configs), 2)

        gconfig1 = gconfigs[1]
        self.assertEqual(gconfig1.name, 'cat')
        self.assertEqual(gconfig1.priority, -1)
        self.assertEqual(gconfig1.result_handler, default_handler)
        self.assertEqual(len(gconfig1.process_configs), 3)

        gconfig1 = gconfigs[2]
        self.assertEqual(gconfig1.name, 'dog')
        self.assertEqual(gconfig1.priority, 1)
        self.assertEqual(gconfig1.result_handler, default_handler)
        self.assertEqual(len(gconfig1.process_configs), 2)

    def test_event_listener_pool_with_event_results_handler(self):
        text = lstrip("""\
        [eventlistener:dog]
        events=PROCESS_COMMUNICATION
        command = /bin/dog
        result_handler = supervisor.tests.base:dummy_handler
        """)
        from supervisor.options import UnhosedConfigParser
        from supervisor.tests.base import dummy_handler
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 1)

        gconfig1 = gconfigs[0]
        self.assertEqual(gconfig1.result_handler, dummy_handler)

    def test_event_listener_pool_noeventsline(self):
        text = lstrip("""\
        [eventlistener:dog]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/dog
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_event_listener_pool_unknown_eventtype(self):
        text = lstrip("""\
        [eventlistener:dog]
        events=PROCESS_COMMUNICATION,THIS_EVENT_TYPE_DOESNT_EXIST
        process_name = %(program_name)s_%(process_num)s
        command = /bin/dog
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_programs_from_parser(self):
        from supervisor.options import FastCGIGroupConfig
        from supervisor.options import FastCGIProcessConfig
        text = lstrip("""\
        [fcgi-program:foo]
        socket = unix:///tmp/%(program_name)s.sock
        socket_owner = testuser:testgroup
        socket_mode = 0666
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1

        [fcgi-program:bar]
        socket = unix:///tmp/%(program_name)s.sock
        process_name = %(program_name)s_%(process_num)s
        command = /bin/bar
        user = testuser
        numprocs = 3

        [fcgi-program:flub]
        socket = unix:///tmp/%(program_name)s.sock
        command = /bin/flub

        [fcgi-program:cub]
        socket = tcp://localhost:6000
        command = /bin/cub
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()

        #Patch pwd and grp module functions to give us sentinel
        #uid/gid values so that the test does not depend on
        #any specific system users
        pwd_mock = Mock()
        pwd_mock.return_value = (None, None, sentinel.uid, sentinel.gid)
        grp_mock = Mock()
        grp_mock.return_value = (None, None, sentinel.gid)
        @patch('pwd.getpwuid', pwd_mock)
        @patch('pwd.getpwnam', pwd_mock)
        @patch('grp.getgrnam', grp_mock)
        def get_process_groups(instance, config):
            return instance.process_groups_from_parser(config)

        gconfigs = get_process_groups(instance, config)

        exp_owner = (sentinel.uid, sentinel.gid)

        self.assertEqual(len(gconfigs), 4)

        gconf_foo = gconfigs[0]
        self.assertEqual(gconf_foo.__class__, FastCGIGroupConfig)
        self.assertEqual(gconf_foo.name, 'foo')
        self.assertEqual(gconf_foo.priority, 1)
        self.assertEqual(gconf_foo.socket_config.url,
                                'unix:///tmp/foo.sock')
        self.assertEqual(exp_owner, gconf_foo.socket_config.get_owner())
        self.assertEqual(438, gconf_foo.socket_config.get_mode()) # 0666 in Py2, 0o666 in Py3
        self.assertEqual(len(gconf_foo.process_configs), 2)
        pconfig_foo = gconf_foo.process_configs[0]
        self.assertEqual(pconfig_foo.__class__, FastCGIProcessConfig)

        gconf_bar = gconfigs[1]
        self.assertEqual(gconf_bar.name, 'bar')
        self.assertEqual(gconf_bar.priority, 999)
        self.assertEqual(gconf_bar.socket_config.url,
                         'unix:///tmp/bar.sock')
        self.assertEqual(exp_owner, gconf_bar.socket_config.get_owner())
        self.assertEqual(448, gconf_bar.socket_config.get_mode()) # 0700 in Py2, 0o700 in Py3
        self.assertEqual(len(gconf_bar.process_configs), 3)

        gconf_cub = gconfigs[2]
        self.assertEqual(gconf_cub.name, 'cub')
        self.assertEqual(gconf_cub.socket_config.url,
                         'tcp://localhost:6000')
        self.assertEqual(len(gconf_cub.process_configs), 1)

        gconf_flub = gconfigs[3]
        self.assertEqual(gconf_flub.name, 'flub')
        self.assertEqual(gconf_flub.socket_config.url,
                         'unix:///tmp/flub.sock')
        self.assertEqual(None, gconf_flub.socket_config.get_owner())
        self.assertEqual(448, gconf_flub.socket_config.get_mode()) # 0700 in Py2, 0o700 in Py3
        self.assertEqual(len(gconf_flub.process_configs), 1)



    def test_fcgi_program_no_socket(self):
        text = lstrip("""\
        [fcgi-program:foo]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_unknown_socket_protocol(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=junk://blah
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_rel_unix_sock_path(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=unix://relative/path
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_bad_tcp_sock_format(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=tcp://missingport
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_bad_expansion_proc_num(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=unix:///tmp/%(process_num)s.sock
        process_name = %(program_name)s_%(process_num)s
        command = /bin/foo
        numprocs = 2
        priority = 1
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_socket_owner_set_for_tcp(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket=tcp://localhost:8000
        socket_owner=nobody:nobody
        command = /bin/foo
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_socket_mode_set_for_tcp(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket = tcp://localhost:8000
        socket_mode = 0777
        command = /bin/foo
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_bad_socket_owner(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket = unix:///tmp/foo.sock
        socket_owner = sometotaljunkuserthatshouldnobethere
        command = /bin/foo
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_fcgi_program_bad_socket_mode(self):
        text = lstrip("""\
        [fcgi-program:foo]
        socket = unix:///tmp/foo.sock
        socket_mode = junk
        command = /bin/foo
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError,instance.process_groups_from_parser,config)

    def test_heterogeneous_process_groups_from_parser(self):
        text = lstrip("""\
        [program:one]
        command = /bin/cat

        [program:two]
        command = /bin/cat

        [group:thegroup]
        programs = one,two
        priority = 5
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 1)
        gconfig = gconfigs[0]
        self.assertEqual(gconfig.name, 'thegroup')
        self.assertEqual(gconfig.priority, 5)
        self.assertEqual(len(gconfig.process_configs), 2)

    def test_mixed_process_groups_from_parser1(self):
        text = lstrip("""\
        [program:one]
        command = /bin/cat

        [program:two]
        command = /bin/cat

        [program:many]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/cat
        numprocs = 2
        priority = 1

        [group:thegroup]
        programs = one,two
        priority = 5
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 2)

        manyconfig = gconfigs[0]
        self.assertEqual(manyconfig.name, 'many')
        self.assertEqual(manyconfig.priority, 1)
        self.assertEqual(len(manyconfig.process_configs), 2)

        gconfig = gconfigs[1]
        self.assertEqual(gconfig.name, 'thegroup')
        self.assertEqual(gconfig.priority, 5)
        self.assertEqual(len(gconfig.process_configs), 2)

    def test_mixed_process_groups_from_parser2(self):
        text = lstrip("""\
        [program:one]
        command = /bin/cat

        [program:two]
        command = /bin/cat

        [program:many]
        process_name = %(program_name)s_%(process_num)s
        command = /bin/cat
        numprocs = 2
        priority = 1

        [group:thegroup]
        programs = one,two, many
        priority = 5
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        gconfigs = instance.process_groups_from_parser(config)
        self.assertEqual(len(gconfigs), 1)

        gconfig = gconfigs[0]
        self.assertEqual(gconfig.name, 'thegroup')
        self.assertEqual(gconfig.priority, 5)
        self.assertEqual(len(gconfig.process_configs), 4)

    def test_unknown_program_in_heterogeneous_group(self):
        text = lstrip("""\
        [program:one]
        command = /bin/cat

        [group:foo]
        programs = notthere
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        self.assertRaises(ValueError, instance.process_groups_from_parser,
                          config)

    def test_rpcinterfaces_from_parser(self):
        text = lstrip("""\
        [rpcinterface:dummy]
        supervisor.rpcinterface_factory = %s
        foo = bar
        """ % __name__)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        factories = instance.get_plugins(config,
                                         'supervisor.rpcinterface_factory',
                                         'rpcinterface:')
        self.assertEqual(len(factories), 1)
        factory = factories[0]
        self.assertEqual(factory[0], 'dummy')
        self.assertEqual(factory[1], sys.modules[__name__])
        self.assertEqual(factory[2], {'foo':'bar'})

    def test_rpcinterfaces_from_parser_factory_missing(self):
        text = lstrip("""\
        [rpcinterface:dummy]
        # note: no supervisor.rpcinterface_factory here
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        try:
            instance.get_plugins(config,
                                 'supervisor.rpcinterface_factory',
                                 'rpcinterface:')
            self.fail('nothing raised')
        except ValueError as exc:
            self.assertEqual(exc.args[0], 'section [rpcinterface:dummy] '
                'does not specify a supervisor.rpcinterface_factory')

    def test_rpcinterfaces_from_parser_factory_not_importable(self):
        text = lstrip("""\
        [rpcinterface:dummy]
        supervisor.rpcinterface_factory = nonexistant
        """)
        from supervisor.options import UnhosedConfigParser
        config = UnhosedConfigParser()
        config.read_string(text)
        instance = self._makeOne()
        try:
            instance.get_plugins(config,
                                 'supervisor.rpcinterface_factory',
                                 'rpcinterface:')
            self.fail('nothing raised')
        except ValueError as exc:
            self.assertEqual(exc.args[0], 'nonexistant cannot be resolved '
                'within [rpcinterface:dummy]')

    def test_clear_autochildlogdir(self):
        dn = tempfile.mkdtemp()
        try:
            instance = self._makeOne()
            instance.childlogdir = dn
            sid = 'supervisor'
            instance.identifier = sid
            logfn = instance.get_autochildlog_name('foo', sid,'stdout')
            first = logfn + '.1'
            second = logfn + '.2'
            f1 = open(first, 'w')
            f2 = open(second, 'w')
            instance.clear_autochildlogdir()
            self.assertFalse(os.path.exists(logfn))
            self.assertFalse(os.path.exists(first))
            self.assertFalse(os.path.exists(second))
            f1.close()
            f2.close()
        finally:
            shutil.rmtree(dn)

    def test_clear_autochildlog_oserror(self):
        instance = self._makeOne()
        instance.childlogdir = '/tmp/this/cant/possibly/existjjjj'
        instance.logger = DummyLogger()
        instance.clear_autochildlogdir()
        self.assertEqual(instance.logger.data, ['Could not clear childlog dir'])

    def test_openhttpservers_reports_friendly_usage_when_eaddrinuse(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def raise_eaddrinuse(supervisord):
            raise socket.error(errno.EADDRINUSE)
        instance.make_http_servers = raise_eaddrinuse

        recorder = []
        def record_usage(message):
            recorder.append(message)
        instance.usage = record_usage

        instance.openhttpservers(supervisord)
        self.assertEqual(len(recorder), 1)
        expected = 'Another program is already listening'
        self.assertTrue(recorder[0].startswith(expected))

    def test_openhttpservers_reports_socket_error_with_errno(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def make_http_servers(supervisord):
            raise socket.error(errno.EPERM)
        instance.make_http_servers = make_http_servers

        recorder = []
        def record_usage(message):
            recorder.append(message)
        instance.usage = record_usage

        instance.openhttpservers(supervisord)
        self.assertEqual(len(recorder), 1)
        expected = ('Cannot open an HTTP server: socket.error '
                    'reported errno.EPERM (%d)' % errno.EPERM)
        self.assertEqual(recorder[0], expected)

    def test_openhttpservers_reports_other_socket_errors(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def make_http_servers(supervisord):
            raise socket.error('uh oh')
        instance.make_http_servers = make_http_servers

        recorder = []
        def record_usage(message):
            recorder.append(message)
        instance.usage = record_usage

        instance.openhttpservers(supervisord)
        self.assertEqual(len(recorder), 1)
        expected = ('Cannot open an HTTP server: socket.error '
                    'reported uh oh')
        self.assertEqual(recorder[0], expected)

    def test_openhttpservers_reports_value_errors(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def make_http_servers(supervisord):
            raise ValueError('not prefixed with help')
        instance.make_http_servers = make_http_servers

        recorder = []
        def record_usage(message):
            recorder.append(message)
        instance.usage = record_usage

        instance.openhttpservers(supervisord)
        self.assertEqual(len(recorder), 1)
        expected = 'not prefixed with help'
        self.assertEqual(recorder[0], expected)

    def test_openhttpservers_does_not_catch_other_exception_types(self):
        supervisord = DummySupervisor()
        instance = self._makeOne()

        def make_http_servers(supervisord):
            raise OverflowError
        instance.make_http_servers = make_http_servers

        # this scenario probably means a bug in supervisor.  we dump
        # all the gory details on the poor user for troubleshooting
        self.assertRaises(OverflowError,
                          instance.openhttpservers, supervisord)

    def test_dropPrivileges_user_none(self):
        instance = self._makeOne()
        msg = instance.dropPrivileges(None)
        self.assertEqual(msg, "No user specified to setuid to!")

    @patch('pwd.getpwuid', Mock(return_value=["foo", None, 12, 34]))
    @patch('os.getuid', Mock(return_value=12))
    def test_dropPrivileges_nonroot_same_user(self):
        instance = self._makeOne()
        msg = instance.dropPrivileges(os.getuid())
        self.assertEqual(msg, None) # no error if same user

    @patch('pwd.getpwuid', Mock(return_value=["foo", None, 55, 34]))
    @patch('os.getuid', Mock(return_value=12))
    def test_dropPrivileges_nonroot_different_user(self):
        instance = self._makeOne()
        msg = instance.dropPrivileges(42)
        self.assertEqual(msg, "Can't drop privilege as nonroot user")

class TestProcessConfig(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import ProcessConfig
        return ProcessConfig

    def _makeOne(self, *arg, **kw):
        defaults = {}
        for name in ('name', 'command', 'directory', 'umask',
                     'priority', 'autostart', 'autorestart',
                     'startsecs', 'startretries', 'uid',
                     'stdout_logfile', 'stdout_capture_maxbytes',
                     'stdout_events_enabled', 'stdout_syslog',
                     'stderr_logfile', 'stderr_capture_maxbytes',
                     'stderr_events_enabled', 'stderr_syslog',
                     'stopsignal', 'stopwaitsecs', 'stopasgroup',
                     'killasgroup', 'exitcodes', 'redirect_stderr',
                     'environment'):
            defaults[name] = name
        for name in ('stdout_logfile_backups', 'stdout_logfile_maxbytes',
                     'stderr_logfile_backups', 'stderr_logfile_maxbytes'):
            defaults[name] = 10
        defaults.update(kw)
        return self._getTargetClass()(*arg, **defaults)

    def test_create_autochildlogs(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        from supervisor.datatypes import Automatic
        instance.stdout_logfile = Automatic
        instance.stderr_logfile = Automatic
        instance.create_autochildlogs()
        self.assertEqual(instance.stdout_logfile, options.tempfile_name)
        self.assertEqual(instance.stderr_logfile, options.tempfile_name)

    def test_make_process(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        process = instance.make_process()
        from supervisor.process import Subprocess
        self.assertEqual(process.__class__, Subprocess)
        self.assertEqual(process.group, None)

    def test_make_process_with_group(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        process = instance.make_process('abc')
        from supervisor.process import Subprocess
        self.assertEqual(process.__class__, Subprocess)
        self.assertEqual(process.group, 'abc')

    def test_make_dispatchers_stderr_not_redirected(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        with _getTempFile('stderr_logfile') as stdout_logfile:
            with _getTempFile('stderr_logfile') as stderr_logfile:
                instance.stdout_logfile = stdout_logfile.name
                instance.stderr_logfile = stderr_logfile.name
                logger.debug('instance.stdout_logfile = %r',
                             instance.stdout_logfile)
                logger.debug('instance.stderr_logfile = %r',
                             instance.stderr_logfile)
                instance.redirect_stderr = False
                process1 = DummyProcess(instance)
                dispatchers, pipes = instance.make_dispatchers(process1)
                self.assertEqual(dispatchers[5].channel, 'stdout')
                from supervisor.events import ProcessCommunicationStdoutEvent
                self.assertEqual(dispatchers[5].event_type,
                                 ProcessCommunicationStdoutEvent)
                self.assertEqual(pipes['stdout'], 5)
                self.assertEqual(dispatchers[7].channel, 'stderr')
                from supervisor.events import ProcessCommunicationStderrEvent
                self.assertEqual(dispatchers[7].event_type,
                                 ProcessCommunicationStderrEvent)
                self.assertEqual(pipes['stderr'], 7)

    def test_make_dispatchers_stderr_redirected(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        with _getTempFile('stderr_logfile') as stdout_logfile:
            instance.stdout_logfile = stdout_logfile.name
            logger.debug('instance.stdout_logfile = %r',
                         instance.stdout_logfile)
            process1 = DummyProcess(instance)
            dispatchers, pipes = instance.make_dispatchers(process1)
            self.assertEqual(dispatchers[5].channel, 'stdout')
            self.assertEqual(pipes['stdout'], 5)
            self.assertEqual(pipes['stderr'], None)

class FastCGIProcessConfigTest(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import FastCGIProcessConfig
        return FastCGIProcessConfig

    def _makeOne(self, *arg, **kw):
        defaults = {}
        for name in ('name', 'command', 'directory', 'umask',
                     'priority', 'autostart', 'autorestart',
                     'startsecs', 'startretries', 'uid',
                     'stdout_logfile', 'stdout_capture_maxbytes',
                     'stdout_events_enabled', 'stdout_syslog',
                     'stderr_logfile', 'stderr_capture_maxbytes',
                     'stderr_events_enabled', 'stderr_syslog',
                     'stopsignal', 'stopwaitsecs', 'stopasgroup',
                     'killasgroup', 'exitcodes', 'redirect_stderr',
                     'environment'):
            defaults[name] = name
        for name in ('stdout_logfile_backups', 'stdout_logfile_maxbytes',
                     'stderr_logfile_backups', 'stderr_logfile_maxbytes'):
            defaults[name] = 10
        defaults.update(kw)
        return self._getTargetClass()(*arg, **defaults)

    def test_make_process(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        self.assertRaises(NotImplementedError, instance.make_process)

    def test_make_process_with_group(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        process = instance.make_process('abc')
        from supervisor.process import FastCGISubprocess
        self.assertEqual(process.__class__, FastCGISubprocess)
        self.assertEqual(process.group, 'abc')

    def test_make_dispatchers(self):
        options = DummyOptions()
        instance = self._makeOne(options)
        with _getTempFile('stderr_logfile') as stdout_logfile:
            with _getTempFile('stderr_logfile') as stderr_logfile:
                instance.stdout_logfile = stdout_logfile.name
                instance.stderr_logfile = stderr_logfile.name
                logger.debug('instance.stdout_logfile = %r',
                             instance.stdout_logfile)
                logger.debug('instance.stderr_logfile = %r',
                             instance.stderr_logfile)
                instance.redirect_stderr = False
                process1 = DummyProcess(instance)
                dispatchers, pipes = instance.make_dispatchers(process1)
                self.assertEqual(dispatchers[4].channel, 'stdin')
                self.assertEqual(dispatchers[4].closed, True)
                self.assertEqual(dispatchers[5].channel, 'stdout')
                from supervisor.events import ProcessCommunicationStdoutEvent
                self.assertEqual(dispatchers[5].event_type,
                                 ProcessCommunicationStdoutEvent)
                self.assertEqual(pipes['stdout'], 5)
                self.assertEqual(dispatchers[7].channel, 'stderr')
                from supervisor.events import ProcessCommunicationStderrEvent
                self.assertEqual(dispatchers[7].event_type,
                                 ProcessCommunicationStderrEvent)
                self.assertEqual(pipes['stderr'], 7)

class ProcessGroupConfigTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import ProcessGroupConfig
        return ProcessGroupConfig

    def _makeOne(self, options, name, priority, pconfigs):
        return self._getTargetClass()(options, name, priority, pconfigs)

    def test_ctor(self):
        options = DummyOptions()
        instance = self._makeOne(options, 'whatever', 999, [])
        self.assertEqual(instance.options, options)
        self.assertEqual(instance.name, 'whatever')
        self.assertEqual(instance.priority, 999)
        self.assertEqual(instance.process_configs, [])

    def test_after_setuid(self):
        options = DummyOptions()
        pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
        instance = self._makeOne(options, 'whatever', 999, pconfigs)
        instance.after_setuid()
        self.assertEqual(pconfigs[0].autochildlogs_created, True)

    def test_make_group(self):
        options = DummyOptions()
        pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
        instance = self._makeOne(options, 'whatever', 999, pconfigs)
        group = instance.make_group()
        from supervisor.process import ProcessGroup
        self.assertEqual(group.__class__, ProcessGroup)

class FastCGIGroupConfigTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.options import FastCGIGroupConfig
        return FastCGIGroupConfig

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_ctor(self):
        options = DummyOptions()
        sock_config = DummySocketConfig(6)
        instance = self._makeOne(options, 'whatever', 999, [], sock_config)
        self.assertEqual(instance.options, options)
        self.assertEqual(instance.name, 'whatever')
        self.assertEqual(instance.priority, 999)
        self.assertEqual(instance.process_configs, [])
        self.assertEqual(instance.socket_config, sock_config)

    def test_same_sockets_are_equal(self):
        options = DummyOptions()
        sock_config1 = DummySocketConfig(6)
        instance1 = self._makeOne(options, 'whatever', 999, [], sock_config1)

        sock_config2 = DummySocketConfig(6)
        instance2 = self._makeOne(options, 'whatever', 999, [], sock_config2)

        self.assertTrue(instance1 == instance2)
        self.assertFalse(instance1 != instance2)

    def test_diff_sockets_are_not_equal(self):
        options = DummyOptions()
        sock_config1 = DummySocketConfig(6)
        instance1 = self._makeOne(options, 'whatever', 999, [], sock_config1)

        sock_config2 = DummySocketConfig(7)
        instance2 = self._makeOne(options, 'whatever', 999, [], sock_config2)

        self.assertTrue(instance1 != instance2)
        self.assertFalse(instance1 == instance2)

class SignalReceiverTests(unittest.TestCase):
    def test_returns_None_initially(self):
        from supervisor.options import SignalReceiver
        sr = SignalReceiver()
        self.assertEqual(sr.get_signal(), None)

    def test_returns_signals_in_order_received(self):
        from supervisor.options import SignalReceiver
        sr = SignalReceiver()
        sr.receive(signal.SIGTERM, 'frame')
        sr.receive(signal.SIGCHLD, 'frame')
        self.assertEqual(sr.get_signal(), signal.SIGTERM)
        self.assertEqual(sr.get_signal(), signal.SIGCHLD)
        self.assertEqual(sr.get_signal(), None)

    def test_does_not_queue_duplicate_signals(self):
        from supervisor.options import SignalReceiver
        sr = SignalReceiver()
        sr.receive(signal.SIGTERM, 'frame')
        sr.receive(signal.SIGTERM, 'frame')
        self.assertEqual(sr.get_signal(), signal.SIGTERM)
        self.assertEqual(sr.get_signal(), None)

    def test_queues_again_after_being_emptied(self):
        from supervisor.options import SignalReceiver
        sr = SignalReceiver()
        sr.receive(signal.SIGTERM, 'frame')
        self.assertEqual(sr.get_signal(), signal.SIGTERM)
        self.assertEqual(sr.get_signal(), None)
        sr.receive(signal.SIGCHLD, 'frame')
        self.assertEqual(sr.get_signal(), signal.SIGCHLD)
        self.assertEqual(sr.get_signal(), None)

class UtilFunctionsTests(unittest.TestCase):
    def test_make_namespec(self):
        from supervisor.options import make_namespec
        self.assertEqual(make_namespec('group', 'process'), 'group:process')
        self.assertEqual(make_namespec('process', 'process'), 'process')

    def test_split_namespec(self):
        from supervisor.options import split_namespec
        s = split_namespec
        self.assertEqual(s('process:group'), ('process', 'group'))
        self.assertEqual(s('process'), ('process', 'process'))
        self.assertEqual(s('group:'), ('group', None))
        self.assertEqual(s('group:*'), ('group', None))

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_process
import errno
import os
import signal
import sys
import time
import unittest

from supervisor.compat import as_bytes
from supervisor.compat import maxint

from supervisor.tests.base import Mock, patch, sentinel
from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyPGroupConfig
from supervisor.tests.base import DummyDispatcher
from supervisor.tests.base import DummyEvent
from supervisor.tests.base import DummyFCGIGroupConfig
from supervisor.tests.base import DummySocketConfig
from supervisor.tests.base import DummyProcessGroup
from supervisor.tests.base import DummyFCGIProcessGroup

from supervisor.process import Subprocess
from supervisor.options import BadCommand

class SubprocessTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.process import Subprocess
        return Subprocess

    def _makeOne(self, *arg, **kw):
        return self._getTargetClass()(*arg, **kw)

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def test_getProcessStateDescription(self):
        from supervisor.states import ProcessStates
        from supervisor.process import getProcessStateDescription
        for statename, code in ProcessStates.__dict__.items():
            if isinstance(code, int):
                self.assertEqual(getProcessStateDescription(code), statename)

    def test_ctor(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'cat', 'bin/cat',
                              stdout_logfile='/tmp/temp123.log',
                              stderr_logfile='/tmp/temp456.log')
        instance = self._makeOne(config)
        self.assertEqual(instance.config, config)
        self.assertEqual(instance.config.options, options)
        self.assertEqual(instance.laststart, 0)
        self.assertEqual(instance.pid, 0)
        self.assertEqual(instance.laststart, 0)
        self.assertEqual(instance.laststop, 0)
        self.assertEqual(instance.delay, 0)
        self.assertEqual(instance.administrative_stop, 0)
        self.assertEqual(instance.killing, 0)
        self.assertEqual(instance.backoff, 0)
        self.assertEqual(instance.pipes, {})
        self.assertEqual(instance.dispatchers, {})
        self.assertEqual(instance.spawnerr, None)

    def test_repr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'cat', 'bin/cat')
        instance = self._makeOne(config)
        s = repr(instance)
        self.assertTrue(s.startswith('<Subprocess at'))
        self.assertTrue(s.endswith('with name cat in state STOPPED>'))

    def test_reopenlogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.dispatchers = {0:DummyDispatcher(readable=True),
                                1:DummyDispatcher(writable=True)}
        instance.reopenlogs()
        self.assertEqual(instance.dispatchers[0].logs_reopened, True)
        self.assertEqual(instance.dispatchers[1].logs_reopened, False)

    def test_removelogs(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.dispatchers = {0:DummyDispatcher(readable=True),
                                1:DummyDispatcher(writable=True)}
        instance.removelogs()
        self.assertEqual(instance.dispatchers[0].logs_removed, True)
        self.assertEqual(instance.dispatchers[1].logs_removed, False)

    def test_drain(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test',
                              stdout_logfile='/tmp/foo',
                              stderr_logfile='/tmp/bar')
        instance = self._makeOne(config)
        instance.dispatchers = {0:DummyDispatcher(readable=True),
                                1:DummyDispatcher(writable=True)}
        instance.drain()
        self.assertTrue(instance.dispatchers[0].read_event_handled)
        self.assertTrue(instance.dispatchers[1].write_event_handled)

    def test_get_execv_args_bad_command_extraquote(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'extraquote', 'extraquote"')
        instance = self._makeOne(config)
        self.assertRaises(BadCommand, instance.get_execv_args)

    def test_get_execv_args_bad_command_empty(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'empty', '')
        instance = self._makeOne(config)
        self.assertRaises(BadCommand, instance.get_execv_args)

    def test_get_execv_args_bad_command_whitespaceonly(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'whitespaceonly', ' \t ')
        instance = self._makeOne(config)
        self.assertRaises(BadCommand, instance.get_execv_args)

    def test_get_execv_args_abs_missing(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere')
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(args, ('/notthere', ['/notthere']))

    def test_get_execv_args_abs_withquotes_missing(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere "an argument"')
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(args, ('/notthere', ['/notthere', 'an argument']))

    def test_get_execv_args_rel_missing(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', 'notthere')
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(args, ('notthere', ['notthere']))

    def test_get_execv_args_rel_withquotes_missing(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', 'notthere "an argument"')
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(args, ('notthere', ['notthere', 'an argument']))

    def test_get_execv_args_abs(self):
        executable = '/bin/sh foo'
        options = DummyOptions()
        config = DummyPConfig(options, 'sh', executable)
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(len(args), 2)
        self.assertEqual(args[0], '/bin/sh')
        self.assertEqual(args[1], ['/bin/sh', 'foo'])

    def test_get_execv_args_rel(self):
        executable = 'sh foo'
        options = DummyOptions()
        config = DummyPConfig(options, 'sh', executable)
        instance = self._makeOne(config)
        args = instance.get_execv_args()
        self.assertEqual(len(args), 2)
        self.assertEqual(args[0], '/bin/sh')
        self.assertEqual(args[1], ['sh', 'foo'])

    def test_record_spawnerr(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.record_spawnerr('foo')
        self.assertEqual(instance.spawnerr, 'foo')
        self.assertEqual(options.logger.data[0], 'spawnerr: foo')

    def test_spawn_already_running(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'sh', '/bin/sh')
        instance = self._makeOne(config)
        instance.pid = True
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.RUNNING
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.logger.data[0], "process 'sh' already running")
        self.assertEqual(instance.state, ProcessStates.RUNNING)

    def test_spawn_fail_check_execv_args(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'bad', '/bad/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr, 'bad filename')
        self.assertEqual(options.logger.data[0], "spawnerr: bad filename")
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1 = L[0]
        event2 = L[1]
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_fail_make_pipes_emfile(self):
        options = DummyOptions()
        options.make_pipes_error = errno.EMFILE
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr,
                         "too many open files to spawn 'good'")
        self.assertEqual(options.logger.data[0],
                         "spawnerr: too many open files to spawn 'good'")
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_fail_make_pipes_other(self):
        options = DummyOptions()
        options.make_pipes_error = 1
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr, 'unknown error: EPERM')
        self.assertEqual(options.logger.data[0],
                         "spawnerr: unknown error: EPERM")
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_fork_fail_eagain(self):
        options = DummyOptions()
        options.fork_error = errno.EAGAIN
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr,
                         "Too many processes in process table to spawn 'good'")
        self.assertEqual(options.logger.data[0],
             "spawnerr: Too many processes in process table to spawn 'good'")
        self.assertEqual(len(options.parent_pipes_closed), 6)
        self.assertEqual(len(options.child_pipes_closed), 6)
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_fork_fail_other(self):
        options = DummyOptions()
        options.fork_error = 1
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.BACKOFF
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(instance.spawnerr, 'unknown error: EPERM')
        self.assertEqual(options.logger.data[0],
                         "spawnerr: unknown error: EPERM")
        self.assertEqual(len(options.parent_pipes_closed), 6)
        self.assertEqual(len(options.child_pipes_closed), 6)
        self.assertTrue(instance.delay)
        self.assertTrue(instance.backoff)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.BACKOFF)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__, events.ProcessStateStartingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateBackoffEvent)

    def test_spawn_as_child_setuid_ok(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.privsdropped, 1)
        self.assertEqual(options.execv_args,
                         ('/good/filename', ['/good/filename']) )
        self.assertEqual(options.execve_called, True)
        # if the real execve() succeeds, the code that writes the
        # "was not spawned" message won't be reached.  this assertion
        # is to test that no other errors were written.
        self.assertEqual(options.written,
             {2: "supervisor: child process was not spawned\n"})

    def test_spawn_as_child_setuid_fail(self):
        options = DummyOptions()
        options.forkpid = 0
        options.setuid_msg = 'failure reason'
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.written,
             {2: "supervisor: couldn't setuid to 1: failure reason\n"
                 "supervisor: child process was not spawned\n"})
        self.assertEqual(options.privsdropped, None)
        self.assertEqual(options.execve_called, False)
        self.assertEqual(options._exitcode, 127)

    def test_spawn_as_child_cwd_ok(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename',
                              directory='/tmp')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.execv_args,
                         ('/good/filename', ['/good/filename']) )
        self.assertEqual(options.changed_directory, True)
        self.assertEqual(options.execve_called, True)
        # if the real execve() succeeds, the code that writes the
        # "was not spawned" message won't be reached.  this assertion
        # is to test that no other errors were written.
        self.assertEqual(options.written,
             {2: "supervisor: child process was not spawned\n"})

    def test_spawn_as_child_sets_umask(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', umask=2)
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.execv_args,
                         ('/good/filename', ['/good/filename']) )
        self.assertEqual(options.umaskset, 2)
        self.assertEqual(options.execve_called, True)
        # if the real execve() succeeds, the code that writes the
        # "was not spawned" message won't be reached.  this assertion
        # is to test that no other errors were written.
        self.assertEqual(options.written,
             {2: "supervisor: child process was not spawned\n"})

    def test_spawn_as_child_cwd_fail(self):
        options = DummyOptions()
        options.forkpid = 0
        options.chdir_error = 2
        config = DummyPConfig(options, 'good', '/good/filename',
                              directory='/tmp')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.execv_args, None)
        out = {2: "supervisor: couldn't chdir to /tmp: ENOENT\n"
                  "supervisor: child process was not spawned\n"}
        self.assertEqual(options.written, out)
        self.assertEqual(options._exitcode, 127)
        self.assertEqual(options.changed_directory, False)
        self.assertEqual(options.execve_called, False)

    def test_spawn_as_child_execv_fail_oserror(self):
        options = DummyOptions()
        options.forkpid = 0
        options.execv_error = 1
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        out = {2: "supervisor: couldn't exec /good/filename: EPERM\n"
                  "supervisor: child process was not spawned\n"}
        self.assertEqual(options.written, out)
        self.assertEqual(options.privsdropped, None)
        self.assertEqual(options._exitcode, 127)

    def test_spawn_as_child_execv_fail_runtime_error(self):
        options = DummyOptions()
        options.forkpid = 0
        options.execv_error = 2
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        msg = options.written[2] # dict, 2 is fd #
        head = "supervisor: couldn't exec /good/filename:"
        self.assertTrue(msg.startswith(head))
        self.assertTrue("RuntimeError" in msg)
        self.assertEqual(options.privsdropped, None)
        self.assertEqual(options._exitcode, 127)

    def test_spawn_as_child_uses_pconfig_environment(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'cat', '/bin/cat',
                              environment={'_TEST_':'1'})
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.execv_args, ('/bin/cat', ['/bin/cat']) )
        self.assertEqual(options.execv_environment['_TEST_'], '1')

    def test_spawn_as_child_environment_supervisor_envvars(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'cat', '/bin/cat')
        instance = self._makeOne(config)
        class Dummy:
            name = 'dummy'
        instance.group = Dummy()
        instance.group.config = Dummy()
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.execv_args, ('/bin/cat', ['/bin/cat']) )
        self.assertEqual(
            options.execv_environment['SUPERVISOR_ENABLED'], '1')
        self.assertEqual(
            options.execv_environment['SUPERVISOR_PROCESS_NAME'], 'cat')
        self.assertEqual(
            options.execv_environment['SUPERVISOR_GROUP_NAME'], 'dummy')
        self.assertEqual(
            options.execv_environment['SUPERVISOR_SERVER_URL'],
            'http://localhost:9001')

    def test_spawn_as_child_stderr_redirected(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        config.redirect_stderr = True
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(options.child_pipes_closed, None)
        self.assertEqual(options.pgrp_set, True)
        self.assertEqual(len(options.duped), 2)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)
        self.assertEqual(options.privsdropped, 1)
        self.assertEqual(options.execv_args,
                         ('/good/filename', ['/good/filename']) )
        self.assertEqual(options.execve_called, True)
        # if the real execve() succeeds, the code that writes the
        # "was not spawned" message won't be reached.  this assertion
        # is to test that no other errors were written.
        self.assertEqual(options.written,
             {2: "supervisor: child process was not spawned\n"})

    def test_spawn_as_parent(self):
        options = DummyOptions()
        options.forkpid = 10
        config = DummyPConfig(options, 'good', '/good/filename')
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, 10)
        self.assertEqual(instance.dispatchers[4].__class__, DummyDispatcher)
        self.assertEqual(instance.dispatchers[5].__class__, DummyDispatcher)
        self.assertEqual(instance.dispatchers[7].__class__, DummyDispatcher)
        self.assertEqual(instance.pipes['stdin'], 4)
        self.assertEqual(instance.pipes['stdout'], 5)
        self.assertEqual(instance.pipes['stderr'], 7)
        self.assertEqual(options.parent_pipes_closed, None)
        self.assertEqual(len(options.child_pipes_closed), 6)
        self.assertEqual(options.logger.data[0], "spawned: 'good' with pid 10")
        self.assertEqual(instance.spawnerr, None)
        self.assertTrue(instance.delay)
        self.assertEqual(instance.config.options.pidhistory[10], instance)
        from supervisor.states import ProcessStates
        self.assertEqual(instance.state, ProcessStates.STARTING)

    def test_spawn_redirect_stderr(self):
        options = DummyOptions()
        options.forkpid = 10
        config = DummyPConfig(options, 'good', '/good/filename',
                              redirect_stderr=True)
        instance = self._makeOne(config)
        result = instance.spawn()
        self.assertEqual(result, 10)
        self.assertEqual(instance.dispatchers[4].__class__, DummyDispatcher)
        self.assertEqual(instance.dispatchers[5].__class__, DummyDispatcher)
        self.assertEqual(instance.pipes['stdin'], 4)
        self.assertEqual(instance.pipes['stdout'], 5)
        self.assertEqual(instance.pipes['stderr'], None)

    def test_write(self):
        executable = '/bin/cat'
        options = DummyOptions()
        config = DummyPConfig(options, 'output', executable)
        instance = self._makeOne(config)
        sent = 'a' * (1 << 13)
        self.assertRaises(OSError, instance.write, sent)
        options.forkpid = 1
        instance.spawn()
        instance.write(sent)
        stdin_fd = instance.pipes['stdin']
        self.assertEqual(sent, instance.dispatchers[stdin_fd].input_buffer)
        instance.killing = True
        self.assertRaises(OSError, instance.write, sent)

    def test_write_dispatcher_closed(self):
        executable = '/bin/cat'
        options = DummyOptions()
        config = DummyPConfig(options, 'output', executable)
        instance = self._makeOne(config)
        sent = 'a' * (1 << 13)
        self.assertRaises(OSError, instance.write, sent)
        options.forkpid = 1
        instance.spawn()
        stdin_fd = instance.pipes['stdin']
        instance.dispatchers[stdin_fd].close()
        self.assertRaises(OSError, instance.write, sent)

    def test_write_stdin_fd_none(self):
        executable = '/bin/cat'
        options = DummyOptions()
        config = DummyPConfig(options, 'output', executable)
        instance = self._makeOne(config)
        options.forkpid = 1
        instance.spawn()
        stdin_fd = instance.pipes['stdin']
        instance.dispatchers[stdin_fd].close()
        instance.pipes['stdin'] = None
        try:
            instance.write('foo')
            self.fail('nothing raised')
        except OSError as exc:
            self.assertEqual(exc.args[0], errno.EPIPE)
            self.assertEqual(exc.args[1], 'Process has no stdin channel')

    def test_write_dispatcher_flush_raises_epipe(self):
        executable = '/bin/cat'
        options = DummyOptions()
        config = DummyPConfig(options, 'output', executable)
        instance = self._makeOne(config)
        sent = 'a' * (1 << 13)
        self.assertRaises(OSError, instance.write, sent)
        options.forkpid = 1
        instance.spawn()
        stdin_fd = instance.pipes['stdin']
        instance.dispatchers[stdin_fd].flush_error = errno.EPIPE
        self.assertRaises(OSError, instance.write, sent)

    def _dont_test_spawn_and_kill(self):
        # this is a functional test
        from supervisor.tests.base import makeSpew
        try:
            called = 0
            def foo(*args):
                called = 1
            signal.signal(signal.SIGCHLD, foo)
            executable = makeSpew()
            options = DummyOptions()
            config = DummyPConfig(options, 'spew', executable)
            instance = self._makeOne(config)
            result = instance.spawn()
            msg = options.logger.data[0]
            self.assertTrue(msg.startswith("spawned: 'spew' with pid"))
            self.assertEqual(len(instance.pipes), 6)
            self.assertTrue(instance.pid)
            self.assertEqual(instance.pid, result)
            origpid = instance.pid
            while 1:
                try:
                    data = os.popen('ps').read()
                    break
                except IOError as why:
                    if why.args[0] != errno.EINTR:
                        raise
                        # try again ;-)
            time.sleep(0.1) # arbitrary, race condition possible
            self.assertTrue(data.find(as_bytes(repr(origpid))) != -1 )
            msg = instance.kill(signal.SIGTERM)
            time.sleep(0.1) # arbitrary, race condition possible
            self.assertEqual(msg, None)
            pid, sts = os.waitpid(-1, os.WNOHANG)
            data = os.popen('ps').read()
            self.assertEqual(data.find(as_bytes(repr(origpid))), -1) # dubious
        finally:
            try:
                os.remove(executable)
            except:
                pass
            signal.signal(signal.SIGCHLD, signal.SIG_DFL)

    def test_stop(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        dispatcher = DummyDispatcher(writable=True)
        instance.dispatchers = {'foo':dispatcher}
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.RUNNING
        instance.stop()
        self.assertEqual(instance.administrative_stop, 1)
        self.assertTrue(instance.delay)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGTERM')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[11], signal.SIGTERM)

    def test_stop_not_in_stoppable_state_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        dispatcher = DummyDispatcher(writable=True)
        instance.dispatchers = {'foo':dispatcher}
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.STOPPED
        try:
            instance.stop()
            self.fail('nothing raised')
        except AssertionError as exc:
            self.assertEqual(exc.args[0], 'Assertion failed for test: '
                'STOPPED not in RUNNING STARTING STOPPING')

    def test_give_up(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        instance.state = ProcessStates.BACKOFF
        instance.give_up()
        self.assertEqual(instance.system_stop, 1)
        self.assertFalse(instance.delay)
        self.assertFalse(instance.backoff)
        self.assertEqual(instance.state, ProcessStates.FATAL)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateFatalEvent)

    def test_kill_nopid(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0],
              'attempted to kill test with sig SIGTERM but it wasn\'t running')
        self.assertEqual(instance.killing, 0)

    def test_kill_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        options.kill_error = 1
        instance = self._makeOne(config)
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.ProcessStateEvent,
                         lambda x: L.append(x))
        instance.pid = 11
        instance.state = ProcessStates.RUNNING
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGTERM')
        self.assertTrue(options.logger.data[1].startswith(
            'unknown problem killing test'))
        self.assertEqual(instance.killing, 0)
        self.assertEqual(len(L), 2)
        event1 = L[0]
        event2 = L[1]
        self.assertEqual(event1.__class__, events.ProcessStateStoppingEvent)
        self.assertEqual(event2.__class__, events.ProcessStateUnknownEvent)

    def test_kill_from_starting(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.ProcessStateEvent,lambda x: L.append(x))
        instance.state = ProcessStates.STARTING
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGTERM')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[11], signal.SIGTERM)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppingEvent)

    def test_kill_from_running(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        instance.state = ProcessStates.RUNNING
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGTERM')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[11], signal.SIGTERM)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppingEvent)

    def test_kill_from_stopping(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.Event,lambda x: L.append(x))
        instance.state = ProcessStates.STOPPING
        instance.kill(signal.SIGKILL)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
                         'signal SIGKILL')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[11], signal.SIGKILL)
        self.assertEqual(L, []) # no event because we didn't change state

    def test_kill_from_backoff(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.Event, L.append)
        instance.state = ProcessStates.BACKOFF
        instance.kill(signal.SIGKILL)
        self.assertEqual(options.logger.data[0],
                         'Attempted to kill test, which is in BACKOFF state.')
        self.assertEqual(instance.killing, 0)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppedEvent)

    def test_kill_from_stopping_w_killasgroup(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test', killasgroup=True)
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.Event,lambda x: L.append(x))
        instance.state = ProcessStates.STOPPING
        instance.kill(signal.SIGKILL)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) '
                         'process group with signal SIGKILL')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[-11], signal.SIGKILL)
        self.assertEqual(L, []) # no event because we didn't change state

    def test_stopasgroup(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test', stopasgroup=True)
        instance = self._makeOne(config)
        instance.pid = 11
        L = []
        from supervisor.states import ProcessStates
        from supervisor import events
        events.subscribe(events.Event,lambda x: L.append(x))
        instance.state = ProcessStates.RUNNING
        instance.kill(signal.SIGTERM)
        self.assertEqual(options.logger.data[0], 'killing test (pid 11) '
                         'process group with signal SIGTERM')
        self.assertEqual(instance.killing, 1)
        self.assertEqual(options.kills[-11], signal.SIGTERM)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppingEvent)
        self.assertEqual(event.extra_values, [('pid', 11)])
        self.assertEqual(event.from_state, ProcessStates.RUNNING)

    def test_finish(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo')
        instance = self._makeOne(config)
        instance.waitstatus = (123, 1) # pid, waitstatus
        instance.config.options.pidhistory[123] = instance
        instance.killing = 1
        pipes = {'stdout':'','stderr':''}
        instance.pipes = pipes
        from supervisor.states import ProcessStates
        from supervisor import events
        instance.state = ProcessStates.STOPPING
        L = []
        events.subscribe(events.ProcessStateStoppedEvent, lambda x: L.append(x))
        instance.pid = 123
        instance.finish(123, 1)
        self.assertEqual(instance.killing, 0)
        self.assertEqual(instance.pid, 0)
        self.assertEqual(options.parent_pipes_closed, pipes)
        self.assertEqual(instance.pipes, {})
        self.assertEqual(instance.dispatchers, {})
        self.assertEqual(options.logger.data[0], 'stopped: notthere '
                         '(terminated by SIGHUP)')
        self.assertEqual(instance.exitstatus, -1)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStoppedEvent)
        self.assertEqual(event.extra_values, [('pid', 123)])
        self.assertEqual(event.from_state, ProcessStates.STOPPING)

    def test_finish_expected(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo')
        instance = self._makeOne(config)
        instance.config.options.pidhistory[123] = instance
        pipes = {'stdout':'','stderr':''}
        instance.pipes = pipes
        instance.config.exitcodes =[-1]
        from supervisor.states import ProcessStates
        from supervisor import events
        instance.state = ProcessStates.RUNNING
        L = []
        events.subscribe(events.ProcessStateExitedEvent, lambda x: L.append(x))
        instance.pid = 123
        instance.finish(123, 1)
        self.assertEqual(instance.killing, 0)
        self.assertEqual(instance.pid, 0)
        self.assertEqual(options.parent_pipes_closed, pipes)
        self.assertEqual(instance.pipes, {})
        self.assertEqual(instance.dispatchers, {})
        self.assertEqual(options.logger.data[0],
                         'exited: notthere (terminated by SIGHUP; expected)')
        self.assertEqual(instance.exitstatus, -1)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__,
                         events.ProcessStateExitedEvent)
        self.assertEqual(event.expected, True)
        self.assertEqual(event.extra_values, [('expected', True), ('pid', 123)])
        self.assertEqual(event.from_state, ProcessStates.RUNNING)

    def test_finish_tooquickly(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo', startsecs=10)
        instance = self._makeOne(config)
        instance.config.options.pidhistory[123] = instance
        pipes = {'stdout':'','stderr':''}
        instance.pipes = pipes
        instance.config.exitcodes =[-1]
        import time
        instance.laststart = time.time()
        from supervisor.states import ProcessStates
        from supervisor import events
        instance.state = ProcessStates.STARTING
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        instance.pid = 123
        instance.finish(123, 1)
        self.assertEqual(instance.killing, 0)
        self.assertEqual(instance.pid, 0)
        self.assertEqual(options.parent_pipes_closed, pipes)
        self.assertEqual(instance.pipes, {})
        self.assertEqual(instance.dispatchers, {})
        self.assertEqual(options.logger.data[0],
                      'exited: notthere (terminated by SIGHUP; not expected)')
        self.assertEqual(instance.exitstatus, None)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateBackoffEvent)
        self.assertEqual(event.from_state, ProcessStates.STARTING)

    def test_finish_with_current_event_sends_rejected(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        events.subscribe(events.EventRejectedEvent, lambda x: L.append(x))
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo', startsecs=10)
        instance = self._makeOne(config)
        from supervisor.states import ProcessStates
        instance.state = ProcessStates.RUNNING
        event = DummyEvent()
        instance.event = event
        instance.finish(123, 1)
        self.assertEqual(len(L), 2)
        event1, event2 = L
        self.assertEqual(event1.__class__,
                         events.ProcessStateExitedEvent)
        self.assertEqual(event2.__class__, events.EventRejectedEvent)
        self.assertEqual(event2.process, instance)
        self.assertEqual(event2.event, event)
        self.assertEqual(instance.event, None)

    def test_set_uid_no_uid(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.set_uid()
        self.assertEqual(options.privsdropped, None)

    def test_set_uid(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test', uid=1)
        instance = self._makeOne(config)
        msg = instance.set_uid()
        self.assertEqual(options.privsdropped, 1)
        self.assertEqual(msg, None)

    def test_cmp_bypriority(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'notthere', '/notthere',
                              stdout_logfile='/tmp/foo',
                              priority=1)
        instance = self._makeOne(config)

        config = DummyPConfig(options, 'notthere1', '/notthere',
                              stdout_logfile='/tmp/foo',
                              priority=2)
        instance1 = self._makeOne(config)

        config = DummyPConfig(options, 'notthere2', '/notthere',
                              stdout_logfile='/tmp/foo',
                              priority=3)
        instance2 = self._makeOne(config)

        L = [instance2, instance, instance1]
        L.sort()

        self.assertEqual(L, [instance, instance1, instance2])

    def test_transition_stopped_to_starting_supervisor_stopping(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.SHUTDOWN

        # this should not be spawned, as supervisor is shutting down
        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 0
        process.state = ProcessStates.STOPPED
        process.transition()
        self.assertEqual(process.state, ProcessStates.STOPPED)
        self.assertEqual(L, [])

    def test_transition_stopped_to_starting_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.RUNNING

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 0
        process.state = ProcessStates.STOPPED
        process.transition()
        self.assertEqual(process.state, ProcessStates.STARTING)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStartingEvent)

    def test_transition_exited_to_starting_supervisor_stopping(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.SHUTDOWN

        # this should not be spawned, as supervisor is shutting down
        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        from supervisor.datatypes import RestartUnconditionally
        pconfig.autorestart = RestartUnconditionally
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.system_stop = 1
        process.state = ProcessStates.EXITED
        process.transition()
        self.assertEqual(process.state, ProcessStates.EXITED)
        self.assertEqual(process.system_stop, 1)
        self.assertEqual(L, [])

    def test_transition_exited_to_starting_uncond_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        from supervisor.datatypes import RestartUnconditionally
        pconfig.autorestart = RestartUnconditionally
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.state = ProcessStates.EXITED
        process.transition()
        self.assertEqual(process.state, ProcessStates.STARTING)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStartingEvent)

    def test_transition_exited_to_starting_condit_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        from supervisor.datatypes import RestartWhenExitUnexpected
        pconfig.autorestart = RestartWhenExitUnexpected
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.state = ProcessStates.EXITED
        process.exitstatus = 'bogus'
        process.transition()
        self.assertEqual(process.state, ProcessStates.STARTING)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateStartingEvent)

    def test_transition_exited_to_starting_condit_fls_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        from supervisor.datatypes import RestartWhenExitUnexpected
        pconfig.autorestart = RestartWhenExitUnexpected
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.state = ProcessStates.EXITED
        process.exitstatus = 0
        process.transition()
        self.assertEqual(process.state, ProcessStates.EXITED)
        self.assertEqual(L, [])

    def test_transition_backoff_to_starting_supervisor_stopping(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.SHUTDOWN

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.delay = 0
        process.backoff = 0
        process.state = ProcessStates.BACKOFF
        process.transition()
        self.assertEqual(process.state, ProcessStates.BACKOFF)
        self.assertEqual(L, [])

    def test_transition_backoff_to_starting_supervisor_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.RUNNING

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.delay = 0
        process.backoff = 0
        process.state = ProcessStates.BACKOFF
        process.transition()
        self.assertEqual(process.state, ProcessStates.STARTING)
        self.assertEqual(len(L), 1)
        self.assertEqual(L[0].__class__, events.ProcessStateStartingEvent)

    def test_transition_backoff_to_starting_supervisor_running_notyet(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates, SupervisorStates
        options = DummyOptions()
        options.mood = SupervisorStates.RUNNING

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.delay = maxint
        process.backoff = 0
        process.state = ProcessStates.BACKOFF
        process.transition()
        self.assertEqual(process.state, ProcessStates.BACKOFF)
        self.assertEqual(L, [])

    def test_transition_starting_to_running(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates

        options = DummyOptions()

        # this should go from STARTING to RUNNING via transition()
        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.backoff = 1
        process.delay = 1
        process.system_stop = 0
        process.laststart = 1
        process.pid = 1
        process.stdout_buffer = 'abc'
        process.stderr_buffer = 'def'
        process.state = ProcessStates.STARTING
        process.transition()

        # this implies RUNNING
        self.assertEqual(process.backoff, 0)
        self.assertEqual(process.delay, 0)
        self.assertEqual(process.system_stop, 0)
        self.assertEqual(options.logger.data[0],
                         'success: process entered RUNNING state, process has '
                         'stayed up for > than 10 seconds (startsecs)')
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateRunningEvent)

    def test_transition_backoff_to_fatal(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        # this should go from BACKOFF to FATAL via transition()
        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.laststart = 1
        process.backoff = 10000
        process.delay = 1
        process.system_stop = 0
        process.stdout_buffer = 'abc'
        process.stderr_buffer = 'def'
        process.state = ProcessStates.BACKOFF

        process.transition()

        # this implies FATAL
        self.assertEqual(process.backoff, 0)
        self.assertEqual(process.delay, 0)
        self.assertEqual(process.system_stop, 1)
        self.assertEqual(options.logger.data[0],
                         'gave up: process entered FATAL state, too many start'
                         ' retries too quickly')
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.__class__, events.ProcessStateFatalEvent)

    def test_transition_stops_unkillable_notyet(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.delay = maxint
        process.state = ProcessStates.STOPPING

        process.transition()
        self.assertEqual(process.state, ProcessStates.STOPPING)
        self.assertEqual(L, [])

    def test_transition_stops_unkillable(self):
        from supervisor import events
        L = []
        events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
        process = self._makeOne(pconfig)
        process.delay = 0
        process.pid = 1
        process.killing = 0
        process.state = ProcessStates.STOPPING

        process.transition()
        self.assertEqual(process.killing, 1)
        self.assertNotEqual(process.delay, 0)
        self.assertEqual(process.state, ProcessStates.STOPPING)
        self.assertEqual(options.logger.data[0],
                         "killing 'process' (1) with SIGKILL")
        import signal
        self.assertEqual(options.kills[1], signal.SIGKILL)
        self.assertEqual(L, [])

    def test_change_state_doesnt_notify_if_no_state_change(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.state = 10
        self.assertEqual(instance.change_state(10), False)

    def test_change_state_sets_backoff_and_delay(self):
        from supervisor.states import ProcessStates
        options = DummyOptions()
        config = DummyPConfig(options, 'test', '/test')
        instance = self._makeOne(config)
        instance.state = 10
        instance.change_state(ProcessStates.BACKOFF)
        self.assertEqual(instance.backoff, 1)
        self.assertTrue(instance.delay > 0)

class FastCGISubprocessTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.process import FastCGISubprocess
        return FastCGISubprocess

    def _makeOne(self, *arg, **kw):
        return self._getTargetClass()(*arg, **kw)

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def test_no_group(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        self.assertRaises(NotImplementedError, instance.spawn)

    def test_no_socket_manager(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.group = DummyProcessGroup(DummyPGroupConfig(options))
        self.assertRaises(NotImplementedError, instance.spawn)

    def test_prepare_child_fds(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        sock_config = DummySocketConfig(7)
        gconfig = DummyFCGIGroupConfig(options, 'whatever', 999, None,
                                       sock_config)
        instance.group = DummyFCGIProcessGroup(gconfig)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(len(options.duped), 3)
        self.assertEqual(options.duped[7], 0)
        self.assertEqual(options.duped[instance.pipes['child_stdout']], 1)
        self.assertEqual(options.duped[instance.pipes['child_stderr']], 2)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)

    def test_prepare_child_fds_stderr_redirected(self):
        options = DummyOptions()
        options.forkpid = 0
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        config.redirect_stderr = True
        instance = self._makeOne(config)
        sock_config = DummySocketConfig(13)
        gconfig = DummyFCGIGroupConfig(options, 'whatever', 999, None,
                                       sock_config)
        instance.group = DummyFCGIProcessGroup(gconfig)
        result = instance.spawn()
        self.assertEqual(result, None)
        self.assertEqual(len(options.duped), 2)
        self.assertEqual(options.duped[13], 0)
        self.assertEqual(len(options.fds_closed), options.minfds - 3)

    def test_before_spawn_gets_socket_ref(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        sock_config = DummySocketConfig(7)
        gconfig = DummyFCGIGroupConfig(options, 'whatever', 999, None,
                                       sock_config)
        instance.group = DummyFCGIProcessGroup(gconfig)
        self.assertTrue(instance.fcgi_sock is None)
        instance.before_spawn()
        self.assertFalse(instance.fcgi_sock is None)

    def test_after_finish_removes_socket_ref(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.fcgi_sock = 'hello'
        instance.after_finish()
        self.assertTrue(instance.fcgi_sock is None)

    #Patch Subprocess.finish() method for this test to verify override
    @patch.object(Subprocess, 'finish', Mock(return_value=sentinel.finish_result))
    def test_finish_override(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.after_finish = Mock()
        result = instance.finish(sentinel.pid, sentinel.sts)
        self.assertEqual(sentinel.finish_result, result,
                        'FastCGISubprocess.finish() did not pass thru result')
        self.assertEqual(1, instance.after_finish.call_count,
                            'FastCGISubprocess.after_finish() not called once')
        finish_mock = Subprocess.finish
        self.assertEqual(1, finish_mock.call_count,
                            'Subprocess.finish() not called once')
        pid_arg = finish_mock.call_args[0][1]
        sts_arg = finish_mock.call_args[0][2]
        self.assertEqual(sentinel.pid, pid_arg,
                            'Subprocess.finish() pid arg was not passed')
        self.assertEqual(sentinel.sts, sts_arg,
                            'Subprocess.finish() sts arg was not passed')

    #Patch Subprocess.spawn() method for this test to verify override
    @patch.object(Subprocess, 'spawn', Mock(return_value=sentinel.ppid))
    def test_spawn_override_success(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.before_spawn = Mock()
        result = instance.spawn()
        self.assertEqual(sentinel.ppid, result,
                        'FastCGISubprocess.spawn() did not pass thru result')
        self.assertEqual(1, instance.before_spawn.call_count,
                            'FastCGISubprocess.before_spawn() not called once')
        spawn_mock = Subprocess.spawn
        self.assertEqual(1, spawn_mock.call_count,
                            'Subprocess.spawn() not called once')

    #Patch Subprocess.spawn() method for this test to verify error handling
    @patch.object(Subprocess, 'spawn', Mock(return_value=None))
    def test_spawn_error(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'good', '/good/filename', uid=1)
        instance = self._makeOne(config)
        instance.before_spawn = Mock()
        instance.fcgi_sock = 'nuke me on error'
        result = instance.spawn()
        self.assertEqual(None, result,
                        'FastCGISubprocess.spawn() did return None on error')
        self.assertEqual(1, instance.before_spawn.call_count,
                            'FastCGISubprocess.before_spawn() not called once')
        self.assertEqual(None, instance.fcgi_sock,
                'FastCGISubprocess.spawn() did not remove sock ref on error')

class ProcessGroupBaseTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.process import ProcessGroupBase
        return ProcessGroupBase

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_get_unstopped_processes(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        group = self._makeOne(gconfig)
        group.processes = { 'process1': process1 }
        unstopped = group.get_unstopped_processes()
        self.assertEqual(unstopped, [process1])

    def test_stop_all(self):
        from supervisor.states import ProcessStates
        options = DummyOptions()

        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPED)

        pconfig2 = DummyPConfig(options, 'process2', 'process2','/bin/process2')
        process2 = DummyProcess(pconfig2, state=ProcessStates.RUNNING)

        pconfig3 = DummyPConfig(options, 'process3', 'process3','/bin/process3')
        process3 = DummyProcess(pconfig3, state=ProcessStates.STARTING)
        pconfig4 = DummyPConfig(options, 'process4', 'process4','/bin/process4')
        process4 = DummyProcess(pconfig4, state=ProcessStates.BACKOFF)
        process4.delay = 1000
        process4.backoff = 10
        gconfig = DummyPGroupConfig(
            options,
            pconfigs=[pconfig1, pconfig2, pconfig3, pconfig4])
        group = self._makeOne(gconfig)
        group.processes = {'process1': process1, 'process2': process2,
                           'process3':process3, 'process4':process4}

        group.stop_all()
        self.assertEqual(process1.stop_called, False)
        self.assertEqual(process2.stop_called, True)
        self.assertEqual(process3.stop_called, True)
        self.assertEqual(process4.stop_called, False)
        self.assertEqual(process4.state, ProcessStates.FATAL)

    def test_get_dispatchers(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        process1.dispatchers = {4:None}
        pconfig2 = DummyPConfig(options, 'process2', 'process2','/bin/process2')
        process2 = DummyProcess(pconfig2, state=ProcessStates.STOPPING)
        process2.dispatchers = {5:None}
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1, pconfig2])
        group = self._makeOne(gconfig)
        group.processes = { 'process1': process1, 'process2': process2 }
        result= group.get_dispatchers()
        self.assertEqual(result, {4:None, 5:None})

    def test_reopenlogs(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        group = self._makeOne(gconfig)
        group.processes = {'process1': process1}
        group.reopenlogs()
        self.assertEqual(process1.logs_reopened, True)

    def test_removelogs(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        group = self._makeOne(gconfig)
        group.processes = {'process1': process1}
        group.removelogs()
        self.assertEqual(process1.logsremoved, True)

    def test_cmp(self):
        options = DummyOptions()
        gconfig1 = DummyPGroupConfig(options)
        group1 = self._makeOne(gconfig1)

        gconfig2 = DummyPGroupConfig(options)
        group2 = self._makeOne(gconfig2)

        group1.config.priority = 5
        group2.config.priority = 1

        L = [group1, group2]
        L.sort()

        self.assertEqual(L, [group2, group1])

class ProcessGroupTests(ProcessGroupBaseTests):
    def _getTargetClass(self):
        from supervisor.process import ProcessGroup
        return ProcessGroup

    def test_repr(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        group = self._makeOne(gconfig)
        s = repr(group)
        self.assertTrue('supervisor.process.ProcessGroup' in s)
        self.assertTrue(s.endswith('named whatever>'), s)

    def test_transition(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        group = self._makeOne(gconfig)
        group.processes = {'process1': process1}
        group.transition()
        self.assertEqual(process1.transitioned, True)

class EventListenerPoolTests(ProcessGroupBaseTests):
    def setUp(self):
        from supervisor.events import clear
        clear()

    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.process import EventListenerPool
        return EventListenerPool

    def test_ctor(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        class EventType:
            pass
        gconfig.pool_events = (EventType,)
        pool = self._makeOne(gconfig)
        from supervisor import events
        self.assertEqual(len(events.callbacks), 2)
        self.assertEqual(events.callbacks[0],
            (EventType, pool._acceptEvent))
        self.assertEqual(events.callbacks[1],
            (events.EventRejectedEvent, pool.handle_rejected))
        self.assertEqual(pool.serial, -1)

    def test__eventEnvelope(self):
        options = DummyOptions()
        options.identifier = 'thesupervisorname'
        gconfig = DummyPGroupConfig(options)
        gconfig.name = 'thepoolname'
        pool = self._makeOne(gconfig)
        from supervisor import events
        result = pool._eventEnvelope(
            events.EventTypes.PROCESS_COMMUNICATION_STDOUT, 80, 20, 'payload\n')
        header, payload = result.split('\n', 1)
        headers = header.split()
        self.assertEqual(headers[0], 'ver:3.0')
        self.assertEqual(headers[1], 'server:thesupervisorname')
        self.assertEqual(headers[2], 'serial:80')
        self.assertEqual(headers[3], 'pool:thepoolname')
        self.assertEqual(headers[4], 'poolserial:20')
        self.assertEqual(headers[5], 'eventname:PROCESS_COMMUNICATION_STDOUT')
        self.assertEqual(headers[6], 'len:8')
        self.assertEqual(payload, 'payload\n')

    def test_handle_rejected_no_overflow(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        pool.event_buffer = [None, None]
        class DummyEvent1:
            serial = 'abc'
        class DummyEvent2:
            process = process1
            event = DummyEvent1()
        dummyevent = DummyEvent2()
        dummyevent.serial = 1
        pool.handle_rejected(dummyevent)
        self.assertEqual(pool.event_buffer, [dummyevent.event, None, None])

    def test_handle_rejected_event_buffer_overflowed(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        gconfig.buffer_size = 3
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        class DummyEvent:
            def __init__(self, serial):
                self.serial = serial
        class DummyRejectedEvent:
            def __init__(self, serial):
                self.process = process1
                self.event = DummyEvent(serial)
        event_a = DummyEvent('a')
        event_b = DummyEvent('b')
        event_c = DummyEvent('c')
        rej_event = DummyRejectedEvent('rejected')
        pool.event_buffer = [event_a, event_b, event_c]
        pool.handle_rejected(rej_event)
        serials = [ x.serial for x in pool.event_buffer ]
        # we popped a, and we inserted the rejected event into the 1st pos
        self.assertEqual(serials, ['rejected', 'b', 'c'])
        self.assertEqual(pool.config.options.logger.data[0],
            'pool whatever event buffer overflowed, discarding event a')

    def test_dispatch_pipe_error(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        from supervisor.states import EventListenerStates
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        process1 = pool.processes['process1']
        process1.write_error = errno.EPIPE
        process1.listener_state = EventListenerStates.READY
        event = DummyEvent()
        pool._acceptEvent(event)
        pool.dispatch()
        self.assertEqual(process1.listener_state, EventListenerStates.READY)
        self.assertEqual(pool.event_buffer, [event])
        self.assertEqual(options.logger.data[0],
                         'rebuffering event abc for pool whatever (bufsize 0)')

    def test__acceptEvent_attaches_pool_serial_and_serial(self):
        from supervisor.process import GlobalSerial
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        process1 = pool.processes['process1']
        from supervisor.states import EventListenerStates
        process1.listener_state = EventListenerStates.READY
        event = DummyEvent(None)
        pool._acceptEvent(event)
        self.assertEqual(event.serial, GlobalSerial.serial)
        self.assertEqual(event.pool_serials['whatever'], pool.serial)

    def test_repr(self):
        options = DummyOptions()
        gconfig = DummyPGroupConfig(options)
        pool = self._makeOne(gconfig)
        s = repr(pool)
        self.assertTrue('supervisor.process.EventListenerPool' in s)
        self.assertTrue(s.endswith('named whatever>'))

    def test_transition_nobody_ready(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STARTING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        event = DummyEvent()
        event.serial = 'a'
        from supervisor.states import EventListenerStates
        process1.listener_state = EventListenerStates.BUSY
        pool._acceptEvent(event)
        pool.transition()
        self.assertEqual(process1.transitioned, True)
        self.assertEqual(pool.event_buffer, [event])

    def test_transition_event_proc_not_running(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.STARTING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        event = DummyEvent()
        from supervisor.states import EventListenerStates
        event.serial = 1
        process1.listener_state = EventListenerStates.READY
        pool._acceptEvent(event)
        pool.transition()
        self.assertEqual(process1.transitioned, True)
        self.assertEqual(pool.event_buffer, [event])
        self.assertEqual(process1.stdin_buffer, '')
        self.assertEqual(process1.listener_state, EventListenerStates.READY)

    def test_transition_event_proc_running(self):
        options = DummyOptions()
        from supervisor.states import ProcessStates
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        process1 = DummyProcess(pconfig1, state=ProcessStates.RUNNING)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
        pool = self._makeOne(gconfig)
        pool.processes = {'process1': process1}
        event = DummyEvent()
        from supervisor.states import EventListenerStates
        process1.listener_state = EventListenerStates.READY
        class DummyGroup:
            config = gconfig
        process1.group = DummyGroup
        pool._acceptEvent(event)
        pool.transition()
        self.assertEqual(process1.transitioned, True)
        self.assertEqual(pool.event_buffer, [])
        header, payload = process1.stdin_buffer.split('\n', 1)
        self.assertEqual(payload, 'dummy event', payload)
        self.assertEqual(process1.listener_state, EventListenerStates.BUSY)
        self.assertEqual(process1.event, event)

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_rpcinterfaces
# -*- coding: utf-8 -*-
import unittest
import sys
import os
import time
import errno

from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyPGroupConfig
from supervisor.tests.base import DummyProcessGroup
from supervisor.tests.base import PopulatedDummySupervisor
from supervisor.tests.base import _NOW
from supervisor.tests.base import _TIMEFORMAT

class TestBase(unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def _assertRPCError(self, code, callable, *args, **kw):
        from supervisor import xmlrpc
        try:
            callable(*args, **kw)
        except xmlrpc.RPCError as inst:
            self.assertEqual(inst.code, code)
        else:
            raise AssertionError("Didn't raise")

class MainXMLRPCInterfaceTests(TestBase):

    def _getTargetClass(self):
        from supervisor import xmlrpc
        return xmlrpc.RootRPCInterface

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_ctor(self):
        interface = self._makeOne([('supervisor', None)])
        self.assertEqual(interface.supervisor, None)

    def test_traverse(self):
        dummy = DummyRPCInterface()
        interface = self._makeOne([('dummy', dummy)])
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.UNKNOWN_METHOD,
                             xmlrpc.traverse, interface, 'notthere.hello', [])
        self._assertRPCError(xmlrpc.Faults.UNKNOWN_METHOD,
                             xmlrpc.traverse, interface,
                             'supervisor._readFile', [])
        self._assertRPCError(xmlrpc.Faults.INCORRECT_PARAMETERS,
                             xmlrpc.traverse, interface,
                             'dummy.hello', [1])
        self.assertEqual(xmlrpc.traverse(
            interface, 'dummy.hello', []), 'Hello!')

class SupervisorNamespaceXMLRPCInterfaceTests(TestBase):
    def _getTargetClass(self):
        from supervisor import rpcinterface
        return rpcinterface.SupervisorNamespaceRPCInterface

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_update(self):
        from supervisor import xmlrpc
        from supervisor.supervisord import SupervisorStates
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        interface._update('foo')
        self.assertEqual(interface.update_text, 'foo')
        supervisord.options.mood = SupervisorStates.SHUTDOWN
        self._assertRPCError(xmlrpc.Faults.SHUTDOWN_STATE, interface._update,
                             'foo')

    def test_getAPIVersion(self):
        from supervisor import rpcinterface
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        version = interface.getAPIVersion()
        self.assertEqual(version, rpcinterface.API_VERSION)
        self.assertEqual(interface.update_text, 'getAPIVersion')

    def test_getAPIVersion_aliased_to_deprecated_getVersion(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.getAPIVersion, interface.getVersion)

    def test_getSupervisorVersion(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        version = interface.getSupervisorVersion()
        from supervisor import options
        self.assertEqual(version, options.VERSION)
        self.assertEqual(interface.update_text, 'getSupervisorVersion')


    def test_getIdentification(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        identifier = interface.getIdentification()
        self.assertEqual(identifier, supervisord.options.identifier)
        self.assertEqual(interface.update_text, 'getIdentification')

    def test_getState(self):
        from supervisor.states import getSupervisorStateDescription
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        stateinfo = interface.getState()
        statecode = supervisord.options.mood
        statename = getSupervisorStateDescription(statecode)
        self.assertEqual(stateinfo['statecode'], statecode)
        self.assertEqual(stateinfo['statename'], statename)
        self.assertEqual(interface.update_text, 'getState')

    def test_getPID(self):
        options = DummyOptions()
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.getPID(), options.get_pid())
        self.assertEqual(interface.update_text, 'getPID')

    def test_readLog_aliased_to_deprecated_readMainLog(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.readMainLog, interface.readLog)

    def test_readLog_unreadable(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.NO_FILE, interface.readLog,
                             offset=0, length=1)

    def test_readLog_badargs(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        try:
            logfile = supervisord.options.logfile
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.close()
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readLog, offset=-1, length=1)
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readLog, offset=-1,
                                 length=-1)
        finally:
            os.remove(logfile)

    def test_readLog(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        logfile = supervisord.options.logfile
        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.write('y' * 2048)
            f.close()
            data = interface.readLog(offset=0, length=0)
            self.assertEqual(interface.update_text, 'readLog')
            self.assertEqual(data, ('x' * 2048) + ('y' * 2048))
            data = interface.readLog(offset=2048, length=0)
            self.assertEqual(data, 'y' * 2048)
            data = interface.readLog(offset=0, length=2048)
            self.assertEqual(data, 'x' * 2048)
            data = interface.readLog(offset=-4, length=0)
            self.assertEqual(data, 'y' * 4)
        finally:
            os.remove(logfile)

    def test_clearLog_unreadable(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.NO_FILE, interface.clearLog)

    def test_clearLog_unremoveable(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        options.existing = [options.logfile]
        options.remove_error = 1
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)
        self.assertRaises(xmlrpc.RPCError, interface.clearLog)
        self.assertEqual(interface.update_text, 'clearLog')

    def test_clearLog(self):
        options = DummyOptions()
        options.existing = [options.logfile]
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)
        result = interface.clearLog()
        self.assertEqual(interface.update_text, 'clearLog')
        self.assertEqual(result, True)
        self.assertEqual(options.removed[0], options.logfile)
        for handler in supervisord.options.logger.handlers:
            self.assertEqual(handler.reopened, True)

    def test_shutdown(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        value = interface.shutdown()
        self.assertEqual(value, True)
        self.assertEqual(supervisord.options.mood, -1)

    def test_restart(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        value = interface.restart()
        self.assertEqual(value, True)
        self.assertEqual(supervisord.options.mood, 0)

    def test_reloadConfig(self):
        options = DummyOptions()
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)

        changes = [ [DummyPGroupConfig(options, 'added')],
                    [DummyPGroupConfig(options, 'changed')],
                    [DummyPGroupConfig(options, 'dropped')] ]

        supervisord.diff_to_active = lambda : changes

        value = interface.reloadConfig()
        self.assertEqual(value, [[['added'], ['changed'], ['dropped']]])

    def test_reloadConfig_process_config_raises_ValueError(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        def raise_exc(*arg, **kw):
            raise ValueError('foo')
        options.process_config = raise_exc
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.CANT_REREAD, interface.reloadConfig)

    def test_addProcessGroup(self):
        from supervisor.supervisord import Supervisor
        from supervisor import xmlrpc
        options = DummyOptions()
        supervisord = Supervisor(options)
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
        supervisord.options.process_group_configs = [gconfig]

        interface = self._makeOne(supervisord)

        result = interface.addProcessGroup('group1')
        self.assertTrue(result)
        self.assertEqual(list(supervisord.process_groups.keys()), ['group1'])

        self._assertRPCError(xmlrpc.Faults.ALREADY_ADDED,
                             interface.addProcessGroup, 'group1')
        self.assertEqual(list(supervisord.process_groups.keys()), ['group1'])

        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.addProcessGroup, 'asdf')
        self.assertEqual(list(supervisord.process_groups.keys()), ['group1'])

    def test_removeProcessGroup(self):
        from supervisor.supervisord import Supervisor
        options = DummyOptions()
        supervisord = Supervisor(options)
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
        supervisord.options.process_group_configs = [gconfig]

        interface = self._makeOne(supervisord)

        interface.addProcessGroup('group1')
        result = interface.removeProcessGroup('group1')
        self.assertTrue(result)
        self.assertEqual(list(supervisord.process_groups.keys()), [])

    def test_removeProcessGroup_bad_name(self):
        from supervisor.supervisord import Supervisor
        from supervisor import xmlrpc
        options = DummyOptions()
        supervisord = Supervisor(options)
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
        supervisord.options.process_group_configs = [gconfig]

        interface = self._makeOne(supervisord)

        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.removeProcessGroup, 'asdf')

    def test_removeProcessGroup_still_running(self):
        from supervisor.supervisord import Supervisor
        from supervisor import xmlrpc
        options = DummyOptions()
        supervisord = Supervisor(options)
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])
        supervisord.options.process_group_configs = [gconfig]
        process = DummyProcessGroup(gconfig)
        process.unstopped_processes = [123]
        supervisord.process_groups = {'group1':process}
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.STILL_RUNNING,
                             interface.removeProcessGroup, 'group1')


    def test_startProcess_already_started(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'pid', 10)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo')
        self._assertRPCError(xmlrpc.Faults.ALREADY_STARTED,
                             callback)

    def test_startProcess_bad_group_name(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        supervisord = PopulatedDummySupervisor(options, 'group1', pconfig)
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.startProcess, 'group2:foo')

    def test_startProcess_bad_process_name(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        supervisord = PopulatedDummySupervisor(options, 'group1', pconfig)
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.startProcess, 'group1:bar')

    def test_startProcess_file_not_found(self):
        options = DummyOptions()
        pconfig  = DummyPConfig(options, 'foo', '/foo/bar', autostart=False)
        from supervisor.options import NotFound
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        process = supervisord.process_groups['foo'].processes['foo']
        process.execv_arg_exception = NotFound
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NO_FILE,
                             interface.startProcess, 'foo')

    def test_startProcess_file_not_executable(self):
        options = DummyOptions()
        pconfig  = DummyPConfig(options, 'foo', '/foo/bar', autostart=False)
        from supervisor.options import NotExecutable
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        process = supervisord.process_groups['foo'].processes['foo']
        process.execv_arg_exception = NotExecutable
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NOT_EXECUTABLE,
                             interface.startProcess, 'foo')

    def test_startProcess_spawnerr(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        process = supervisord.process_groups['foo'].processes['foo']
        process.spawnerr = 'abc'
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo')
        self._assertRPCError(xmlrpc.Faults.SPAWN_ERROR, callback)

    def test_startProcess(self):
        from supervisor import http
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False,
                               startsecs=.01)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo')
        self.assertEqual(callback(), http.NOT_DONE_YET)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')
        process.state = ProcessStates.RUNNING
        time.sleep(.02)
        result = callback()
        self.assertEqual(result, True)

    def test_startProcess_nowait(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo', wait=False)
        self.assertEqual(callback(), True)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')

    def test_startProcess_nostartsecs(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False,
                               startsecs=0)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo', wait=True)
        self.assertEqual(callback(), True)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')

    def test_startProcess_abnormal_term_process_not_running(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo', 100) # milliseconds
        result = callback()
        from supervisor import http
        self.assertEqual(result, http.NOT_DONE_YET)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')
        process.state = ProcessStates.BACKOFF

        time.sleep(.1)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.ABNORMAL_TERMINATION, callback)

    def test_startProcess_abnormal_term_startsecs_exceeded(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__, autostart=False,
                               startsecs=.01)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcess('foo', 100) # milliseconds
        result = callback()
        from supervisor import http
        self.assertEqual(result, http.NOT_DONE_YET)
        supervisord.set_procattr('foo', 'state', ProcessStates.STARTING)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.spawned, True)
        self.assertEqual(interface.update_text, 'startProcess')
        process.state = ProcessStates.STARTING

        time.sleep(.2)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.ABNORMAL_TERMINATION, callback)

    def test_startProcess_splat_calls_startProcessGroup(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, autostart=False,
                               startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo',
                                               pconfig1, pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        interface.startProcess('foo:*')
        self.assertEqual(interface.update_text, 'startProcessGroup')

    def test_startProcessGroup(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1,
                                startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcessGroup('foo')

        from supervisor.http import NOT_DONE_YET

        # create callbacks in startall()
        self.assertEqual(callback(), NOT_DONE_YET)
        # start first process
        self.assertEqual(callback(), NOT_DONE_YET)
        # start second process
        self.assertEqual(callback(), NOT_DONE_YET)

        # wait for timeout 1
        time.sleep(.02)
        self.assertEqual(callback(), NOT_DONE_YET)

        # wait for timeout 2
        time.sleep(.02)
        result = callback()

        self.assertEqual(len(result), 2)

        from supervisor.xmlrpc import Faults

        # XXX not sure about this ordering, I think process1 should
        # probably show up first
        self.assertEqual(result[0]['name'], 'process2')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process1')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

        self.assertEqual(interface.update_text, 'startProcess')

        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.spawned, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.spawned, True)

    def test_startProcessGroup_nowait(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1,
                                startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startProcessGroup('foo', wait=False)
        from supervisor.http import NOT_DONE_YET
        from supervisor.xmlrpc import Faults

        # create callbacks in startall()
        self.assertEqual(callback(), NOT_DONE_YET)

        # get a result
        result = callback()

        self.assertEqual(len(result), 2)
        self.assertEqual(result[0]['name'], 'process1')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process2')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

    def test_startProcessGroup_badname(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.startProcessGroup, 'foo')


    def test_startAllProcesses(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1,
                                startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startAllProcesses()

        from supervisor.http import NOT_DONE_YET

        # create callbacks in startall()
        self.assertEqual(callback(), NOT_DONE_YET)
        # start first process
        self.assertEqual(callback(), NOT_DONE_YET)
        # start second process
        self.assertEqual(callback(), NOT_DONE_YET)

        # wait for timeout 1
        time.sleep(.02)
        self.assertEqual(callback(), NOT_DONE_YET)

        # wait for timeout 2
        time.sleep(.02)
        result = callback()

        self.assertEqual(len(result), 2)

        from supervisor.xmlrpc import Faults

        # XXX not sure about this ordering, I think process1 should
        # probably show up first
        self.assertEqual(result[0]['name'], 'process2')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process1')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

        self.assertEqual(interface.update_text, 'startProcess')

        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.spawned, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.spawned, True)

    def test_startAllProcesses_nowait(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1,
                                startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        callback = interface.startAllProcesses(wait=False)
        from supervisor.http import NOT_DONE_YET
        from supervisor.xmlrpc import Faults

        # create callbacks in startall()
        self.assertEqual(callback(), NOT_DONE_YET)

        # get a result
        result = callback()

        self.assertEqual(len(result), 2)
        self.assertEqual(result[0]['name'], 'process1')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process2')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

    def test_stopProcess(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcess('foo')
        self.assertEqual(interface.update_text, 'stopProcess')
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.backoff, 0)
        self.assertEqual(process.delay, 0)
        self.assertEqual(process.killing, 0)
        from supervisor import http
        self.assertEqual(callback(), http.NOT_DONE_YET)
        self.assertEqual(process.state, ProcessStates.STOPPED)
        self.assertEqual(callback(), True)
        self.assertEqual(len(supervisord.process_groups['foo'].processes), 1)
        self.assertEqual(interface.update_text, 'stopProcess')

    def test_stopProcess_nowait(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', __file__)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcess('foo', wait=False)
        self.assertEqual(callback(), True)
        process = supervisord.process_groups['foo'].processes['foo']
        self.assertEqual(process.stop_called, True)
        self.assertEqual(interface.update_text, 'stopProcess')

    def test_stopProcess_bad_name(self):
        from supervisor.xmlrpc import Faults
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(Faults.BAD_NAME,
                             interface.stopProcess, 'foo')

    def test_stopProcess_not_running(self):
        from supervisor.states import ProcessStates
        from supervisor.xmlrpc import Faults
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.EXITED)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcess('foo')
        self._assertRPCError(Faults.NOT_RUNNING, callback)

    def test_stopProcess_failed(self):
        from supervisor.xmlrpc import Faults
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'stop', lambda: 'unstoppable')
        interface = self._makeOne(supervisord)
        callback = interface.stopProcess('foo')
        self._assertRPCError(Faults.FAILED, callback)

    def test_stopProcessGroup(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', '/bin/foo', priority=1)
        pconfig2 = DummyPConfig(options, 'process2', '/bin/foo2', priority=2)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)
        supervisord.set_procattr('process2', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcessGroup('foo')
        self.assertEqual(interface.update_text, 'stopProcessGroup')
        from supervisor import http
        value = http.NOT_DONE_YET
        while 1:
            value = callback()
            if value is not http.NOT_DONE_YET:
                break

        self.assertEqual(value, [
            {'status':80,'group':'foo','name': 'process1','description': 'OK'},
            {'status':80,'group':'foo','name': 'process2','description': 'OK'},
            ] )
        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.stop_called, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.stop_called, True)

    def test_stopProcessGroup_nowait(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)
        supervisord.set_procattr('process2', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopProcessGroup('foo', wait=False)
        from supervisor.http import NOT_DONE_YET
        from supervisor.xmlrpc import Faults

        # create callbacks in killall()
        self.assertEqual(callback(), NOT_DONE_YET)

        # get a result
        result = callback()

        self.assertEqual(len(result), 2)
        self.assertEqual(result[0]['name'], 'process1')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process2')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

    def test_stopProcessGroup_badname(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.stopProcessGroup, 'foo')

    def test_stopProcess_splat_calls_stopProcessGroup(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, autostart=False,
                               startsecs=.01)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2,
                                startsecs=.01)
        supervisord = PopulatedDummySupervisor(options, 'foo',
                                               pconfig1, pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.STOPPED)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)
        interface = self._makeOne(supervisord)
        interface.stopProcess('foo:*')
        self.assertEqual(interface.update_text, 'stopProcessGroup')

    def test_stopAllProcesses(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', '/bin/foo', priority=1)
        pconfig2 = DummyPConfig(options, 'process2', '/bin/foo2', priority=2)
        from supervisor.process import ProcessStates
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)
        supervisord.set_procattr('process2', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopAllProcesses()
        self.assertEqual(interface.update_text, 'stopAllProcesses')
        from supervisor import http
        value = http.NOT_DONE_YET
        while 1:
            value = callback()
            if value is not http.NOT_DONE_YET:
                break

        self.assertEqual(value, [
            {'status':80,'group':'foo','name': 'process1','description': 'OK'},
            {'status':80,'group':'foo','name': 'process2','description': 'OK'},
            ] )
        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.stop_called, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.stop_called, True)

    def test_stopAllProcesses_nowait(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', __file__, priority=1)
        pconfig2 = DummyPConfig(options, 'process2', __file__, priority=2)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        from supervisor.process import ProcessStates
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)
        supervisord.set_procattr('process2', 'state', ProcessStates.RUNNING)
        interface = self._makeOne(supervisord)
        callback = interface.stopAllProcesses(wait=False)
        from supervisor.http import NOT_DONE_YET
        from supervisor.xmlrpc import Faults

        # create callbacks in killall()
        self.assertEqual(callback(), NOT_DONE_YET)

        # get a result
        result = callback()

        self.assertEqual(len(result), 2)
        self.assertEqual(result[0]['name'], 'process1')
        self.assertEqual(result[0]['group'], 'foo')
        self.assertEqual(result[0]['status'],  Faults.SUCCESS)
        self.assertEqual(result[0]['description'], 'OK')

        self.assertEqual(result[1]['name'], 'process2')
        self.assertEqual(result[1]['group'], 'foo')
        self.assertEqual(result[1]['status'],  Faults.SUCCESS)
        self.assertEqual(result[1]['description'], 'OK')

    def test_getAllConfigInfo(self):
        options = DummyOptions()
        supervisord = DummySupervisor(options, 'foo')

        pconfig1 = DummyPConfig(options, 'process1', __file__)
        pconfig2 = DummyPConfig(options, 'process2', __file__)
        gconfig = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig1, pconfig2])
        supervisord.process_groups = {'group1': DummyProcessGroup(gconfig)}
        supervisord.options.process_group_configs = [gconfig]

        interface = self._makeOne(supervisord)
        configs = interface.getAllConfigInfo()
        self.assertEqual(configs, [{ 'group': 'group1',
                                     'name': 'process1',
                                     'inuse': True,
                                     'autostart': True,
                                     'process_prio': 999,
                                     'group_prio': 999 },
                                   { 'group': 'group1',
                                     'name': 'process2',
                                     'inuse': True,
                                     'autostart': True,
                                     'process_prio': 999,
                                     'group_prio': 999 }])

    def test__interpretProcessInfo(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        start = _NOW -100
        stop  = _NOW -1
        from supervisor.process import ProcessStates
        running = {'name':'running',
                   'pid':1,
                   'state':ProcessStates.RUNNING,
                   'start':start,
                   'stop':stop,
                   'now':_NOW}

        description = interface._interpretProcessInfo(running)
        self.assertEqual(description, 'pid 1, uptime 0:01:40')

        fatal = {'name':'fatal',
                 'pid':2,
                 'state':ProcessStates.FATAL,
                 'start':start,
                 'stop':stop,
                 'now':_NOW,
                 'spawnerr':'Hosed'}

        description = interface._interpretProcessInfo(fatal)
        self.assertEqual(description, 'Hosed')

        fatal2 = {'name':'fatal',
                  'pid':2,
                  'state':ProcessStates.FATAL,
                  'start':start,
                  'stop':stop,
                  'now':_NOW,
                  'spawnerr':'',}

        description = interface._interpretProcessInfo(fatal2)
        self.assertEqual(description, 'unknown error (try "tail fatal")')

        stopped = {'name':'stopped',
                   'pid':3,
                   'state':ProcessStates.STOPPED,
                   'start':start,
                   'stop':stop,
                   'now':_NOW,
                   'spawnerr':'',}

        description = interface._interpretProcessInfo(stopped)
        from datetime import datetime
        stoptime = datetime(*time.localtime(stop)[:7])
        self.assertEqual(description, stoptime.strftime(_TIMEFORMAT))

        stopped2 = {'name':'stopped',
                   'pid':3,
                   'state':ProcessStates.STOPPED,
                   'start':0,
                   'stop':stop,
                   'now':_NOW,
                   'spawnerr':'',}

        description = interface._interpretProcessInfo(stopped2)
        self.assertEqual(description, 'Not started')


    def test_getProcessInfo(self):
        from supervisor.process import ProcessStates

        options = DummyOptions()
        config = DummyPConfig(options, 'foo', '/bin/foo',
                              stdout_logfile='/tmp/fleeb.bar')
        process = DummyProcess(config)
        process.pid = 111
        process.laststart = 10
        process.laststop = 11
        pgroup_config = DummyPGroupConfig(options, name='foo')
        pgroup = DummyProcessGroup(pgroup_config)
        pgroup.processes = {'foo':process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        data = interface.getProcessInfo('foo')

        self.assertEqual(interface.update_text, 'getProcessInfo')
        self.assertEqual(data['logfile'], '/tmp/fleeb.bar')
        self.assertEqual(data['stdout_logfile'], '/tmp/fleeb.bar')
        self.assertEqual(data['stderr_logfile'], '')
        self.assertEqual(data['name'], 'foo')
        self.assertEqual(data['pid'], 111)
        self.assertEqual(data['start'], 10)
        self.assertEqual(data['stop'], 11)
        self.assertEqual(data['state'], ProcessStates.RUNNING)
        self.assertEqual(data['statename'], 'RUNNING')
        self.assertEqual(data['exitstatus'], 0)
        self.assertEqual(data['spawnerr'], '')
        self.assertTrue(data['description'].startswith('pid 111'))

    def test_getProcessInfo_logfile_NONE(self):
        options = DummyOptions()
        config = DummyPConfig(options, 'foo', '/bin/foo',
                              stdout_logfile=None)
        process = DummyProcess(config)
        process.pid = 111
        process.laststart = 10
        process.laststop = 11
        pgroup_config = DummyPGroupConfig(options, name='foo')
        pgroup = DummyProcessGroup(pgroup_config)
        pgroup.processes = {'foo':process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        data = interface.getProcessInfo('foo')
        self.assertEqual(data['logfile'], '')
        self.assertEqual(data['stdout_logfile'], '')

    def test_getProcessInfo_unknown_state(self):
        from supervisor.states import ProcessStates
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        supervisord.set_procattr('foo', 'state', ProcessStates.UNKNOWN)
        interface = self._makeOne(supervisord)
        data = interface.getProcessInfo('foo')
        self.assertEqual(data['statename'], 'UNKNOWN')
        self.assertEqual(data['description'], '')

    def test_getProcessInfo_bad_name_when_bad_process(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.getProcessInfo, 'nonexistant')

    def test_getProcessInfo_bad_name_when_no_process(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.getProcessInfo, 'foo:')

    def test_getAllProcessInfo(self):
        from supervisor.process import ProcessStates
        options = DummyOptions()

        p1config = DummyPConfig(options, 'process1', '/bin/process1',
                                priority=1,
                                stdout_logfile='/tmp/process1.log')

        p2config = DummyPConfig(options, 'process2', '/bin/process2',
                                priority=2,
                                stdout_logfile='/tmp/process2.log')

        supervisord = PopulatedDummySupervisor(options, 'gname', p1config,
                                               p2config)
        supervisord.set_procattr('process1', 'pid', 111)
        supervisord.set_procattr('process1', 'laststart', 10)
        supervisord.set_procattr('process1', 'laststop', 11)
        supervisord.set_procattr('process1', 'state', ProcessStates.RUNNING)

        supervisord.set_procattr('process2', 'pid', 0)
        supervisord.set_procattr('process2', 'laststart', 20)
        supervisord.set_procattr('process2', 'laststop', 11)
        supervisord.set_procattr('process2', 'state', ProcessStates.STOPPED)

        interface = self._makeOne(supervisord)

        info = interface.getAllProcessInfo()

        self.assertEqual(interface.update_text, 'getProcessInfo')
        self.assertEqual(len(info), 2)

        p1info = info[0]
        self.assertEqual(p1info['logfile'], '/tmp/process1.log')
        self.assertEqual(p1info['stdout_logfile'], '/tmp/process1.log')
        self.assertEqual(p1info['stderr_logfile'], '')
        self.assertEqual(p1info['name'], 'process1')
        self.assertEqual(p1info['pid'], 111)
        self.assertEqual(p1info['start'], 10)
        self.assertEqual(p1info['stop'], 11)
        self.assertEqual(p1info['state'], ProcessStates.RUNNING)
        self.assertEqual(p1info['statename'], 'RUNNING')
        self.assertEqual(p1info['exitstatus'], 0)
        self.assertEqual(p1info['spawnerr'], '')
        self.assertEqual(p1info['group'], 'gname')
        self.assertTrue(p1info['description'].startswith('pid 111'))

        p2info = info[1]
        process2 = supervisord.process_groups['gname'].processes['process2']
        self.assertEqual(p2info['logfile'], '/tmp/process2.log')
        self.assertEqual(p2info['stdout_logfile'], '/tmp/process2.log')
        self.assertEqual(p1info['stderr_logfile'], '')
        self.assertEqual(p2info['name'], 'process2')
        self.assertEqual(p2info['pid'], 0)
        self.assertEqual(p2info['start'], process2.laststart)
        self.assertEqual(p2info['stop'], 11)
        self.assertEqual(p2info['state'], ProcessStates.STOPPED)
        self.assertEqual(p2info['statename'], 'STOPPED')
        self.assertEqual(p2info['exitstatus'], 0)
        self.assertEqual(p2info['spawnerr'], '')
        self.assertEqual(p1info['group'], 'gname')

        from datetime import datetime
        starttime = datetime(*time.localtime(process2.laststart)[:7])
        self.assertEqual(p2info['description'],
                            starttime.strftime(_TIMEFORMAT))

    def test_readProcessStdoutLog_unreadable(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                               stdout_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.NO_FILE,
                             interface.readProcessStdoutLog,
                             'process1', offset=0, length=1)

    def test_readProcessStdoutLog_badargs(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                              stdout_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['process1'].processes['process1']
        logfile = process.config.stdout_logfile

        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.close()
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readProcessStdoutLog,
                                 'process1', offset=-1, length=1)
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readProcessStdoutLog, 'process1',
                                 offset=-1, length=-1)
        finally:
            os.remove(logfile)

    def test_readProcessStdoutLog(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                              stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stdout_logfile
        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.write('y' * 2048)
            f.close()
            data = interface.readProcessStdoutLog('foo', offset=0, length=0)
            self.assertEqual(interface.update_text, 'readProcessStdoutLog')
            self.assertEqual(data, ('x' * 2048) + ('y' * 2048))
            data = interface.readProcessStdoutLog('foo', offset=2048, length=0)
            self.assertEqual(data, 'y' * 2048)
            data = interface.readProcessStdoutLog('foo', offset=0, length=2048)
            self.assertEqual(data, 'x' * 2048)
            data = interface.readProcessStdoutLog('foo', offset=-4, length=0)
            self.assertEqual(data, 'y' * 4)
        finally:
            os.remove(logfile)

    def test_readProcessLogAliasedTo_readProcessStdoutLog(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.readProcessLog,
                         interface.readProcessStdoutLog)

    def test_readProcessStderrLog_unreadable(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                               stderr_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.NO_FILE,
                             interface.readProcessStderrLog,
                             'process1', offset=0, length=1)

    def test_readProcessStderrLog_badargs(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1,
                              stderr_logfile='/tmp/process1.log')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['process1'].processes['process1']
        logfile = process.config.stderr_logfile

        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.close()
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readProcessStderrLog,
                                 'process1', offset=-1, length=1)
            self._assertRPCError(xmlrpc.Faults.BAD_ARGUMENTS,
                                 interface.readProcessStderrLog, 'process1',
                                 offset=-1, length=-1)
        finally:
            os.remove(logfile)

    def test_readProcessStderrLog(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                              stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stderr_logfile
        try:
            f = open(logfile, 'w+')
            f.write('x' * 2048)
            f.write('y' * 2048)
            f.close()
            data = interface.readProcessStderrLog('foo', offset=0, length=0)
            self.assertEqual(interface.update_text, 'readProcessStderrLog')
            self.assertEqual(data, ('x' * 2048) + ('y' * 2048))
            data = interface.readProcessStderrLog('foo', offset=2048, length=0)
            self.assertEqual(data, 'y' * 2048)
            data = interface.readProcessStderrLog('foo', offset=0, length=2048)
            self.assertEqual(data, 'x' * 2048)
            data = interface.readProcessStderrLog('foo', offset=-4, length=0)
            self.assertEqual(data, 'y' * 4)
        finally:
            os.remove(logfile)

    def test_tailProcessStdoutLog_bad_name(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.tailProcessStdoutLog, 'BAD_NAME', 0, 10)

    def test_tailProcessStdoutLog_all(self):
        # test entire log is returned when offset==0 and logsize < length
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stdout_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                        offset=0,
                                                        length=len(letters))
            self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, letters)
        finally:
            os.remove(logfile)

    def test_tailProcessStdoutLog_none(self):
        # test nothing is returned when offset <= logsize
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stdout_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            # offset==logsize
            data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                        offset=len(letters),
                                                        length=100)
            self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, '')

            # offset > logsize
            data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                        offset=len(letters)+5,
                                                        length=100)
            self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, '')
        finally:
            os.remove(logfile)

    def test_tailProcessStdoutLog_overflow(self):
        # test buffer overflow occurs when logsize > offset+length
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                              stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stdout_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                        offset=0, length=5)
            self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
            self.assertEqual(overflow, True)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, letters[-5:])
        finally:
            os.remove(logfile)

    def test_tailProcessStdoutLog_unreadable(self):
        # test nothing is returned if the log doesn't exist yet
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stdout_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)

        data, offset, overflow = interface.tailProcessStdoutLog('foo',
                                                    offset=0, length=100)
        self.assertEqual(interface.update_text, 'tailProcessStdoutLog')
        self.assertEqual(overflow, False)
        self.assertEqual(offset, 0)
        self.assertEqual(data, '')

    def test_tailProcessLogAliasedTo_tailProcessStdoutLog(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.tailProcessLog,
                         interface.tailProcessStdoutLog)

    def test_tailProcessStderrLog_bad_name(self):
        from supervisor import xmlrpc
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.tailProcessStderrLog, 'BAD_NAME', 0, 10)

    def test_tailProcessStderrLog_all(self):
        # test entire log is returned when offset==0 and logsize < length
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stderr_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                        offset=0,
                                                        length=len(letters))
            self.assertEqual(interface.update_text, 'tailProcessStderrLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, letters)
        finally:
            os.remove(logfile)

    def test_tailProcessStderrLog_none(self):
        # test nothing is returned when offset <= logsize
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stderr_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            # offset==logsize
            data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                        offset=len(letters),
                                                        length=100)
            self.assertEqual(interface.update_text, 'tailProcessStderrLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, '')

            # offset > logsize
            data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                        offset=len(letters)+5,
                                                        length=100)
            self.assertEqual(interface.update_text, 'tailProcessStderrLog')
            self.assertEqual(overflow, False)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, '')
        finally:
            os.remove(logfile)

    def test_tailProcessStderrLog_overflow(self):
        # test buffer overflow occurs when logsize > offset+length
        try:
            from string import letters
        except ImportError:
            from string import ascii_letters as letters
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                              stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        process = supervisord.process_groups['foo'].processes['foo']
        logfile = process.config.stderr_logfile
        try:
            f = open(logfile, 'w+')
            f.write(letters)
            f.close()

            data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                        offset=0, length=5)
            self.assertEqual(interface.update_text, 'tailProcessStderrLog')
            self.assertEqual(overflow, True)
            self.assertEqual(offset, len(letters))
            self.assertEqual(data, letters[-5:])
        finally:
            os.remove(logfile)

    def test_tailProcessStderrLog_unreadable(self):
        # test nothing is returned if the log doesn't exist yet
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',
                               stderr_logfile='/tmp/fooooooo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)

        data, offset, overflow = interface.tailProcessStderrLog('foo',
                                                    offset=0, length=100)
        self.assertEqual(interface.update_text, 'tailProcessStderrLog')
        self.assertEqual(overflow, False)
        self.assertEqual(offset, 0)
        self.assertEqual(data, '')

    def test_clearProcessLogs_bad_name_no_group(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo')
        process = DummyProcess(pconfig)
        pgroup = DummyProcessGroup(None)
        pgroup.processes = {'foo': process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.clearProcessLogs,
                             'badgroup')

    def test_clearProcessLogs_bad_name_no_process(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo')
        process = DummyProcess(pconfig)
        pgroup = DummyProcessGroup(None)
        pgroup.processes = {'foo': process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.clearProcessLogs,
                             'foo:*')

    def test_clearProcessLogs(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo')
        process = DummyProcess(pconfig)
        pgroup = DummyProcessGroup(None)
        pgroup.processes = {'foo': process}
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        interface.clearProcessLogs('foo')
        self.assertEqual(process.logsremoved, True)

    def test_clearProcessLogs_failed(self):
        from supervisor import xmlrpc
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo')
        process = DummyProcess(pconfig)
        pgroup = DummyProcessGroup(None)
        pgroup.processes = {'foo': process}
        process.error_at_clear = True
        supervisord = DummySupervisor(process_groups={'foo':pgroup})
        interface = self._makeOne(supervisord)
        self.assertRaises(xmlrpc.RPCError, interface.clearProcessLogs, 'foo')

    def test_clearProcessLogAliasedTo_clearProcessLogs(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', '/bin/foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig)
        interface = self._makeOne(supervisord)
        self.assertEqual(interface.clearProcessLog,
                         interface.clearProcessLogs)

    def test_clearAllProcessLogs(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo', priority=1)
        pconfig2 = DummyPConfig(options, 'process2', 'bar', priority=2)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        interface = self._makeOne(supervisord)
        callback = interface.clearAllProcessLogs()
        callback()
        results = callback()
        from supervisor import xmlrpc
        self.assertEqual(results[0],
                         {'name':'process1',
                          'group':'foo',
                          'status':xmlrpc.Faults.SUCCESS,
                          'description':'OK'})
        self.assertEqual(results[1],
                         {'name':'process2',
                          'group':'foo',
                          'status':xmlrpc.Faults.SUCCESS,
                          'description':'OK'})
        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.logsremoved, True)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.logsremoved, True)

    def test_clearAllProcessLogs_onefails(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo', priority=1)
        pconfig2 = DummyPConfig(options, 'process2', 'bar', priority=2)
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1,
                                               pconfig2)
        supervisord.set_procattr('process1', 'error_at_clear', True)
        interface = self._makeOne(supervisord)
        callback = interface.clearAllProcessLogs()
        callback()
        results = callback()
        process1 = supervisord.process_groups['foo'].processes['process1']
        self.assertEqual(process1.logsremoved, False)
        process2 = supervisord.process_groups['foo'].processes['process2']
        self.assertEqual(process2.logsremoved, True)
        self.assertEqual(len(results), 2)
        from supervisor import xmlrpc
        self.assertEqual(results[0],
                         {'name':'process1',
                          'group':'foo',
                          'status':xmlrpc.Faults.FAILED,
                          'description':'FAILED: foo:process1'})
        self.assertEqual(results[1],
                         {'name':'process2',
                          'group':'foo',
                          'status':xmlrpc.Faults.SUCCESS,
                          'description':'OK'})

    def test_clearAllProcessLogs_no_processes(self):
        supervisord = DummySupervisor()
        self.assertEqual(supervisord.process_groups, {})
        interface = self._makeOne(supervisord)
        callback = interface.clearAllProcessLogs()
        results = callback()
        self.assertEqual(results, [])

    def test_sendProcessStdin_raises_incorrect_params_when_not_chars(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'foo', pconfig1)
        interface   = self._makeOne(supervisord)
        thing_not_chars = 42
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.INCORRECT_PARAMETERS,
                             interface.sendProcessStdin,
                             'process1', thing_not_chars)

    def test_sendProcessStdin_raises_bad_name_when_bad_process(self):
        supervisord = DummySupervisor()
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.sendProcessStdin,
                             'nonexistant', 'chars for stdin')

    def test_sendProcessStdin_raises_bad_name_when_no_process(self):
        options = DummyOptions()
        supervisord = PopulatedDummySupervisor(options, 'foo')
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.BAD_NAME,
                             interface.sendProcessStdin,
                             'foo:*', 'chars for stdin')

    def test_sendProcessStdin_raises_not_running_when_not_process_pid(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 0)
        interface = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NOT_RUNNING,
                            interface.sendProcessStdin,
                            'process1', 'chars for stdin')

    def test_sendProcessStdin_raises_not_running_when_killing(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 42)
        supervisord.set_procattr('process1', 'killing',True)
        interface   = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NOT_RUNNING,
                             interface.sendProcessStdin,
                             'process1', 'chars for stdin')

    def test_sendProcessStdin_raises_no_file_when_write_raises_epipe(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 42)
        supervisord.set_procattr('process1', 'killing', False)
        supervisord.set_procattr('process1', 'write_error', errno.EPIPE)
        interface   = self._makeOne(supervisord)
        from supervisor import xmlrpc
        self._assertRPCError(xmlrpc.Faults.NO_FILE,
                             interface.sendProcessStdin,
                             'process1', 'chars for stdin')

    def test_sendProcessStdin_reraises_other_oserrors(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 42)
        supervisord.set_procattr('process1', 'killing', False)
        supervisord.set_procattr('process1', 'write_error', errno.EINTR)
        interface   = self._makeOne(supervisord)
        self.assertRaises(OSError,
                          interface.sendProcessStdin,
                          'process1', 'chars for stdin')

    def test_sendProcessStdin_writes_chars_and_returns_true(self):
        options = DummyOptions()
        pconfig1 = DummyPConfig(options, 'process1', 'foo')
        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
        supervisord.set_procattr('process1', 'pid', 42)
        interface   = self._makeOne(supervisord)
        chars = 'chars for stdin'
        self.assertTrue(interface.sendProcessStdin('process1', chars))
        self.assertEqual('sendProcessStdin', interface.update_text)
        process1 = supervisord.process_groups['process1'].processes['process1']
        self.assertEqual(process1.stdin_buffer, chars)

#    def test_sendProcessStdin_unicode_encoded_to_utf8(self):
#        options = DummyOptions()
#        pconfig1 = DummyPConfig(options, 'process1', 'foo')
#        supervisord = PopulatedDummySupervisor(options, 'process1', pconfig1)
#        supervisord.set_procattr('process1', 'pid', 42)
#        interface   = self._makeOne(supervisord)
#        interface.sendProcessStdin('process1', u'fi\xed')
#        process1 = supervisord.process_groups['process1'].processes['process1']
#        self.assertEqual(process1.stdin_buffer, 'fi\xc3\xad')

    def test_sendRemoteCommEvent_notifies_subscribers(self):
        options = DummyOptions()
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)

        from supervisor import events
        L = []
        def callback(event):
            L.append(event)

        try:
            events.callbacks[:] = [(events.RemoteCommunicationEvent, callback)]
            result = interface.sendRemoteCommEvent('foo', 'bar')
        finally:
            events.callbacks[:] = []
            events.clear()

        self.assertTrue(result)
        self.assertEqual(len(L), 1)
        event = L[0]
        self.assertEqual(event.type, 'foo')
        self.assertEqual(event.data, 'bar')

    def test_sendRemoteCommEvent_unicode_encoded_to_utf8(self):
        from supervisor.compat import as_string, PY3
        options = DummyOptions()
        supervisord = DummySupervisor(options)
        interface = self._makeOne(supervisord)

        from supervisor import events
        L = []
        def callback(event):
            L.append(event)

        try:
            events.callbacks[:] = [(events.RemoteCommunicationEvent, callback)]
            result = interface.sendRemoteCommEvent(
                as_string('fií once'),
                as_string('fií twice'),
                )
        finally:
            events.callbacks[:] = []
            events.clear()

        self.assertTrue(result)
        self.assertEqual(len(L), 1)
        event = L[0]
        if PY3:
            self.assertEqual(event.type, 'fií once')
            self.assertEqual(event.data, 'fií twice')
        else:
            self.assertEqual(event.type, 'fi\xc3\xad once')
            self.assertEqual(event.data, 'fi\xc3\xad twice')


class SystemNamespaceXMLRPCInterfaceTests(TestBase):
    def _getTargetClass(self):
        from supervisor import xmlrpc
        return xmlrpc.SystemNamespaceRPCInterface

    def _makeOne(self):
        from supervisor import rpcinterface
        supervisord = DummySupervisor()
        supervisor = rpcinterface.SupervisorNamespaceRPCInterface(supervisord)
        return self._getTargetClass()(
            [('supervisor', supervisor),
             ]
            )

    def test_ctor(self):
        interface = self._makeOne()
        self.assertTrue(interface.namespaces['supervisor'])
        self.assertTrue(interface.namespaces['system'])

    def test_listMethods(self):
        interface = self._makeOne()
        methods = interface.listMethods()
        methods.sort()
        keys = list(interface._listMethods().keys())
        keys.sort()
        self.assertEqual(methods, keys)

    def test_methodSignature(self):
        from supervisor import xmlrpc
        interface = self._makeOne()
        self._assertRPCError(xmlrpc.Faults.SIGNATURE_UNSUPPORTED,
                             interface.methodSignature,
                             ['foo.bar'])
        result = interface.methodSignature('system.methodSignature')
        self.assertEqual(result, ['array', 'string'])

    def test_allMethodDocs(self):
        from supervisor import xmlrpc
        # belt-and-suspenders test for docstring-as-typing parsing correctness
        # and documentation validity vs. implementation
        _RPCTYPES = ['int', 'double', 'string', 'boolean', 'dateTime.iso8601',
                     'base64', 'binary', 'array', 'struct']
        interface = self._makeOne()
        methods = interface._listMethods()
        for k in methods.keys():
            # if a method doesn't have a @return value, an RPCError is raised.
            # Detect that here.
            try:
                interface.methodSignature(k)
            except xmlrpc.RPCError:
                raise AssertionError('methodSignature for %s raises '
                                     'RPCError (missing @return doc?)' % k)

            # we want to test that the number of arguments implemented in
            # the function is the same as the number of arguments implied by
            # the doc @params, and that they show up in the same order.
            ns_name, method_name = k.split('.', 1)
            namespace = interface.namespaces[ns_name]
            meth = getattr(namespace, method_name)
            try:
                code = meth.func_code
            except Exception:
                code = meth.__code__
            argnames = code.co_varnames[1:code.co_argcount]
            parsed = xmlrpc.gettags(str(meth.__doc__))

            plines = []
            ptypes = []
            pnames = []
            ptexts = []

            rlines = []
            rtypes = []
            rnames = []
            rtexts = []

            for thing in parsed:
                if thing[1] == 'param': # tag name
                    plines.append(thing[0]) # doc line number
                    ptypes.append(thing[2]) # data type
                    pnames.append(thing[3]) # function name
                    ptexts.append(thing[4])  # description
                elif thing[1] == 'return': # tag name
                    rlines.append(thing[0]) # doc line number
                    rtypes.append(thing[2]) # data type
                    rnames.append(thing[3]) # function name
                    rtexts.append(thing[4])  # description
                elif thing[1] is not None:
                    raise AssertionError(
                        'unknown tag type %s for %s, parsed %s' % (thing[1],
                                                                   k,
                                                                   parsed))
            # param tokens

            if len(argnames) != len(pnames):
                raise AssertionError('Incorrect documentation '
                                     '(%s args, %s doc params) in %s'
                                     % (len(argnames), len(pnames), k))
            for docline in plines:
                self.assertTrue(type(docline) == int, (docline,
                                                       type(docline),
                                                       k,
                                                       parsed))
            for doctype in ptypes:
                self.assertTrue(doctype in _RPCTYPES, doctype)
            for x in range(len(pnames)):
                if pnames[x] != argnames[x]:
                    msg = 'Name wrong: (%s vs. %s in %s)\n%s' % (pnames[x],
                                                                 argnames[x],
                                                                 k,
                                                                 parsed)
                    raise AssertionError(msg)
            for doctext in ptexts:
                self.assertTrue(type(doctext) == type(''), doctext)

            # result tokens

            if len(rlines) > 1:
                raise AssertionError(
                    'Duplicate @return values in docs for %s' % k)
            for docline in rlines:
                self.assertTrue(type(docline) == int, (docline,
                                                       type(docline), k))
            for doctype in rtypes:
                self.assertTrue(doctype in _RPCTYPES, doctype)
            for docname in rnames:
                self.assertTrue(type(docname) == type(''), (docname,
                                                            type(docname),
                                                            k))
            for doctext in rtexts:
                self.assertTrue(type(doctext) == type(''), (doctext,
                                                            type(doctext), k))

    def test_multicall_simplevals(self):
        interface = self._makeOne()
        callback = interface.multicall([
            {'methodName':'system.methodHelp', 'params':['system.methodHelp']},
            {'methodName':'system.listMethods', 'params':[]},
            ])
        from supervisor import http
        result = http.NOT_DONE_YET
        while result is http.NOT_DONE_YET:
            result = callback()
        self.assertEqual(result[0], interface.methodHelp('system.methodHelp'))
        self.assertEqual(result[1], interface.listMethods())

    def test_multicall_recursion_guard(self):
        from supervisor import xmlrpc
        interface = self._makeOne()
        callback = interface.multicall([
            {'methodName': 'system.multicall', 'params': []},
        ])

        from supervisor import http
        result = http.NOT_DONE_YET
        while result is http.NOT_DONE_YET:
            result = callback()

        code = xmlrpc.Faults.INCORRECT_PARAMETERS
        desc = xmlrpc.getFaultDescription(code)
        recursion_fault = {'faultCode': code, 'faultString': desc}

        self.assertEqual(result, [recursion_fault])

    def test_multicall_nested_callback(self):
        interface = self._makeOne()
        callback = interface.multicall([
            {'methodName':'supervisor.stopAllProcesses'}])
        from supervisor import http
        result = http.NOT_DONE_YET
        while result is http.NOT_DONE_YET:
            result = callback()
        self.assertEqual(result[0], [])

    def test_methodHelp(self):
        from supervisor import xmlrpc
        interface = self._makeOne()
        self._assertRPCError(xmlrpc.Faults.SIGNATURE_UNSUPPORTED,
                             interface.methodHelp,
                             ['foo.bar'])
        help = interface.methodHelp('system.methodHelp')
        self.assertEqual(help, interface.methodHelp.__doc__)

class DummyRPCInterface:
    def hello(self):
        return 'Hello!'

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_socket_manager
"""Test suite for supervisor.socket_manager"""

import sys
import os
import unittest
import supervisor.medusa.text_socket as socket
import tempfile

from supervisor.tests.base import DummySocketConfig
from supervisor.tests.base import DummyLogger
from supervisor.datatypes import UnixStreamSocketConfig
from supervisor.datatypes import InetStreamSocketConfig

class TestObject:

    def __init__(self):
        self.value = 5

    def getValue(self):
        return self.value

    def setValue(self, val):
        self.value = val

class ProxyTest(unittest.TestCase):

    def setUp(self):
        self.on_deleteCalled = False

    def _getTargetClass(self):
        from supervisor.socket_manager import Proxy
        return Proxy

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def setOnDeleteCalled(self):
        self.on_deleteCalled = True

    def test_proxy_getattr(self):
        proxy = self._makeOne(TestObject())
        self.assertEqual(5, proxy.getValue())

    def test_on_delete(self):
        proxy = self._makeOne(TestObject(), on_delete=self.setOnDeleteCalled)
        self.assertEqual(5, proxy.getValue())
        proxy = None
        self.assertTrue(self.on_deleteCalled)

class ReferenceCounterTest(unittest.TestCase):

    def setUp(self):
        self.running = False

    def start(self):
        self.running = True

    def stop(self):
        self.running = False

    def _getTargetClass(self):
        from supervisor.socket_manager import ReferenceCounter
        return ReferenceCounter

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_incr_and_decr(self):
        ctr = self._makeOne(on_zero=self.stop,on_non_zero=self.start)
        self.assertFalse(self.running)
        ctr.increment()
        self.assertTrue(self.running)
        self.assertEqual(1, ctr.get_count())
        ctr.increment()
        self.assertTrue(self.running)
        self.assertEqual(2, ctr.get_count())
        ctr.decrement()
        self.assertTrue(self.running)
        self.assertEqual(1, ctr.get_count())
        ctr.decrement()
        self.assertFalse(self.running)
        self.assertEqual(0, ctr.get_count())

    def test_decr_at_zero_raises_error(self):
        ctr = self._makeOne(on_zero=self.stop,on_non_zero=self.start)
        self.assertRaises(Exception, ctr.decrement)

class SocketManagerTest(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.socket_manager import SocketManager
        return SocketManager

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_repr(self):
        conf = DummySocketConfig(2)
        sock_manager = self._makeOne(conf)
        expected = "<%s at %s for %s>" % (
            sock_manager.__class__, id(sock_manager), conf.url)
        self.assertEqual(repr(sock_manager), expected)

    def test_get_config(self):
        conf = DummySocketConfig(2)
        sock_manager = self._makeOne(conf)
        self.assertEqual(conf, sock_manager.config())

    def test_tcp_w_hostname(self):
        conf = InetStreamSocketConfig('localhost', 51041)
        sock_manager = self._makeOne(conf)
        self.assertEqual(sock_manager.socket_config, conf)
        sock = sock_manager.get_socket()
        self.assertEqual(sock.getsockname(), ('127.0.0.1', 51041))

    def test_tcp_w_ip(self):
        conf = InetStreamSocketConfig('127.0.0.1', 51041)
        sock_manager = self._makeOne(conf)
        self.assertEqual(sock_manager.socket_config, conf)
        sock = sock_manager.get_socket()
        self.assertEqual(sock.getsockname(), ('127.0.0.1', 51041))

    def test_unix(self):
        (tf_fd, tf_name) = tempfile.mkstemp()
        conf = UnixStreamSocketConfig(tf_name)
        sock_manager = self._makeOne(conf)
        self.assertEqual(sock_manager.socket_config, conf)
        sock = sock_manager.get_socket()
        self.assertEqual(sock.getsockname(), tf_name)
        sock = None
        os.close(tf_fd)

    def test_socket_lifecycle(self):
        conf = DummySocketConfig(2)
        sock_manager = self._makeOne(conf)
        # Assert that sockets are created on demand
        self.assertFalse(sock_manager.is_prepared())
        # Get two socket references
        sock = sock_manager.get_socket()
        self.assertTrue(sock_manager.is_prepared()) #socket created on demand
        sock_id = id(sock._get())
        sock2 = sock_manager.get_socket()
        sock2_id = id(sock2._get())
        # Assert that they are not the same proxy object
        self.assertNotEqual(sock, sock2)
        # Assert that they are the same underlying socket
        self.assertEqual(sock_id, sock2_id)
        # Socket not actually closed yet b/c ref ct is 2
        self.assertEqual(2, sock_manager.get_socket_ref_count())
        self.assertTrue(sock_manager.is_prepared())
        self.assertFalse(sock_manager.socket.close_called)
        sock = None
        # Socket not actually closed yet b/c ref ct is 1
        self.assertTrue(sock_manager.is_prepared())
        self.assertFalse(sock_manager.socket.close_called)
        sock2 = None
        # Socket closed
        self.assertFalse(sock_manager.is_prepared())
        self.assertTrue(sock_manager.socket.close_called)

        # Get a new socket reference
        sock3 = sock_manager.get_socket()
        self.assertTrue(sock_manager.is_prepared())
        sock3_id = id(sock3._get())
        # Assert that it is not the same socket
        self.assertNotEqual(sock_id, sock3_id)
        # Drop ref ct to zero
        del sock3
        # Now assert that socket is closed
        self.assertFalse(sock_manager.is_prepared())
        self.assertTrue(sock_manager.socket.close_called)

    def test_logging(self):
        conf = DummySocketConfig(1)
        logger = DummyLogger()
        sock_manager = self._makeOne(conf, logger=logger)
        # socket open
        sock = sock_manager.get_socket()
        self.assertEqual(len(logger.data), 1)
        self.assertEqual('Creating socket %s' % repr(conf), logger.data[0])
        # socket close
        del sock
        self.assertEqual(len(logger.data), 2)
        self.assertEqual('Closing socket %s' % repr(conf), logger.data[1])

    def test_prepare_socket(self):
        conf = DummySocketConfig(1)
        sock_manager = self._makeOne(conf)
        sock = sock_manager.get_socket()
        self.assertTrue(sock_manager.is_prepared())
        self.assertFalse(sock.bind_called)
        self.assertTrue(sock.listen_called)
        self.assertEqual(sock.listen_backlog, socket.SOMAXCONN)
        self.assertFalse(sock.close_called)

    def test_tcp_socket_already_taken(self):
        conf = InetStreamSocketConfig('127.0.0.1', 51041)
        sock_manager = self._makeOne(conf)
        sock = sock_manager.get_socket()
        sock_manager2 = self._makeOne(conf)
        self.assertRaises(socket.error, sock_manager2.get_socket)
        del sock

    def test_unix_bad_sock(self):
        conf = UnixStreamSocketConfig('/notthere/foo.sock')
        sock_manager = self._makeOne(conf)
        self.assertRaises(socket.error, sock_manager.get_socket)

    def test_close_requires_prepared_socket(self):
        conf = InetStreamSocketConfig('127.0.0.1', 51041)
        sock_manager = self._makeOne(conf)
        self.assertFalse(sock_manager.is_prepared())
        try:
            sock_manager._close()
            self.fail()
        except Exception as e:
            self.assertEqual(e.args[0], 'Socket has not been prepared')

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_states
"""Test suite for supervisor.states"""

import sys
import unittest
from supervisor import states

class TopLevelProcessStateTests(unittest.TestCase):
    def test_module_has_process_states(self):
        self.assertTrue(hasattr(states, 'ProcessStates'))
    
    def test_stopped_states_do_not_overlap_with_running_states(self):
        for state in states.STOPPED_STATES:
            self.assertFalse(state in states.RUNNING_STATES)

    def test_running_states_do_not_overlap_with_stopped_states(self):
        for state in states.RUNNING_STATES:
            self.assertFalse(state in states.STOPPED_STATES)

    def test_getProcessStateDescription_returns_string_when_found(self):
        state = states.ProcessStates.STARTING
        self.assertEqual(states.getProcessStateDescription(state),
            'STARTING')

    def test_getProcessStateDescription_returns_None_when_not_found(self):
        self.assertEqual(states.getProcessStateDescription(3.14159),
            None)

class TopLevelSupervisorStateTests(unittest.TestCase):
    def test_module_has_supervisor_states(self):
        self.assertTrue(hasattr(states, 'SupervisorStates'))

    def test_getSupervisorStateDescription_returns_string_when_found(self):
        state = states.SupervisorStates.RUNNING
        self.assertEqual(states.getSupervisorStateDescription(state),
            'RUNNING')

    def test_getSupervisorStateDescription_returns_None_when_not_found(self):
        self.assertEqual(states.getSupervisorStateDescription(3.14159),
            None)

class TopLevelEventListenerStateTests(unittest.TestCase):
    def test_module_has_eventlistener_states(self):
        self.assertTrue(hasattr(states, 'EventListenerStates'))

    def test_getEventListenerStateDescription_returns_string_when_found(self):
        state = states.EventListenerStates.ACKNOWLEDGED
        self.assertEqual(states.getEventListenerStateDescription(state),
            'ACKNOWLEDGED')

    def test_getEventListenerStateDescription_returns_None_when_not_found(self):
        self.assertEqual(states.getEventListenerStateDescription(3.14159),
            None)
    

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_supervisorctl
import sys
import unittest
try:
    from StringIO import StringIO
    import xmlrpclib
except ImportError:
    from io import StringIO
    import xmlrpc.client as xmlrpclib

from supervisor.tests.base import DummyRPCServer

class ControllerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.supervisorctl import Controller
        return Controller

    def _makeOne(self, options):
        return self._getTargetClass()(options)

    def test_ctor(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        self.assertEqual(controller.prompt, options.prompt + '> ')

    def test__upcheck(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        result = controller.upcheck()
        self.assertEqual(result, True)

    def test__upcheck_wrong_server_version(self):
        options = DummyClientOptions()
        options._server.supervisor.getVersion = lambda *x: '1.0'
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        result = controller.upcheck()
        self.assertEqual(result, False)
        self.assertEqual(controller.stdout.getvalue(),
                         'Sorry, this version of supervisorctl expects'
                         ' to talk to a server with API version 3.0, but'
                         ' the remote version is 1.0.\n')

    def test__upcheck_unknown_method(self):
        options = DummyClientOptions()
        from supervisor.xmlrpc import Faults
        def getVersion():
            raise xmlrpclib.Fault(Faults.UNKNOWN_METHOD, 'duh')
        options._server.supervisor.getVersion = getVersion
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        result = controller.upcheck()
        self.assertEqual(result, False)
        self.assertEqual(controller.stdout.getvalue(),
                         'Sorry, supervisord responded but did not recognize'
                         ' the supervisor namespace commands that'
                         ' supervisorctl uses to control it.  Please check'
                         ' that the [rpcinterface:supervisor] section is'
                         ' enabled in the configuration file'
                         ' (see sample.conf).\n')

    def test__upcheck_reraises_other_xmlrpc_faults(self):
        options = DummyClientOptions()
        from supervisor.xmlrpc import Faults
        def f(*arg, **kw):
            raise xmlrpclib.Fault(Faults.FAILED, '')
        options._server.supervisor.getVersion = f
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        self.assertRaises(xmlrpclib.Fault, controller.upcheck)

    def test__upcheck_catches_socket_error_ECONNREFUSED(self):
        options = DummyClientOptions()
        import supervisor.medusa.text_socket as socket
        import errno
        def raise_fault(*arg, **kw):
            raise socket.error(errno.ECONNREFUSED, 'nobody home')
        options._server.supervisor.getVersion = raise_fault

        controller = self._makeOne(options)
        controller.stdout = StringIO()

        result = controller.upcheck()
        self.assertEqual(result, False)

        output = controller.stdout.getvalue()
        self.assertTrue('refused connection' in output)

    def test__upcheck_catches_socket_error_ENOENT(self):
        options = DummyClientOptions()
        import supervisor.medusa.text_socket as socket
        import errno
        def raise_fault(*arg, **kw):
            raise socket.error(errno.ENOENT, 'nobody home')
        options._server.supervisor.getVersion = raise_fault

        controller = self._makeOne(options)
        controller.stdout = StringIO()

        result = controller.upcheck()
        self.assertEqual(result, False)

        output = controller.stdout.getvalue()
        self.assertTrue('no such file' in output)

    def test__upcheck_reraises_other_socket_faults(self):
        options = DummyClientOptions()
        import supervisor.medusa.text_socket as socket
        import errno
        def f(*arg, **kw):
            raise socket.error(errno.EBADF, '')
        options._server.supervisor.getVersion = f
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        self.assertRaises(socket.error, controller.upcheck)

    def test_onecmd(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        plugin = DummyPlugin()
        controller.options.plugins = (plugin,)
        result = controller.onecmd('help')
        self.assertEqual(result, None)
        self.assertEqual(plugin.helped, True)

    def test_onecmd_multi_colonseparated(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        plugin = DummyPlugin()
        controller.options.plugins = (plugin,)
        result = controller.onecmd('help; help')
        self.assertEqual(result, None)
        self.assertEqual(controller.cmdqueue, [' help'])
        self.assertEqual(plugin.helped, True)

    def test_onecmd_empty_does_not_repeat_previous_cmd(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        plugin = DummyPlugin()
        controller.options.plugins = (plugin,)
        plugin.helped = False
        controller.onecmd('help')
        self.assertTrue(plugin.helped)
        plugin.helped = False
        controller.onecmd('')
        self.assertFalse(plugin.helped)

    def test_onecmd_clears_completion_cache(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        controller._complete_info = {}
        controller.onecmd('help')
        self.assertEqual(controller._complete_info, None)

    def test_complete_action_empty(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help']
        result = controller.complete('', 0, line='')
        self.assertEqual(result, 'help ')
        result = controller.complete('', 1, line='')
        self.assertEqual(result, None)

    def test_complete_action_partial(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help']
        result = controller.complete('h', 0, line='h')
        self.assertEqual(result, 'help ')
        result = controller.complete('h', 1, line='h')
        self.assertEqual(result, None)

    def test_complete_action_whole(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help']
        result = controller.complete('help', 0, line='help')
        self.assertEqual(result, 'help ')

    def test_complete_unknown_action_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        result = controller.complete('bad', 0, line='bad')
        self.assertEqual(result, None)

    def test_complete_unknown_action_arg_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'add']
        result = controller.complete('', 1, line='bad ')
        self.assertEqual(result, None)

    def test_complete_help_empty(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('', 0, line='help ')
        self.assertEqual(result, 'help ')
        result = controller.complete('', 1, line='help ')
        self.assertEqual(result, 'start ')
        result = controller.complete('', 2, line='help ')
        self.assertEqual(result, None)

    def test_complete_help_action(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('he', 0, line='help he')
        self.assertEqual(result, 'help ')
        result = controller.complete('he', 1, line='help he')
        self.assertEqual(result, None)

    def test_complete_start_empty(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('', 0, line='start ')
        self.assertEqual(result, 'foo ')
        result = controller.complete('', 1, line='start ')
        self.assertEqual(result, 'bar ')
        result = controller.complete('', 2, line='start ')
        self.assertEqual(result, 'baz:baz_01 ')
        result = controller.complete('', 3, line='start ')
        self.assertEqual(result, 'baz:* ')
        result = controller.complete('', 4, line='start ')
        self.assertEqual(result, None)

    def test_complete_start_no_colon(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('f', 0, line='start f')
        self.assertEqual(result, 'foo ')
        result = controller.complete('f', 1, line='start f')
        self.assertEqual(result, None)

    def test_complete_start_with_colon(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('foo:', 0, line='start foo:')
        self.assertEqual(result, 'foo:foo ')
        result = controller.complete('foo:', 1, line='start foo:')
        self.assertEqual(result, 'foo:* ')
        result = controller.complete('foo:', 2, line='start foo:')
        self.assertEqual(result, None)

    def test_complete_start_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('bad', 0, line='start bad')
        self.assertEqual(result, None)

    def test_complete_caches_process_info(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('', 0, line='start ')
        self.assertNotEqual(result, None)
        def f(*arg, **kw):
            raise Exception("should not have called getAllProcessInfo")
        controller.options._server.supervisor.getAllProcessInfo = f
        controller.complete('', 1, line='start ')

    def test_complete_add_empty(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'add']
        result = controller.complete('', 0, line='add ')
        self.assertEqual(result, 'foo ')
        result = controller.complete('', 1, line='add ')
        self.assertEqual(result, 'bar ')
        result = controller.complete('', 2, line='add ')
        self.assertEqual(result, 'baz ')
        result = controller.complete('', 3, line='add ')
        self.assertEqual(result, None)

    def test_complete_add_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'add']
        result = controller.complete('bad', 0, line='add bad')
        self.assertEqual(result, None)

    def test_complete_add_group(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'add']
        result = controller.complete('f', 0, line='add f')
        self.assertEqual(result, 'foo ')
        result = controller.complete('f', 1, line='add f')
        self.assertEqual(result, None)

    def test_complete_reload_arg_uncompletable(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'reload']
        result = controller.complete('', 1, line='reload ')
        self.assertEqual(result, None)

    def test_complete_semicolon_separated_commands(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout=StringIO()
        controller.vocab = ['help', 'start']
        result = controller.complete('f', 0, line='help;start f')
        self.assertEqual(result, 'foo ')
        result = controller.complete('f', 1, line='help;start f')
        self.assertEqual(result, None)

    def test_nohelp(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        self.assertEqual(controller.nohelp, '*** No help on %s')

    def test_do_help(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        results = controller.do_help('')
        helpval = controller.stdout.getvalue()
        self.assertEqual(results, None)
        self.assertEqual(helpval, 'foo helped')

    def test_do_help_for_help(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)
        controller.stdout = StringIO()
        results = controller.do_help("help")
        self.assertEqual(results, None)
        helpval = controller.stdout.getvalue()
        self.assertTrue("help\t\tPrint a list" in helpval)

    def test_get_supervisor_returns_serverproxy_supervisor_namespace(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)

        proxy = controller.get_supervisor()
        expected = options.getServerProxy().supervisor
        self.assertEqual(proxy, expected)

    def test_get_server_proxy_with_no_args_returns_serverproxy(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)

        proxy = controller.get_server_proxy()
        expected = options.getServerProxy()
        self.assertEqual(proxy, expected)

    def test_get_server_proxy_with_namespace_returns_that_namespace(self):
        options = DummyClientOptions()
        controller = self._makeOne(options)

        proxy = controller.get_server_proxy('system')
        expected = options.getServerProxy().system
        self.assertEqual(proxy, expected)

    def test_real_controller_initialization(self):
        from supervisor.options import ClientOptions
        args = [] # simulating starting without parameters
        options = ClientOptions()

        # No default config file search in case they would exist
        self.assertTrue(len(options.searchpaths) > 0)
        options.searchpaths = []

        options.realize(args, doc=__doc__)
        self._makeOne(options) # should not raise


class TestControllerPluginBase(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.supervisorctl import ControllerPluginBase
        return ControllerPluginBase

    def _makeOne(self, *arg, **kw):
        klass = self._getTargetClass()
        options = DummyClientOptions()
        ctl = DummyController(options)
        plugin = klass(ctl, *arg, **kw)
        return plugin

    def test_do_help_noarg(self):
        plugin = self._makeOne()
        result = plugin.do_help(None)
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), '\n')
        self.assertEqual(len(plugin.ctl.topics_printed), 1)
        topics = plugin.ctl.topics_printed[0]
        self.assertEqual(topics[0], 'unnamed commands (type help <topic>):')
        self.assertEqual(topics[1], [])
        self.assertEqual(topics[2], 15)
        self.assertEqual(topics[3], 80)

    def test_do_help_witharg(self):
        plugin = self._makeOne()
        result = plugin.do_help('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), 'no help on foo\n')
        self.assertEqual(len(plugin.ctl.topics_printed), 0)

class TestDefaultControllerPlugin(unittest.TestCase):

    def _getTargetClass(self):
        from supervisor.supervisorctl import DefaultControllerPlugin
        return DefaultControllerPlugin

    def _makeOne(self, *arg, **kw):
        klass = self._getTargetClass()
        options = DummyClientOptions()
        ctl = DummyController(options)
        plugin = klass(ctl, *arg, **kw)
        return plugin

    def test_tail_toofewargs(self):
        plugin = self._makeOne()
        result = plugin.do_tail('')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: too few arguments')

    def test_tail_toomanyargs(self):
        plugin = self._makeOne()
        result = plugin.do_tail('one two three four')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: too many arguments')

    def test_tail_f_noprocname(self):
        plugin = self._makeOne()
        result = plugin.do_tail('-f')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: tail requires process name')

    def test_tail_bad_modifier(self):
        plugin = self._makeOne()
        result = plugin.do_tail('-z foo')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: bad argument -z')

    def test_tail_defaults(self):
        plugin = self._makeOne()
        result = plugin.do_tail('foo')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 12)
        self.assertEqual(lines[0], 'output line')

    def test_tail_no_file(self):
        plugin = self._makeOne()
        result = plugin.do_tail('NO_FILE')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 2)
        self.assertEqual(lines[0], 'NO_FILE: ERROR (no log file)')

    def test_tail_failed(self):
        plugin = self._makeOne()
        result = plugin.do_tail('FAILED')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 2)
        self.assertEqual(lines[0], 'FAILED: ERROR (unknown error reading log)')

    def test_tail_bad_name(self):
        plugin = self._makeOne()
        result = plugin.do_tail('BAD_NAME')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 2)
        self.assertEqual(lines[0], 'BAD_NAME: ERROR (no such process name)')

    def test_tail_bytesmodifier(self):
        plugin = self._makeOne()
        result = plugin.do_tail('-10 foo')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 3)
        self.assertEqual(lines[0], 'tput line')

    def test_tail_explicit_channel_stdout_nomodifier(self):
        plugin = self._makeOne()
        result = plugin.do_tail('foo stdout')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 12)
        self.assertEqual(lines[0], 'output line')

    def test_tail_explicit_channel_stderr_nomodifier(self):
        plugin = self._makeOne()
        result = plugin.do_tail('foo stderr')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 12)
        self.assertEqual(lines[0], 'output line')

    def test_tail_explicit_channel_unrecognized(self):
        plugin = self._makeOne()
        result = plugin.do_tail('foo fudge')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().strip()
        self.assertEqual(value, "Error: bad channel 'fudge'")

    def test_tail_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.readProcessStdoutLog = f
        plugin.do_tail('foo')
        self.assertEqual(called, [])

    def test_status_help(self):
        plugin = self._makeOne()
        plugin.help_status()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("status <name>" in out)

    def test_status_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.getAllProcessInfo = f
        plugin.do_status('')
        self.assertEqual(called, [])

    def test_status_table_process_column_min_width(self):
        plugin = self._makeOne()
        result = plugin.do_status('')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split("\n")
        self.assertEqual(lines[0].index("RUNNING"), 33)

    def test_status_table_process_column_expands(self):
        plugin = self._makeOne()
        options = plugin.ctl.options
        def f(*arg, **kw):
            from supervisor.states import ProcessStates
            return [{'name': 'foo'*50, # long name
                     'group':'foo',
                     'pid': 11,
                     'state': ProcessStates.RUNNING,
                     'statename': 'RUNNING',
                     'start': 0,
                     'stop': 0,
                     'spawnerr': '',
                     'now': 0,
                     'description':'foo description'},
                    {
                    'name': 'bar', # short name
                    'group': 'bar',
                    'pid': 12,
                    'state': ProcessStates.FATAL,
                    'statename': 'RUNNING',
                    'start': 0,
                    'stop': 0,
                    'spawnerr': '',
                    'now': 0,
                    'description': 'bar description',
                    }]
        options._server.supervisor.getAllProcessInfo = f
        self.assertEqual(plugin.do_status(''), None)
        lines = plugin.ctl.stdout.getvalue().split("\n")
        self.assertEqual(lines[0].index("RUNNING"), 157)
        self.assertEqual(lines[1].index("RUNNING"), 157)

    def test_status_all_processes_no_arg(self):
        plugin = self._makeOne()
        result = plugin.do_status('')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])
        self.assertEqual(value[1].split(None, 2),
                         ['bar', 'FATAL', 'bar description'])
        self.assertEqual(value[2].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_status_all_processes_all_arg(self):
        plugin = self._makeOne()
        result = plugin.do_status('all')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])
        self.assertEqual(value[1].split(None, 2),
                         ['bar', 'FATAL', 'bar description'])
        self.assertEqual(value[2].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_status_process_name(self):
        plugin = self._makeOne()
        result = plugin.do_status('foo')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().strip()
        self.assertEqual(value.split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])

    def test_status_group_name(self):
        plugin = self._makeOne()
        result = plugin.do_status('baz:*')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_status_mixed_names(self):
        plugin = self._makeOne()
        result = plugin.do_status('foo baz:*')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])
        self.assertEqual(value[1].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_status_bad_group_name(self):
        plugin = self._makeOne()
        result = plugin.do_status('badgroup:*')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0], "badgroup: ERROR (no such group)")

    def test_status_bad_process_name(self):
        plugin = self._makeOne()
        result = plugin.do_status('badprocess')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0], "badprocess: ERROR (no such process)")

    def test_status_bad_process_name_with_group(self):
        plugin = self._makeOne()
        result = plugin.do_status('badgroup:badprocess')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0], "badgroup:badprocess: "
                                   "ERROR (no such process)")

    def test_start_help(self):
        plugin = self._makeOne()
        plugin.help_start()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("start <name>" in out)

    def test_start_fail(self):
        plugin = self._makeOne()
        result = plugin.do_start('')
        self.assertEqual(result, None)
        expected = "Error: start requires a process name"
        self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0], expected)

    def test_start_badname(self):
        plugin = self._makeOne()
        result = plugin.do_start('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such process)\n')

    def test_start_no_file(self):
        plugin = self._makeOne()
        result = plugin.do_start('NO_FILE')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'NO_FILE: ERROR (no such file)\n')

    def test_start_not_executable(self):
        plugin = self._makeOne()
        result = plugin.do_start('NOT_EXECUTABLE')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'NOT_EXECUTABLE: ERROR (file is not executable)\n')

    def test_start_alreadystarted(self):
        plugin = self._makeOne()
        result = plugin.do_start('ALREADY_STARTED')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ALREADY_STARTED: ERROR (already started)\n')

    def test_start_spawnerror(self):
        plugin = self._makeOne()
        result = plugin.do_start('SPAWN_ERROR')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'SPAWN_ERROR: ERROR (spawn error)\n')

    def test_start_abnormaltermination(self):
        plugin = self._makeOne()
        result = plugin.do_start('ABNORMAL_TERMINATION')
        self.assertEqual(result, None)
        expected = 'ABNORMAL_TERMINATION: ERROR (abnormal termination)\n'
        self.assertEqual(plugin.ctl.stdout.getvalue(), expected)

    def test_start_one_success(self):
        plugin = self._makeOne()
        result = plugin.do_start('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: started\n')

    def test_start_one_with_group_name_success(self):
        plugin = self._makeOne()
        result = plugin.do_start('foo:foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: started\n')

    def test_start_many(self):
        plugin = self._makeOne()
        result = plugin.do_start('foo bar')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: started\nbar: started\n')

    def test_start_group(self):
        plugin = self._makeOne()
        result = plugin.do_start('foo:')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo:foo_00: started\n'
                         'foo:foo_01: started\n')

    def test_start_group_bad_name(self):
        plugin = self._makeOne()
        result = plugin.do_start('BAD_NAME:')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such group)\n')

    def test_start_all(self):
        plugin = self._makeOne()
        result = plugin.do_start('all')
        self.assertEqual(result, None)

        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: started\n'
                         'foo2: started\n'
                         'failed_group:failed: ERROR (spawn error)\n')

    def test_start_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.startAllProcesses = f
        supervisor.startProcessGroup = f
        plugin.do_start('foo')
        self.assertEqual(called, [])

    def test_stop_help(self):
        plugin = self._makeOne()
        plugin.help_stop()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("stop <name>" in out)

    def test_stop_fail(self):
        plugin = self._makeOne()
        result = plugin.do_stop('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
                         "Error: stop requires a process name")

    def test_stop_badname(self):
        plugin = self._makeOne()
        result = plugin.do_stop('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such process)\n')

    def test_stop_notrunning(self):
        plugin = self._makeOne()
        result = plugin.do_stop('NOT_RUNNING')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'NOT_RUNNING: ERROR (not running)\n')

    def test_stop_failed(self):
        plugin = self._makeOne()
        result = plugin.do_stop('FAILED')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), 'FAILED\n')

    def test_stop_one_success(self):
        plugin = self._makeOne()
        result = plugin.do_stop('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\n')

    def test_stop_one_with_group_name_success(self):
        plugin = self._makeOne()
        result = plugin.do_stop('foo:foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\n')

    def test_stop_many(self):
        plugin = self._makeOne()
        result = plugin.do_stop('foo bar')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\n'
                         'bar: stopped\n')

    def test_stop_group(self):
        plugin = self._makeOne()
        result = plugin.do_stop('foo:')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo:foo_00: stopped\n'
                         'foo:foo_01: stopped\n')

    def test_stop_group_bad_name(self):
        plugin = self._makeOne()
        result = plugin.do_stop('BAD_NAME:')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such group)\n')

    def test_stop_all(self):
        plugin = self._makeOne()
        result = plugin.do_stop('all')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\n'
                         'foo2: stopped\n'
                         'failed_group:failed: ERROR (no such process)\n')

    def test_stop_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.stopAllProcesses = f
        supervisor.stopProcessGroup = f
        plugin.do_stop('foo')
        self.assertEqual(called, [])

    def test_restart_help(self):
        plugin = self._makeOne()
        plugin.help_restart()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("restart <name>" in out)

    def test_restart_fail(self):
        plugin = self._makeOne()
        result = plugin.do_restart('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
                         'Error: restart requires a process name')

    def test_restart_one(self):
        plugin = self._makeOne()
        result = plugin.do_restart('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\nfoo: started\n')

    def test_restart_all(self):
        plugin = self._makeOne()
        result = plugin.do_restart('all')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: stopped\nfoo2: stopped\n'
                         'failed_group:failed: ERROR (no such process)\n'
                         'foo: started\nfoo2: started\n'
                         'failed_group:failed: ERROR (spawn error)\n')

    def test_restart_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.stopAllProcesses = f
        supervisor.stopProcessGroup = f
        plugin.do_restart('foo')
        self.assertEqual(called, [])

    def test_clear_help(self):
        plugin = self._makeOne()
        plugin.help_clear()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("clear <name>" in out)

    def test_clear_fail(self):
        plugin = self._makeOne()
        result = plugin.do_clear('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
                         "Error: clear requires a process name")

    def test_clear_badname(self):
        plugin = self._makeOne()
        result = plugin.do_clear('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'BAD_NAME: ERROR (no such process)\n')

    def test_clear_one_success(self):
        plugin = self._makeOne()
        result = plugin.do_clear('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: cleared\n')

    def test_clear_one_with_group_success(self):
        plugin = self._makeOne()
        result = plugin.do_clear('foo:foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: cleared\n')

    def test_clear_many(self):
        plugin = self._makeOne()
        result = plugin.do_clear('foo bar')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: cleared\nbar: cleared\n')

    def test_clear_all(self):
        plugin = self._makeOne()
        result = plugin.do_clear('all')
        self.assertEqual(result, None)

        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'foo: cleared\n'
                         'foo2: cleared\n'
                         'failed_group:failed: ERROR (failed)\n')

    def test_clear_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.clearAllProcessLogs = f
        supervisor.clearProcessLogs = f
        plugin.do_clear('foo')
        self.assertEqual(called, [])

    def test_open_help(self):
        plugin = self._makeOne()
        plugin.help_open()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("open <url>" in out)

    def test_open_fail(self):
        plugin = self._makeOne()
        result = plugin.do_open('badname')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: url must be http:// or unix://\n')

    def test_open_succeed(self):
        plugin = self._makeOne()
        result = plugin.do_open('http://localhost:9002')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(value[0].split(None, 2),
                         ['foo', 'RUNNING', 'foo description'])
        self.assertEqual(value[1].split(None, 2),
                         ['bar', 'FATAL', 'bar description'])
        self.assertEqual(value[2].split(None, 2),
                         ['baz:baz_01', 'STOPPED', 'baz description'])

    def test_version_help(self):
        plugin = self._makeOne()
        plugin.help_version()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Show the version of the remote supervisord" in out)

    def test_version(self):
        plugin = self._makeOne()
        plugin.do_version(None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), '3000\n')

    def test_version_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.getSupervisorVersion = f
        plugin.do_version('')
        self.assertEqual(called, [])

    def test_reload_help(self):
        plugin = self._makeOne()
        plugin.help_reload()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Restart the remote supervisord" in out)

    def test_reload_fail(self):
        plugin = self._makeOne()
        options = plugin.ctl.options
        options._server.supervisor._restartable = False
        result = plugin.do_reload('')
        self.assertEqual(result, None)
        self.assertEqual(options._server.supervisor._restarted, False)

    def test_reload(self):
        plugin = self._makeOne()
        options = plugin.ctl.options
        result = plugin.do_reload('')
        self.assertEqual(result, None)
        self.assertEqual(options._server.supervisor._restarted, True)

    def test_shutdown_help(self):
        plugin = self._makeOne()
        plugin.help_shutdown()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Shut the remote supervisord down" in out)

    def test_shutdown(self):
        plugin = self._makeOne()
        options = plugin.ctl.options
        result = plugin.do_shutdown('')
        self.assertEqual(result, None)
        self.assertEqual(options._server.supervisor._shutdown, True)

    def test_shutdown_catches_xmlrpc_fault_shutdown_state(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc

        def raise_fault(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'bye')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        result = plugin.do_shutdown('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: already shutting down\n')

    def test_shutdown_reraises_other_xmlrpc_faults(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc

        def raise_fault(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'ouch')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        self.assertRaises(xmlrpclib.Fault,
                          plugin.do_shutdown, '')

    def test_shutdown_catches_socket_error_ECONNREFUSED(self):
        plugin = self._makeOne()
        import supervisor.medusa.text_socket as socket
        import errno

        def raise_fault(*arg, **kw):
            raise socket.error(errno.ECONNREFUSED, 'nobody home')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        result = plugin.do_shutdown('')
        self.assertEqual(result, None)

        output = plugin.ctl.stdout.getvalue()
        self.assertTrue('refused connection (already shut down?)' in output)

    def test_shutdown_catches_socket_error_ENOENT(self):
        plugin = self._makeOne()
        import supervisor.medusa.text_socket as socket
        import errno

        def raise_fault(*arg, **kw):
            raise socket.error(errno.ENOENT, 'no file')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        result = plugin.do_shutdown('')
        self.assertEqual(result, None)

        output = plugin.ctl.stdout.getvalue()
        self.assertTrue('no such file (already shut down?)' in output)

    def test_shutdown_reraises_other_socket_errors(self):
        plugin = self._makeOne()
        import supervisor.medusa.text_socket as socket
        import errno

        def raise_fault(*arg, **kw):
            raise socket.error(errno.EPERM, 'denied')
        plugin.ctl.options._server.supervisor.shutdown = raise_fault

        self.assertRaises(socket.error,
                          plugin.do_shutdown, '')

    def test__formatChanges(self):
        plugin = self._makeOne()
        # Don't explode, plz
        plugin._formatChanges([['added'], ['changed'], ['removed']])
        plugin._formatChanges([[], [], []])

    def test_reread_help(self):
        plugin = self._makeOne()
        plugin.help_reread()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Reload the daemon's configuration files" in out)

    def test_reread(self):
        plugin = self._makeOne()
        calls = []
        plugin._formatChanges = lambda x: calls.append(x)
        result = plugin.do_reread(None)
        self.assertEqual(result, None)
        self.assertEqual(calls[0], [['added'], ['changed'], ['removed']])

    def test_reread_cant_reread(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc
        def reloadConfig(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'cant')
        plugin.ctl.options._server.supervisor.reloadConfig = reloadConfig
        plugin.do_reread(None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: cant\n')

    def test_reread_shutdown_state(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc
        def reloadConfig(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
        plugin.ctl.options._server.supervisor.reloadConfig = reloadConfig
        plugin.do_reread(None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: supervisor shutting down\n')

    def test_reread_reraises_other_faults(self):
        plugin = self._makeOne()
        from supervisor import xmlrpc
        def reloadConfig(*arg, **kw):
            raise xmlrpclib.Fault(xmlrpc.Faults.FAILED, '')
        plugin.ctl.options._server.supervisor.reloadConfig = reloadConfig
        self.assertRaises(xmlrpclib.Fault, plugin.do_reread, '')

    def test__formatConfigInfo(self):
        info = { 'group': 'group1',
                 'name': 'process1',
                 'inuse': True,
                 'autostart': True,
                 'process_prio': 999,
                 'group_prio': 999 }
        plugin = self._makeOne()
        result = plugin._formatConfigInfo(info)
        self.assertTrue('in use' in result)
        info = { 'group': 'group1',
                 'name': 'process1',
                 'inuse': False,
                 'autostart': False,
                 'process_prio': 999,
                 'group_prio': 999 }
        result = plugin._formatConfigInfo(info)
        self.assertTrue('avail' in result)

    def test_avail_help(self):
        plugin = self._makeOne()
        plugin.help_avail()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Display all configured" in out)

    def test_avail(self):
        calls = []
        plugin = self._makeOne()

        class FakeSupervisor(object):
            def getAllConfigInfo(self):
                return [{ 'group': 'group1', 'name': 'process1',
                          'inuse': False, 'autostart': False,
                          'process_prio': 999, 'group_prio': 999 }]

        plugin.ctl.get_supervisor = lambda : FakeSupervisor()
        plugin.ctl.output = calls.append
        result = plugin.do_avail('')
        self.assertEqual(result, None)

    def test_avail_shutdown_state(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def getAllConfigInfo():
            from supervisor import xmlrpc
            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
        supervisor.getAllConfigInfo = getAllConfigInfo

        result = plugin.do_avail('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: supervisor shutting down\n')

    def test_avail_reraises_other_faults(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def getAllConfigInfo():
            from supervisor import xmlrpc
            raise xmlrpclib.Fault(xmlrpc.Faults.FAILED, '')
        supervisor.getAllConfigInfo = getAllConfigInfo

        self.assertRaises(xmlrpclib.Fault, plugin.do_avail, '')

    def test_add_help(self):
        plugin = self._makeOne()
        plugin.help_add()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("add <name>" in out)

    def test_add(self):
        plugin = self._makeOne()
        result = plugin.do_add('foo')
        self.assertEqual(result, None)
        supervisor = plugin.ctl.options._server.supervisor
        self.assertEqual(supervisor.processes, ['foo'])

    def test_add_already_added(self):
        plugin = self._makeOne()
        result = plugin.do_add('ALREADY_ADDED')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: process group already active\n')

    def test_add_bad_name(self):
        plugin = self._makeOne()
        result = plugin.do_add('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: no such process/group: BAD_NAME\n')

    def test_add_shutdown_state(self):
        plugin = self._makeOne()
        result = plugin.do_add('SHUTDOWN_STATE')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: shutting down\n')

    def test_add_reraises_other_faults(self):
        plugin = self._makeOne()
        self.assertRaises(xmlrpclib.Fault, plugin.do_add, 'FAILED')

    def test_remove_help(self):
        plugin = self._makeOne()
        plugin.help_remove()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("remove <name>" in out)

    def test_remove(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.processes = ['foo']
        result = plugin.do_remove('foo')
        self.assertEqual(result, None)
        self.assertEqual(supervisor.processes, [])

    def test_remove_bad_name(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.processes = ['foo']
        result = plugin.do_remove('BAD_NAME')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: no such process/group: BAD_NAME\n')

    def test_remove_still_running(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor
        supervisor.processes = ['foo']
        result = plugin.do_remove('STILL_RUNNING')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'ERROR: process/group still running: STILL_RUNNING\n')

    def test_remove_reraises_other_faults(self):
        plugin = self._makeOne()
        self.assertRaises(xmlrpclib.Fault, plugin.do_remove, 'FAILED')

    def test_update_help(self):
        plugin = self._makeOne()
        plugin.help_update()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Reload config and add/remove" in out)

    def test_update_not_on_shutdown(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor
        def reloadConfig():
            from supervisor import xmlrpc
            raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'blah')
        supervisor.reloadConfig = reloadConfig
        supervisor.processes = ['removed']
        plugin.do_update('')
        self.assertEqual(supervisor.processes, ['removed'])

    def test_update_added_procs(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def reloadConfig():
            return [[['new_proc'], [], []]]
        supervisor.reloadConfig = reloadConfig

        result = plugin.do_update('')
        self.assertEqual(result, None)
        self.assertEqual(supervisor.processes, ['new_proc'])

    def test_update_with_gname(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def reloadConfig():
            return [[['added1', 'added2'], ['changed'], ['removed']]]
        supervisor.reloadConfig = reloadConfig
        supervisor.processes = ['changed', 'removed']

        plugin.do_update('changed')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'removed']))

        plugin.do_update('added1 added2')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'removed', 'added1', 'added2']))

        plugin.do_update('removed')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'added1', 'added2']))

        supervisor.processes = ['changed', 'removed']
        plugin.do_update('removed added1')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'added1']))

        supervisor.processes = ['changed', 'removed']
        plugin.do_update('all')
        self.assertEqual(sorted(supervisor.processes),
                         sorted(['changed', 'added1', 'added2']))


    def test_update_changed_procs(self):
        from supervisor import xmlrpc

        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        calls = []
        def reloadConfig():
            return [[[], ['changed_group'], []]]
        supervisor.reloadConfig = reloadConfig
        supervisor.startProcess = lambda x: calls.append(('start', x))

        supervisor.addProcessGroup('changed_group') # fake existence
        results = [{'name':        'changed_process',
                    'group':       'changed_group',
                    'status':      xmlrpc.Faults.SUCCESS,
                    'description': 'blah'}]
        def stopProcessGroup(name):
            calls.append(('stop', name))
            return results
        supervisor.stopProcessGroup = stopProcessGroup

        plugin.do_update('')
        self.assertEqual(calls, [('stop', 'changed_group')])

        supervisor.addProcessGroup('changed_group') # fake existence
        calls[:] = []
        results[:] = [{'name':        'changed_process1',
                       'group':       'changed_group',
                       'status':      xmlrpc.Faults.NOT_RUNNING,
                       'description': 'blah'},
                      {'name':        'changed_process2',
                       'group':       'changed_group',
                       'status':      xmlrpc.Faults.FAILED,
                       'description': 'blah'}]

        plugin.do_update('')
        self.assertEqual(calls, [('stop', 'changed_group')])

        supervisor.addProcessGroup('changed_group') # fake existence
        calls[:] = []
        results[:] = [{'name':        'changed_process1',
                       'group':       'changed_group',
                       'status':      xmlrpc.Faults.FAILED,
                       'description': 'blah'},
                      {'name':        'changed_process2',
                       'group':       'changed_group',
                       'status':      xmlrpc.Faults.SUCCESS,
                       'description': 'blah'}]

        plugin.do_update('')
        self.assertEqual(calls, [('stop', 'changed_group')])

    def test_update_removed_procs(self):
        from supervisor import xmlrpc

        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def reloadConfig():
            return [[[], [], ['removed_group']]]
        supervisor.reloadConfig = reloadConfig

        results = [{'name':        'removed_process',
                    'group':       'removed_group',
                    'status':      xmlrpc.Faults.SUCCESS,
                    'description': 'blah'}]
        supervisor.processes = ['removed_group']

        def stopProcessGroup(name):
            return results
        supervisor.stopProcessGroup = stopProcessGroup

        plugin.do_update('')
        self.assertEqual(supervisor.processes, [])

        results[:] = [{'name':        'removed_process',
                       'group':       'removed_group',
                       'status':      xmlrpc.Faults.NOT_RUNNING,
                       'description': 'blah'}]
        supervisor.processes = ['removed_group']

        plugin.do_update('')
        self.assertEqual(supervisor.processes, [])

        results[:] = [{'name':        'removed_process',
                       'group':       'removed_group',
                       'status':      xmlrpc.Faults.FAILED,
                       'description': 'blah'}]
        supervisor.processes = ['removed_group']

        plugin.do_update('')
        self.assertEqual(supervisor.processes, ['removed_group'])

    def test_update_reraises_other_faults(self):
        plugin = self._makeOne()
        supervisor = plugin.ctl.options._server.supervisor

        def reloadConfig():
            from supervisor import xmlrpc
            raise xmlrpclib.Fault(xmlrpc.Faults.FAILED, 'FAILED')
        supervisor.reloadConfig = reloadConfig

        self.assertRaises(xmlrpclib.Fault, plugin.do_update, '')

    def test_pid_help(self):
        plugin = self._makeOne()
        plugin.help_pid()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("pid <name>" in out)

    def test_pid_supervisord(self):
        plugin = self._makeOne()
        result = plugin.do_pid('')
        self.assertEqual(result, None)
        options = plugin.ctl.options
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(len(lines), 2)
        self.assertEqual(lines[0], str(options._server.supervisor.getPID()))

    def test_pid_allprocesses(self):
        plugin = self._makeOne()
        result = plugin.do_pid('all')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().strip()
        self.assertEqual(value.split(), ['11', '12', '13'])

    def test_pid_badname(self):
        plugin = self._makeOne()
        result = plugin.do_pid('BAD_NAME')
        self.assertEqual(result, None)
        value = plugin.ctl.stdout.getvalue().strip()
        self.assertEqual(value, 'No such process BAD_NAME')

    def test_pid_oneprocess(self):
        plugin = self._makeOne()
        result = plugin.do_pid('foo')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue().strip(), '11')

    def test_pid_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.getPID = f
        plugin.do_pid('')
        self.assertEqual(called, [])

    def test_maintail_help(self):
        plugin = self._makeOne()
        plugin.help_maintail()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("tail of supervisor main log file" in out)

    def test_maintail_toomanyargs(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('foo bar')
        self.assertEqual(result, None)
        val = plugin.ctl.stdout.getvalue()
        self.assertTrue(val.startswith('Error: too many'), val)

    def test_maintail_minus_string_fails(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('-wrong')
        self.assertEqual(result, None)
        val = plugin.ctl.stdout.getvalue()
        self.assertTrue(val.startswith('Error: bad argument -wrong'), val)

    def test_maintail_wrong(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('wrong')
        self.assertEqual(result, None)
        val = plugin.ctl.stdout.getvalue()
        self.assertTrue(val.startswith('Error: bad argument wrong'), val)

    def test_maintail_dashf(self):
        plugin = self._makeOne()
        plugin.listener = DummyListener()
        result = plugin.do_maintail('-f')
        self.assertEqual(result, None)
        errors = plugin.listener.errors
        self.assertEqual(len(errors), 1)
        error = errors[0]
        self.assertEqual(plugin.listener.closed,
                         'http://localhost:65532/mainlogtail')
        self.assertEqual(error[0],
                         'http://localhost:65532/mainlogtail')
        self.assertTrue('Cannot connect' in error[1])

    def test_maintail_bad_modifier(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('-z')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: bad argument -z')

    def test_maintail_nobytes(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')

    def test_maintail_dashbytes(self):
        plugin = self._makeOne()
        result = plugin.do_maintail('-100')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')

    def test_maintail_readlog_error_nofile(self):
        plugin = self._makeOne()
        supervisor_rpc = plugin.ctl.get_supervisor()
        from supervisor import xmlrpc
        supervisor_rpc._readlog_error = xmlrpc.Faults.NO_FILE
        result = plugin.do_maintail('-100')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'supervisord: ERROR (no log file)\n')

    def test_maintail_readlog_error_failed(self):
        plugin = self._makeOne()
        supervisor_rpc = plugin.ctl.get_supervisor()
        from supervisor import xmlrpc
        supervisor_rpc._readlog_error = xmlrpc.Faults.FAILED
        result = plugin.do_maintail('-100')
        self.assertEqual(result, None)
        self.assertEqual(plugin.ctl.stdout.getvalue(),
                         'supervisord: ERROR (unknown error reading log)\n')

    def test_maintail_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.readLog = f
        plugin.do_maintail('')
        self.assertEqual(called, [])

    def test_fg_help(self):
        plugin = self._makeOne()
        plugin.help_fg()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("fg <process>" in out)

    def test_fg_too_few_args(self):
        plugin = self._makeOne()
        result = plugin.do_fg('')
        self.assertEqual(result, None)
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(lines[0], 'Error: no process name supplied')

    def test_fg_too_many_args(self):
        plugin = self._makeOne()
        result = plugin.do_fg('foo bar')
        self.assertEqual(result, None)
        line = plugin.ctl.stdout.getvalue()
        self.assertEqual(line, 'Error: too many process names supplied\n')

    def test_fg_badprocname(self):
        plugin = self._makeOne()
        result = plugin.do_fg('BAD_NAME')
        self.assertEqual(result, None)
        line = plugin.ctl.stdout.getvalue()
        self.assertEqual(line, 'Error: bad process name supplied\n')

    def test_fg_procnotrunning(self):
        plugin = self._makeOne()
        result = plugin.do_fg('bar')
        self.assertEqual(result, None)
        line = plugin.ctl.stdout.getvalue()
        self.assertEqual(line, 'Error: process not running\n')
        result = plugin.do_fg('baz_01')
        lines = plugin.ctl.stdout.getvalue().split('\n')
        self.assertEqual(result, None)
        self.assertEqual(lines[-2], 'Error: process not running')

    def test_fg_upcheck_failed(self):
        plugin = self._makeOne()
        plugin.ctl.upcheck = lambda: False
        called = []
        def f(*arg, **kw):
            called.append(True)
        plugin.ctl.options._server.supervisor.getProcessInfo = f
        plugin.do_fg('foo')
        self.assertEqual(called, [])

    def test_exit_help(self):
        plugin = self._makeOne()
        plugin.help_exit()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Exit the supervisor shell" in out)

    def test_quit_help(self):
        plugin = self._makeOne()
        plugin.help_quit()
        out = plugin.ctl.stdout.getvalue()
        self.assertTrue("Exit the supervisor shell" in out)

class DummyListener:
    def __init__(self):
        self.errors = []
    def error(self, url, msg):
        self.errors.append((url, msg))
    def close(self, url):
        self.closed = url

class DummyPluginFactory:
    def __init__(self, ctl, **kw):
        self.ctl = ctl

    def do_help(self, arg):
        self.ctl.stdout.write('foo helped')

class DummyClientOptions:
    def __init__(self):
        self.prompt = 'supervisor'
        self.serverurl = 'http://localhost:65532'
        self.username = 'chrism'
        self.password = '123'
        self.history_file = None
        self.plugins = ()
        self._server = DummyRPCServer()
        self.interactive = False
        self.plugin_factories = [('dummy', DummyPluginFactory, {})]

    def getServerProxy(self):
        return self._server

class DummyController:
    nohelp = 'no help on %s'
    def __init__(self, options):
        self.options = options
        self.topics_printed = []
        self.stdout = StringIO()

    def upcheck(self):
        return True

    def get_supervisor(self):
        return self.get_server_proxy('supervisor')

    def get_server_proxy(self, namespace=None):
        proxy = self.options.getServerProxy()
        if namespace is None:
            return proxy
        else:
            return getattr(proxy, namespace)

    def output(self, data):
        self.stdout.write(data + '\n')

    def print_topics(self, doc_headers, cmds_doc, rows, cols):
        self.topics_printed.append((doc_headers, cmds_doc, rows, cols))

class DummyPlugin:
    def __init__(self, controller=None):
        self.ctl = controller

    def do_help(self, arg):
        self.helped = True

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_supervisord
import unittest
import time
import signal
import sys
import os
import tempfile
import shutil

from supervisor.tests.base import DummyOptions
from supervisor.tests.base import DummyPConfig
from supervisor.tests.base import DummyPGroupConfig
from supervisor.tests.base import DummyProcess
from supervisor.tests.base import DummyProcessGroup
from supervisor.tests.base import DummyDispatcher

try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO

class EntryPointTests(unittest.TestCase):
    def test_main_noprofile(self):
        from supervisor.supervisord import main
        conf = os.path.join(
            os.path.abspath(os.path.dirname(__file__)), 'fixtures',
            'donothing.conf')
        new_stdout = StringIO()
        old_stdout = sys.stdout
        try:
            tempdir = tempfile.mkdtemp()
            log = os.path.join(tempdir, 'log')
            pid = os.path.join(tempdir, 'pid')
            sys.stdout = new_stdout
            main(args=['-c', conf, '-l', log, '-j', pid, '-n'],
                 test=True)
        finally:
            sys.stdout = old_stdout
            shutil.rmtree(tempdir)
        output = new_stdout.getvalue()
        self.assertTrue(output.find('supervisord started') != 1, output)

    if sys.version_info[:2] >= (2, 4):
        def test_main_profile(self):
            from supervisor.supervisord import main
            conf = os.path.join(
                os.path.abspath(os.path.dirname(__file__)), 'fixtures',
                'donothing.conf')
            new_stdout = StringIO()
            old_stdout = sys.stdout
            try:
                tempdir = tempfile.mkdtemp()
                log = os.path.join(tempdir, 'log')
                pid = os.path.join(tempdir, 'pid')
                sys.stdout = new_stdout
                main(args=['-c', conf, '-l', log, '-j', pid, '-n',
                           '--profile_options=cumulative,calls'], test=True)
            finally:
                sys.stdout = old_stdout
                shutil.rmtree(tempdir)
            output = new_stdout.getvalue()
            self.assertTrue(output.find('cumulative time, call count') != -1,
                            output)

class SupervisordTests(unittest.TestCase):
    def tearDown(self):
        from supervisor.events import clear
        clear()

    def _getTargetClass(self):
        from supervisor.supervisord import Supervisor
        return Supervisor

    def _makeOne(self, options):
        return self._getTargetClass()(options)

    def test_main_first(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfigs = [DummyPGroupConfig(options,'foo', pconfigs=[pconfig])]
        options.process_group_configs = gconfigs
        options.test = True
        options.first = True
        supervisord = self._makeOne(options)
        supervisord.main()
        self.assertEqual(options.environment_processed, True)
        self.assertEqual(options.fds_cleaned_up, False)
        self.assertEqual(options.rlimits_set, True)
        self.assertEqual(options.make_logger_messages,
                         (['setuid_called'], [], ['rlimits_set']))
        self.assertEqual(options.autochildlogdir_cleared, True)
        self.assertEqual(len(supervisord.process_groups), 1)
        self.assertEqual(supervisord.process_groups['foo'].config.options,
                         options)
        self.assertEqual(options.environment_processed, True)
        self.assertEqual(options.httpservers_opened, True)
        self.assertEqual(options.signals_set, True)
        self.assertEqual(options.daemonized, True)
        self.assertEqual(options.pidfile_written, True)
        self.assertEqual(options.cleaned_up, True)

    def test_main_notfirst(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfigs = [DummyPGroupConfig(options,'foo', pconfigs=[pconfig])]
        options.process_group_configs = gconfigs
        options.test = True
        options.first = False
        supervisord = self._makeOne(options)
        supervisord.main()
        self.assertEqual(options.environment_processed, True)
        self.assertEqual(options.fds_cleaned_up, True)
        self.assertFalse(hasattr(options, 'rlimits_set'))
        self.assertEqual(options.make_logger_messages,
                         (['setuid_called'], [], []))
        self.assertEqual(options.autochildlogdir_cleared, True)
        self.assertEqual(len(supervisord.process_groups), 1)
        self.assertEqual(supervisord.process_groups['foo'].config.options,
                         options)
        self.assertEqual(options.environment_processed, True)
        self.assertEqual(options.httpservers_opened, True)
        self.assertEqual(options.signals_set, True)
        self.assertEqual(options.daemonized, False)
        self.assertEqual(options.pidfile_written, True)
        self.assertEqual(options.cleaned_up, True)

    def test_reap(self):
        options = DummyOptions()
        options.waitpid_return = 1, 1
        pconfig = DummyPConfig(options, 'process', 'process', '/bin/process1')
        process = DummyProcess(pconfig)
        process.drained = False
        process.killing = 1
        process.laststop = None
        process.waitstatus = None, None
        options.pidhistory = {1:process}
        supervisord = self._makeOne(options)

        supervisord.reap(once=True)
        self.assertEqual(process.finished, (1,1))

    def test_reap_unknown_pid(self):
        options = DummyOptions()
        options.waitpid_return = 2, 0 # pid, status
        pconfig = DummyPConfig(options, 'process', 'process', '/bin/process1')
        process = DummyProcess(pconfig)
        process.drained = False
        process.killing = True
        process.laststop = None
        process.waitstatus = None, None
        options.pidhistory = {1: process}
        supervisord = self._makeOne(options)

        supervisord.reap(once=True)
        self.assertEqual(process.finished, None)
        self.assertEqual(options.logger.data[0],
                         'reaped unknown pid 2')

    def test_handle_sigterm(self):
        options = DummyOptions()
        options._signal = signal.SIGTERM
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, -1)
        self.assertEqual(options.logger.data[0],
                         'received SIGTERM indicating exit request')

    def test_handle_sigint(self):
        options = DummyOptions()
        options._signal = signal.SIGINT
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, -1)
        self.assertEqual(options.logger.data[0],
                         'received SIGINT indicating exit request')

    def test_handle_sigquit(self):
        options = DummyOptions()
        options._signal = signal.SIGQUIT
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, -1)
        self.assertEqual(options.logger.data[0],
                         'received SIGQUIT indicating exit request')

    def test_handle_sighup(self):
        options = DummyOptions()
        options._signal = signal.SIGHUP
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, 0)
        self.assertEqual(options.logger.data[0],
                         'received SIGHUP indicating restart request')

    def test_handle_sigchld(self):
        options = DummyOptions()
        options._signal = signal.SIGCHLD
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, 1)
        # supervisor.options.signame(signal.SIGCHLD) may return "SIGCLD"
        # on linux or other systems where SIGCHLD = SIGCLD.
        msgs = ('received SIGCHLD indicating a child quit',
                'received SIGCLD indicating a child quit')
        self.assertTrue(options.logger.data[0] in msgs)

    def test_handle_sigusr2(self):
        options = DummyOptions()
        options._signal = signal.SIGUSR2
        pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
        from supervisor.process import ProcessStates
        process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
        process1.delay = time.time() - 1
        supervisord = self._makeOne(options)
        pconfigs = [DummyPConfig(options, 'foo', 'foo', '/bin/foo')]
        options.process_group_configs = DummyPGroupConfig(
            options, 'foo',
            pconfigs=pconfigs)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, 1)
        self.assertEqual(options.logs_reopened, True)
        self.assertEqual(options.logger.data[0],
                         'received SIGUSR2 indicating log reopen request')

    def test_handle_unknown_signal(self):
        options = DummyOptions()
        options._signal = signal.SIGUSR1
        supervisord = self._makeOne(options)
        supervisord.handle_signal()
        self.assertEqual(supervisord.options.mood, 1)
        self.assertEqual(options.logger.data[0],
                         'received SIGUSR1 indicating nothing')

    def test_diff_add_remove(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)

        pconfig = DummyPConfig(options, 'process1', 'process1')
        group1 = DummyPGroupConfig(options, 'group1', pconfigs=[pconfig])

        pconfig = DummyPConfig(options, 'process2', 'process2')
        group2 = DummyPGroupConfig(options, 'group2', pconfigs=[pconfig])

        new = [group1, group2]

        added, changed, removed = supervisord.diff_to_active()
        self.assertEqual(added, [])
        self.assertEqual(changed, [])
        self.assertEqual(removed, [])

        added, changed, removed = supervisord.diff_to_active(new)
        self.assertEqual(added, new)
        self.assertEqual(changed, [])
        self.assertEqual(removed, [])

        supervisord.options.process_group_configs = new
        added, changed, removed = supervisord.diff_to_active()
        self.assertEqual(added, new)

        supervisord.add_process_group(group1)
        supervisord.add_process_group(group2)

        pconfig = DummyPConfig(options, 'process3', 'process3')
        new_group1 = DummyPGroupConfig(options, pconfigs=[pconfig])

        pconfig = DummyPConfig(options, 'process4', 'process4')
        new_group2 = DummyPGroupConfig(options, pconfigs=[pconfig])

        new = [group2, new_group1, new_group2]

        added, changed, removed = supervisord.diff_to_active(new)
        self.assertEqual(added, [new_group1, new_group2])
        self.assertEqual(changed, [])
        self.assertEqual(removed, [group1])

    def test_diff_changed(self):
        from supervisor.options import ProcessConfig, ProcessGroupConfig

        options = DummyOptions()
        supervisord = self._makeOne(options)

        def make_pconfig(name, command, **params):
            result = {
                'name': name, 'command': command,
                'directory': None, 'umask': None, 'priority': 999, 'autostart': True,
                'autorestart': True, 'startsecs': 10, 'startretries': 999,
                'uid': None, 'stdout_logfile': None, 'stdout_capture_maxbytes': 0,
                'stdout_events_enabled': False,
                'stdout_logfile_backups': 0, 'stdout_logfile_maxbytes': 0,
                'stdout_syslog': False,
                'stderr_logfile': None, 'stderr_capture_maxbytes': 0,
                'stderr_events_enabled': False,
                'stderr_logfile_backups': 0, 'stderr_logfile_maxbytes': 0,
                'stderr_syslog': False,
                'redirect_stderr': False,
                'stopsignal': None, 'stopwaitsecs': 10,
                'stopasgroup': False,
                'killasgroup': False,
                'exitcodes': (0,2), 'environment': None, 'serverurl': None,
            }
            result.update(params)
            return ProcessConfig(options, **result)

        def make_gconfig(name, pconfigs):
            return ProcessGroupConfig(options, name, 25, pconfigs)

        pconfig = make_pconfig('process1', 'process1', uid='new')
        group1 = make_gconfig('group1', [pconfig])

        pconfig = make_pconfig('process2', 'process2')
        group2 = make_gconfig('group2', [pconfig])
        new = [group1, group2]

        pconfig = make_pconfig('process1', 'process1', uid='old')
        group3 = make_gconfig('group1', [pconfig])

        pconfig = make_pconfig('process2', 'process2')
        group4 = make_gconfig('group2', [pconfig])
        supervisord.add_process_group(group3)
        supervisord.add_process_group(group4)

        added, changed, removed = supervisord.diff_to_active(new)

        self.assertEqual([added, removed], [[], []])
        self.assertEqual(changed, [group1])

        options = DummyOptions()
        supervisord = self._makeOne(options)

        pconfig1 = make_pconfig('process1', 'process1')
        pconfig2 = make_pconfig('process2', 'process2')
        group1 = make_gconfig('group1', [pconfig1, pconfig2])
        new = [group1]

        supervisord.add_process_group(make_gconfig('group1', [pconfig1]))

        added, changed, removed = supervisord.diff_to_active(new)
        self.assertEqual([added, removed], [[], []])
        self.assertEqual(changed, [group1])

    def test_add_process_group(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
        options.process_group_configs = [gconfig]
        supervisord = self._makeOne(options)

        self.assertEqual(supervisord.process_groups, {})

        result = supervisord.add_process_group(gconfig)
        self.assertEqual(list(supervisord.process_groups.keys()), ['foo'])
        self.assertTrue(result)

        group = supervisord.process_groups['foo']
        result = supervisord.add_process_group(gconfig)
        self.assertEqual(group, supervisord.process_groups['foo'])
        self.assertTrue(not result)

    def test_add_process_group_event(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        events.subscribe(events.ProcessGroupAddedEvent, callback)
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
        options.process_group_configs = [gconfig]
        supervisord = self._makeOne(options)

        supervisord.add_process_group(gconfig)

        options.test = True
        supervisord.runforever()
        self.assertEqual(L, [1])

    def test_remove_process_group(self):
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfig = DummyPGroupConfig(options, 'foo', pconfigs=[pconfig])
        supervisord = self._makeOne(options)

        self.assertRaises(KeyError, supervisord.remove_process_group, 'asdf')

        supervisord.add_process_group(gconfig)
        result = supervisord.remove_process_group('foo')
        self.assertEqual(supervisord.process_groups, {})
        self.assertTrue(result)

        supervisord.add_process_group(gconfig)
        supervisord.process_groups['foo'].unstopped_processes = [DummyProcess(None)]
        result = supervisord.remove_process_group('foo')
        self.assertEqual(list(supervisord.process_groups.keys()), ['foo'])
        self.assertTrue(not result)

    def test_remove_process_group_event(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        events.subscribe(events.ProcessGroupRemovedEvent, callback)
        options = DummyOptions()
        pconfig = DummyPConfig(options, 'foo', 'foo', '/bin/foo')
        gconfig = DummyPGroupConfig(options,'foo', pconfigs=[pconfig])
        options.process_group_configs = [gconfig]
        supervisord = self._makeOne(options)

        supervisord.add_process_group(gconfig)
        supervisord.process_groups['foo'].stopped_processes = [DummyProcess(None)]
        supervisord.remove_process_group('foo')
        options.test = True
        supervisord.runforever()

        self.assertEqual(L, [1])

    def test_runforever_emits_generic_startup_event(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(1)
        events.subscribe(events.SupervisorStateChangeEvent, callback)
        options = DummyOptions()
        supervisord = self._makeOne(options)
        options.test = True
        supervisord.runforever()
        self.assertEqual(L, [1])

    def test_runforever_emits_generic_specific_event(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(2)
        events.subscribe(events.SupervisorRunningEvent, callback)
        options = DummyOptions()
        options.test = True
        supervisord = self._makeOne(options)
        supervisord.runforever()
        self.assertEqual(L, [2])

    def test_runforever_calls_tick(self):
        options = DummyOptions()
        options.test = True
        supervisord = self._makeOne(options)
        self.assertEqual(len(supervisord.ticks), 0)
        supervisord.runforever()
        self.assertEqual(len(supervisord.ticks), 3)

    def test_runforever_select_eintr(self):
        options = DummyOptions()
        import errno
        options.select_error = errno.EINTR
        supervisord = self._makeOne(options)
        options.test = True
        supervisord.runforever()
        self.assertEqual(options.logger.data[0], 'EINTR encountered in select')

    def test_runforever_select_uncaught_exception(self):
        options = DummyOptions()
        import errno
        options.select_error = errno.EBADF
        supervisord = self._makeOne(options)
        import select
        options.test = True
        self.assertRaises(select.error, supervisord.runforever)

    def test_runforever_select_dispatchers(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
        pgroup = DummyProcessGroup(gconfig)
        readable = DummyDispatcher(readable=True)
        writable = DummyDispatcher(writable=True)
        error = DummyDispatcher(writable=True, error=OSError)
        pgroup.dispatchers = {6:readable, 7:writable, 8:error}
        supervisord.process_groups = {'foo': pgroup}
        options.select_result = [6], [7, 8], []
        options.test = True
        supervisord.runforever()
        self.assertEqual(pgroup.transitioned, True)
        self.assertEqual(readable.read_event_handled, True)
        self.assertEqual(writable.write_event_handled, True)
        self.assertEqual(error.error_handled, True)

    def test_runforever_select_dispatcher_exitnow(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
        pgroup = DummyProcessGroup(gconfig)
        from supervisor.medusa import asyncore_25 as asyncore
        exitnow = DummyDispatcher(readable=True, error=asyncore.ExitNow)
        pgroup.dispatchers = {6:exitnow}
        supervisord.process_groups = {'foo': pgroup}
        options.select_result = [6], [], []
        options.test = True
        self.assertRaises(asyncore.ExitNow, supervisord.runforever)

    def test_runforever_stopping_emits_events(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        gconfig = DummyPGroupConfig(options)
        pgroup = DummyProcessGroup(gconfig)
        supervisord.process_groups = {'foo': pgroup}
        supervisord.options.mood = -1
        L = []
        def callback(event):
            L.append(event)
        from supervisor import events
        events.subscribe(events.SupervisorStateChangeEvent, callback)
        from supervisor.medusa import asyncore_25 as asyncore
        options.test = True
        self.assertRaises(asyncore.ExitNow, supervisord.runforever)
        self.assertTrue(pgroup.all_stopped)
        self.assertTrue(isinstance(L[0], events.SupervisorRunningEvent))
        self.assertTrue(isinstance(L[0], events.SupervisorStateChangeEvent))
        self.assertTrue(isinstance(L[1], events.SupervisorStoppingEvent))
        self.assertTrue(isinstance(L[1], events.SupervisorStateChangeEvent))

    def test_exit(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
        pgroup = DummyProcessGroup(gconfig)
        L = []
        def callback():
            L.append(1)
        supervisord.process_groups = {'foo': pgroup}
        supervisord.options.mood = 0
        supervisord.options.test = True
        from supervisor.medusa import asyncore_25 as asyncore
        self.assertRaises(asyncore.ExitNow, supervisord.runforever)
        self.assertEqual(pgroup.all_stopped, True)

    def test_exit_delayed(self):
        options = DummyOptions()
        supervisord = self._makeOne(options)
        pconfig = DummyPConfig(options, 'foo', '/bin/foo',)
        process = DummyProcess(pconfig)
        gconfig = DummyPGroupConfig(options, pconfigs=[pconfig])
        pgroup = DummyProcessGroup(gconfig)
        pgroup.unstopped_processes = [process]
        L = []
        def callback():
            L.append(1)
        supervisord.process_groups = {'foo': pgroup}
        supervisord.options.mood = 0
        supervisord.options.test = True
        supervisord.runforever()
        self.assertNotEqual(supervisord.lastshutdownreport, 0)

    def test_getSupervisorStateDescription(self):
        from supervisor.states import getSupervisorStateDescription
        from supervisor.states import SupervisorStates
        result = getSupervisorStateDescription(SupervisorStates.RUNNING)
        self.assertEqual(result, 'RUNNING')

    def test_tick(self):
        from supervisor import events
        L = []
        def callback(event):
            L.append(event)
        events.subscribe(events.TickEvent, callback)
        options = DummyOptions()
        supervisord = self._makeOne(options)

        supervisord.tick(now=0)
        self.assertEqual(supervisord.ticks[5], 0)
        self.assertEqual(supervisord.ticks[60], 0)
        self.assertEqual(supervisord.ticks[3600], 0)
        self.assertEqual(len(L), 0)

        supervisord.tick(now=6)
        self.assertEqual(supervisord.ticks[5], 5)
        self.assertEqual(supervisord.ticks[60], 0)
        self.assertEqual(supervisord.ticks[3600], 0)
        self.assertEqual(len(L), 1)
        self.assertEqual(L[-1].__class__, events.Tick5Event)

        supervisord.tick(now=61)
        self.assertEqual(supervisord.ticks[5], 60)
        self.assertEqual(supervisord.ticks[60], 60)
        self.assertEqual(supervisord.ticks[3600], 0)
        self.assertEqual(len(L), 3)
        self.assertEqual(L[-1].__class__, events.Tick60Event)

        supervisord.tick(now=3601)
        self.assertEqual(supervisord.ticks[5], 3600)
        self.assertEqual(supervisord.ticks[60], 3600)
        self.assertEqual(supervisord.ticks[3600], 3600)
        self.assertEqual(len(L), 6)
        self.assertEqual(L[-1].__class__, events.Tick3600Event)

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = test_web
import sys
import unittest

from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import DummyRequest

class DeferredWebProducerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.web import DeferredWebProducer
        return DeferredWebProducer

    def _makeOne(self, request, callback):
        producer = self._getTargetClass()(request, callback)
        return producer

    def test_ctor(self):
        request = DummyRequest('/index.html', [], '', '')
        callback = lambda *x: None
        callback.delay = 1
        producer = self._makeOne(request, callback)
        self.assertEqual(producer.callback, callback)
        self.assertEqual(producer.request, request)
        self.assertEqual(producer.finished, False)
        self.assertEqual(producer.delay, 1)

    def test_more_not_done_yet(self):
        request = DummyRequest('/index.html', [], '', '')
        from supervisor.http import NOT_DONE_YET
        callback = lambda *x: NOT_DONE_YET
        callback.delay = 1
        producer = self._makeOne(request, callback)
        self.assertEqual(producer.more(), NOT_DONE_YET)

    def test_more_exception_caught(self):
        request = DummyRequest('/index.html', [], '', '')
        def callback(*arg):
            raise ValueError('foo')
        callback.delay = 1
        producer = self._makeOne(request, callback)
        self.assertEqual(producer.more(), None)
        logdata = request.channel.server.logger.logged
        self.assertEqual(len(logdata), 1)
        logged = logdata[0]
        self.assertEqual(logged[0], 'Web interface error')
        self.assertTrue(logged[1].startswith('Traceback'), logged[1])
        self.assertEqual(producer.finished, True)
        self.assertEqual(request._error, 500)

    def test_sendresponse_redirect(self):
        request = DummyRequest('/index.html', [], '', '')
        callback = lambda *arg: None
        callback.delay = 1
        producer = self._makeOne(request, callback)
        response = {'headers': {'Location':'abc'}}
        result = producer.sendresponse(response)
        self.assertEqual(result, None)
        self.assertEqual(request._error, 301)
        self.assertEqual(request.headers['Content-Type'], 'text/plain')
        self.assertEqual(request.headers['Content-Length'], 0)

    def test_sendresponse_withbody_and_content_type(self):
        request = DummyRequest('/index.html', [], '', '')
        callback = lambda *arg: None
        callback.delay = 1
        producer = self._makeOne(request, callback)
        response = {'body': 'abc', 'headers':{'Content-Type':'text/html'}}
        result = producer.sendresponse(response)
        self.assertEqual(result, None)
        self.assertEqual(request.headers['Content-Type'], 'text/html')
        self.assertEqual(request.headers['Content-Length'], 3)
        self.assertEqual(request.producers[0], 'abc')

class UIHandlerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.web import supervisor_ui_handler
        return supervisor_ui_handler

    def _makeOne(self):
        supervisord = DummySupervisor()
        handler = self._getTargetClass()(supervisord)
        return handler

    def test_handle_request_no_view_method(self):
        request = DummyRequest('/foo.css', [], '', '', {'PATH_INFO':'/foo.css'})
        handler = self._makeOne()
        data = handler.handle_request(request)
        self.assertEqual(data, None)

    def test_handle_request_default(self):
        request = DummyRequest('/index.html', [], '', '',
                               {'PATH_INFO':'/index.html'})
        handler = self._makeOne()
        data = handler.handle_request(request)
        self.assertEqual(data, None)
        self.assertEqual(request.channel.producer.request, request)
        from supervisor.web import StatusView
        self.assertEqual(request.channel.producer.callback.__class__,StatusView)

    def test_handle_request_index_html(self):
        request = DummyRequest('/index.html', [], '', '',
                               {'PATH_INFO':'/index.html'})
        handler = self._makeOne()
        handler.handle_request(request)
        from supervisor.web import StatusView
        view = request.channel.producer.callback
        self.assertEqual(view.__class__, StatusView)
        self.assertEqual(view.context.template, 'ui/status.html')

    def test_handle_request_tail_html(self):
        request = DummyRequest('/tail.html', [], '', '',
                               {'PATH_INFO':'/tail.html'})
        handler = self._makeOne()
        handler.handle_request(request)
        from supervisor.web import TailView
        view = request.channel.producer.callback
        self.assertEqual(view.__class__, TailView)
        self.assertEqual(view.context.template, 'ui/tail.html')

    def test_handle_request_ok_html(self):
        request = DummyRequest('/tail.html', [], '', '',
                               {'PATH_INFO':'/ok.html'})
        handler = self._makeOne()
        handler.handle_request(request)
        from supervisor.web import OKView
        view = request.channel.producer.callback
        self.assertEqual(view.__class__, OKView)
        self.assertEqual(view.context.template, None)


class StatusViewTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.web import StatusView
        return StatusView

    def _makeOne(self, context):
        klass = self._getTargetClass()
        return klass(context)

    def test_make_callback_noaction(self):
        context = DummyContext()
        context.supervisord = DummySupervisor()
        context.template = 'ui/status.html'
        context.form = {}
        view = self._makeOne(context)
        self.assertRaises(ValueError, view.make_callback, 'process', None)

    def test_render_noaction(self):
        context = DummyContext()
        context.supervisord = DummySupervisor()
        context.template = 'ui/status.html'
        context.request = DummyRequest('/foo', [], '', '')
        context.form = {}
        context.response = {}
        view = self._makeOne(context)
        data = view.render()
        self.assertTrue(data.startswith('<!DOCTYPE html PUBLIC'), data)

    def test_render_refresh(self):
        context = DummyContext()
        context.supervisord = DummySupervisor()
        context.template = 'ui/status.html'
        context.response = {}
        context.form = {'action':'refresh'}
        view = self._makeOne(context)
        data = view.render()
        from supervisor.http import NOT_DONE_YET
        self.assertTrue(data is NOT_DONE_YET, data)

class DummyContext:
    pass

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

########NEW FILE########
__FILENAME__ = test_xmlrpc
import sys
import unittest

from supervisor.tests.base import DummySupervisor
from supervisor.tests.base import DummyRequest
from supervisor.tests.base import DummySupervisorRPCNamespace

try:
    import xmlrpclib
    import httplib
except ImportError:
    import xmlrpc.client as xmlrpclib
    import http.client as httplib

class GetFaultDescriptionTests(unittest.TestCase):
    def test_returns_description_for_known_fault(self):
        from supervisor import xmlrpc
        desc = xmlrpc.getFaultDescription(xmlrpc.Faults.SHUTDOWN_STATE)
        self.assertEqual(desc, 'SHUTDOWN_STATE')

    def test_returns_unknown_for_unknown_fault(self):
        from supervisor import xmlrpc
        desc = xmlrpc.getFaultDescription(999999)
        self.assertEqual(desc, 'UNKNOWN')

class RPCErrorTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.xmlrpc import RPCError
        return RPCError

    def _makeOne(self, code, extra=None):
        return self._getTargetClass()(code, extra)

    def test_sets_text_with_fault_name_only(self):
        from supervisor import xmlrpc
        e = self._makeOne(xmlrpc.Faults.FAILED)
        self.assertEqual(e.text, 'FAILED')

    def test_sets_text_with_fault_name_and_extra(self):
        from supervisor import xmlrpc
        e = self._makeOne(xmlrpc.Faults.FAILED, 'oops')
        self.assertEqual(e.text, 'FAILED: oops')

class XMLRPCMarshallingTests(unittest.TestCase):
    def test_xmlrpc_marshal(self):
        from supervisor import xmlrpc
        data = xmlrpc.xmlrpc_marshal(1)
        self.assertEqual(data, xmlrpclib.dumps((1,), methodresponse=True))
        fault = xmlrpclib.Fault(1, 'foo')
        data = xmlrpc.xmlrpc_marshal(fault)
        self.assertEqual(data, xmlrpclib.dumps(fault))

class XMLRPCHandlerTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.xmlrpc import supervisor_xmlrpc_handler
        return supervisor_xmlrpc_handler

    def _makeOne(self, supervisord, subinterfaces):
        return self._getTargetClass()(supervisord, subinterfaces)

    def test_ctor(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        self.assertEqual(handler.supervisord, supervisor)
        from supervisor.xmlrpc import RootRPCInterface
        self.assertEqual(handler.rpcinterface.__class__, RootRPCInterface)

    def test_match(self):
        class DummyRequest2:
            def __init__(self, uri):
                self.uri = uri
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        self.assertEqual(handler.match(DummyRequest2('/RPC2')), True)
        self.assertEqual(handler.match(DummyRequest2('/nope')), False)

    def test_continue_request_nosuchmethod(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = xmlrpclib.dumps(('a', 'b'), 'supervisor.noSuchMethod')
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 2
        else:
            expected = 3
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-2],
                         'XML-RPC method called: supervisor.noSuchMethod()')
        self.assertEqual(logdata[-1],
           ('XML-RPC method supervisor.noSuchMethod() returned fault: '
            '[1] UNKNOWN_METHOD'))
        self.assertEqual(len(request.producers), 1)
        xml_response = request.producers[0]
        self.assertRaises(xmlrpclib.Fault, xmlrpclib.loads, xml_response)

    def test_continue_request_methodsuccess(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = xmlrpclib.dumps((), 'supervisor.getAPIVersion')
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 2
        else:
            expected = 3
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-2],
               'XML-RPC method called: supervisor.getAPIVersion()')
        self.assertEqual(logdata[-1],
            'XML-RPC method supervisor.getAPIVersion() returned successfully')
        self.assertEqual(len(request.producers), 1)
        xml_response = request.producers[0]
        response = xmlrpclib.loads(xml_response)
        from supervisor.rpcinterface import API_VERSION
        self.assertEqual(response[0][0], API_VERSION)
        self.assertEqual(request._done, True)
        self.assertEqual(request.headers['Content-Type'], 'text/xml')
        self.assertEqual(request.headers['Content-Length'], len(xml_response))

    def test_continue_request_no_params_in_request(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = '<?xml version="1.0" encoding="UTF-8"?>' \
               '<methodCall>' \
               '<methodName>supervisor.getAPIVersion</methodName>' \
               '</methodCall>'
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 2
        else:
            expected = 3
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-2],
               'XML-RPC method called: supervisor.getAPIVersion()')
        self.assertEqual(logdata[-1],
            'XML-RPC method supervisor.getAPIVersion() returned successfully')
        self.assertEqual(len(request.producers), 1)
        xml_response = request.producers[0]
        response = xmlrpclib.loads(xml_response)
        from supervisor.rpcinterface import API_VERSION
        self.assertEqual(response[0][0], API_VERSION)
        self.assertEqual(request._done, True)
        self.assertEqual(request.headers['Content-Type'], 'text/xml')
        self.assertEqual(request.headers['Content-Length'], len(xml_response))

    def test_continue_request_400_if_method_name_is_empty(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = '<?xml version="1.0" encoding="UTF-8"?>' \
               '<methodCall><methodName></methodName></methodCall>'
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 1
        else:
            expected = 2
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-1],
               'XML-RPC request received with no method name')
        self.assertEqual(len(request.producers), 0)
        self.assertEqual(request._error, 400)

    def test_continue_request_500(self):
        supervisor = DummySupervisor()
        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
        handler = self._makeOne(supervisor, subinterfaces)
        data = xmlrpclib.dumps((), 'supervisor.raiseError')
        request = DummyRequest('/what/ever', None, None, None)
        handler.continue_request(data, request)
        logdata = supervisor.options.logger.data
        from supervisor.xmlrpc import loads
        if loads:
            expected = 2
        else:
            expected = 3
        self.assertEqual(len(logdata), expected)
        self.assertEqual(logdata[-2],
               'XML-RPC method called: supervisor.raiseError()')
        self.assertTrue(logdata[-1].startswith('Traceback'))
        self.assertTrue(logdata[-1].endswith('ValueError: error\n'))
        self.assertEqual(len(request.producers), 0)
        self.assertEqual(request._error, 500)

class TraverseTests(unittest.TestCase):
    def test_underscore(self):
        from supervisor import xmlrpc
        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, None, '_', None)

    def test_notfound(self):
        from supervisor import xmlrpc
        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, None, 'foo', None)

    def test_badparams(self):
        from supervisor import xmlrpc
        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, self,
                          'test_badparams', (1, 2, 3))

    def test_success(self):
        from supervisor import xmlrpc
        L = []
        class Dummy:
            def foo(self, a):
                L.append(a)
        dummy = Dummy()
        xmlrpc.traverse(dummy, 'foo', [1])
        self.assertEqual(L, [1])

class SupervisorTransportTests(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.xmlrpc import SupervisorTransport
        return SupervisorTransport

    def _makeOne(self, *arg, **kw):
        return self._getTargetClass()(*arg, **kw)

    def test_ctor_unix(self):
        from supervisor import xmlrpc
        transport = self._makeOne('user', 'pass', 'unix:///foo/bar')
        conn = transport._get_connection()
        self.assertTrue(isinstance(conn, xmlrpc.UnixStreamHTTPConnection))
        self.assertEqual(conn.host, 'localhost')
        self.assertEqual(conn.socketfile, '/foo/bar')

    def test__get_connection_http_9001(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1:9001/')
        conn = transport._get_connection()
        self.assertTrue(isinstance(conn, httplib.HTTPConnection))
        self.assertEqual(conn.host, '127.0.0.1')
        self.assertEqual(conn.port, 9001)

    def test__get_connection_http_80(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
        conn = transport._get_connection()
        self.assertTrue(isinstance(conn, httplib.HTTPConnection))
        self.assertEqual(conn.host, '127.0.0.1')
        self.assertEqual(conn.port, 80)

    def test_request_non_200_response(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
        dummy_conn = DummyConnection(400, '')
        def getconn():
            return dummy_conn
        transport._get_connection = getconn
        self.assertRaises(xmlrpclib.ProtocolError,
                          transport.request, 'localhost', '/', '')
        self.assertEqual(transport.connection, None)
        self.assertEqual(dummy_conn.closed, True)

    def test_request_400_response(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
        dummy_conn = DummyConnection(400, '')
        def getconn():
            return dummy_conn
        transport._get_connection = getconn
        self.assertRaises(xmlrpclib.ProtocolError,
                          transport.request, 'localhost', '/', '')
        self.assertEqual(transport.connection, None)
        self.assertEqual(dummy_conn.closed, True)
        self.assertEqual(dummy_conn.requestargs[0], 'POST')
        self.assertEqual(dummy_conn.requestargs[1], '/')
        self.assertEqual(dummy_conn.requestargs[2], '')
        self.assertEqual(dummy_conn.requestargs[3]['Content-Length'], '0')
        self.assertEqual(dummy_conn.requestargs[3]['Content-Type'], 'text/xml')
        self.assertEqual(dummy_conn.requestargs[3]['Authorization'],
                         'Basic dXNlcjpwYXNz')
        self.assertEqual(dummy_conn.requestargs[3]['Accept'], 'text/xml')

    def test_request_200_response(self):
        transport = self._makeOne('user', 'pass', 'http://127.0.0.1/')
        response = """<?xml version="1.0"?>
        <methodResponse>
        <params>
        <param>
        <value><string>South Dakota</string></value>
        </param>
        </params>
        </methodResponse>"""
        dummy_conn = DummyConnection(200, response)
        def getconn():
            return dummy_conn
        transport._get_connection = getconn
        result = transport.request('localhost', '/', '')
        self.assertEqual(transport.connection, dummy_conn)
        self.assertEqual(dummy_conn.closed, False)
        self.assertEqual(dummy_conn.requestargs[0], 'POST')
        self.assertEqual(dummy_conn.requestargs[1], '/')
        self.assertEqual(dummy_conn.requestargs[2], '')
        self.assertEqual(dummy_conn.requestargs[3]['Content-Length'], '0')
        self.assertEqual(dummy_conn.requestargs[3]['Content-Type'], 'text/xml')
        self.assertEqual(dummy_conn.requestargs[3]['Authorization'],
                         'Basic dXNlcjpwYXNz')
        self.assertEqual(dummy_conn.requestargs[3]['Accept'], 'text/xml')
        self.assertEqual(result, ('South Dakota',))

    def test_works_with_py25(self):
        instance = self._makeOne('username', 'password', 'http://127.0.0.1')
        # the test is just to insure that this method can be called; failure
        # would be an AttributeError for _use_datetime under Python 2.5
        parser, unmarshaller = instance.getparser() # this uses _use_datetime

class IterparseLoadsTests(unittest.TestCase):
    def test_iterparse_loads_methodcall(self):
        s = """<?xml version="1.0"?>
        <methodCall>
        <methodName>examples.getStateName</methodName>
        <params>
        <param>
        <value><i4>41</i4></value>
        </param>
        <param>
        <value><int>14</int></value>
        </param>
        <param>
        <value><boolean>1</boolean></value>
        </param>
        <param>
        <value><string>hello world</string></value>
        </param>
        <param>
        <value><double>-12.214</double></value>
        </param>
        <param>
        <value><dateTime.iso8601>19980717T14:08:55</dateTime.iso8601></value>
        </param>
        <param>
        <value><base64>eW91IGNhbid0IHJlYWQgdGhpcyE=</base64></value>
        </param>
        <param>
        <struct>
          <member><name>k</name><value><i4>5</i4></value></member>
        </struct>
        </param>
        <param>
        <array>
          <data>
            <value><i4>12</i4></value>
            <value><i4>34</i4></value>
          </data>
        </array>
        </param>
        <param>
        <struct>
        <member>
          <name>k</name>
          <value><array><data><value><i4>1</i4></value></data></array></value>
        </member>
        </struct>
        </param>
        </params>
        </methodCall>
        """
        from supervisor.xmlrpc import loads
        if loads is None:
            return # no cElementTree
        result = loads(s)
        params, method = result
        import datetime
        self.assertEqual(method, 'examples.getStateName')
        self.assertEqual(params[0], 41)
        self.assertEqual(params[1], 14)
        self.assertEqual(params[2], True)
        self.assertEqual(params[3], 'hello world')
        self.assertEqual(params[4], -12.214)
        self.assertEqual(params[5], datetime.datetime(1998, 7, 17, 14, 8, 55))
        self.assertEqual(params[6], "you can't read this!")
        self.assertEqual(params[7], {'k': 5})
        self.assertEqual(params[8], [12, 34])
        self.assertEqual(params[9], {'k': [1]})

class TestDeferredXMLRPCResponse(unittest.TestCase):
    def _getTargetClass(self):
        from supervisor.xmlrpc import DeferredXMLRPCResponse
        return DeferredXMLRPCResponse

    def _makeOne(self, request=None, callback=None):
        if request is None:
            request = DummyRequest(None, None, None, None, None)
        if callback is None:
            callback = Dummy()
            callback.delay = 1
        return self._getTargetClass()(request, callback)

    def test_ctor(self):
        callback = Dummy()
        callback.delay = 1
        inst = self._makeOne(request='request', callback=callback)
        self.assertEqual(inst.callback, callback)
        self.assertEqual(inst.delay, 1.0)
        self.assertEqual(inst.request, 'request')
        self.assertEqual(inst.finished, False)

    def test_more_finished(self):
        inst = self._makeOne()
        inst.finished = True
        result = inst.more()
        self.assertEqual(result,  '')

    def test_more_callback_returns_not_done_yet(self):
        from supervisor.http import NOT_DONE_YET
        def callback():
            return NOT_DONE_YET
        callback.delay = 1
        inst = self._makeOne(callback=callback)
        self.assertEqual(inst.more(), NOT_DONE_YET)

    def test_more_callback_raises_RPCError(self):
        from supervisor.xmlrpc import RPCError, Faults
        def callback():
            raise RPCError(Faults.UNKNOWN_METHOD)
        callback.delay = 1
        inst = self._makeOne(callback=callback)
        self.assertEqual(inst.more(), None)
        self.assertEqual(len(inst.request.producers), 1)
        self.assertTrue('UNKNOWN_METHOD' in inst.request.producers[0])
        self.assertTrue(inst.finished)

    def test_more_callback_returns_value(self):
        def callback():
            return 'abc'
        callback.delay = 1
        inst = self._makeOne(callback=callback)
        self.assertEqual(inst.more(), None)
        self.assertEqual(len(inst.request.producers), 1)
        self.assertTrue('abc' in inst.request.producers[0])
        self.assertTrue(inst.finished)

    def test_more_callback_raises_unexpected_exception(self):
        def callback():
            raise ValueError('foo')
        callback.delay = 1
        inst = self._makeOne(callback=callback)
        inst.traceback = Dummy()
        called = []
        inst.traceback.print_exc = lambda: called.append(True)
        self.assertEqual(inst.more(), None)
        self.assertEqual(inst.request._error, 500)
        self.assertTrue(inst.finished)
        self.assertTrue(called)
        
class DummyResponse:
    def __init__(self, status=200, body='', reason='reason'):
        self.status = status
        self.body = body
        self.reason = reason

    def read(self):
        return self.body

class Dummy(object):
    pass

class DummyConnection:
    closed = False
    def __init__(self, status=200, body='', reason='reason'):
        self.response = DummyResponse(status, body, reason)

    def getresponse(self):
        return self.response

    def request(self, *arg, **kw):
        self.requestargs = arg
        self.requestkw = kw

    def close(self):
        self.closed = True

def test_suite():
    return unittest.findTestCases(sys.modules[__name__])

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')


########NEW FILE########
__FILENAME__ = trackrefs
##############################################################################
#
# Copyright (c) 2007 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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
#
##############################################################################

""" Code from the zope.testing module to help track down memory leaks

TrackRefs works only in a python compiled with the --with-pydebug flag.
An example of how to use TrackRefs in a function is below.

glen = 0
rc = 0

def doit():
    newglen = gc.collect()
    global glen
    if newglen > glen:
        print
        print "-------------------------------------"
        print "more garbage", newglen - glen
        glen = newglen
        print "-------------------------------------"
        print
    if refs:
        newrc = sys.gettotalrefcount()
        global rc
        if newrc > rc:
            refs.update()
            refs.detailed_refcounts(newrc, rc)
        rc = newrc
"""

import sys
import gc

class TrackRefs(object):
    """Object to track reference counts across test runs."""

    def __init__(self):
        self.type2count = {}
        self.type2all = {}
        self.delta = None
        self.n = 0
        self.update()
        self.delta = None

    def update(self):
        gc.collect()
        obs = sys.getobjects(0)
        type2count = {}
        type2all = {}
        n = 0
        for o in obs:
            if type(o) is str and o == '<dummy key>':
                # avoid dictionary madness
                continue

            all = sys.getrefcount(o) - 3
            n += all

            t = type(o)
            try:
                t = o.__class__
            except Exception:
                pass

            if t in type2count:
                type2count[t] += 1
                type2all[t] += all
            else:
                type2count[t] = 1
                type2all[t] = all


        ct = [(
               type_or_class_title(t),
               type2count[t] - self.type2count.get(t, 0),
               type2all[t] - self.type2all.get(t, 0),
               )
              for t in type2count.keys()]
        ct += [(
                type_or_class_title(t),
                - self.type2count[t],
                - self.type2all[t],
                )
               for t in self.type2count.keys()
               if t not in type2count]
        ct.sort()
        self.delta = ct
        self.type2count = type2count
        self.type2all = type2all
        self.n = n


    def output(self):
        printed = False
        s1 = s2 = 0
        for t, delta1, delta2 in self.delta:
            if delta1 or delta2:
                if not printed:
                    print (
                        '    Leak details, changes in instances and refcounts'
                        ' by type/class:')
                    print("    %-55s %6s %6s" % ('type/class', 'insts', 'refs'))
                    print("    %-55s %6s %6s" % ('-' * 55, '-----', '----'))
                    printed = True
                print("    %-55s %6d %6d" % (t, delta1, delta2))
                s1 += delta1
                s2 += delta2

        if printed:
            print("    %-55s %6s %6s" % ('-' * 55, '-----', '----'))
            print("    %-55s %6s %6s" % ('total', s1, s2))

        self.delta = None

    def detailed_refcounts(self, rc, prev):
        """Report a change in reference counts, with extra detail."""
        print ("  sum detail refcount=%-8d"
               " sys refcount=%-8d"
               " change=%-6d"
               % (self.n, rc, rc - prev))
        self.output()

def type_or_class_title(t):
    module = getattr(t, '__module__', '__builtin__')
    if module == '__builtin__':
        return t.__name__
    return "%s.%s" % (module, t.__name__)


########NEW FILE########
__FILENAME__ = web
import os
import re
import time
import traceback
import datetime

import meld3

from supervisor.compat import StringIO
from supervisor.compat import urllib
from supervisor.compat import parse_qs
from supervisor.compat import parse_qsl
from supervisor.compat import as_string

from supervisor.medusa import producers
from supervisor.medusa.http_server import http_date
from supervisor.medusa.http_server import get_header
from supervisor.medusa.xmlrpc_handler import collector

from supervisor.process import ProcessStates
from supervisor.http import NOT_DONE_YET

from supervisor.options import VERSION
from supervisor.options import make_namespec
from supervisor.options import split_namespec

from supervisor.xmlrpc import SystemNamespaceRPCInterface
from supervisor.xmlrpc import RootRPCInterface
from supervisor.xmlrpc import Faults
from supervisor.xmlrpc import RPCError

from supervisor.rpcinterface import SupervisorNamespaceRPCInterface

class DeferredWebProducer:
    """ A medusa producer that implements a deferred callback; requires
    a subclass of asynchat.async_chat that handles NOT_DONE_YET sentinel """
    CONNECTION = re.compile ('Connection: (.*)', re.IGNORECASE)

    def __init__(self, request, callback):
        self.callback = callback
        self.request = request
        self.finished = False
        self.delay = float(callback.delay)

    def more(self):
        if self.finished:
            return ''
        try:
            response = self.callback()
            if response is NOT_DONE_YET:
                return NOT_DONE_YET

            self.finished = True
            return self.sendresponse(response)

        except:
            io = StringIO()
            traceback.print_exc(file=io)
            # this should go to the main supervisor log file
            self.request.channel.server.logger.log('Web interface error',
                                                  io.getvalue())
            self.finished = True
            self.request.error(500)

    def sendresponse(self, response):

        headers = response.get('headers', {})
        for header in headers:
            self.request[header] = headers[header]

        if 'Content-Type' not in self.request:
            self.request['Content-Type'] = 'text/plain'

        if headers.get('Location'):
            self.request['Content-Length'] = 0
            self.request.error(301)
            return

        body = response.get('body', '')
        self.request['Content-Length'] = len(body)

        self.request.push(body)

        connection = get_header(self.CONNECTION, self.request.header)

        close_it = 0
        wrap_in_chunking = 0

        if self.request.version == '1.0':
            if connection == 'keep-alive':
                if not self.request.has_key('Content-Length'):
                    close_it = 1
                else:
                    self.request['Connection'] = 'Keep-Alive'
            else:
                close_it = 1
        elif self.request.version == '1.1':
            if connection == 'close':
                close_it = 1
            elif 'Content-Length' not in self.request:
                if 'Transfer-Encoding' in self.request:
                    if not self.request['Transfer-Encoding'] == 'chunked':
                        close_it = 1
                elif self.request.use_chunked:
                    self.request['Transfer-Encoding'] = 'chunked'
                    wrap_in_chunking = 1
                else:
                    close_it = 1
        elif self.request.version is None:
            close_it = 1

        outgoing_header = producers.simple_producer (
            self.request.build_reply_header())

        if close_it:
            self.request['Connection'] = 'close'

        if wrap_in_chunking:
            outgoing_producer = producers.chunked_producer (
                    producers.composite_producer (self.request.outgoing)
                    )
            # prepend the header
            outgoing_producer = producers.composite_producer(
                [outgoing_header, outgoing_producer]
                )
        else:
            # prepend the header
            self.request.outgoing.insert(0, outgoing_header)
            outgoing_producer = producers.composite_producer (
                self.request.outgoing)

        # apply a few final transformations to the output
        self.request.channel.push_with_producer (
                # globbing gives us large packets
                producers.globbing_producer (
                        # hooking lets us log the number of bytes sent
                        producers.hooked_producer (
                                outgoing_producer,
                                self.request.log
                                )
                        )
                )

        self.request.channel.current_request = None

        if close_it:
            self.request.channel.close_when_done()

class ViewContext:
    def __init__(self, **kw):
        self.__dict__.update(kw)

class MeldView:

    content_type = 'text/html'
    delay = .5

    def __init__(self, context):
        self.context = context
        template = self.context.template
        if not os.path.isabs(template):
            here = os.path.abspath(os.path.dirname(__file__))
            template = os.path.join(here, template)
        self.root = meld3.parse_xml(template)
        self.callback = None

    def __call__(self):
        body = self.render()
        if body is NOT_DONE_YET:
            return NOT_DONE_YET

        response = self.context.response
        headers = response['headers']
        headers['Content-Type'] = self.content_type
        headers['Pragma'] = 'no-cache'
        headers['Cache-Control'] = 'no-cache'
        headers['Expires'] = http_date.build_http_date(0)
        response['body'] = as_string(body)
        return response

    def render(self):
        pass

    def clone(self):
        return self.root.clone()

class TailView(MeldView):
    def render(self):
        supervisord = self.context.supervisord
        form = self.context.form

        if not 'processname' in form:
            tail = 'No process name found'
            processname = None
        else:
            processname = form['processname']
            offset = 0
            limit = form.get('limit', '1024')
            limit = min(-1024, int(limit)*-1 if limit.isdigit() else -1024)
            if not processname:
                tail = 'No process name found'
            else:
                rpcinterface = SupervisorNamespaceRPCInterface(supervisord)
                try:
                    tail = rpcinterface.readProcessLog(processname, limit, offset)
                    tail = rpcinterface.readProcessLog(processname, -1024, 0)
                except RPCError as e:
                    if e.code == Faults.NO_FILE:
                        tail = 'No file for %s' % processname
                    else:
                        raise

        root = self.clone()

        title = root.findmeld('title')
        title.content('Supervisor tail of process %s' % processname)
        tailbody = root.findmeld('tailbody')
        tailbody.content(tail)

        refresh_anchor = root.findmeld('refresh_anchor')
        if processname is not None:
            refresh_anchor.attributes(
                href='tail.html?processname=%s&limit=%s' % (
                    urllib.quote(processname), urllib.quote(str(abs(limit)))
                    )
            )
        else:
            refresh_anchor.deparent()

        return as_string(root.write_xhtmlstring())

class StatusView(MeldView):
    def actions_for_process(self, process):
        state = process.get_state()
        processname = urllib.quote(make_namespec(process.group.config.name,
                                                 process.config.name))
        start = {
        'name':'Start',
        'href':'index.html?processname=%s&amp;action=start' % processname,
        'target':None,
        }
        restart = {
        'name':'Restart',
        'href':'index.html?processname=%s&amp;action=restart' % processname,
        'target':None,
        }
        stop = {
        'name':'Stop',
        'href':'index.html?processname=%s&amp;action=stop' % processname,
        'target':None,
        }
        clearlog = {
        'name':'Clear Log',
        'href':'index.html?processname=%s&amp;action=clearlog' % processname,
        'target':None,
        }
        tailf = {
        'name':'Tail -f',
        'href':'logtail/%s' % processname,
        'target':'_blank'
        }
        if state == ProcessStates.RUNNING:
            actions = [restart, stop, clearlog, tailf]
        elif state in (ProcessStates.STOPPED, ProcessStates.EXITED,
                       ProcessStates.FATAL):
            actions = [start, None, clearlog, tailf]
        else:
            actions = [None, None, clearlog, tailf]
        return actions

    def css_class_for_state(self, state):
        if state == ProcessStates.RUNNING:
            return 'statusrunning'
        elif state in (ProcessStates.FATAL, ProcessStates.BACKOFF):
            return 'statuserror'
        else:
            return 'statusnominal'

    def make_callback(self, namespec, action):
        supervisord = self.context.supervisord

        # the rpc interface code is already written to deal properly in a
        # deferred world, so just use it
        main =   ('supervisor', SupervisorNamespaceRPCInterface(supervisord))
        system = ('system', SystemNamespaceRPCInterface([main]))

        rpcinterface = RootRPCInterface([main, system])

        if action:

            if action == 'refresh':
                def donothing():
                    message = 'Page refreshed at %s' % time.ctime()
                    return message
                donothing.delay = 0.05
                return donothing

            elif action == 'stopall':
                callback = rpcinterface.supervisor.stopAllProcesses()
                def stopall():
                    if callback() is NOT_DONE_YET:
                        return NOT_DONE_YET
                    else:
                        return 'All stopped at %s' % time.ctime()
                stopall.delay = 0.05
                return stopall

            elif action == 'restartall':
                callback = rpcinterface.system.multicall(
                    [ {'methodName':'supervisor.stopAllProcesses'},
                      {'methodName':'supervisor.startAllProcesses'} ] )
                def restartall():
                    result = callback()
                    if result is NOT_DONE_YET:
                        return NOT_DONE_YET
                    return 'All restarted at %s' % time.ctime()
                restartall.delay = 0.05
                return restartall

            elif namespec:
                def wrong():
                    return 'No such process named %s' % namespec
                wrong.delay = 0.05
                group_name, process_name = split_namespec(namespec)
                group = supervisord.process_groups.get(group_name)
                if group is None:
                    return wrong
                process = group.processes.get(process_name)
                if process is None:
                    return wrong

                elif action == 'stop':
                    callback = rpcinterface.supervisor.stopProcess(namespec)
                    def stopprocess():
                        result = callback()
                        if result is NOT_DONE_YET:
                            return NOT_DONE_YET
                        return 'Process %s stopped' % namespec
                    stopprocess.delay = 0.05
                    return stopprocess

                elif action == 'restart':
                    callback = rpcinterface.system.multicall(
                        [ {'methodName':'supervisor.stopProcess',
                           'params': [namespec]},
                          {'methodName':'supervisor.startProcess',
                           'params': [namespec]},
                          ]
                        )
                    def restartprocess():
                        result = callback()
                        if result is NOT_DONE_YET:
                            return NOT_DONE_YET
                        return 'Process %s restarted' % namespec
                    restartprocess.delay = 0.05
                    return restartprocess

                elif action == 'start':
                    try:
                        callback = rpcinterface.supervisor.startProcess(
                            namespec)
                    except RPCError as e:
                        if e.code == Faults.SPAWN_ERROR:
                            def spawnerr():
                                return 'Process %s spawn error' % namespec
                            spawnerr.delay = 0.05
                            return spawnerr
                    def startprocess():
                        if callback() is NOT_DONE_YET:
                            return NOT_DONE_YET
                        return 'Process %s started' % namespec
                    startprocess.delay = 0.05
                    return startprocess

                elif action == 'clearlog':
                    callback = rpcinterface.supervisor.clearProcessLog(
                        namespec)
                    def clearlog():
                        return 'Log for %s cleared' % namespec
                    clearlog.delay = 0.05
                    return clearlog

        raise ValueError(action)

    def render(self):
        form = self.context.form
        response = self.context.response
        processname = form.get('processname')
        action = form.get('action')
        message = form.get('message')

        if action:
            if not self.callback:
                self.callback = self.make_callback(processname, action)
                return NOT_DONE_YET

            else:
                message =  self.callback()
                if message is NOT_DONE_YET:
                    return NOT_DONE_YET
                if message is not None:
                    server_url = form['SERVER_URL']
                    location = server_url + '?message=%s' % urllib.quote(
                        message)
                    response['headers']['Location'] = location

        supervisord = self.context.supervisord
        rpcinterface = RootRPCInterface(
            [('supervisor',
              SupervisorNamespaceRPCInterface(supervisord))]
            )

        processnames = []
        for group in supervisord.process_groups.values():
            for gprocname in group.processes.keys():
                processnames.append((group.config.name, gprocname))

        processnames.sort()

        data = []
        for groupname, processname in processnames:
            actions = self.actions_for_process(
                supervisord.process_groups[groupname].processes[processname])
            sent_name = make_namespec(groupname, processname)
            info = rpcinterface.supervisor.getProcessInfo(sent_name)
            data.append({
                'status':info['statename'],
                'name':processname,
                'group':groupname,
                'actions':actions,
                'state':info['state'],
                'description':info['description'],
                })

        root = self.clone()

        if message is not None:
            statusarea = root.findmeld('statusmessage')
            statusarea.attrib['class'] = 'status_msg'
            statusarea.content(message)

        if data:
            iterator = root.findmeld('tr').repeat(data)
            shaded_tr = False

            for tr_element, item in iterator:
                status_text = tr_element.findmeld('status_text')
                status_text.content(item['status'].lower())
                status_text.attrib['class'] = self.css_class_for_state(
                    item['state'])

                info_text = tr_element.findmeld('info_text')
                info_text.content(item['description'])

                anchor = tr_element.findmeld('name_anchor')
                processname = make_namespec(item['group'], item['name'])
                anchor.attributes(href='tail.html?processname=%s' %
                                  urllib.quote(processname))
                anchor.content(processname)

                actions = item['actions']
                actionitem_td = tr_element.findmeld('actionitem_td')

                for li_element, actionitem in actionitem_td.repeat(actions):
                    anchor = li_element.findmeld('actionitem_anchor')
                    if actionitem is None:
                        anchor.attrib['class'] = 'hidden'
                    else:
                        anchor.attributes(href=actionitem['href'],
                                          name=actionitem['name'])
                        anchor.content(actionitem['name'])
                        if actionitem['target']:
                            anchor.attributes(target=actionitem['target'])
                if shaded_tr:
                    tr_element.attrib['class'] = 'shade'
                shaded_tr = not shaded_tr
        else:
            table = root.findmeld('statustable')
            table.replace('No programs to manage')

        root.findmeld('supervisor_version').content(VERSION)
        copyright_year = str(datetime.date.today().year)
        root.findmeld('copyright_date').content(copyright_year)

        return as_string(root.write_xhtmlstring())

class OKView:
    delay = 0
    def __init__(self, context):
        self.context = context

    def __call__(self):
        return {'body':'OK'}

VIEWS = {
    'index.html': {
          'template':'ui/status.html',
          'view':StatusView
          },
    'tail.html': {
           'template':'ui/tail.html',
           'view':TailView,
           },
    'ok.html': {
           'template':None,
           'view':OKView,
           },
    }


class supervisor_ui_handler:
    IDENT = 'Supervisor Web UI HTTP Request Handler'

    def __init__(self, supervisord):
        self.supervisord = supervisord

    def match(self, request):
        if request.command not in ('POST', 'GET'):
            return False

        path, params, query, fragment = request.split_uri()

        while path.startswith('/'):
            path = path[1:]

        if not path:
            path = 'index.html'

        for viewname in VIEWS.keys():
            if viewname == path:
                return True

    def handle_request(self, request):
        if request.command == 'POST':
            request.collector = collector(self, request)
        else:
            self.continue_request('', request)

    def continue_request (self, data, request):
        form = {}
        cgi_env = request.cgi_environment()
        form.update(cgi_env)
        if 'QUERY_STRING' not in form:
            form['QUERY_STRING'] = ''

        query = form['QUERY_STRING']

        # we only handle x-www-form-urlencoded values from POSTs
        form_urlencoded = parse_qsl(data)
        query_data = parse_qs(query)

        for k, v in query_data.items():
            # ignore dupes
            form[k] = v[0]

        for k, v in form_urlencoded:
            # ignore dupes
            form[k] = v

        form['SERVER_URL'] = request.get_server_url()

        path = form['PATH_INFO']
        # strip off all leading slashes
        while path and path[0] == '/':
            path = path[1:]
        if not path:
            path = 'index.html'

        viewinfo = VIEWS.get(path)
        if viewinfo is None:
            # this should never happen if our match method works
            return

        response = {'headers': {}}

        viewclass = viewinfo['view']
        viewtemplate = viewinfo['template']
        context = ViewContext(template=viewtemplate,
                              request = request,
                              form = form,
                              response = response,
                              supervisord=self.supervisord)
        view = viewclass(context)
        pushproducer = request.channel.push_with_producer
        pushproducer(DeferredWebProducer(request, view))


########NEW FILE########
__FILENAME__ = xmlrpc
import types
import re
import traceback
import sys

from supervisor.compat import xmlrpclib
from supervisor.compat import func_attribute
from supervisor.compat import StringIO
from supervisor.compat import urllib
from supervisor.compat import as_bytes
from supervisor.compat import as_string
from supervisor.compat import encodestring
from supervisor.compat import decodestring
from supervisor.compat import httplib
from supervisor.compat import iterparse

import supervisor.medusa.text_socket as socket
from supervisor.medusa.http_server import get_header
from supervisor.medusa.xmlrpc_handler import xmlrpc_handler
from supervisor.medusa import producers

from supervisor.http import NOT_DONE_YET

class Faults:
    UNKNOWN_METHOD = 1
    INCORRECT_PARAMETERS = 2
    BAD_ARGUMENTS = 3
    SIGNATURE_UNSUPPORTED = 4
    SHUTDOWN_STATE = 6
    BAD_NAME = 10
    NO_FILE = 20
    NOT_EXECUTABLE = 21
    FAILED = 30
    ABNORMAL_TERMINATION = 40
    SPAWN_ERROR = 50
    ALREADY_STARTED = 60
    NOT_RUNNING = 70
    SUCCESS = 80
    ALREADY_ADDED = 90
    STILL_RUNNING = 91
    CANT_REREAD = 92

def getFaultDescription(code):
    for faultname in Faults.__dict__:
        if getattr(Faults, faultname) == code:
            return faultname
    return 'UNKNOWN'

class RPCError(Exception):
    def __init__(self, code, extra=None):
        self.code = code
        self.text = getFaultDescription(code)
        if extra is not None:
            self.text = '%s: %s' % (self.text, extra)

class DeferredXMLRPCResponse:
    """ A medusa producer that implements a deferred callback; requires
    a subclass of asynchat.async_chat that handles NOT_DONE_YET sentinel """
    CONNECTION = re.compile ('Connection: (.*)', re.IGNORECASE)
    traceback = traceback # for testing override

    def __init__(self, request, callback):
        self.callback = callback
        self.request = request
        self.finished = False
        self.delay = float(callback.delay)

    def more(self):
        if self.finished:
            return ''
        try:
            try:
                value = self.callback()
                if value is NOT_DONE_YET:
                    return NOT_DONE_YET
            except RPCError as err:
                value = xmlrpclib.Fault(err.code, err.text)

            body = xmlrpc_marshal(value)

            self.finished = True

            return self.getresponse(body)

        except:
            # report unexpected exception back to server
            self.traceback.print_exc()
            self.finished = True
            self.request.error(500)

    def getresponse(self, body):
        self.request['Content-Type'] = 'text/xml'
        self.request['Content-Length'] = len(body)
        self.request.push(body)
        connection = get_header(self.CONNECTION, self.request.header)

        close_it = 0
        wrap_in_chunking = 0

        if self.request.version == '1.0':
            if connection == 'keep-alive':
                if 'Content-Length' not in self.request:
                    close_it = 1
                else:
                    self.request['Connection'] = 'Keep-Alive'
            else:
                close_it = 1
        elif self.request.version == '1.1':
            if connection == 'close':
                close_it = 1
            elif 'Content-Length' not in self.request:
                if 'Transfer-Encoding' in self.request:
                    if not self.request['Transfer-Encoding'] == 'chunked':
                        close_it = 1
                elif self.request.use_chunked:
                    self.request['Transfer-Encoding'] = 'chunked'
                    wrap_in_chunking = 1
                else:
                    close_it = 1
        elif self.request.version is None:
            close_it = 1

        outgoing_header = producers.simple_producer (
            self.request.build_reply_header())

        if close_it:
            self.request['Connection'] = 'close'

        if wrap_in_chunking:
            outgoing_producer = producers.chunked_producer (
                    producers.composite_producer (self.request.outgoing)
                    )
            # prepend the header
            outgoing_producer = producers.composite_producer(
                [outgoing_header, outgoing_producer]
                )
        else:
            # prepend the header
            self.request.outgoing.insert(0, outgoing_header)
            outgoing_producer = producers.composite_producer (
                self.request.outgoing)

        # apply a few final transformations to the output
        self.request.channel.push_with_producer (
                # globbing gives us large packets
                producers.globbing_producer (
                        # hooking lets us log the number of bytes sent
                        producers.hooked_producer (
                                outgoing_producer,
                                self.request.log
                                )
                        )
                )

        self.request.channel.current_request = None

        if close_it:
            self.request.channel.close_when_done()

def xmlrpc_marshal(value):
    ismethodresponse = not isinstance(value, xmlrpclib.Fault)
    if ismethodresponse:
        if not isinstance(value, tuple):
            value = (value,)
        body = xmlrpclib.dumps(value,  methodresponse=ismethodresponse)
    else:
        body = xmlrpclib.dumps(value)
    return body

class SystemNamespaceRPCInterface:
    def __init__(self, namespaces):
        self.namespaces = {}
        for name, inst in namespaces:
            self.namespaces[name] = inst
        self.namespaces['system'] = self

    def _listMethods(self):
        methods = {}
        for ns_name in self.namespaces:
            namespace = self.namespaces[ns_name]
            for method_name in namespace.__class__.__dict__:
                # introspect; any methods that don't start with underscore
                # are published
                func = getattr(namespace, method_name)
                meth = getattr(func, func_attribute, None)
                if meth is not None:
                    if not method_name.startswith('_'):
                        sig = '%s.%s' % (ns_name, method_name)
                        methods[sig] = str(func.__doc__)
        return methods

    def listMethods(self):
        """ Return an array listing the available method names

        @return array result  An array of method names available (strings).
        """
        methods = self._listMethods()
        keys = list(methods.keys())
        keys.sort()
        return keys

    def methodHelp(self, name):
        """ Return a string showing the method's documentation

        @param string name   The name of the method.
        @return string result The documentation for the method name.
        """
        methods = self._listMethods()
        for methodname in methods.keys():
            if methodname == name:
                return methods[methodname]
        raise RPCError(Faults.SIGNATURE_UNSUPPORTED)

    def methodSignature(self, name):
        """ Return an array describing the method signature in the
        form [rtype, ptype, ptype...] where rtype is the return data type
        of the method, and ptypes are the parameter data types that the
        method accepts in method argument order.

        @param string name  The name of the method.
        @return array result  The result.
        """
        methods = self._listMethods()
        for method in methods:
            if method == name:
                rtype = None
                ptypes = []
                parsed = gettags(methods[method])
                for thing in parsed:
                    if thing[1] == 'return': # tag name
                        rtype = thing[2] # datatype
                    elif thing[1] == 'param': # tag name
                        ptypes.append(thing[2]) # datatype
                if rtype is None:
                    raise RPCError(Faults.SIGNATURE_UNSUPPORTED)
                return [rtype] + ptypes
        raise RPCError(Faults.SIGNATURE_UNSUPPORTED)

    def multicall(self, calls):
        """Process an array of calls, and return an array of
        results. Calls should be structs of the form {'methodName':
        string, 'params': array}. Each result will either be a
        single-item array containing the result value, or a struct of
        the form {'faultCode': int, 'faultString': string}. This is
        useful when you need to make lots of small calls without lots
        of round trips.

        @param array calls  An array of call requests
        @return array result  An array of results
        """
        producers = []

        for call in calls:
            try:
                name = call['methodName']
                params = call.get('params', [])
                if name == 'system.multicall':
                    # Recursive system.multicall forbidden
                    raise RPCError(Faults.INCORRECT_PARAMETERS)
                root = AttrDict(self.namespaces)
                value = traverse(root, name, params)
            except RPCError as inst:
                value = {'faultCode': inst.code,
                         'faultString': inst.text}
            except:
                errmsg = "%s:%s" % (sys.exc_info()[0], sys.exc_info()[1])
                value = {'faultCode': 1, 'faultString': errmsg}
            producers.append(value)

        results = []

        def multiproduce():
            """ Run through all the producers in order """
            if not producers:
                return []

            callback = producers.pop(0)

            if isinstance(callback, types.FunctionType):
                try:
                    value = callback()
                except RPCError as inst:
                    value = {'faultCode':inst.code, 'faultString':inst.text}

                if value is NOT_DONE_YET:
                    # push it back in the front of the queue because we
                    # need to finish the calls in requested order
                    producers.insert(0, callback)
                    return NOT_DONE_YET
            else:
                value = callback

            results.append(value)

            if producers:
                # only finish when all producers are finished
                return NOT_DONE_YET

            return results

        multiproduce.delay = .05
        return multiproduce

class AttrDict(dict):
    # hack to make a dict's getattr equivalent to its getitem
    def __getattr__(self, name):
        return self[name]

class RootRPCInterface:
    def __init__(self, subinterfaces):
        for name, rpcinterface in subinterfaces:
            setattr(self, name, rpcinterface)

class supervisor_xmlrpc_handler(xmlrpc_handler):
    path = '/RPC2'
    IDENT = 'Supervisor XML-RPC Handler'
    def __init__(self, supervisord, subinterfaces):
        self.rpcinterface = RootRPCInterface(subinterfaces)
        self.supervisord = supervisord
        if loads:
            self.loads = loads
        else:
            self.supervisord.options.logger.warn(
                'cElementTree not installed, using slower XML parser for '
                'XML-RPC'
                )
            self.loads = xmlrpclib.loads

    def match(self, request):
        return request.uri.startswith(self.path)

    def continue_request (self, data, request):
        logger = self.supervisord.options.logger

        try:

            params, method = self.loads(data)

            # no <methodName> in the request or name is an empty string
            if not method:
                logger.trace('XML-RPC request received with no method name')
                request.error(400)
                return

            # we allow xml-rpc clients that do not send empty <params>
            # when there are no parameters for the method call
            if params is None:
                params = ()

            try:
                logger.trace('XML-RPC method called: %s()' % method)
                value = self.call(method, params)
                # application-specific: instead of we never want to
                # marshal None (even though we could by saying allow_none=True
                # in dumps within xmlrpc_marshall), this is meant as
                # a debugging fixture, see issue 223.
                assert value is not None, (
                    'return value from method %r with params %r is None' %
                    (method, params)
                    )
                logger.trace('XML-RPC method %s() returned successfully' %
                             method)
            except RPCError as err:
                # turn RPCError reported by method into a Fault instance
                value = xmlrpclib.Fault(err.code, err.text)
                logger.trace('XML-RPC method %s() returned fault: [%d] %s' % (
                    method,
                    err.code, err.text))

            if isinstance(value, types.FunctionType):
                # returning a function from an RPC method implies that
                # this needs to be a deferred response (it needs to block).
                pushproducer = request.channel.push_with_producer
                pushproducer(DeferredXMLRPCResponse(request, value))

            else:
                # if we get anything but a function, it implies that this
                # response doesn't need to be deferred, we can service it
                # right away.
                body = xmlrpc_marshal(value)
                request['Content-Type'] = 'text/xml'
                request['Content-Length'] = len(body)
                request.push(body)
                request.done()

        except:
            io = StringIO()
            traceback.print_exc(file=io)
            val = io.getvalue()
            logger.critical(val)
            # internal error, report as HTTP server error
            request.error(500)

    def call(self, method, params):
        return traverse(self.rpcinterface, method, params)

def traverse(ob, method, params):
    path = method.split('.')
    for name in path:
        if name.startswith('_'):
            # security (don't allow things that start with an underscore to
            # be called remotely)
            raise RPCError(Faults.UNKNOWN_METHOD)
        ob = getattr(ob, name, None)
        if ob is None:
            raise RPCError(Faults.UNKNOWN_METHOD)

    try:
        return ob(*params)
    except TypeError:
        raise RPCError(Faults.INCORRECT_PARAMETERS)

class SupervisorTransport(xmlrpclib.Transport):
    """
    Provides a Transport for xmlrpclib that uses
    httplib.HTTPConnection in order to support persistent
    connections.  Also support basic auth and UNIX domain socket
    servers.
    """
    connection = None

    _use_datetime = 0 # python 2.5 fwd compatibility
    def __init__(self, username=None, password=None, serverurl=None):
        xmlrpclib.Transport.__init__(self)
        self.username = username
        self.password = password
        self.verbose = False
        self.serverurl = serverurl
        if serverurl.startswith('http://'):
            type, uri = urllib.splittype(serverurl)
            host, path = urllib.splithost(uri)
            host, port = urllib.splitport(host)
            if port is None:
                port = 80
            else:
                port = int(port)
            def get_connection(host=host, port=port):
                return httplib.HTTPConnection(host, port)
            self._get_connection = get_connection
        elif serverurl.startswith('unix://'):
            def get_connection(serverurl=serverurl):
                # we use 'localhost' here because domain names must be
                # < 64 chars (or we'd use the serverurl filename)
                conn = UnixStreamHTTPConnection('localhost')
                conn.socketfile = serverurl[7:]
                return conn
            self._get_connection = get_connection
        else:
            raise ValueError('Unknown protocol for serverurl %s' % serverurl)

    def request(self, host, handler, request_body, verbose=0):
        if not self.connection:
            self.connection = self._get_connection()
            self.headers = {
                "User-Agent" : self.user_agent,
                "Content-Type" : "text/xml",
                "Accept": "text/xml"
                }

            # basic auth
            if self.username is not None and self.password is not None:
                unencoded = "%s:%s" % (self.username, self.password)
                encoded = as_string(encodestring(as_bytes(unencoded)))
                encoded = encoded.replace('\n', '')
                encoded = encoded.replace('\012', '')
                self.headers["Authorization"] = "Basic %s" % encoded

        self.headers["Content-Length"] = str(len(request_body))

        self.connection.request('POST', handler, request_body, self.headers)

        r = self.connection.getresponse()

        if r.status != 200:
            self.connection.close()
            self.connection = None
            raise xmlrpclib.ProtocolError(host + handler,
                                          r.status,
                                          r.reason,
                                          '' )
        data = r.read()
        p, u = self.getparser()
        p.feed(data)
        p.close()
        return u.close()

class UnixStreamHTTPConnection(httplib.HTTPConnection):
    def connect(self):
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        # we abuse the host parameter as the socketname
        self.sock.connect(self.socketfile)

def gettags(comment):
    """ Parse documentation strings into JavaDoc-like tokens """

    tags = []

    tag = None
    datatype = None
    name = None
    tag_lineno = lineno = 0
    tag_text = []

    for line in comment.split('\n'):
        line = line.strip()
        if line.startswith("@"):
            tags.append((tag_lineno, tag, datatype, name, '\n'.join(tag_text)))
            parts = line.split(None, 3)
            if len(parts) == 1:
                datatype = ''
                name = ''
                tag_text = []
            elif len(parts) == 2:
                datatype = parts[1]
                name = ''
                tag_text = []
            elif len(parts) == 3:
                datatype = parts[1]
                name = parts[2]
                tag_text = []
            elif len(parts) == 4:
                datatype = parts[1]
                name = parts[2]
                tag_text = [parts[3].lstrip()]
            tag = parts[0][1:]
            tag_lineno = lineno
        else:
            if line:
                tag_text.append(line)
        lineno += 1

    tags.append((tag_lineno, tag, datatype, name, '\n'.join(tag_text)))

    return tags


if iterparse is not None:
    import datetime, time

    def make_datetime(text):
        return datetime.datetime(
            *time.strptime(text, "%Y%m%dT%H:%M:%S")[:6]
        )

    unmarshallers = {
        "int": lambda x: int(x.text),
        "i4": lambda x: int(x.text),
        "boolean": lambda x: x.text == "1",
        "string": lambda x: x.text or "",
        "double": lambda x: float(x.text),
        "dateTime.iso8601": lambda x: make_datetime(x.text),
        "array": lambda x: x[0].text,
        "data": lambda x: [v.text for v in x],
        "struct": lambda x: dict([(k.text or "", v.text) for k, v in x]),
        "base64": lambda x: as_string(decodestring(as_bytes(x.text or ""))),
        "value": lambda x: x[0].text,
        "param": lambda x: x[0].text,
    }

    def loads(data):
        params = method = None
        for action, elem in iterparse(StringIO(data)):
            unmarshall = unmarshallers.get(elem.tag)
            if unmarshall:
                data = unmarshall(elem)
                elem.clear()
                elem.text = data
            elif elem.tag == "methodName":
                method = elem.text
            elif elem.tag == "params":
                params = tuple([v.text for v in elem])
        return params, method
else:
    loads = None

########NEW FILE########
