serv.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. import os,sys,logging
  2. import signal, time, atexit, threading
  3. from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
  4. import xmlrpclib
  5. try:
  6. import sqlite3
  7. except ImportError:
  8. from pysqlite2 import dbapi2 as sqlite3
  9. import bb.server.xmlrpc
  10. import prserv
  11. import prserv.db
  12. logger = logging.getLogger("BitBake.PRserv")
  13. if sys.hexversion < 0x020600F0:
  14. print("Sorry, python 2.6 or later is required.")
  15. sys.exit(1)
  16. class Handler(SimpleXMLRPCRequestHandler):
  17. def _dispatch(self,method,params):
  18. try:
  19. value=self.server.funcs[method](*params)
  20. except:
  21. import traceback
  22. traceback.print_exc()
  23. raise
  24. return value
  25. PIDPREFIX = "/tmp/PRServer_%s_%s.pid"
  26. singleton = None
  27. class PRServer(SimpleXMLRPCServer):
  28. def __init__(self, dbfile, logfile, interface, daemon=True):
  29. ''' constructor '''
  30. SimpleXMLRPCServer.__init__(self, interface,
  31. requestHandler=SimpleXMLRPCRequestHandler,
  32. logRequests=False, allow_none=True)
  33. self.dbfile=dbfile
  34. self.daemon=daemon
  35. self.logfile=logfile
  36. self.working_thread=None
  37. self.host, self.port = self.socket.getsockname()
  38. self.db=prserv.db.PRData(dbfile)
  39. self.table=self.db["PRMAIN"]
  40. self.pidfile=PIDPREFIX % (self.host, self.port)
  41. self.register_function(self.getPR, "getPR")
  42. self.register_function(self.quit, "quit")
  43. self.register_function(self.ping, "ping")
  44. self.register_function(self.export, "export")
  45. self.register_function(self.importone, "importone")
  46. self.register_introspection_functions()
  47. def export(self, version=None, pkgarch=None, checksum=None, colinfo=True):
  48. try:
  49. return self.table.export(version, pkgarch, checksum, colinfo)
  50. except sqlite3.Error as exc:
  51. logger.error(str(exc))
  52. return None
  53. def importone(self, version, pkgarch, checksum, value):
  54. return self.table.importone(version, pkgarch, checksum, value)
  55. def ping(self):
  56. return not self.quit
  57. def getinfo(self):
  58. return (self.host, self.port)
  59. def getPR(self, version, pkgarch, checksum):
  60. try:
  61. return self.table.getValue(version, pkgarch, checksum)
  62. except prserv.NotFoundError:
  63. logger.error("can not find value for (%s, %s)",version, checksum)
  64. return None
  65. except sqlite3.Error as exc:
  66. logger.error(str(exc))
  67. return None
  68. def quit(self):
  69. self.quit=True
  70. return
  71. def work_forever(self,):
  72. self.quit = False
  73. self.timeout = 0.5
  74. logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
  75. (self.dbfile, self.host, self.port, str(os.getpid())))
  76. while not self.quit:
  77. self.handle_request()
  78. logger.info("PRServer: stopping...")
  79. self.server_close()
  80. return
  81. def start(self):
  82. pid = self.daemonize()
  83. # Ensure both the parent sees this and the child from the work_forever log entry above
  84. logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
  85. (self.dbfile, self.host, self.port, str(pid)))
  86. def delpid(self):
  87. os.remove(self.pidfile)
  88. def daemonize(self):
  89. """
  90. See Advanced Programming in the UNIX, Sec 13.3
  91. """
  92. try:
  93. pid = os.fork()
  94. if pid > 0:
  95. os.waitpid(pid, 0)
  96. #parent return instead of exit to give control
  97. return pid
  98. except OSError as e:
  99. raise Exception("%s [%d]" % (e.strerror, e.errno))
  100. os.setsid()
  101. """
  102. fork again to make sure the daemon is not session leader,
  103. which prevents it from acquiring controlling terminal
  104. """
  105. try:
  106. pid = os.fork()
  107. if pid > 0: #parent
  108. os._exit(0)
  109. except OSError as e:
  110. raise Exception("%s [%d]" % (e.strerror, e.errno))
  111. os.umask(0)
  112. os.chdir("/")
  113. sys.stdout.flush()
  114. sys.stderr.flush()
  115. si = file('/dev/null', 'r')
  116. so = file(self.logfile, 'a+')
  117. se = so
  118. os.dup2(si.fileno(),sys.stdin.fileno())
  119. os.dup2(so.fileno(),sys.stdout.fileno())
  120. os.dup2(se.fileno(),sys.stderr.fileno())
  121. # Ensure logging makes it to the logfile
  122. streamhandler = logging.StreamHandler()
  123. streamhandler.setLevel(logging.DEBUG)
  124. formatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
  125. streamhandler.setFormatter(formatter)
  126. logger.addHandler(streamhandler)
  127. # write pidfile
  128. pid = str(os.getpid())
  129. pf = file(self.pidfile, 'w')
  130. pf.write("%s\n" % pid)
  131. pf.close()
  132. self.work_forever()
  133. self.delpid
  134. os._exit(0)
  135. class PRServSingleton():
  136. def __init__(self, dbfile, logfile, interface):
  137. self.dbfile = dbfile
  138. self.logfile = logfile
  139. self.interface = interface
  140. self.host = None
  141. self.port = None
  142. def start(self):
  143. self.prserv = PRServer(self.dbfile, self.logfile, self.interface)
  144. self.prserv.start()
  145. self.host, self.port = self.prserv.getinfo()
  146. del self.prserv.db
  147. def getinfo(self):
  148. return (self.host, self.port)
  149. class PRServerConnection():
  150. def __init__(self, host, port):
  151. if is_local_special(host, port):
  152. host, port = singleton.getinfo()
  153. self.host = host
  154. self.port = port
  155. self.connection, self.transport = bb.server.xmlrpc._create_server(self.host, self.port)
  156. def terminate(self):
  157. # Don't wait for server indefinitely
  158. import socket
  159. socket.setdefaulttimeout(2)
  160. try:
  161. logger.info("Terminating PRServer...")
  162. self.connection.quit()
  163. except Exception as exc:
  164. sys.stderr.write("%s\n" % str(exc))
  165. def getPR(self, version, pkgarch, checksum):
  166. return self.connection.getPR(version, pkgarch, checksum)
  167. def ping(self):
  168. return self.connection.ping()
  169. def export(self,version=None, pkgarch=None, checksum=None, colinfo=True):
  170. return self.connection.export(version, pkgarch, checksum, colinfo)
  171. def importone(self, version, pkgarch, checksum, value):
  172. return self.connection.importone(version, pkgarch, checksum, value)
  173. def getinfo(self):
  174. return self.host, self.port
  175. def start_daemon(dbfile, host, port, logfile):
  176. pidfile = PIDPREFIX % (host, port)
  177. try:
  178. pf = file(pidfile,'r')
  179. pid = int(pf.readline().strip())
  180. pf.close()
  181. except IOError:
  182. pid = None
  183. if pid:
  184. sys.stderr.write("pidfile %s already exist. Daemon already running?\n"
  185. % pidfile)
  186. return 1
  187. server = PRServer(os.path.abspath(dbfile), os.path.abspath(logfile), (host,port))
  188. server.start()
  189. return 0
  190. def stop_daemon(host, port):
  191. pidfile = PIDPREFIX % (host, port)
  192. try:
  193. pf = file(pidfile,'r')
  194. pid = int(pf.readline().strip())
  195. pf.close()
  196. except IOError:
  197. pid = None
  198. if not pid:
  199. sys.stderr.write("pidfile %s does not exist. Daemon not running?\n"
  200. % pidfile)
  201. try:
  202. PRServerConnection(host, port).terminate()
  203. except:
  204. logger.critical("Stop PRService %s:%d failed" % (host,port))
  205. time.sleep(0.5)
  206. try:
  207. if pid:
  208. if os.path.exists(pidfile):
  209. os.remove(pidfile)
  210. os.kill(pid,signal.SIGTERM)
  211. time.sleep(0.1)
  212. except OSError as e:
  213. err = str(e)
  214. if err.find("No such process") <= 0:
  215. raise e
  216. return 0
  217. def is_local_special(host, port):
  218. if host.strip().upper() == 'localhost'.upper() and (not port):
  219. return True
  220. else:
  221. return False
  222. class PRServiceConfigError(Exception):
  223. pass
  224. def auto_start(d):
  225. global singleton
  226. host_params = filter(None, (d.getVar('PRSERV_HOST', True) or '').split(':'))
  227. if not host_params:
  228. return None
  229. if len(host_params) != 2:
  230. logger.critical('\n'.join(['PRSERV_HOST: incorrect format',
  231. 'Usage: PRSERV_HOST = "<hostname>:<port>"']))
  232. raise PRServiceConfigError
  233. if is_local_special(host_params[0], int(host_params[1])) and not singleton:
  234. import bb.utils
  235. cachedir = (d.getVar("PERSISTENT_DIR", True) or d.getVar("CACHE", True))
  236. if not cachedir:
  237. logger.critical("Please set the 'PERSISTENT_DIR' or 'CACHE' variable")
  238. raise PRServiceConfigError
  239. bb.utils.mkdirhier(cachedir)
  240. dbfile = os.path.join(cachedir, "prserv.sqlite3")
  241. logfile = os.path.join(cachedir, "prserv.log")
  242. singleton = PRServSingleton(os.path.abspath(dbfile), os.path.abspath(logfile), ("localhost",0))
  243. singleton.start()
  244. if singleton:
  245. host, port = singleton.getinfo()
  246. else:
  247. host = host_params[0]
  248. port = int(host_params[1])
  249. try:
  250. connection = PRServerConnection(host,port)
  251. connection.ping()
  252. realhost, realport = connection.getinfo()
  253. return str(realhost) + ":" + str(realport)
  254. except Exception:
  255. logger.critical("PRservice %s:%d not available" % (host, port))
  256. raise PRServiceConfigError
  257. def auto_shutdown(d=None):
  258. global singleton
  259. if singleton:
  260. host, port = singleton.getinfo()
  261. try:
  262. PRServerConnection(host, port).terminate()
  263. except:
  264. logger.critical("Stop PRService %s:%d failed" % (host,port))
  265. singleton = None
  266. def ping(host, port):
  267. conn=PRServerConnection(host, port)
  268. return conn.ping()