/usr/share/pyshared/desktopcouch/application/service.py is in python-desktopcouch-application 1.0.8-0ubuntu3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | #!/usr/bin/python
# Copyright 2009 Canonical Ltd.
#
# This file is part of desktopcouch.
#
# desktopcouch is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# desktopcouch is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with desktopcouch. If not, see <http://www.gnu.org/licenses/>.
#
# Authors: Stuart Langridge <stuart.langridge@canonical.com>
# Tim Cole <tim.cole@canonical.com>
"""CouchDB port advertiser.
A command-line utility which exports a
desktopCouch.getPort method on the bus which returns
that port, so other apps (specifically, the contacts API) can work out
where CouchDB is running so it can be talked to.
Calculates the port number by looking in the CouchDB log.
If CouchDB is not running, then run the script to start it and then
start advertising the port.
This file should be started by D-Bus activation.
"""
import os
import time
import logging
import logging.handlers
import signal
import gobject
from desktopcouch.application import local_files
from desktopcouch.application import replication
from desktopcouch.application import migration
from desktopcouch.application import stop_local_couchdb
from desktopcouch.application.platform import (
PortAdvertiser, find_pid, direct_access_find_port, CACHE_HOME)
from desktopcouch.application.plugins import load_plugins
def set_up_logging(name):
"""Set logging preferences for this process."""
log_directory = os.path.join(CACHE_HOME, "desktop-couch/log")
try:
os.makedirs(log_directory)
except:
pass # pylint: disable=W0702
rotating_log = logging.handlers.TimedRotatingFileHandler(
os.path.join(log_directory, "desktop-couch-%s.log" % (name,)),
"midnight", 1, 14)
rotating_log.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'%(asctime)s %(levelname)-8s %(message)s')
rotating_log.setFormatter(formatter)
logging.getLogger('').addHandler(rotating_log)
console_log = logging.StreamHandler()
console_log.setLevel(logging.WARNING)
console_log.setFormatter(logging.Formatter(
"%s %%(asctime)s - %%(message)s" % (name,)))
logging.getLogger('').addHandler(console_log)
logging.getLogger('').setLevel(logging.DEBUG)
class DesktopcouchService():
"""Host the services."""
# pylint: disable=C0301
def __init__(self, main_loop, pid_finder=find_pid,
port_finder=direct_access_find_port,
ctx=local_files.DEFAULT_CONTEXT,
stop_couchdb=stop_local_couchdb.stop_couchdb,
replication_actions=replication,
advertiser_factory=PortAdvertiser, set_logging=set_up_logging,
fork=os.fork, nice=os.nice,
kill=os.kill, sleep=time.sleep, set_type=set,
gobject_module=gobject):
self._mainloop = main_loop
self._pid_finder = pid_finder
self._port_finder = port_finder
self._ctx = ctx
self._stop_couchdb = stop_couchdb
self._replication = replication_actions
self._advertiser_factory = advertiser_factory
self._logging = set_logging
self._fork = fork
self._nice = nice
self._kill = kill
self._sleep = sleep
self._set = set_type
self._gobject = gobject_module
# pylint: enable=C0301
def _start_replicator_main(self, couchdb_port):
"""Start replicator."""
replication_runtime = self._replication.set_up(
lambda: couchdb_port)
try:
logging.debug("starting replicator main loop")
self._mainloop.run()
finally:
logging.debug("ending replicator main loop")
if replication_runtime:
replication.tear_down(*replication_runtime)
def _start_server_main(self, couchdb_port):
"""Start server answering DBus calls, and run plugins first."""
def if_all_semaphores_cleared(blocking_semaphores,
func, *args, **kwargs):
"""Run a function if no semaphores exist, else try later."""
if blocking_semaphores:
self._sleep(0.2) # Never peg the CPU
return True # Make idle call try us again.
else:
func(*args, **kwargs)
return False # Handled!
blocking_semaphores = self._set()
load_plugins(couchdb_port, blocking_semaphores, self._gobject)
# Answering queries on DBus signals that we are ready for users
# to connect. We mustn't begin that until every plugin has a chance
# to run to completion if it needs it.
self._gobject.idle_add(if_all_semaphores_cleared, blocking_semaphores,
self._advertiser_factory,
self._mainloop.stop,
self._ctx)
logging.debug("starting dbus main loop")
try:
self._mainloop.run()
finally:
logging.debug("ending dbus main loop")
def start(self):
"""Start the services used by desktopcouch."""
maintained_child_pids = list()
couchdb_pid = self._pid_finder(start_if_not_running=False,
ctx=self._ctx)
if couchdb_pid is None:
logging.warn("Starting up personal couchdb.")
couchdb_pid = self._pid_finder(start_if_not_running=True,
ctx=self._ctx)
if couchdb_pid:
maintained_child_pids.append(couchdb_pid)
else:
logging.warn("Personal couchdb is already running at PID#%d.",
couchdb_pid)
couchdb_port = self._port_finder(pid=couchdb_pid, ctx=self._ctx)
child_pid = self._fork() # Split!
if child_pid == 0:
# Let's be the replicator!
self._logging("replication")
self._nice(10)
self._start_replicator_main(couchdb_port)
return
else:
assert child_pid > 0
maintained_child_pids.append(child_pid)
child_pid = self._fork() # Split!
if child_pid == 0:
# Let's be the migration tool!
self._logging("migration")
self._sleep(60) # wait a minute to let user finish
try:
logging.info("Attempting update of design docs")
migration.update_design_documents(ctx=self._ctx)
logging.info("Attempting migration of data")
migration.run_needed_migrations(ctx=self._ctx)
logging.info("Completed")
except Exception: # pylint: disable=W0703
logging.exception("failed to finish migrating.")
return
else:
assert child_pid > 0
maintained_child_pids.append(child_pid)
# Let's be the dbus server!
# This is the parent process. When we exit, we kill children.
def receive_signal(signum, stackframe):
"""On signal, quit main loop gracefully."""
logging.warn("Received signal %d. Quitting.", signum)
self._mainloop.stop()
signal.signal(signal.SIGHUP, receive_signal)
signal.signal(signal.SIGTERM, receive_signal)
try:
set_up_logging("dbus")
# offer the dbus service
self._start_server_main(couchdb_port)
except:
logging.exception( # pylint: disable=W0702
"uncaught exception makes us shut down.")
finally:
logging.info("exiting.")
self._stop_couchdb(ctx=self._ctx)
for child_pid in maintained_child_pids:
try:
self._kill(child_pid, signal.SIGTERM)
logging.warn("Sent SIGTERM to %d", child_pid)
except OSError:
pass
self._sleep(1)
for child_pid in maintained_child_pids:
try:
self._kill(child_pid, signal.SIGKILL)
logging.warn("Sent SIGKILL to %d", child_pid)
except OSError:
pass
|