This file is indexed.

/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