blob: a9e997e1f6381bfb344e3a390c3df02d19cd8a31 [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002
3import os
4import sys
5import warnings
6sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
7from bb import fetch2
8import logging
9import bb
10import select
11import errno
12import signal
Patrick Williamsc0f7c042017-02-23 20:41:17 -060013import pickle
Brad Bishop37a0e4d2017-12-04 01:01:44 -050014import traceback
15import queue
Patrick Williamsf1e5d692016-03-30 15:21:19 -050016from multiprocessing import Lock
Brad Bishop37a0e4d2017-12-04 01:01:44 -050017from threading import Thread
Patrick Williamsc124f4f2015-09-15 14:41:29 -050018
Patrick Williamsc0f7c042017-02-23 20:41:17 -060019if sys.getfilesystemencoding() != "utf-8":
Brad Bishopd7bf8c12018-02-25 22:55:05 -050020 sys.exit("Please use a locale setting which supports UTF-8 (such as LANG=en_US.UTF-8).\nPython can't change the filesystem locale after loading so we need a UTF-8 when Python starts or things won't work.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060021
Patrick Williamsc124f4f2015-09-15 14:41:29 -050022# Users shouldn't be running this code directly
23if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
24 print("bitbake-worker is meant for internal execution by bitbake itself, please don't use it standalone.")
25 sys.exit(1)
26
27profiling = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050028if sys.argv[1].startswith("decafbadbad"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029 profiling = True
30 try:
31 import cProfile as profile
32 except:
33 import profile
34
35# Unbuffer stdout to avoid log truncation in the event
36# of an unorderly exit as well as to provide timely
37# updates to log files for use with tail
38try:
39 if sys.stdout.name == '<stdout>':
Patrick Williamsc0f7c042017-02-23 20:41:17 -060040 import fcntl
41 fl = fcntl.fcntl(sys.stdout.fileno(), fcntl.F_GETFL)
42 fl |= os.O_SYNC
43 fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, fl)
44 #sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045except:
46 pass
47
48logger = logging.getLogger("BitBake")
49
Patrick Williamsc124f4f2015-09-15 14:41:29 -050050worker_pipe = sys.stdout.fileno()
51bb.utils.nonblockingfd(worker_pipe)
Patrick Williamsf1e5d692016-03-30 15:21:19 -050052# Need to guard against multiprocessing being used in child processes
53# and multiple processes trying to write to the parent at the same time
54worker_pipe_lock = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055
56handler = bb.event.LogHandler()
57logger.addHandler(handler)
58
59if 0:
60 # Code to write out a log file of all events passing through the worker
61 logfilename = "/tmp/workerlogfile"
62 format_str = "%(levelname)s: %(message)s"
63 conlogformat = bb.msg.BBLogFormatter(format_str)
64 consolelog = logging.FileHandler(logfilename)
65 bb.msg.addDefaultlogFilter(consolelog)
66 consolelog.setFormatter(conlogformat)
67 logger.addHandler(consolelog)
68
Brad Bishop37a0e4d2017-12-04 01:01:44 -050069worker_queue = queue.Queue()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070
71def worker_fire(event, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060072 data = b"<event>" + pickle.dumps(event) + b"</event>"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050073 worker_fire_prepickled(data)
74
75def worker_fire_prepickled(event):
76 global worker_queue
77
Brad Bishop37a0e4d2017-12-04 01:01:44 -050078 worker_queue.put(event)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050079
Brad Bishop37a0e4d2017-12-04 01:01:44 -050080#
81# We can end up with write contention with the cooker, it can be trying to send commands
82# and we can be trying to send event data back. Therefore use a separate thread for writing
83# back data to cooker.
84#
85worker_thread_exit = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086
Brad Bishop37a0e4d2017-12-04 01:01:44 -050087def worker_flush(worker_queue):
88 worker_queue_int = b""
89 global worker_pipe, worker_thread_exit
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090
Brad Bishop37a0e4d2017-12-04 01:01:44 -050091 while True:
92 try:
93 worker_queue_int = worker_queue_int + worker_queue.get(True, 1)
94 except queue.Empty:
95 pass
96 while (worker_queue_int or not worker_queue.empty()):
97 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050098 (_, ready, _) = select.select([], [worker_pipe], [], 1)
Brad Bishop37a0e4d2017-12-04 01:01:44 -050099 if not worker_queue.empty():
100 worker_queue_int = worker_queue_int + worker_queue.get()
101 written = os.write(worker_pipe, worker_queue_int)
102 worker_queue_int = worker_queue_int[written:]
103 except (IOError, OSError) as e:
104 if e.errno != errno.EAGAIN and e.errno != errno.EPIPE:
105 raise
106 if worker_thread_exit and worker_queue.empty() and not worker_queue_int:
107 return
108
109worker_thread = Thread(target=worker_flush, args=(worker_queue,))
110worker_thread.start()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111
112def worker_child_fire(event, d):
113 global worker_pipe
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500114 global worker_pipe_lock
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600116 data = b"<event>" + pickle.dumps(event) + b"</event>"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500117 try:
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500118 worker_pipe_lock.acquire()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119 worker_pipe.write(data)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500120 worker_pipe_lock.release()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500121 except IOError:
122 sigterm_handler(None, None)
123 raise
124
125bb.event.worker_fire = worker_fire
126
127lf = None
128#lf = open("/tmp/workercommandlog", "w+")
129def workerlog_write(msg):
130 if lf:
131 lf.write(msg)
132 lf.flush()
133
134def sigterm_handler(signum, frame):
135 signal.signal(signal.SIGTERM, signal.SIG_DFL)
136 os.killpg(0, signal.SIGTERM)
137 sys.exit()
138
Brad Bishop19323692019-04-05 15:28:33 -0400139def fork_off_task(cfg, data, databuilder, workerdata, fn, task, taskname, taskhash, unihash, appends, taskdepdata, extraconfigdata, quieterrors=False, dry_run_exec=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500140 # We need to setup the environment BEFORE the fork, since
141 # a fork() or exec*() activates PSEUDO...
142
143 envbackup = {}
144 fakeenv = {}
145 umask = None
146
147 taskdep = workerdata["taskdeps"][fn]
148 if 'umask' in taskdep and taskname in taskdep['umask']:
149 # umask might come in as a number or text string..
150 try:
151 umask = int(taskdep['umask'][taskname],8)
152 except TypeError:
153 umask = taskdep['umask'][taskname]
154
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500155 dry_run = cfg.dry_run or dry_run_exec
156
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500157 # We can't use the fakeroot environment in a dry run as it possibly hasn't been built
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500158 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not dry_run:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500159 envvars = (workerdata["fakerootenv"][fn] or "").split()
160 for key, value in (var.split('=') for var in envvars):
161 envbackup[key] = os.environ.get(key)
162 os.environ[key] = value
163 fakeenv[key] = value
164
165 fakedirs = (workerdata["fakerootdirs"][fn] or "").split()
166 for p in fakedirs:
167 bb.utils.mkdirhier(p)
168 logger.debug(2, 'Running %s:%s under fakeroot, fakedirs: %s' %
169 (fn, taskname, ', '.join(fakedirs)))
170 else:
171 envvars = (workerdata["fakerootnoenv"][fn] or "").split()
172 for key, value in (var.split('=') for var in envvars):
173 envbackup[key] = os.environ.get(key)
174 os.environ[key] = value
175 fakeenv[key] = value
176
177 sys.stdout.flush()
178 sys.stderr.flush()
179
180 try:
181 pipein, pipeout = os.pipe()
182 pipein = os.fdopen(pipein, 'rb', 4096)
183 pipeout = os.fdopen(pipeout, 'wb', 0)
184 pid = os.fork()
185 except OSError as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600186 logger.critical("fork failed: %d (%s)" % (e.errno, e.strerror))
187 sys.exit(1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500188
189 if pid == 0:
190 def child():
191 global worker_pipe
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500192 global worker_pipe_lock
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500193 pipein.close()
194
195 signal.signal(signal.SIGTERM, sigterm_handler)
196 # Let SIGHUP exit as SIGTERM
197 signal.signal(signal.SIGHUP, sigterm_handler)
198 bb.utils.signal_on_parent_exit("SIGTERM")
199
200 # Save out the PID so that the event can include it the
201 # events
202 bb.event.worker_pid = os.getpid()
203 bb.event.worker_fire = worker_child_fire
204 worker_pipe = pipeout
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500205 worker_pipe_lock = Lock()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500206
207 # Make the child the process group leader and ensure no
208 # child process will be controlled by the current terminal
209 # This ensures signals sent to the controlling terminal like Ctrl+C
210 # don't stop the child processes.
211 os.setsid()
212 # No stdin
213 newsi = os.open(os.devnull, os.O_RDWR)
214 os.dup2(newsi, sys.stdin.fileno())
215
216 if umask:
217 os.umask(umask)
218
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600220 bb_cache = bb.cache.NoCache(databuilder)
221 (realfn, virtual, mc) = bb.cache.virtualfn2realfn(fn)
222 the_data = databuilder.mcdata[mc]
223 the_data.setVar("BB_WORKERCONTEXT", "1")
224 the_data.setVar("BB_TASKDEPDATA", taskdepdata)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500225 if cfg.limited_deps:
226 the_data.setVar("BB_LIMITEDDEPS", "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600227 the_data.setVar("BUILDNAME", workerdata["buildname"])
228 the_data.setVar("DATE", workerdata["date"])
229 the_data.setVar("TIME", workerdata["time"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500230 for varname, value in extraconfigdata.items():
231 the_data.setVar(varname, value)
232
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600233 bb.parse.siggen.set_taskdata(workerdata["sigdata"])
234 ret = 0
235
236 the_data = bb_cache.loadDataFull(fn, appends)
Brad Bishop19323692019-04-05 15:28:33 -0400237 the_data.setVar('BB_TASKHASH', taskhash)
238 the_data.setVar('BB_UNIHASH', unihash)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500239
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500240 bb.utils.set_process_name("%s:%s" % (the_data.getVar("PN"), taskname.replace("do_", "")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500241
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500242 # exported_vars() returns a generator which *cannot* be passed to os.environ.update()
243 # successfully. We also need to unset anything from the environment which shouldn't be there
244 exports = bb.data.exported_vars(the_data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600245
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500246 bb.utils.empty_environment()
247 for e, v in exports:
248 os.environ[e] = v
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600249
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500250 for e in fakeenv:
251 os.environ[e] = fakeenv[e]
252 the_data.setVar(e, fakeenv[e])
253 the_data.setVarFlag(e, 'export', "1")
254
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500255 task_exports = the_data.getVarFlag(taskname, 'exports')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600256 if task_exports:
257 for e in task_exports.split():
258 the_data.setVarFlag(e, 'export', '1')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500259 v = the_data.getVar(e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600260 if v is not None:
261 os.environ[e] = v
262
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500263 if quieterrors:
264 the_data.setVarFlag(taskname, "quieterrors", "1")
265
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500266 except Exception:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267 if not quieterrors:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500268 logger.critical(traceback.format_exc())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269 os._exit(1)
270 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500271 if dry_run:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272 return 0
273 return bb.build.exec_task(fn, taskname, the_data, cfg.profile)
274 except:
275 os._exit(1)
276 if not profiling:
277 os._exit(child())
278 else:
279 profname = "profile-%s.log" % (fn.replace("/", "-") + "-" + taskname)
280 prof = profile.Profile()
281 try:
282 ret = profile.Profile.runcall(prof, child)
283 finally:
284 prof.dump_stats(profname)
285 bb.utils.process_profilelog(profname)
286 os._exit(ret)
287 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600288 for key, value in iter(envbackup.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500289 if value is None:
290 del os.environ[key]
291 else:
292 os.environ[key] = value
293
294 return pid, pipein, pipeout
295
296class runQueueWorkerPipe():
297 """
298 Abstraction for a pipe between a worker thread and the worker server
299 """
300 def __init__(self, pipein, pipeout):
301 self.input = pipein
302 if pipeout:
303 pipeout.close()
304 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500306
307 def read(self):
308 start = len(self.queue)
309 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600310 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311 except (OSError, IOError) as e:
312 if e.errno != errno.EAGAIN:
313 raise
314
315 end = len(self.queue)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316 index = self.queue.find(b"</event>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 while index != -1:
318 worker_fire_prepickled(self.queue[:index+8])
319 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600320 index = self.queue.find(b"</event>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321 return (end > start)
322
323 def close(self):
324 while self.read():
325 continue
326 if len(self.queue) > 0:
327 print("Warning, worker child left partial message: %s" % self.queue)
328 self.input.close()
329
330normalexit = False
331
332class BitbakeWorker(object):
333 def __init__(self, din):
334 self.input = din
335 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600336 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500337 self.cookercfg = None
338 self.databuilder = None
339 self.data = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500340 self.extraconfigdata = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341 self.build_pids = {}
342 self.build_pipes = {}
343
344 signal.signal(signal.SIGTERM, self.sigterm_exception)
345 # Let SIGHUP exit as SIGTERM
346 signal.signal(signal.SIGHUP, self.sigterm_exception)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500347 if "beef" in sys.argv[1]:
348 bb.utils.set_process_name("Worker (Fakeroot)")
349 else:
350 bb.utils.set_process_name("Worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351
352 def sigterm_exception(self, signum, stackframe):
353 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500354 bb.warn("Worker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500356 bb.warn("Worker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500357 self.handle_finishnow(None)
358 signal.signal(signal.SIGTERM, signal.SIG_DFL)
359 os.kill(os.getpid(), signal.SIGTERM)
360
361 def serve(self):
362 while True:
363 (ready, _, _) = select.select([self.input] + [i.input for i in self.build_pipes.values()], [] , [], 1)
364 if self.input in ready:
365 try:
366 r = self.input.read()
367 if len(r) == 0:
368 # EOF on pipe, server must have terminated
369 self.sigterm_exception(signal.SIGTERM, None)
370 self.queue = self.queue + r
371 except (OSError, IOError):
372 pass
373 if len(self.queue):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600374 self.handle_item(b"cookerconfig", self.handle_cookercfg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500375 self.handle_item(b"extraconfigdata", self.handle_extraconfigdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600376 self.handle_item(b"workerdata", self.handle_workerdata)
377 self.handle_item(b"runtask", self.handle_runtask)
378 self.handle_item(b"finishnow", self.handle_finishnow)
379 self.handle_item(b"ping", self.handle_ping)
380 self.handle_item(b"quit", self.handle_quit)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500381
382 for pipe in self.build_pipes:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500383 if self.build_pipes[pipe].input in ready:
384 self.build_pipes[pipe].read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500385 if len(self.build_pids):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500386 while self.process_waitpid():
387 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500388
389
390 def handle_item(self, item, func):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600391 if self.queue.startswith(b"<" + item + b">"):
392 index = self.queue.find(b"</" + item + b">")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393 while index != -1:
394 func(self.queue[(len(item) + 2):index])
395 self.queue = self.queue[(index + len(item) + 3):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600396 index = self.queue.find(b"</" + item + b">")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500397
398 def handle_cookercfg(self, data):
399 self.cookercfg = pickle.loads(data)
400 self.databuilder = bb.cookerdata.CookerDataBuilder(self.cookercfg, worker=True)
401 self.databuilder.parseBaseConfiguration()
402 self.data = self.databuilder.data
403
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500404 def handle_extraconfigdata(self, data):
405 self.extraconfigdata = pickle.loads(data)
406
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407 def handle_workerdata(self, data):
408 self.workerdata = pickle.loads(data)
409 bb.msg.loggerDefaultDebugLevel = self.workerdata["logdefaultdebug"]
410 bb.msg.loggerDefaultVerbose = self.workerdata["logdefaultverbose"]
411 bb.msg.loggerVerboseLogs = self.workerdata["logdefaultverboselogs"]
412 bb.msg.loggerDefaultDomains = self.workerdata["logdefaultdomain"]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600413 for mc in self.databuilder.mcdata:
414 self.databuilder.mcdata[mc].setVar("PRSERV_HOST", self.workerdata["prhost"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500415
416 def handle_ping(self, _):
417 workerlog_write("Handling ping\n")
418
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600419 logger.warning("Pong from bitbake-worker!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420
421 def handle_quit(self, data):
422 workerlog_write("Handling quit\n")
423
424 global normalexit
425 normalexit = True
426 sys.exit(0)
427
428 def handle_runtask(self, data):
Brad Bishop19323692019-04-05 15:28:33 -0400429 fn, task, taskname, taskhash, unihash, quieterrors, appends, taskdepdata, dry_run_exec = pickle.loads(data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500430 workerlog_write("Handling runtask %s %s %s\n" % (task, fn, taskname))
431
Brad Bishop19323692019-04-05 15:28:33 -0400432 pid, pipein, pipeout = fork_off_task(self.cookercfg, self.data, self.databuilder, self.workerdata, fn, task, taskname, taskhash, unihash, appends, taskdepdata, self.extraconfigdata, quieterrors, dry_run_exec)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433
434 self.build_pids[pid] = task
435 self.build_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
436
437 def process_waitpid(self):
438 """
439 Return none is there are no processes awaiting result collection, otherwise
440 collect the process exit codes and close the information pipe.
441 """
442 try:
443 pid, status = os.waitpid(-1, os.WNOHANG)
444 if pid == 0 or os.WIFSTOPPED(status):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500445 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 except OSError:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500447 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500448
449 workerlog_write("Exit code of %s for pid %s\n" % (status, pid))
450
451 if os.WIFEXITED(status):
452 status = os.WEXITSTATUS(status)
453 elif os.WIFSIGNALED(status):
454 # Per shell conventions for $?, when a process exits due to
455 # a signal, we return an exit code of 128 + SIGNUM
456 status = 128 + os.WTERMSIG(status)
457
458 task = self.build_pids[pid]
459 del self.build_pids[pid]
460
461 self.build_pipes[pid].close()
462 del self.build_pipes[pid]
463
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600464 worker_fire_prepickled(b"<exitcode>" + pickle.dumps((task, status)) + b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500465
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500466 return True
467
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468 def handle_finishnow(self, _):
469 if self.build_pids:
470 logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600471 for k, v in iter(self.build_pids.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500472 try:
473 os.kill(-k, signal.SIGTERM)
474 os.waitpid(-1, 0)
475 except:
476 pass
477 for pipe in self.build_pipes:
478 self.build_pipes[pipe].read()
479
480try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600481 worker = BitbakeWorker(os.fdopen(sys.stdin.fileno(), 'rb'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 if not profiling:
483 worker.serve()
484 else:
485 profname = "profile-worker.log"
486 prof = profile.Profile()
487 try:
488 profile.Profile.runcall(prof, worker.serve)
489 finally:
490 prof.dump_stats(profname)
491 bb.utils.process_profilelog(profname)
492except BaseException as e:
493 if not normalexit:
494 import traceback
495 sys.stderr.write(traceback.format_exc())
496 sys.stderr.write(str(e))
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500497
498worker_thread_exit = True
499worker_thread.join()
500
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500501workerlog_write("exitting")
502sys.exit(0)