diff options
author | Paul Barker <pbarker@konsulko.com> | 2021-04-29 15:11:11 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-05-06 11:08:07 +0100 |
commit | d66a1d83f53811da8ecbee91e9d0c3f189b661ea (patch) | |
tree | a867d87b48ef690a2519a5164c232bfb1b8beb45 /bitbake | |
parent | 802024fd2a63bdbffbfaf125513561799452e4d2 (diff) | |
download | poky-d66a1d83f53811da8ecbee91e9d0c3f189b661ea.tar.gz |
bitbake: prserv: Extract daemonization from PRServer class
The code to start the prservice process as a daemon is extracted out of
the PRServer class and simplified. This makes the PRServer class easier
to modernise as it no longer needs to worry about process management.
(Bitbake rev: 39c7c158c52157b18f5ccbbd673e3298e6402f52)
Signed-off-by: Paul Barker <pbarker@konsulko.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-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 |