text: #!/usr/bin/env python
  # -*- coding: utf-8 -*-
  # Copyright 2014-2016 OpenMarket Ltd
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  import argparse
  import collections
  import glob
  import os
  import os.path
  import signal
  import subprocess
  import sys
  import yaml
  import errno
  import time
  
  SYNAPSE = [sys.executable, "-B", "-m", "synapse.app.homeserver"]
  
  GREEN = "\x1b[1;32m"
  YELLOW = "\x1b[1;33m"
  RED = "\x1b[1;31m"
  NORMAL = "\x1b[m"
  
  
  def pid_running(pid):
      try:
          os.kill(pid, 0)
          return True
      except OSError, err:
          if err.errno == errno.EPERM:
              return True
          return False
  
  
  def write(message, colour=NORMAL, stream=sys.stdout):
      if colour == NORMAL:
          stream.write(message + "\n")
      else:
          stream.write(colour + message + NORMAL + "\n")
  
  
  def abort(message, colour=RED, stream=sys.stderr):
      write(message, colour, stream)
      sys.exit(1)
  
  
  def start(configfile):
      write("Starting ...")
      args = SYNAPSE
      args.extend(["--daemonize", "-c", configfile])
  
      try:
          subprocess.check_call(args)
          write("started synapse.app.homeserver(%r)" %
                (configfile,), colour=GREEN)
      except subprocess.CalledProcessError as e:
          write(
              "error starting (exit code: %d); see above for logs" % e.returncode,
              colour=RED,
          )
  
  
  def start_worker(app, configfile, worker_configfile):
      args = [
          "python", "-B",
          "-m", app,
          "-c", configfile,
          "-c", worker_configfile
      ]
  
      try:
          subprocess.check_call(args)
          write("started %s(%r)" % (app, worker_configfile), colour=GREEN)
      except subprocess.CalledProcessError as e:
          write(
              "error starting %s(%r) (exit code: %d); see above for logs" % (
                  app, worker_configfile, e.returncode,
              ),
              colour=RED,
          )
  
  
  def stop(pidfile, app):
      if os.path.exists(pidfile):
          pid = int(open(pidfile).read())
          try:
              os.kill(pid, signal.SIGTERM)
              write("stopped %s" % (app,), colour=GREEN)
          except OSError, err:
              if err.errno == errno.ESRCH:
                  write("%s not running" % (app,), colour=YELLOW)
              elif err.errno == errno.EPERM:
                  abort("Cannot stop %s: Operation not permitted" % (app,))
              else:
                  abort("Cannot stop %s: Unknown error" % (app,))
  
  
  Worker = collections.namedtuple("Worker", [
      "app", "configfile", "pidfile", "cache_factor"
  ])
  
  
  def main():
  
      parser = argparse.ArgumentParser()
  
      parser.add_argument(
          "action",
          choices=["start", "stop", "restart"],
          help="whether to start, stop or restart the synapse",
      )
      parser.add_argument(
          "configfile",
          nargs="?",
          default="homeserver.yaml",
          help="the homeserver config file, defaults to homeserver.yaml",
      )
      parser.add_argument(
          "-w", "--worker",
          metavar="WORKERCONFIG",
          help="start or stop a single worker",
      )
      parser.add_argument(
          "-a", "--all-processes",
          metavar="WORKERCONFIGDIR",
          help="start or stop all the workers in the given directory"
               " and the main synapse process",
      )
  
      options = parser.parse_args()
  
      if options.worker and options.all_processes:
          write(
              'Cannot use "--worker" with "--all-processes"',
              stream=sys.stderr
          )
          sys.exit(1)
  
      configfile = options.configfile
  
      if not os.path.exists(configfile):
          write(
              "No config file found\n"
              "To generate a config file, run '%s -c %s --generate-config"
              " --server-name=<server name>'\n" % (
                  " ".join(SYNAPSE), options.configfile
              ),
              stream=sys.stderr,
          )
          sys.exit(1)
  
      with open(configfile) as stream:
          config = yaml.load(stream)
  
      pidfile = config["pid_file"]
      cache_factor = config.get("synctl_cache_factor")
      start_stop_synapse = True
  
      if cache_factor:
          os.environ["SYNAPSE_CACHE_FACTOR"] = str(cache_factor)
  
      worker_configfiles = []
      if options.worker:
          start_stop_synapse = False
          worker_configfile = options.worker
          if not os.path.exists(worker_configfile):
              write(
                  "No worker config found at %r" % (worker_configfile,),
                  stream=sys.stderr,
              )
              sys.exit(1)
          worker_configfiles.append(worker_configfile)
  
      if options.all_processes:
          worker_configdir = options.all_processes
          if not os.path.isdir(worker_configdir):
              write(
                  "No worker config directory found at %r" % (worker_configdir,),
                  stream=sys.stderr,
              )
              sys.exit(1)
          worker_configfiles.extend(sorted(glob.glob(
              os.path.join(worker_configdir, "*.yaml")
          )))
  
      workers = []
      for worker_configfile in worker_configfiles:
          with open(worker_configfile) as stream:
              worker_config = yaml.load(stream)
          worker_app = worker_config["worker_app"]
          worker_pidfile = worker_config["worker_pid_file"]
          worker_daemonize = worker_config["worker_daemonize"]
          assert worker_daemonize, "In config %r: expected '%s' to be True" % (
              worker_configfile, "worker_daemonize")
          worker_cache_factor = worker_config.get("synctl_cache_factor")
          workers.append(Worker(
              worker_app, worker_configfile, worker_pidfile, worker_cache_factor,
          ))
  
      action = options.action
  
      if action == "stop" or action == "restart":
          for worker in workers:
              stop(worker.pidfile, worker.app)
  
          if start_stop_synapse:
              stop(pidfile, "synapse.app.homeserver")
  
      # Wait for synapse to actually shutdown before starting it again
      if action == "restart":
          running_pids = []
          if start_stop_synapse and os.path.exists(pidfile):
              running_pids.append(int(open(pidfile).read()))
          for worker in workers:
              if os.path.exists(worker.pidfile):
                  running_pids.append(int(open(worker.pidfile).read()))
          if len(running_pids) > 0:
              write("Waiting for process to exit before restarting...")
              for running_pid in running_pids:
                  while pid_running(running_pid):
                      time.sleep(0.2)
  
      if action == "start" or action == "restart":
          if start_stop_synapse:
              # Check if synapse is already running
              if os.path.exists(pidfile) and pid_running(int(open(pidfile).read())):
                  abort("synapse.app.homeserver already running")
              start(configfile)
  
          for worker in workers:
              if worker.cache_factor:
                  os.environ["SYNAPSE_CACHE_FACTOR"] = str(worker.cache_factor)
  
              start_worker(worker.app, configfile, worker.configfile)
  
              if cache_factor:
                  os.environ["SYNAPSE_CACHE_FACTOR"] = str(cache_factor)
              else:
                  os.environ.pop("SYNAPSE_CACHE_FACTOR", None)
  
  
  if __name__ == "__main__":
      main()
  