2
# Copyright 2009 Canonical Ltd.
4
# This file is part of desktopcouch.
6
# desktopcouch is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU Lesser General Public License version 3
8
# as published by the Free Software Foundation.
10
# desktopcouch is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU Lesser General Public License for more details.
15
# You should have received a copy of the GNU Lesser General Public License
16
# along with desktopcouch. If not, see <http://www.gnu.org/licenses/>.
18
# Authors: Stuart Langridge <stuart.langridge@canonical.com>
19
# Tim Cole <tim.cole@canonical.com>
21
"""CouchDB port advertiser.
23
A command-line utility which exports a
24
desktopCouch.getPort method on the bus which returns
25
that port, so other apps (specifically, the contacts API) can work out
26
where CouchDB is running so it can be talked to.
28
Calculates the port number by looking in the CouchDB log.
30
If CouchDB is not running, then run the script to start it and then
31
start advertising the port.
33
This file should be started by D-Bus activation.
40
import logging.handlers
43
from twisted.internet import reactor as mainloop
47
from desktopcouch import local_files
48
from desktopcouch import replication
49
from desktopcouch import stop_local_couchdb
51
def set_up_logging(name):
52
"""Set logging preferences for this process."""
53
import xdg.BaseDirectory
54
log_directory = os.path.join(xdg.BaseDirectory.xdg_cache_home,
57
os.makedirs(log_directory)
61
rotating_log = logging.handlers.TimedRotatingFileHandler(
62
os.path.join(log_directory, "desktop-couch-%s.log" % (name,)),
64
rotating_log.setLevel(logging.DEBUG)
65
formatter = logging.Formatter(
66
'%(asctime)s %(levelname)-8s %(message)s')
67
rotating_log.setFormatter(formatter)
68
logging.getLogger('').addHandler(rotating_log)
69
console_log = logging.StreamHandler()
70
console_log.setLevel(logging.WARNING)
71
console_log.setFormatter(logging.Formatter(
72
"%s %%(asctime)s - %%(message)s" % (name,)))
73
logging.getLogger('').addHandler(console_log)
74
logging.getLogger('').setLevel(logging.DEBUG)
76
class PortAdvertiser(dbus.service.Object):
77
"Advertise the discovered port number on the D-Bus Session bus"
78
def __init__(self, death):
79
self.conn = dbus.SessionBus()
81
super(PortAdvertiser, self).__init__(object_path="/",
84
# Here we commit to being ready to answer function calls.
85
self.bus_name = dbus.service.BusName("org.desktopcouch.CouchDB",
88
@dbus.service.method(dbus_interface='org.desktopcouch.CouchDB',
89
in_signature='', out_signature='i')
91
"Exported method to return the port"
92
port = int(desktopcouch._direct_access_find_port())
95
@dbus.service.method(dbus_interface='org.desktopcouch.CouchDB',
96
in_signature='', out_signature='')
98
"Exported method to quit the program"
101
class DesktopcouchService():
102
"""Host the services."""
104
def __init__(self, main_loop,
105
pid_finder=desktopcouch.find_pid,
106
port_finder=desktopcouch._direct_access_find_port,
107
ctx=local_files.DEFAULT_CONTEXT,
108
stop_couchdb=stop_local_couchdb.stop_couchdb,
109
replication=replication,
110
advertiser_factory=PortAdvertiser,
111
logging=set_up_logging,
116
self._mainloop = main_loop
117
self._pid_finder = pid_finder
118
self._port_finder = port_finder
120
self._stop_couchdb = stop_couchdb
121
self._replication = replication
122
self._advertiser_factory = advertiser_factory
123
self._logging = logging
129
def _start_replicator_main(self, couchdb_port):
130
replication_runtime = self._replication.set_up(
131
lambda: couchdb_port)
133
logging.debug("starting replicator main loop")
136
logging.debug("ending replicator main loop")
137
if replication_runtime:
138
replication.tear_down(*replication_runtime)
140
def _start_server_main(self):
141
portAdvertiser = self._advertiser_factory(self._mainloop.stop)
142
logging.debug("starting dbus main loop")
146
logging.debug("ending dbus main loop")
149
"""Start the services used by desktopcouch."""
150
should_shut_down_couchdb = False
151
couchdb_pid = self._pid_finder(start_if_not_running=False,
153
if couchdb_pid is None:
154
logging.warn("Starting up personal couchdb.")
155
couchdb_pid = self._pid_finder(start_if_not_running=True,
157
should_shut_down_couchdb = True
159
logging.warn("Personal couchdb is already running at PID#%d.",
162
couchdb_port = self._port_finder(pid=couchdb_pid, ctx=self._ctx)
163
child_pid = self._fork() # Split!
165
# Let's be the replicator!
166
self._logging("replication")
168
self._start_replicator_main(couchdb_port)
172
# Let's be the dbus server!
173
# This is the parent process. When we exit, we kill children.
175
set_up_logging("dbus")
176
# offer the dbus service
177
self._start_server_main()
180
"uncaught exception makes us shut down.")
182
logging.info("exiting.")
183
if should_shut_down_couchdb:
184
logging.warn("shutting down personal couchdb.")
185
self._stop_couchdb(ctx=self._ctx)
188
self._kill(child_pid, signal.SIGTERM)
190
self._kill(child_pid, signal.SIGKILL)