diff options
| -rw-r--r-- | bitbake/lib/prserv/serv.py | 175 |
1 files changed, 72 insertions, 103 deletions
diff --git a/bitbake/lib/prserv/serv.py b/bitbake/lib/prserv/serv.py index 5d6520da80..24a8c4bacf 100644 --- a/bitbake/lib/prserv/serv.py +++ b/bitbake/lib/prserv/serv.py | |||
| @@ -34,7 +34,7 @@ singleton = None | |||
| 34 | 34 | ||
| 35 | 35 | ||
| 36 | class PRServer(SimpleXMLRPCServer): | 36 | class PRServer(SimpleXMLRPCServer): |
| 37 | def __init__(self, dbfile, logfile, interface, daemon=True): | 37 | def __init__(self, dbfile, logfile, interface): |
| 38 | ''' constructor ''' | 38 | ''' constructor ''' |
| 39 | try: | 39 | try: |
| 40 | SimpleXMLRPCServer.__init__(self, interface, | 40 | SimpleXMLRPCServer.__init__(self, interface, |
| @@ -47,7 +47,6 @@ class PRServer(SimpleXMLRPCServer): | |||
| 47 | raise PRServiceConfigError | 47 | raise PRServiceConfigError |
| 48 | 48 | ||
| 49 | self.dbfile=dbfile | 49 | self.dbfile=dbfile |
| 50 | self.daemon=daemon | ||
| 51 | self.logfile=logfile | 50 | self.logfile=logfile |
| 52 | self.working_thread=None | 51 | self.working_thread=None |
| 53 | self.host, self.port = self.socket.getsockname() | 52 | self.host, self.port = self.socket.getsockname() |
| @@ -176,106 +175,6 @@ class PRServer(SimpleXMLRPCServer): | |||
| 176 | os.close(self.quitpipein) | 175 | os.close(self.quitpipein) |
| 177 | return | 176 | return |
| 178 | 177 | ||
| 179 | def start(self): | ||
| 180 | if self.daemon: | ||
| 181 | pid = self.daemonize() | ||
| 182 | else: | ||
| 183 | pid = self.fork() | ||
| 184 | self.pid = pid | ||
| 185 | |||
| 186 | # Ensure both the parent sees this and the child from the work_forever log entry above | ||
| 187 | logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" % | ||
| 188 | (self.dbfile, self.host, self.port, str(pid))) | ||
| 189 | |||
| 190 | def delpid(self): | ||
| 191 | os.remove(self.pidfile) | ||
| 192 | |||
| 193 | def daemonize(self): | ||
| 194 | """ | ||
| 195 | See Advanced Programming in the UNIX, Sec 13.3 | ||
| 196 | """ | ||
| 197 | try: | ||
| 198 | pid = os.fork() | ||
| 199 | if pid > 0: | ||
| 200 | os.waitpid(pid, 0) | ||
| 201 | #parent return instead of exit to give control | ||
| 202 | return pid | ||
| 203 | except OSError as e: | ||
| 204 | raise Exception("%s [%d]" % (e.strerror, e.errno)) | ||
| 205 | |||
| 206 | os.setsid() | ||
| 207 | """ | ||
| 208 | fork again to make sure the daemon is not session leader, | ||
| 209 | which prevents it from acquiring controlling terminal | ||
| 210 | """ | ||
| 211 | try: | ||
| 212 | pid = os.fork() | ||
| 213 | if pid > 0: #parent | ||
| 214 | os._exit(0) | ||
| 215 | except OSError as e: | ||
| 216 | raise Exception("%s [%d]" % (e.strerror, e.errno)) | ||
| 217 | |||
| 218 | self.cleanup_handles() | ||
| 219 | os._exit(0) | ||
| 220 | |||
| 221 | def fork(self): | ||
| 222 | try: | ||
| 223 | pid = os.fork() | ||
| 224 | if pid > 0: | ||
| 225 | self.socket.close() # avoid ResourceWarning in parent | ||
| 226 | return pid | ||
| 227 | except OSError as e: | ||
| 228 | raise Exception("%s [%d]" % (e.strerror, e.errno)) | ||
| 229 | |||
| 230 | bb.utils.signal_on_parent_exit("SIGTERM") | ||
| 231 | self.cleanup_handles() | ||
| 232 | os._exit(0) | ||
| 233 | |||
| 234 | def cleanup_handles(self): | ||
| 235 | os.chdir("/") | ||
| 236 | |||
| 237 | sys.stdout.flush() | ||
| 238 | sys.stderr.flush() | ||
| 239 | |||
| 240 | # We could be called from a python thread with io.StringIO as | ||
| 241 | # stdout/stderr or it could be 'real' unix fd forking where we need | ||
| 242 | # to physically close the fds to prevent the program launching us from | ||
| 243 | # potentially hanging on a pipe. Handle both cases. | ||
| 244 | si = open('/dev/null', 'r') | ||
| 245 | try: | ||
| 246 | os.dup2(si.fileno(),sys.stdin.fileno()) | ||
| 247 | except (AttributeError, io.UnsupportedOperation): | ||
| 248 | sys.stdin = si | ||
| 249 | so = open(self.logfile, 'a+') | ||
| 250 | try: | ||
| 251 | os.dup2(so.fileno(),sys.stdout.fileno()) | ||
| 252 | except (AttributeError, io.UnsupportedOperation): | ||
| 253 | sys.stdout = so | ||
| 254 | try: | ||
| 255 | os.dup2(so.fileno(),sys.stderr.fileno()) | ||
| 256 | except (AttributeError, io.UnsupportedOperation): | ||
| 257 | sys.stderr = so | ||
| 258 | |||
| 259 | # Clear out all log handlers prior to the fork() to avoid calling | ||
| 260 | # event handlers not part of the PRserver | ||
| 261 | for logger_iter in logging.Logger.manager.loggerDict.keys(): | ||
| 262 | logging.getLogger(logger_iter).handlers = [] | ||
| 263 | |||
| 264 | # Ensure logging makes it to the logfile | ||
| 265 | streamhandler = logging.StreamHandler() | ||
| 266 | streamhandler.setLevel(logging.DEBUG) | ||
| 267 | formatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s") | ||
| 268 | streamhandler.setFormatter(formatter) | ||
| 269 | logger.addHandler(streamhandler) | ||
| 270 | |||
| 271 | # write pidfile | ||
| 272 | pid = str(os.getpid()) | ||
| 273 | with open(self.pidfile, 'w') as pf: | ||
| 274 | pf.write("%s\n" % pid) | ||
| 275 | |||
| 276 | self.work_forever() | ||
| 277 | self.delpid() | ||
| 278 | |||
| 279 | class PRServSingleton(object): | 178 | class PRServSingleton(object): |
| 280 | def __init__(self, dbfile, logfile, interface): | 179 | def __init__(self, dbfile, logfile, interface): |
| 281 | self.dbfile = dbfile | 180 | self.dbfile = dbfile |
| @@ -324,6 +223,76 @@ class PRServerConnection(object): | |||
| 324 | def getinfo(self): | 223 | def getinfo(self): |
| 325 | return self.host, self.port | 224 | return self.host, self.port |
| 326 | 225 | ||
| 226 | def run_as_daemon(func, pidfile, logfile): | ||
| 227 | """ | ||
| 228 | See Advanced Programming in the UNIX, Sec 13.3 | ||
| 229 | """ | ||
| 230 | try: | ||
| 231 | pid = os.fork() | ||
| 232 | if pid > 0: | ||
| 233 | os.waitpid(pid, 0) | ||
| 234 | #parent return instead of exit to give control | ||
| 235 | return pid | ||
| 236 | except OSError as e: | ||
| 237 | raise Exception("%s [%d]" % (e.strerror, e.errno)) | ||
| 238 | |||
| 239 | os.setsid() | ||
| 240 | """ | ||
| 241 | fork again to make sure the daemon is not session leader, | ||
| 242 | which prevents it from acquiring controlling terminal | ||
| 243 | """ | ||
| 244 | try: | ||
| 245 | pid = os.fork() | ||
| 246 | if pid > 0: #parent | ||
| 247 | os._exit(0) | ||
| 248 | except OSError as e: | ||
| 249 | raise Exception("%s [%d]" % (e.strerror, e.errno)) | ||
| 250 | |||
| 251 | os.chdir("/") | ||
| 252 | |||
| 253 | sys.stdout.flush() | ||
| 254 | sys.stderr.flush() | ||
| 255 | |||
| 256 | # We could be called from a python thread with io.StringIO as | ||
| 257 | # stdout/stderr or it could be 'real' unix fd forking where we need | ||
| 258 | # to physically close the fds to prevent the program launching us from | ||
| 259 | # potentially hanging on a pipe. Handle both cases. | ||
| 260 | si = open('/dev/null', 'r') | ||
| 261 | try: | ||
| 262 | os.dup2(si.fileno(),sys.stdin.fileno()) | ||
| 263 | except (AttributeError, io.UnsupportedOperation): | ||
| 264 | sys.stdin = si | ||
| 265 | so = open(logfile, 'a+') | ||
| 266 | try: | ||
| 267 | os.dup2(so.fileno(),sys.stdout.fileno()) | ||
| 268 | except (AttributeError, io.UnsupportedOperation): | ||
| 269 | sys.stdout = so | ||
| 270 | try: | ||
| 271 | os.dup2(so.fileno(),sys.stderr.fileno()) | ||
| 272 | except (AttributeError, io.UnsupportedOperation): | ||
| 273 | sys.stderr = so | ||
| 274 | |||
| 275 | # Clear out all log handlers prior to the fork() to avoid calling | ||
| 276 | # event handlers not part of the PRserver | ||
| 277 | for logger_iter in logging.Logger.manager.loggerDict.keys(): | ||
| 278 | logging.getLogger(logger_iter).handlers = [] | ||
| 279 | |||
| 280 | # Ensure logging makes it to the logfile | ||
| 281 | streamhandler = logging.StreamHandler() | ||
| 282 | streamhandler.setLevel(logging.DEBUG) | ||
| 283 | formatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s") | ||
| 284 | streamhandler.setFormatter(formatter) | ||
| 285 | logger.addHandler(streamhandler) | ||
| 286 | |||
| 287 | # write pidfile | ||
| 288 | pid = str(os.getpid()) | ||
| 289 | with open(pidfile, 'w') as pf: | ||
| 290 | pf.write("%s\n" % pid) | ||
| 291 | |||
| 292 | func() | ||
| 293 | os.remove(pidfile) | ||
| 294 | os._exit(0) | ||
| 295 | |||
| 327 | def start_daemon(dbfile, host, port, logfile): | 296 | def start_daemon(dbfile, host, port, logfile): |
| 328 | ip = socket.gethostbyname(host) | 297 | ip = socket.gethostbyname(host) |
| 329 | pidfile = PIDPREFIX % (ip, port) | 298 | pidfile = PIDPREFIX % (ip, port) |
| @@ -339,7 +308,7 @@ def start_daemon(dbfile, host, port, logfile): | |||
| 339 | return 1 | 308 | return 1 |
| 340 | 309 | ||
| 341 | server = PRServer(os.path.abspath(dbfile), os.path.abspath(logfile), (ip,port)) | 310 | server = PRServer(os.path.abspath(dbfile), os.path.abspath(logfile), (ip,port)) |
| 342 | server.start() | 311 | run_as_daemon(server.work_forever, pidfile, os.path.abspath(logfile)) |
| 343 | 312 | ||
| 344 | # Sometimes, the port (i.e. localhost:0) indicated by the user does not match with | 313 | # Sometimes, the port (i.e. localhost:0) indicated by the user does not match with |
| 345 | # the one the server actually is listening, so at least warn the user about it | 314 | # the one the server actually is listening, so at least warn the user about it |
