Merge lp:~rafalcieslak256/ubuntu-accomplishments-daemon/daemon-launcher into lp:ubuntu-accomplishments-daemon

Proposed by Rafał Cieślak
Status: Merged
Merged at revision: 125
Proposed branch: lp:~rafalcieslak256/ubuntu-accomplishments-daemon/daemon-launcher
Merge into: lp:ubuntu-accomplishments-daemon
Diff against target: 310 lines (+164/-78)
4 files modified
accomplishments/daemon/api.py (+9/-1)
accomplishments/daemon/dbusapi.py (+10/-5)
bin/accomplishments-daemon (+145/-34)
bin/accomplishments-daemon-fg (+0/-38)
To merge this branch: bzr merge lp:~rafalcieslak256/ubuntu-accomplishments-daemon/daemon-launcher
Reviewer Review Type Date Requested Status
Matt Fischer Approve
Review via email: mp+118344@code.launchpad.net

Description of the change

This implements an elegant launcher for the accomplishments daemon. It is launcher by executing `accomplishments-daemon` script (it works both when installed and when run from branch).

Supported options include:
 * --help - Guess what it does.
 * --start - Launches the daemon. If the launcher is run without arguments, it behaves just as if with --start.
 * --stop - Calls stop_daemon() via DBUsAPI, which stops the rector. A graceful way to exit the daemon.
 * --kill - Kills the daemon process (by PID).
 * --reload - Reloads accomplishments database. It will also send a DBus signal, but note that the viewer does not support it yet, and it will require restart to get the new accomplishments from daemon.

Note that the viewer is not yet aware of the launcher and runs the daemon 'the old way'. It may slightly confuse the launcher (it will be able to stop a daemon which was started by viewer, but won't be able to kill it, as it does not know where to find the PID file). I don't think there is a point in a separate branch for this fix in viewer, as it's gonna be a 1 line change, therefore simply avoid starting the daemon with viewer when testing this branch (run the daemon manually and then the viewer) and I will apply the fix to the viewer when this branch gets merged in.

And remember to restart the daemon before testing this branch ;-)

To post a comment you must log in.
Revision history for this message
Matt Fischer (mfisch) wrote :

I think this is a great solution and will be clean even when run as different users since dbus is per session. Two minor comments and a question:

typo on line 19: "stop_daemo"

minor issue, line 86: "if the path is set wrongly" change "wrongly" to "incorrectly" as wrongly isn't a valid word ;)

Did you run the unit tests in a pbuilder environment with this change yet? If not, I can try it later today. I'm worried about package changes mainly if there needs to be any, since the daemon doesn't run anyway.

review: Needs Fixing
126. By Rafał Cieślak

Minor fixes for the launcher

Revision history for this message
Rafał Cieślak (rafalcieslak256) wrote :

Updated with several fixes. I am not sure how to correctly run tests in pbuilder, and therefore I would be thankful if you did it for me.

Revision history for this message
Matt Fischer (mfisch) wrote :

Works fine in a pbuilder

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'accomplishments/daemon/api.py'
2--- accomplishments/daemon/api.py 2012-08-04 00:22:49 +0000
3+++ accomplishments/daemon/api.py 2012-08-06 20:55:29 +0000
4@@ -1138,6 +1138,9 @@
5 self.accDB[collection] = collectiondata
6
7 self._update_all_locked_and_completed_statuses()
8+
9+ if not self.test_mode:
10+ self.service.accomplishments_collections_reloaded()
11 # Uncomment following for debugging
12 # print self.accDB\
13
14@@ -1631,7 +1634,7 @@
15 log.msg(type(value))
16 if value == True:
17 log.msg("setting")
18- command = "twistd -noy " + daemon_exec_dir + "/accomplishments-daemon --logfile=" + os.path.join(self.dir_cache, "logs", "daemon.log")
19+ command = os.path.join(daemon_exec_dir, "accomplishments-daemon") + " --start"
20 filetext = "[Desktop Entry]\n\
21 Type=Application\n\
22 Encoding=UTF-8\n\
23@@ -1834,3 +1837,8 @@
24 #Other significant system functions
25 def get_API_version(self):
26 return "0.2"
27+
28+ def stop_daemon(self):
29+ print "Stopping the daemon as stop_daemon() got called."
30+ from twisted.internet import reactor
31+ reactor.stop()
32
33=== modified file 'accomplishments/daemon/dbusapi.py'
34--- accomplishments/daemon/dbusapi.py 2012-07-16 16:22:10 +0000
35+++ accomplishments/daemon/dbusapi.py 2012-08-06 20:55:29 +0000
36@@ -18,16 +18,11 @@
37
38
39 def daemon_is_registered():
40- """
41- """
42 try:
43 obj = dbus.SessionBus().get_object(
44 "org.ubuntu.accomplishments", "/")
45 return True
46 except dbus.exceptions.DBusException:
47- log.msg(
48- "User does not have the accomplishments daemon "
49- "available")
50 return False
51
52
53@@ -883,6 +878,11 @@
54
55 @dbus.service.method(dbus_interface='org.ubuntu.accomplishments',
56 in_signature="", out_signature="")
57+ def stop_daemon(self):
58+ return self.api.stop_daemon()
59+
60+ @dbus.service.method(dbus_interface='org.ubuntu.accomplishments',
61+ in_signature="", out_signature="")
62 def publish_trophies_online(self):
63 return self.api.publish_trophies_online()
64
65@@ -926,3 +926,8 @@
66 @dbus.service.signal(dbus_interface='org.ubuntu.accomplishments')
67 def ubuntu_one_account_ready(self):
68 pass
69+
70+ @dbus.service.signal(dbus_interface='org.ubuntu.accomplishments')
71+ def accomplishments_collections_reloaded(self):
72+ pass
73+
74
75=== modified file 'bin/accomplishments-daemon'
76--- bin/accomplishments-daemon 2012-07-30 17:36:03 +0000
77+++ bin/accomplishments-daemon 2012-08-06 20:55:29 +0000
78@@ -1,18 +1,10 @@
79-#!/usr/bin/twistd -y
80-"""
81-Run the Accomplishments daemon!
82-"""
83+#!/usr/bin/python -O
84+
85 import sys
86 import os
87
88-from twisted.internet import gireactor
89-gireactor.install()
90-
91-import dbus
92-from dbus.mainloop.glib import DBusGMainLoop
93-
94-
95-# Importing from accomplishments.paths would fail, if the path is set wrongly.
96+# Importing from accomplishments.{daemon,util} would fail, if the path
97+# is set incorrectly.
98 # We need to ensure that the script will load the correct version of
99 # accomplishment.* that is installed with this script.
100 scriptpath = os.path.abspath(__file__)
101@@ -23,25 +15,144 @@
102 sys.path.insert(0, basepath + "/lib/python2.7/site-packages")
103 sys.path.insert(0, basepath + "/lib/python2.7/dist-packages")
104
105-
106-# Ensure the sync daemon is running. The following command can be run in the
107-# background (&), because it would block the daemon from running for few
108-# seconds, while the syncdaemon will not be needed immediatelly after
109-# accomplishments daemon startups.
110-os.system("u1sdtool --start &")
111-
112-
113-from accomplishments.daemon import app
114-
115-from accomplishments.util import paths
116-
117-dbus_loop = DBusGMainLoop(set_as_default=True)
118-application = app.applicationFactory(
119- app_name="Ubuntu Accomplishments",
120- bus_name="org.ubuntu.accomplishments",
121- main_loop=dbus_loop,
122- session_bus=dbus.SessionBus(mainloop=dbus_loop),
123- object_path="/",
124- # Let's execute the timer service every 15 minutes
125- update_interval=15 * 60,
126- gpg_key=os.path.join(paths.systemdata_dir, "validation-key.pub"))
127+# This is a global variable
128+pidfile_path = None
129+
130+def main():
131+ # This part of code is executed if the script is run directly.
132+ import argparse
133+ parser = argparse.ArgumentParser(description='Launches the accomplishments daemon.')
134+ mode = parser.add_mutually_exclusive_group()
135+ mode.add_argument('--start',action='store_true',help="Launch the accomplishments daemon (default option when run without arguments).")
136+ mode.add_argument('--stop',action='store_true',help="Stop the daemon gracefully.")
137+ mode.add_argument('--kill',action='store_true',help="Kill the daemon process.")
138+ mode.add_argument('--reload',action='store_true',help="Reload accomplishments collections.")
139+ args = parser.parse_args()
140+
141+ import xdg.BaseDirectory
142+ try:
143+ rootdir = os.environ['ACCOMPLISHMENTS_ROOT_DIR']
144+ cache_dir = os.path.join(
145+ rootdir, "accomplishments", ".cache", "accomplishments")
146+ except KeyError:
147+ cache_dir = os.path.join(
148+ xdg.BaseDirectory.xdg_cache_home, "accomplishments")
149+
150+ global pidfile_path
151+ pidfile_path = os.path.join(cache_dir,"accomplishments-daemon.pid")
152+
153+ if args.start or not (args.start or args.stop or args.kill or args.reload):
154+ from accomplishments.util.paths import daemon_exec_dir
155+
156+ # Check if the daemon is already running.
157+ if is_daemon_running():
158+ print "The daemon is already running."
159+ return
160+
161+ # The daemon is not running.
162+ print "Launching the daemon..."
163+
164+ # Create the cache directories.
165+ if not os.path.exists(cache_dir):
166+ os.makedirs(cache_dir)
167+ if not os.path.exists(os.path.join(cache_dir,"logs")):
168+ os.makedirs(os.path.join(cache_dir,"logs"))
169+
170+ # Remove the pid file if for some reasons it's still present.
171+ # Try killing the process though, maybe it hung or DBus died.
172+ if os.path.exists(pidfile_path):
173+ if kill_daemon_process() is not 0:
174+ # Successfully killed daemon removes the pidfile himself.
175+ os.remove(pidfile_path)
176+
177+ command = "twistd -y " + os.path.join(daemon_exec_dir, "accomplishments-daemon") + " --logfile=" + os.path.join(cache_dir, "logs", "daemon.log") + " --pidfile=" + pidfile_path
178+
179+ os.system(command)
180+
181+ elif args.stop:
182+ if not is_daemon_running():
183+ print "The daemon is not running."
184+ return
185+ print "Stopping the daemon..."
186+
187+ from accomplishments.daemon import dbusapi
188+ api = dbusapi.Accomplishments()
189+ api.stop_daemon()
190+
191+ elif args.kill:
192+ # Killing the daemon
193+ res = kill_daemon_process()
194+ if res is 0:
195+ print "Daemon killed."
196+ elif res is -1:
197+ if not is_daemon_running():
198+ print "The daemon is not running."
199+ else:
200+ print "Unable to kill daemon - its PID is unknown. Try stopping it instead."
201+ elif res is -2:
202+ pass
203+
204+ elif args.reload:
205+ if not is_daemon_running():
206+ print "The daemon is not running."
207+ return
208+ print "Reloading accomplishments collections."
209+
210+ from accomplishments.daemon import dbusapi
211+ api = dbusapi.Accomplishments()
212+ api.reload_accom_database()
213+
214+
215+def kill_daemon_process():
216+ if not os.path.exists(pidfile_path):
217+ return -1
218+ pidfile = open(pidfile_path,'r')
219+ pid = int(pidfile.read())
220+ i = os.system("kill " + str(pid))
221+ if i is not 0:
222+ print "Failed to kill daemon, PID:" + str(pid)
223+ return -2
224+ return 0
225+
226+def is_daemon_running():
227+ from accomplishments.daemon.dbusapi import daemon_is_registered
228+ return daemon_is_registered()
229+
230+
231+if __name__=="__main__":
232+ # If run directly...
233+ main()
234+
235+if __name__=="__builtin__":
236+ # This part of code is executed only if the script is run by twistd.
237+ # It is responsible for setting up and launching the twistd daemon.
238+
239+ # This piece of code cannot be wrapped inside a function, as silly
240+ # twistd blindly searches fora global variable named "application",
241+ # and therefore while this code must be exposed.
242+
243+ # Reactor has to be installed before any import statements.
244+ from twisted.internet import gireactor
245+ gireactor.install()
246+
247+ from accomplishments.daemon import app
248+ from accomplishments.util import paths
249+
250+ # Ensure the sync daemon is running. The following command can be run in the
251+ # background (&), because it would block the daemon from running for few
252+ # seconds, while the syncdaemon will not be needed immediatelly after
253+ # accomplishments daemon startups.
254+ os.system("u1sdtool --start &")
255+
256+ import dbus
257+ from dbus.mainloop.glib import DBusGMainLoop
258+ dbus_loop = DBusGMainLoop(set_as_default=True)
259+ application = app.applicationFactory(
260+ app_name="Ubuntu Accomplishments",
261+ bus_name="org.ubuntu.accomplishments",
262+ main_loop=dbus_loop,
263+ session_bus=dbus.SessionBus(mainloop=dbus_loop),
264+ object_path="/",
265+ # Let's execute the timer service every 15 minutes
266+ update_interval=15 * 60,
267+ gpg_key=os.path.join(paths.systemdata_dir, "validation-key.pub"))
268
269=== removed file 'bin/accomplishments-daemon-fg'
270--- bin/accomplishments-daemon-fg 2012-04-15 14:02:28 +0000
271+++ bin/accomplishments-daemon-fg 1970-01-01 00:00:00 +0000
272@@ -1,38 +0,0 @@
273-#!/usr/bin/twistd -noy
274-"""
275-Run the Accomplishments daemon!
276-"""
277-import sys
278-import os
279-
280-from twisted.internet import glib2reactor
281-glib2reactor.install()
282-
283-import dbus
284-from dbus.mainloop.glib import DBusGMainLoop
285-
286-# Importing from accomplishments.paths would fail, if the path is set wrongly.
287-# We need to ensure that the script will load the correct version of
288-# accomplishment.* that is installed with this script.
289-scriptpath = os.path.abspath(__file__)
290-# basepaths shall be equal to prefix used while installing - if the application is run from source, it does not really matter.
291-basepath = os.path.split(os.path.split(scriptpath)[0])[0]
292-sys.path.insert(0, basepath)
293-sys.path.insert(0, basepath + "/lib/python2.7")
294-sys.path.insert(0, basepath + "/lib/python2.7/site-packages")
295-sys.path.insert(0, basepath + "/lib/python2.7/dist-packages")
296-
297-from accomplishments.daemon import app
298-
299-from accomplishments.util import paths
300-
301-dbus_loop = DBusGMainLoop(set_as_default=True)
302-application = app.applicationFactory(
303- app_name="Ubuntu Accomplishments",
304- bus_name="org.ubuntu.accomplishments",
305- main_loop=dbus_loop,
306- session_bus=dbus.SessionBus(mainloop=dbus_loop),
307- object_path="/",
308- # Let's execute the timer service every 15 minutes
309- update_interval=15 * 60,
310- gpg_key=os.path.join(paths.systemdata_dir, "validation-key.pub"))

Subscribers

People subscribed via source and target branches