blob: 7be39370b326f6c522556a5123334cb734433a01 [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
Brad Bishopc342db32019-05-15 21:57:59 -04002#
Patrick Williams92b42cb2022-09-03 06:53:57 -05003# Copyright BitBake Contributors
4#
Brad Bishopc342db32019-05-15 21:57:59 -04005# SPDX-License-Identifier: GPL-2.0-only
6#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007
8import os
9import sys
10import warnings
Andrew Geissler5199d832021-09-24 16:47:35 -050011warnings.simplefilter("default")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
13from bb import fetch2
14import logging
15import bb
16import select
17import errno
18import signal
Patrick Williamsc0f7c042017-02-23 20:41:17 -060019import pickle
Brad Bishop37a0e4d2017-12-04 01:01:44 -050020import traceback
21import queue
Patrick Williams93c203f2021-10-06 16:15:23 -050022import shlex
23import subprocess
Patrick Williamsf1e5d692016-03-30 15:21:19 -050024from multiprocessing import Lock
Brad Bishop37a0e4d2017-12-04 01:01:44 -050025from threading import Thread
Patrick Williamsc124f4f2015-09-15 14:41:29 -050026
Patrick Williamsc0f7c042017-02-23 20:41:17 -060027if sys.getfilesystemencoding() != "utf-8":
Brad Bishopd7bf8c12018-02-25 22:55:05 -050028 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 -060029
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030# Users shouldn't be running this code directly
31if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
32 print("bitbake-worker is meant for internal execution by bitbake itself, please don't use it standalone.")
33 sys.exit(1)
34
35profiling = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050036if sys.argv[1].startswith("decafbadbad"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050037 profiling = True
38 try:
39 import cProfile as profile
40 except:
41 import profile
42
43# Unbuffer stdout to avoid log truncation in the event
44# of an unorderly exit as well as to provide timely
45# updates to log files for use with tail
46try:
47 if sys.stdout.name == '<stdout>':
Patrick Williamsc0f7c042017-02-23 20:41:17 -060048 import fcntl
49 fl = fcntl.fcntl(sys.stdout.fileno(), fcntl.F_GETFL)
50 fl |= os.O_SYNC
51 fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, fl)
52 #sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053except:
54 pass
55
56logger = logging.getLogger("BitBake")
57
Patrick Williamsc124f4f2015-09-15 14:41:29 -050058worker_pipe = sys.stdout.fileno()
59bb.utils.nonblockingfd(worker_pipe)
Patrick Williamsf1e5d692016-03-30 15:21:19 -050060# Need to guard against multiprocessing being used in child processes
61# and multiple processes trying to write to the parent at the same time
62worker_pipe_lock = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -050063
64handler = bb.event.LogHandler()
65logger.addHandler(handler)
66
67if 0:
68 # Code to write out a log file of all events passing through the worker
69 logfilename = "/tmp/workerlogfile"
70 format_str = "%(levelname)s: %(message)s"
71 conlogformat = bb.msg.BBLogFormatter(format_str)
72 consolelog = logging.FileHandler(logfilename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050073 consolelog.setFormatter(conlogformat)
74 logger.addHandler(consolelog)
75
Brad Bishop37a0e4d2017-12-04 01:01:44 -050076worker_queue = queue.Queue()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077
78def worker_fire(event, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 data = b"<event>" + pickle.dumps(event) + b"</event>"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080 worker_fire_prepickled(data)
81
82def worker_fire_prepickled(event):
83 global worker_queue
84
Brad Bishop37a0e4d2017-12-04 01:01:44 -050085 worker_queue.put(event)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086
Brad Bishop37a0e4d2017-12-04 01:01:44 -050087#
88# We can end up with write contention with the cooker, it can be trying to send commands
89# and we can be trying to send event data back. Therefore use a separate thread for writing
90# back data to cooker.
91#
92worker_thread_exit = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -050093
Brad Bishop37a0e4d2017-12-04 01:01:44 -050094def worker_flush(worker_queue):
95 worker_queue_int = b""
96 global worker_pipe, worker_thread_exit
Patrick Williamsc124f4f2015-09-15 14:41:29 -050097
Brad Bishop37a0e4d2017-12-04 01:01:44 -050098 while True:
99 try:
100 worker_queue_int = worker_queue_int + worker_queue.get(True, 1)
101 except queue.Empty:
102 pass
103 while (worker_queue_int or not worker_queue.empty()):
104 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500105 (_, ready, _) = select.select([], [worker_pipe], [], 1)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500106 if not worker_queue.empty():
107 worker_queue_int = worker_queue_int + worker_queue.get()
108 written = os.write(worker_pipe, worker_queue_int)
109 worker_queue_int = worker_queue_int[written:]
110 except (IOError, OSError) as e:
111 if e.errno != errno.EAGAIN and e.errno != errno.EPIPE:
112 raise
113 if worker_thread_exit and worker_queue.empty() and not worker_queue_int:
114 return
115
116worker_thread = Thread(target=worker_flush, args=(worker_queue,))
117worker_thread.start()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500118
119def worker_child_fire(event, d):
120 global worker_pipe
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500121 global worker_pipe_lock
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500122
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600123 data = b"<event>" + pickle.dumps(event) + b"</event>"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124 try:
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500125 worker_pipe_lock.acquire()
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600126 while(len(data)):
127 written = worker_pipe.write(data)
128 data = data[written:]
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500129 worker_pipe_lock.release()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500130 except IOError:
131 sigterm_handler(None, None)
132 raise
133
134bb.event.worker_fire = worker_fire
135
136lf = None
137#lf = open("/tmp/workercommandlog", "w+")
138def workerlog_write(msg):
139 if lf:
140 lf.write(msg)
141 lf.flush()
142
143def sigterm_handler(signum, frame):
144 signal.signal(signal.SIGTERM, signal.SIG_DFL)
145 os.killpg(0, signal.SIGTERM)
146 sys.exit()
147
Brad Bishop19323692019-04-05 15:28:33 -0400148def 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 -0500149 # We need to setup the environment BEFORE the fork, since
150 # a fork() or exec*() activates PSEUDO...
151
152 envbackup = {}
Patrick Williams93c203f2021-10-06 16:15:23 -0500153 fakeroot = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500154 fakeenv = {}
155 umask = None
156
Andrew Geissler595f6302022-01-24 19:11:47 +0000157 uid = os.getuid()
158 gid = os.getgid()
159
160
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161 taskdep = workerdata["taskdeps"][fn]
162 if 'umask' in taskdep and taskname in taskdep['umask']:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -0600163 umask = taskdep['umask'][taskname]
164 elif workerdata["umask"]:
165 umask = workerdata["umask"]
166 if umask:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500167 # umask might come in as a number or text string..
168 try:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -0600169 umask = int(umask, 8)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500170 except TypeError:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -0600171 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500173 dry_run = cfg.dry_run or dry_run_exec
174
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500175 # 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 -0500176 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not dry_run:
Patrick Williams93c203f2021-10-06 16:15:23 -0500177 fakeroot = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178 envvars = (workerdata["fakerootenv"][fn] or "").split()
179 for key, value in (var.split('=') for var in envvars):
180 envbackup[key] = os.environ.get(key)
181 os.environ[key] = value
182 fakeenv[key] = value
183
184 fakedirs = (workerdata["fakerootdirs"][fn] or "").split()
185 for p in fakedirs:
186 bb.utils.mkdirhier(p)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600187 logger.debug2('Running %s:%s under fakeroot, fakedirs: %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500188 (fn, taskname, ', '.join(fakedirs)))
189 else:
190 envvars = (workerdata["fakerootnoenv"][fn] or "").split()
191 for key, value in (var.split('=') for var in envvars):
192 envbackup[key] = os.environ.get(key)
193 os.environ[key] = value
194 fakeenv[key] = value
195
196 sys.stdout.flush()
197 sys.stderr.flush()
198
199 try:
200 pipein, pipeout = os.pipe()
201 pipein = os.fdopen(pipein, 'rb', 4096)
202 pipeout = os.fdopen(pipeout, 'wb', 0)
203 pid = os.fork()
204 except OSError as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600205 logger.critical("fork failed: %d (%s)" % (e.errno, e.strerror))
206 sys.exit(1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500207
208 if pid == 0:
209 def child():
210 global worker_pipe
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500211 global worker_pipe_lock
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212 pipein.close()
213
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500214 bb.utils.signal_on_parent_exit("SIGTERM")
215
216 # Save out the PID so that the event can include it the
217 # events
218 bb.event.worker_pid = os.getpid()
219 bb.event.worker_fire = worker_child_fire
220 worker_pipe = pipeout
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500221 worker_pipe_lock = Lock()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500222
223 # Make the child the process group leader and ensure no
224 # child process will be controlled by the current terminal
225 # This ensures signals sent to the controlling terminal like Ctrl+C
226 # don't stop the child processes.
227 os.setsid()
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500228
229 signal.signal(signal.SIGTERM, sigterm_handler)
230 # Let SIGHUP exit as SIGTERM
231 signal.signal(signal.SIGHUP, sigterm_handler)
232
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500233 # No stdin
234 newsi = os.open(os.devnull, os.O_RDWR)
235 os.dup2(newsi, sys.stdin.fileno())
236
237 if umask:
238 os.umask(umask)
239
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500240 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600241 bb_cache = bb.cache.NoCache(databuilder)
242 (realfn, virtual, mc) = bb.cache.virtualfn2realfn(fn)
243 the_data = databuilder.mcdata[mc]
244 the_data.setVar("BB_WORKERCONTEXT", "1")
245 the_data.setVar("BB_TASKDEPDATA", taskdepdata)
Andrew Geisslereff27472021-10-29 15:35:00 -0500246 the_data.setVar('BB_CURRENTTASK', taskname.replace("do_", ""))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500247 if cfg.limited_deps:
248 the_data.setVar("BB_LIMITEDDEPS", "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600249 the_data.setVar("BUILDNAME", workerdata["buildname"])
250 the_data.setVar("DATE", workerdata["date"])
251 the_data.setVar("TIME", workerdata["time"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500252 for varname, value in extraconfigdata.items():
253 the_data.setVar(varname, value)
254
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600255 bb.parse.siggen.set_taskdata(workerdata["sigdata"])
Brad Bishop08902b02019-08-20 09:16:51 -0400256 if "newhashes" in workerdata:
257 bb.parse.siggen.set_taskhashes(workerdata["newhashes"])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600258 ret = 0
259
260 the_data = bb_cache.loadDataFull(fn, appends)
Brad Bishop19323692019-04-05 15:28:33 -0400261 the_data.setVar('BB_TASKHASH', taskhash)
262 the_data.setVar('BB_UNIHASH', unihash)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500263
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500264 bb.utils.set_process_name("%s:%s" % (the_data.getVar("PN"), taskname.replace("do_", "")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500265
Andrew Geissler595f6302022-01-24 19:11:47 +0000266 if not the_data.getVarFlag(taskname, 'network', False):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000267 if bb.utils.is_local_uid(uid):
268 logger.debug("Attempting to disable network for %s" % taskname)
269 bb.utils.disable_network(uid, gid)
270 else:
271 logger.debug("Skipping disable network for %s since %s is not a local uid." % (taskname, uid))
Andrew Geissler595f6302022-01-24 19:11:47 +0000272
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500273 # exported_vars() returns a generator which *cannot* be passed to os.environ.update()
274 # successfully. We also need to unset anything from the environment which shouldn't be there
275 exports = bb.data.exported_vars(the_data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600276
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277 bb.utils.empty_environment()
278 for e, v in exports:
279 os.environ[e] = v
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600280
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500281 for e in fakeenv:
282 os.environ[e] = fakeenv[e]
283 the_data.setVar(e, fakeenv[e])
284 the_data.setVarFlag(e, 'export', "1")
285
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500286 task_exports = the_data.getVarFlag(taskname, 'exports')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600287 if task_exports:
288 for e in task_exports.split():
289 the_data.setVarFlag(e, 'export', '1')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500290 v = the_data.getVar(e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600291 if v is not None:
292 os.environ[e] = v
293
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500294 if quieterrors:
295 the_data.setVarFlag(taskname, "quieterrors", "1")
296
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500297 except Exception:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298 if not quieterrors:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500299 logger.critical(traceback.format_exc())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500300 os._exit(1)
301 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500302 if dry_run:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303 return 0
Andrew Geisslereff27472021-10-29 15:35:00 -0500304 try:
305 ret = bb.build.exec_task(fn, taskname, the_data, cfg.profile)
306 finally:
307 if fakeroot:
308 fakerootcmd = shlex.split(the_data.getVar("FAKEROOTCMD"))
309 subprocess.run(fakerootcmd + ['-S'], check=True, stdout=subprocess.PIPE)
Patrick Williams93c203f2021-10-06 16:15:23 -0500310 return ret
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311 except:
312 os._exit(1)
313 if not profiling:
314 os._exit(child())
315 else:
316 profname = "profile-%s.log" % (fn.replace("/", "-") + "-" + taskname)
317 prof = profile.Profile()
318 try:
319 ret = profile.Profile.runcall(prof, child)
320 finally:
321 prof.dump_stats(profname)
322 bb.utils.process_profilelog(profname)
323 os._exit(ret)
324 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600325 for key, value in iter(envbackup.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326 if value is None:
327 del os.environ[key]
328 else:
329 os.environ[key] = value
330
331 return pid, pipein, pipeout
332
333class runQueueWorkerPipe():
334 """
335 Abstraction for a pipe between a worker thread and the worker server
336 """
337 def __init__(self, pipein, pipeout):
338 self.input = pipein
339 if pipeout:
340 pipeout.close()
341 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600342 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500343
344 def read(self):
345 start = len(self.queue)
346 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600347 self.queue = self.queue + (self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500348 except (OSError, IOError) as e:
349 if e.errno != errno.EAGAIN:
350 raise
351
352 end = len(self.queue)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600353 index = self.queue.find(b"</event>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500354 while index != -1:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600355 msg = self.queue[:index+8]
356 assert msg.startswith(b"<event>") and msg.count(b"<event>") == 1
357 worker_fire_prepickled(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500358 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600359 index = self.queue.find(b"</event>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500360 return (end > start)
361
362 def close(self):
363 while self.read():
364 continue
365 if len(self.queue) > 0:
366 print("Warning, worker child left partial message: %s" % self.queue)
367 self.input.close()
368
369normalexit = False
370
371class BitbakeWorker(object):
372 def __init__(self, din):
373 self.input = din
374 bb.utils.nonblockingfd(self.input)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600375 self.queue = b""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376 self.cookercfg = None
377 self.databuilder = None
378 self.data = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500379 self.extraconfigdata = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500380 self.build_pids = {}
381 self.build_pipes = {}
382
383 signal.signal(signal.SIGTERM, self.sigterm_exception)
384 # Let SIGHUP exit as SIGTERM
385 signal.signal(signal.SIGHUP, self.sigterm_exception)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500386 if "beef" in sys.argv[1]:
387 bb.utils.set_process_name("Worker (Fakeroot)")
388 else:
389 bb.utils.set_process_name("Worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500390
391 def sigterm_exception(self, signum, stackframe):
392 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500393 bb.warn("Worker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500394 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500395 bb.warn("Worker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500396 self.handle_finishnow(None)
397 signal.signal(signal.SIGTERM, signal.SIG_DFL)
398 os.kill(os.getpid(), signal.SIGTERM)
399
400 def serve(self):
401 while True:
402 (ready, _, _) = select.select([self.input] + [i.input for i in self.build_pipes.values()], [] , [], 1)
403 if self.input in ready:
404 try:
405 r = self.input.read()
406 if len(r) == 0:
407 # EOF on pipe, server must have terminated
408 self.sigterm_exception(signal.SIGTERM, None)
409 self.queue = self.queue + r
410 except (OSError, IOError):
411 pass
412 if len(self.queue):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600413 self.handle_item(b"cookerconfig", self.handle_cookercfg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500414 self.handle_item(b"extraconfigdata", self.handle_extraconfigdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600415 self.handle_item(b"workerdata", self.handle_workerdata)
Brad Bishop08902b02019-08-20 09:16:51 -0400416 self.handle_item(b"newtaskhashes", self.handle_newtaskhashes)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600417 self.handle_item(b"runtask", self.handle_runtask)
418 self.handle_item(b"finishnow", self.handle_finishnow)
419 self.handle_item(b"ping", self.handle_ping)
420 self.handle_item(b"quit", self.handle_quit)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500421
422 for pipe in self.build_pipes:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500423 if self.build_pipes[pipe].input in ready:
424 self.build_pipes[pipe].read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500425 if len(self.build_pids):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500426 while self.process_waitpid():
427 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500428
429
430 def handle_item(self, item, func):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600431 if self.queue.startswith(b"<" + item + b">"):
432 index = self.queue.find(b"</" + item + b">")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433 while index != -1:
Andrew Geisslereff27472021-10-29 15:35:00 -0500434 try:
435 func(self.queue[(len(item) + 2):index])
436 except pickle.UnpicklingError:
437 workerlog_write("Unable to unpickle data: %s\n" % ":".join("{:02x}".format(c) for c in self.queue))
438 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500439 self.queue = self.queue[(index + len(item) + 3):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600440 index = self.queue.find(b"</" + item + b">")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500441
442 def handle_cookercfg(self, data):
443 self.cookercfg = pickle.loads(data)
444 self.databuilder = bb.cookerdata.CookerDataBuilder(self.cookercfg, worker=True)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000445 self.databuilder.parseBaseConfiguration(worker=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446 self.data = self.databuilder.data
447
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500448 def handle_extraconfigdata(self, data):
449 self.extraconfigdata = pickle.loads(data)
450
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500451 def handle_workerdata(self, data):
452 self.workerdata = pickle.loads(data)
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500453 bb.build.verboseShellLogging = self.workerdata["build_verbose_shell"]
454 bb.build.verboseStdoutLogging = self.workerdata["build_verbose_stdout"]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500455 bb.msg.loggerDefaultLogLevel = self.workerdata["logdefaultlevel"]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500456 bb.msg.loggerDefaultDomains = self.workerdata["logdefaultdomain"]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600457 for mc in self.databuilder.mcdata:
458 self.databuilder.mcdata[mc].setVar("PRSERV_HOST", self.workerdata["prhost"])
Brad Bishopa34c0302019-09-23 22:34:48 -0400459 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.workerdata["hashservaddr"])
Patrick Williams92b42cb2022-09-03 06:53:57 -0500460 self.databuilder.mcdata[mc].setVar("__bbclasstype", "recipe")
Brad Bishop08902b02019-08-20 09:16:51 -0400461
462 def handle_newtaskhashes(self, data):
463 self.workerdata["newhashes"] = pickle.loads(data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500464
465 def handle_ping(self, _):
466 workerlog_write("Handling ping\n")
467
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600468 logger.warning("Pong from bitbake-worker!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500469
470 def handle_quit(self, data):
471 workerlog_write("Handling quit\n")
472
473 global normalexit
474 normalexit = True
475 sys.exit(0)
476
477 def handle_runtask(self, data):
Brad Bishop19323692019-04-05 15:28:33 -0400478 fn, task, taskname, taskhash, unihash, quieterrors, appends, taskdepdata, dry_run_exec = pickle.loads(data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500479 workerlog_write("Handling runtask %s %s %s\n" % (task, fn, taskname))
480
Brad Bishop19323692019-04-05 15:28:33 -0400481 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 -0500482
483 self.build_pids[pid] = task
484 self.build_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
485
486 def process_waitpid(self):
487 """
488 Return none is there are no processes awaiting result collection, otherwise
489 collect the process exit codes and close the information pipe.
490 """
491 try:
492 pid, status = os.waitpid(-1, os.WNOHANG)
493 if pid == 0 or os.WIFSTOPPED(status):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500494 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500495 except OSError:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500496 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500497
498 workerlog_write("Exit code of %s for pid %s\n" % (status, pid))
499
500 if os.WIFEXITED(status):
501 status = os.WEXITSTATUS(status)
502 elif os.WIFSIGNALED(status):
503 # Per shell conventions for $?, when a process exits due to
504 # a signal, we return an exit code of 128 + SIGNUM
505 status = 128 + os.WTERMSIG(status)
506
507 task = self.build_pids[pid]
508 del self.build_pids[pid]
509
510 self.build_pipes[pid].close()
511 del self.build_pipes[pid]
512
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600513 worker_fire_prepickled(b"<exitcode>" + pickle.dumps((task, status)) + b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500514
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500515 return True
516
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517 def handle_finishnow(self, _):
518 if self.build_pids:
519 logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 for k, v in iter(self.build_pids.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500521 try:
522 os.kill(-k, signal.SIGTERM)
523 os.waitpid(-1, 0)
524 except:
525 pass
526 for pipe in self.build_pipes:
527 self.build_pipes[pipe].read()
528
529try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600530 worker = BitbakeWorker(os.fdopen(sys.stdin.fileno(), 'rb'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500531 if not profiling:
532 worker.serve()
533 else:
534 profname = "profile-worker.log"
535 prof = profile.Profile()
536 try:
537 profile.Profile.runcall(prof, worker.serve)
538 finally:
539 prof.dump_stats(profname)
540 bb.utils.process_profilelog(profname)
541except BaseException as e:
542 if not normalexit:
543 import traceback
544 sys.stderr.write(traceback.format_exc())
545 sys.stderr.write(str(e))
Andrew Geissler5199d832021-09-24 16:47:35 -0500546finally:
547 worker_thread_exit = True
548 worker_thread.join()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500549
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500550workerlog_write("exiting")
Andrew Geissler5199d832021-09-24 16:47:35 -0500551if not normalexit:
552 sys.exit(1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553sys.exit(0)