summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/prserv/serv.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/prserv/serv.py')
-rw-r--r--bitbake/lib/prserv/serv.py352
1 files changed, 352 insertions, 0 deletions
diff --git a/bitbake/lib/prserv/serv.py b/bitbake/lib/prserv/serv.py
new file mode 100644
index 0000000000..7864594bb6
--- /dev/null
+++ b/bitbake/lib/prserv/serv.py
@@ -0,0 +1,352 @@
1import os,sys,logging
2import signal, time, atexit, threading
3from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
4import xmlrpclib
5import threading
6import Queue
7
8try:
9 import sqlite3
10except ImportError:
11 from pysqlite2 import dbapi2 as sqlite3
12
13import bb.server.xmlrpc
14import prserv
15import prserv.db
16
17logger = logging.getLogger("BitBake.PRserv")
18
19if sys.hexversion < 0x020600F0:
20 print("Sorry, python 2.6 or later is required.")
21 sys.exit(1)
22
23class Handler(SimpleXMLRPCRequestHandler):
24 def _dispatch(self,method,params):
25 try:
26 value=self.server.funcs[method](*params)
27 except:
28 import traceback
29 traceback.print_exc()
30 raise
31 return value
32
33PIDPREFIX = "/tmp/PRServer_%s_%s.pid"
34singleton = None
35
36
37class PRServer(SimpleXMLRPCServer):
38 def __init__(self, dbfile, logfile, interface, daemon=True):
39 ''' constructor '''
40 SimpleXMLRPCServer.__init__(self, interface,
41 logRequests=False, allow_none=True)
42 self.dbfile=dbfile
43 self.daemon=daemon
44 self.logfile=logfile
45 self.working_thread=None
46 self.host, self.port = self.socket.getsockname()
47 self.pidfile=PIDPREFIX % (self.host, self.port)
48
49 self.register_function(self.getPR, "getPR")
50 self.register_function(self.quit, "quit")
51 self.register_function(self.ping, "ping")
52 self.register_function(self.export, "export")
53 self.register_function(self.importone, "importone")
54 self.register_introspection_functions()
55
56 self.db = prserv.db.PRData(self.dbfile)
57 self.table = self.db["PRMAIN"]
58
59 self.requestqueue = Queue.Queue()
60 self.handlerthread = threading.Thread(target = self.process_request_thread)
61 self.handlerthread.daemon = False
62
63 def process_request_thread(self):
64 """Same as in BaseServer but as a thread.
65
66 In addition, exception handling is done here.
67
68 """
69 while True:
70 (request, client_address) = self.requestqueue.get()
71 try:
72 self.finish_request(request, client_address)
73 self.shutdown_request(request)
74 except:
75 self.handle_error(request, client_address)
76 self.shutdown_request(request)
77 self.table.sync()
78
79 def process_request(self, request, client_address):
80 self.requestqueue.put((request, client_address))
81
82 def export(self, version=None, pkgarch=None, checksum=None, colinfo=True):
83 try:
84 return self.table.export(version, pkgarch, checksum, colinfo)
85 except sqlite3.Error as exc:
86 logger.error(str(exc))
87 return None
88
89 def importone(self, version, pkgarch, checksum, value):
90 return self.table.importone(version, pkgarch, checksum, value)
91
92 def ping(self):
93 return not self.quit
94
95 def getinfo(self):
96 return (self.host, self.port)
97
98 def getPR(self, version, pkgarch, checksum):
99 try:
100 return self.table.getValue(version, pkgarch, checksum)
101 except prserv.NotFoundError:
102 logger.error("can not find value for (%s, %s)",version, checksum)
103 return None
104 except sqlite3.Error as exc:
105 logger.error(str(exc))
106 return None
107
108 def quit(self):
109 self.quit=True
110 return
111
112 def work_forever(self,):
113 self.quit = False
114 self.timeout = 0.5
115
116 logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
117 (self.dbfile, self.host, self.port, str(os.getpid())))
118
119 self.handlerthread.start()
120 while not self.quit:
121 self.handle_request()
122
123 self.table.sync()
124 logger.info("PRServer: stopping...")
125 self.server_close()
126 return
127
128 def start(self):
129 pid = self.daemonize()
130 # Ensure both the parent sees this and the child from the work_forever log entry above
131 logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
132 (self.dbfile, self.host, self.port, str(pid)))
133
134 def delpid(self):
135 os.remove(self.pidfile)
136
137 def daemonize(self):
138 """
139 See Advanced Programming in the UNIX, Sec 13.3
140 """
141 try:
142 pid = os.fork()
143 if pid > 0:
144 os.waitpid(pid, 0)
145 #parent return instead of exit to give control
146 return pid
147 except OSError as e:
148 raise Exception("%s [%d]" % (e.strerror, e.errno))
149
150 os.setsid()
151 """
152 fork again to make sure the daemon is not session leader,
153 which prevents it from acquiring controlling terminal
154 """
155 try:
156 pid = os.fork()
157 if pid > 0: #parent
158 os._exit(0)
159 except OSError as e:
160 raise Exception("%s [%d]" % (e.strerror, e.errno))
161
162 os.umask(0)
163 os.chdir("/")
164
165 sys.stdout.flush()
166 sys.stderr.flush()
167 si = file('/dev/null', 'r')
168 so = file(self.logfile, 'a+')
169 se = so
170 os.dup2(si.fileno(),sys.stdin.fileno())
171 os.dup2(so.fileno(),sys.stdout.fileno())
172 os.dup2(se.fileno(),sys.stderr.fileno())
173
174 # Clear out all log handlers prior to the fork() to avoid calling
175 # event handlers not part of the PRserver
176 for logger_iter in logging.Logger.manager.loggerDict.keys():
177 logging.getLogger(logger_iter).handlers = []
178
179 # Ensure logging makes it to the logfile
180 streamhandler = logging.StreamHandler()
181 streamhandler.setLevel(logging.DEBUG)
182 formatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
183 streamhandler.setFormatter(formatter)
184 logger.addHandler(streamhandler)
185
186 # write pidfile
187 pid = str(os.getpid())
188 pf = file(self.pidfile, 'w')
189 pf.write("%s\n" % pid)
190 pf.close()
191
192 self.work_forever()
193 self.delpid()
194 os._exit(0)
195
196class PRServSingleton(object):
197 def __init__(self, dbfile, logfile, interface):
198 self.dbfile = dbfile
199 self.logfile = logfile
200 self.interface = interface
201 self.host = None
202 self.port = None
203
204 def start(self):
205 self.prserv = PRServer(self.dbfile, self.logfile, self.interface)
206 self.prserv.start()
207 self.host, self.port = self.prserv.getinfo()
208
209 def getinfo(self):
210 return (self.host, self.port)
211
212class PRServerConnection(object):
213 def __init__(self, host, port):
214 if is_local_special(host, port):
215 host, port = singleton.getinfo()
216 self.host = host
217 self.port = port
218 self.connection, self.transport = bb.server.xmlrpc._create_server(self.host, self.port)
219
220 def terminate(self):
221 try:
222 logger.info("Terminating PRServer...")
223 self.connection.quit()
224 except Exception as exc:
225 sys.stderr.write("%s\n" % str(exc))
226
227 def getPR(self, version, pkgarch, checksum):
228 return self.connection.getPR(version, pkgarch, checksum)
229
230 def ping(self):
231 return self.connection.ping()
232
233 def export(self,version=None, pkgarch=None, checksum=None, colinfo=True):
234 return self.connection.export(version, pkgarch, checksum, colinfo)
235
236 def importone(self, version, pkgarch, checksum, value):
237 return self.connection.importone(version, pkgarch, checksum, value)
238
239 def getinfo(self):
240 return self.host, self.port
241
242def start_daemon(dbfile, host, port, logfile):
243 pidfile = PIDPREFIX % (host, port)
244 try:
245 pf = file(pidfile,'r')
246 pid = int(pf.readline().strip())
247 pf.close()
248 except IOError:
249 pid = None
250
251 if pid:
252 sys.stderr.write("pidfile %s already exist. Daemon already running?\n"
253 % pidfile)
254 return 1
255
256 server = PRServer(os.path.abspath(dbfile), os.path.abspath(logfile), (host,port))
257 server.start()
258 return 0
259
260def stop_daemon(host, port):
261 pidfile = PIDPREFIX % (host, port)
262 try:
263 pf = file(pidfile,'r')
264 pid = int(pf.readline().strip())
265 pf.close()
266 except IOError:
267 pid = None
268
269 if not pid:
270 sys.stderr.write("pidfile %s does not exist. Daemon not running?\n"
271 % pidfile)
272
273 try:
274 PRServerConnection(host, port).terminate()
275 except:
276 logger.critical("Stop PRService %s:%d failed" % (host,port))
277 time.sleep(0.5)
278
279 try:
280 if pid:
281 if os.path.exists(pidfile):
282 os.remove(pidfile)
283 os.kill(pid,signal.SIGTERM)
284 time.sleep(0.1)
285 except OSError as e:
286 err = str(e)
287 if err.find("No such process") <= 0:
288 raise e
289
290 return 0
291
292def is_local_special(host, port):
293 if host.strip().upper() == 'localhost'.upper() and (not port):
294 return True
295 else:
296 return False
297
298class PRServiceConfigError(Exception):
299 pass
300
301def auto_start(d):
302 global singleton
303
304 host_params = filter(None, (d.getVar('PRSERV_HOST', True) or '').split(':'))
305 if not host_params:
306 return None
307
308 if len(host_params) != 2:
309 logger.critical('\n'.join(['PRSERV_HOST: incorrect format',
310 'Usage: PRSERV_HOST = "<hostname>:<port>"']))
311 raise PRServiceConfigError
312
313 if is_local_special(host_params[0], int(host_params[1])) and not singleton:
314 import bb.utils
315 cachedir = (d.getVar("PERSISTENT_DIR", True) or d.getVar("CACHE", True))
316 if not cachedir:
317 logger.critical("Please set the 'PERSISTENT_DIR' or 'CACHE' variable")
318 raise PRServiceConfigError
319 bb.utils.mkdirhier(cachedir)
320 dbfile = os.path.join(cachedir, "prserv.sqlite3")
321 logfile = os.path.join(cachedir, "prserv.log")
322 singleton = PRServSingleton(os.path.abspath(dbfile), os.path.abspath(logfile), ("localhost",0))
323 singleton.start()
324 if singleton:
325 host, port = singleton.getinfo()
326 else:
327 host = host_params[0]
328 port = int(host_params[1])
329
330 try:
331 connection = PRServerConnection(host,port)
332 connection.ping()
333 realhost, realport = connection.getinfo()
334 return str(realhost) + ":" + str(realport)
335
336 except Exception:
337 logger.critical("PRservice %s:%d not available" % (host, port))
338 raise PRServiceConfigError
339
340def auto_shutdown(d=None):
341 global singleton
342 if singleton:
343 host, port = singleton.getinfo()
344 try:
345 PRServerConnection(host, port).terminate()
346 except:
347 logger.critical("Stop PRService %s:%d failed" % (host,port))
348 singleton = None
349
350def ping(host, port):
351 conn=PRServerConnection(host, port)
352 return conn.ping()