blob: e8073f2ac3fc8121e929b3c00acdeec15de0ae8b [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
Andrew Geissler517393d2023-01-13 08:55:19 -060027bb.utils.check_system_locale()
Patrick Williamsc0f7c042017-02-23 20:41:17 -060028
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029# Users shouldn't be running this code directly
30if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
31 print("bitbake-worker is meant for internal execution by bitbake itself, please don't use it standalone.")
32 sys.exit(1)
33
34profiling = False
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050035if sys.argv[1].startswith("decafbadbad"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050036 profiling = True
37 try:
38 import cProfile as profile
39 except:
40 import profile
41
42# Unbuffer stdout to avoid log truncation in the event
43# of an unorderly exit as well as to provide timely
44# updates to log files for use with tail
45try:
46 if sys.stdout.name == '<stdout>':
Patrick Williamsc0f7c042017-02-23 20:41:17 -060047 import fcntl
48 fl = fcntl.fcntl(sys.stdout.fileno(), fcntl.F_GETFL)
49 fl |= os.O_SYNC
50 fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, fl)
51 #sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050052except:
53 pass
54
55logger = logging.getLogger("BitBake")
56
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057worker_pipe = sys.stdout.fileno()
58bb.utils.nonblockingfd(worker_pipe)
Patrick Williamsf1e5d692016-03-30 15:21:19 -050059# Need to guard against multiprocessing being used in child processes
60# and multiple processes trying to write to the parent at the same time
61worker_pipe_lock = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062
63handler = bb.event.LogHandler()
64logger.addHandler(handler)
65
66if 0:
67 # Code to write out a log file of all events passing through the worker
68 logfilename = "/tmp/workerlogfile"
69 format_str = "%(levelname)s: %(message)s"
70 conlogformat = bb.msg.BBLogFormatter(format_str)
71 consolelog = logging.FileHandler(logfilename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050072 consolelog.setFormatter(conlogformat)
73 logger.addHandler(consolelog)
74
Brad Bishop37a0e4d2017-12-04 01:01:44 -050075worker_queue = queue.Queue()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050076
77def worker_fire(event, d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060078 data = b"<event>" + pickle.dumps(event) + b"</event>"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050079 worker_fire_prepickled(data)
80
81def worker_fire_prepickled(event):
82 global worker_queue
83
Brad Bishop37a0e4d2017-12-04 01:01:44 -050084 worker_queue.put(event)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050085
Brad Bishop37a0e4d2017-12-04 01:01:44 -050086#
87# We can end up with write contention with the cooker, it can be trying to send commands
88# and we can be trying to send event data back. Therefore use a separate thread for writing
89# back data to cooker.
90#
91worker_thread_exit = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -050092
Brad Bishop37a0e4d2017-12-04 01:01:44 -050093def worker_flush(worker_queue):
Andrew Geissler220dafd2023-10-04 10:18:08 -050094 worker_queue_int = bytearray()
Brad Bishop37a0e4d2017-12-04 01:01:44 -050095 global worker_pipe, worker_thread_exit
Patrick Williamsc124f4f2015-09-15 14:41:29 -050096
Brad Bishop37a0e4d2017-12-04 01:01:44 -050097 while True:
98 try:
Andrew Geissler220dafd2023-10-04 10:18:08 -050099 worker_queue_int.extend(worker_queue.get(True, 1))
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500100 except queue.Empty:
101 pass
102 while (worker_queue_int or not worker_queue.empty()):
103 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500104 (_, ready, _) = select.select([], [worker_pipe], [], 1)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500105 if not worker_queue.empty():
Andrew Geissler220dafd2023-10-04 10:18:08 -0500106 worker_queue_int.extend(worker_queue.get())
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500107 written = os.write(worker_pipe, worker_queue_int)
108 worker_queue_int = worker_queue_int[written:]
109 except (IOError, OSError) as e:
110 if e.errno != errno.EAGAIN and e.errno != errno.EPIPE:
111 raise
112 if worker_thread_exit and worker_queue.empty() and not worker_queue_int:
113 return
114
115worker_thread = Thread(target=worker_flush, args=(worker_queue,))
116worker_thread.start()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500117
118def worker_child_fire(event, d):
119 global worker_pipe
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500120 global worker_pipe_lock
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500121
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600122 data = b"<event>" + pickle.dumps(event) + b"</event>"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123 try:
Andrew Geissler517393d2023-01-13 08:55:19 -0600124 with bb.utils.lock_timeout(worker_pipe_lock):
125 while(len(data)):
126 written = worker_pipe.write(data)
127 data = data[written:]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128 except IOError:
129 sigterm_handler(None, None)
130 raise
131
132bb.event.worker_fire = worker_fire
133
134lf = None
135#lf = open("/tmp/workercommandlog", "w+")
136def workerlog_write(msg):
137 if lf:
138 lf.write(msg)
139 lf.flush()
140
141def sigterm_handler(signum, frame):
142 signal.signal(signal.SIGTERM, signal.SIG_DFL)
143 os.killpg(0, signal.SIGTERM)
144 sys.exit()
145
Andrew Geissler517393d2023-01-13 08:55:19 -0600146def fork_off_task(cfg, data, databuilder, workerdata, extraconfigdata, runtask):
147
148 fn = runtask['fn']
149 task = runtask['task']
150 taskname = runtask['taskname']
151 taskhash = runtask['taskhash']
152 unihash = runtask['unihash']
153 appends = runtask['appends']
Patrick Williamse760df82023-05-26 11:10:49 -0500154 layername = runtask['layername']
Andrew Geissler517393d2023-01-13 08:55:19 -0600155 taskdepdata = runtask['taskdepdata']
156 quieterrors = runtask['quieterrors']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500157 # We need to setup the environment BEFORE the fork, since
158 # a fork() or exec*() activates PSEUDO...
159
160 envbackup = {}
Patrick Williams93c203f2021-10-06 16:15:23 -0500161 fakeroot = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162 fakeenv = {}
163 umask = None
164
Andrew Geissler595f6302022-01-24 19:11:47 +0000165 uid = os.getuid()
166 gid = os.getgid()
167
Andrew Geissler517393d2023-01-13 08:55:19 -0600168 taskdep = runtask['taskdep']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169 if 'umask' in taskdep and taskname in taskdep['umask']:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -0600170 umask = taskdep['umask'][taskname]
171 elif workerdata["umask"]:
172 umask = workerdata["umask"]
173 if umask:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500174 # umask might come in as a number or text string..
175 try:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -0600176 umask = int(umask, 8)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500177 except TypeError:
Andrew Geissler9b4d8b02021-02-19 12:26:16 -0600178 pass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500179
Andrew Geissler517393d2023-01-13 08:55:19 -0600180 dry_run = cfg.dry_run or runtask['dry_run']
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500181
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500182 # 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 -0500183 if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not dry_run:
Patrick Williams93c203f2021-10-06 16:15:23 -0500184 fakeroot = True
Andrew Geissler517393d2023-01-13 08:55:19 -0600185 envvars = (runtask['fakerootenv'] or "").split()
Patrick Williams03514f12024-04-05 07:04:11 -0500186 for key, value in (var.split('=',1) for var in envvars):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500187 envbackup[key] = os.environ.get(key)
188 os.environ[key] = value
189 fakeenv[key] = value
190
Andrew Geissler517393d2023-01-13 08:55:19 -0600191 fakedirs = (runtask['fakerootdirs'] or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500192 for p in fakedirs:
193 bb.utils.mkdirhier(p)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600194 logger.debug2('Running %s:%s under fakeroot, fakedirs: %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195 (fn, taskname, ', '.join(fakedirs)))
196 else:
Andrew Geissler517393d2023-01-13 08:55:19 -0600197 envvars = (runtask['fakerootnoenv'] or "").split()
Patrick Williams03514f12024-04-05 07:04:11 -0500198 for key, value in (var.split('=',1) for var in envvars):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199 envbackup[key] = os.environ.get(key)
200 os.environ[key] = value
201 fakeenv[key] = value
202
203 sys.stdout.flush()
204 sys.stderr.flush()
205
206 try:
207 pipein, pipeout = os.pipe()
208 pipein = os.fdopen(pipein, 'rb', 4096)
209 pipeout = os.fdopen(pipeout, 'wb', 0)
210 pid = os.fork()
211 except OSError as e:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600212 logger.critical("fork failed: %d (%s)" % (e.errno, e.strerror))
213 sys.exit(1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500214
215 if pid == 0:
216 def child():
217 global worker_pipe
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500218 global worker_pipe_lock
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219 pipein.close()
220
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221 bb.utils.signal_on_parent_exit("SIGTERM")
222
223 # Save out the PID so that the event can include it the
224 # events
225 bb.event.worker_pid = os.getpid()
226 bb.event.worker_fire = worker_child_fire
227 worker_pipe = pipeout
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500228 worker_pipe_lock = Lock()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229
230 # Make the child the process group leader and ensure no
231 # child process will be controlled by the current terminal
232 # This ensures signals sent to the controlling terminal like Ctrl+C
233 # don't stop the child processes.
234 os.setsid()
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500235
236 signal.signal(signal.SIGTERM, sigterm_handler)
237 # Let SIGHUP exit as SIGTERM
238 signal.signal(signal.SIGHUP, sigterm_handler)
239
Patrick Williams03514f12024-04-05 07:04:11 -0500240 # No stdin & stdout
241 # stdout is used as a status report channel and must not be used by child processes.
242 dumbio = os.open(os.devnull, os.O_RDWR)
243 os.dup2(dumbio, sys.stdin.fileno())
244 os.dup2(dumbio, sys.stdout.fileno())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500245
Patrick Williams03514f12024-04-05 07:04:11 -0500246 if umask is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500247 os.umask(umask)
248
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500249 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600250 (realfn, virtual, mc) = bb.cache.virtualfn2realfn(fn)
251 the_data = databuilder.mcdata[mc]
252 the_data.setVar("BB_WORKERCONTEXT", "1")
253 the_data.setVar("BB_TASKDEPDATA", taskdepdata)
Andrew Geisslereff27472021-10-29 15:35:00 -0500254 the_data.setVar('BB_CURRENTTASK', taskname.replace("do_", ""))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500255 if cfg.limited_deps:
256 the_data.setVar("BB_LIMITEDDEPS", "1")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600257 the_data.setVar("BUILDNAME", workerdata["buildname"])
258 the_data.setVar("DATE", workerdata["date"])
259 the_data.setVar("TIME", workerdata["time"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500260 for varname, value in extraconfigdata.items():
261 the_data.setVar(varname, value)
262
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600263 bb.parse.siggen.set_taskdata(workerdata["sigdata"])
Brad Bishop08902b02019-08-20 09:16:51 -0400264 if "newhashes" in workerdata:
265 bb.parse.siggen.set_taskhashes(workerdata["newhashes"])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600266 ret = 0
267
Patrick Williamse760df82023-05-26 11:10:49 -0500268 the_data = databuilder.parseRecipe(fn, appends, layername)
Brad Bishop19323692019-04-05 15:28:33 -0400269 the_data.setVar('BB_TASKHASH', taskhash)
270 the_data.setVar('BB_UNIHASH', unihash)
Andrew Geissler517393d2023-01-13 08:55:19 -0600271 bb.parse.siggen.setup_datacache_from_datastore(fn, the_data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500273 bb.utils.set_process_name("%s:%s" % (the_data.getVar("PN"), taskname.replace("do_", "")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500274
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600275 if not bb.utils.to_boolean(the_data.getVarFlag(taskname, 'network')):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000276 if bb.utils.is_local_uid(uid):
277 logger.debug("Attempting to disable network for %s" % taskname)
278 bb.utils.disable_network(uid, gid)
279 else:
280 logger.debug("Skipping disable network for %s since %s is not a local uid." % (taskname, uid))
Andrew Geissler595f6302022-01-24 19:11:47 +0000281
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 # exported_vars() returns a generator which *cannot* be passed to os.environ.update()
283 # successfully. We also need to unset anything from the environment which shouldn't be there
284 exports = bb.data.exported_vars(the_data)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600285
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286 bb.utils.empty_environment()
287 for e, v in exports:
288 os.environ[e] = v
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600289
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290 for e in fakeenv:
291 os.environ[e] = fakeenv[e]
292 the_data.setVar(e, fakeenv[e])
293 the_data.setVarFlag(e, 'export', "1")
294
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500295 task_exports = the_data.getVarFlag(taskname, 'exports')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600296 if task_exports:
297 for e in task_exports.split():
298 the_data.setVarFlag(e, 'export', '1')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500299 v = the_data.getVar(e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600300 if v is not None:
301 os.environ[e] = v
302
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303 if quieterrors:
304 the_data.setVarFlag(taskname, "quieterrors", "1")
305
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500306 except Exception:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307 if not quieterrors:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500308 logger.critical(traceback.format_exc())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500309 os._exit(1)
Patrick Williams03514f12024-04-05 07:04:11 -0500310
311 sys.stdout.flush()
312 sys.stderr.flush()
313
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500315 if dry_run:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500316 return 0
Andrew Geisslereff27472021-10-29 15:35:00 -0500317 try:
318 ret = bb.build.exec_task(fn, taskname, the_data, cfg.profile)
319 finally:
320 if fakeroot:
321 fakerootcmd = shlex.split(the_data.getVar("FAKEROOTCMD"))
322 subprocess.run(fakerootcmd + ['-S'], check=True, stdout=subprocess.PIPE)
Patrick Williams93c203f2021-10-06 16:15:23 -0500323 return ret
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324 except:
325 os._exit(1)
326 if not profiling:
327 os._exit(child())
328 else:
329 profname = "profile-%s.log" % (fn.replace("/", "-") + "-" + taskname)
330 prof = profile.Profile()
331 try:
332 ret = profile.Profile.runcall(prof, child)
333 finally:
334 prof.dump_stats(profname)
335 bb.utils.process_profilelog(profname)
336 os._exit(ret)
337 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600338 for key, value in iter(envbackup.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339 if value is None:
340 del os.environ[key]
341 else:
342 os.environ[key] = value
343
344 return pid, pipein, pipeout
345
346class runQueueWorkerPipe():
347 """
348 Abstraction for a pipe between a worker thread and the worker server
349 """
350 def __init__(self, pipein, pipeout):
351 self.input = pipein
352 if pipeout:
353 pipeout.close()
354 bb.utils.nonblockingfd(self.input)
Andrew Geissler220dafd2023-10-04 10:18:08 -0500355 self.queue = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500356
357 def read(self):
358 start = len(self.queue)
359 try:
Andrew Geissler220dafd2023-10-04 10:18:08 -0500360 self.queue.extend(self.input.read(102400) or b"")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361 except (OSError, IOError) as e:
362 if e.errno != errno.EAGAIN:
363 raise
364
365 end = len(self.queue)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600366 index = self.queue.find(b"</event>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367 while index != -1:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600368 msg = self.queue[:index+8]
369 assert msg.startswith(b"<event>") and msg.count(b"<event>") == 1
370 worker_fire_prepickled(msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371 self.queue = self.queue[index+8:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600372 index = self.queue.find(b"</event>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500373 return (end > start)
374
375 def close(self):
376 while self.read():
377 continue
378 if len(self.queue) > 0:
379 print("Warning, worker child left partial message: %s" % self.queue)
380 self.input.close()
381
382normalexit = False
383
384class BitbakeWorker(object):
385 def __init__(self, din):
386 self.input = din
387 bb.utils.nonblockingfd(self.input)
Andrew Geissler220dafd2023-10-04 10:18:08 -0500388 self.queue = bytearray()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500389 self.cookercfg = None
390 self.databuilder = None
391 self.data = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500392 self.extraconfigdata = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393 self.build_pids = {}
394 self.build_pipes = {}
395
396 signal.signal(signal.SIGTERM, self.sigterm_exception)
397 # Let SIGHUP exit as SIGTERM
398 signal.signal(signal.SIGHUP, self.sigterm_exception)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500399 if "beef" in sys.argv[1]:
400 bb.utils.set_process_name("Worker (Fakeroot)")
401 else:
402 bb.utils.set_process_name("Worker")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403
404 def sigterm_exception(self, signum, stackframe):
405 if signum == signal.SIGTERM:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500406 bb.warn("Worker received SIGTERM, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407 elif signum == signal.SIGHUP:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500408 bb.warn("Worker received SIGHUP, shutting down...")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500409 self.handle_finishnow(None)
410 signal.signal(signal.SIGTERM, signal.SIG_DFL)
411 os.kill(os.getpid(), signal.SIGTERM)
412
413 def serve(self):
414 while True:
415 (ready, _, _) = select.select([self.input] + [i.input for i in self.build_pipes.values()], [] , [], 1)
416 if self.input in ready:
417 try:
418 r = self.input.read()
419 if len(r) == 0:
420 # EOF on pipe, server must have terminated
421 self.sigterm_exception(signal.SIGTERM, None)
Andrew Geissler220dafd2023-10-04 10:18:08 -0500422 self.queue.extend(r)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500423 except (OSError, IOError):
424 pass
425 if len(self.queue):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600426 self.handle_item(b"cookerconfig", self.handle_cookercfg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500427 self.handle_item(b"extraconfigdata", self.handle_extraconfigdata)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600428 self.handle_item(b"workerdata", self.handle_workerdata)
Brad Bishop08902b02019-08-20 09:16:51 -0400429 self.handle_item(b"newtaskhashes", self.handle_newtaskhashes)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600430 self.handle_item(b"runtask", self.handle_runtask)
431 self.handle_item(b"finishnow", self.handle_finishnow)
432 self.handle_item(b"ping", self.handle_ping)
433 self.handle_item(b"quit", self.handle_quit)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434
435 for pipe in self.build_pipes:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500436 if self.build_pipes[pipe].input in ready:
437 self.build_pipes[pipe].read()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500438 if len(self.build_pids):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500439 while self.process_waitpid():
440 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500441
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500442 def handle_item(self, item, func):
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600443 opening_tag = b"<" + item + b">"
444 if not self.queue.startswith(opening_tag):
445 return
446
447 tag_len = len(opening_tag)
448 if len(self.queue) < tag_len + 4:
449 # we need to receive more data
450 return
451 header = self.queue[tag_len:tag_len + 4]
452 payload_len = int.from_bytes(header, 'big')
453 # closing tag has length (tag_len + 1)
454 if len(self.queue) < tag_len * 2 + 1 + payload_len:
455 # we need to receive more data
456 return
457
458 index = self.queue.find(b"</" + item + b">")
459 if index != -1:
460 try:
461 func(self.queue[(tag_len + 4):index])
462 except pickle.UnpicklingError:
463 workerlog_write("Unable to unpickle data: %s\n" % ":".join("{:02x}".format(c) for c in self.queue))
464 raise
465 self.queue = self.queue[(index + len(b"</") + len(item) + len(b">")):]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466
467 def handle_cookercfg(self, data):
468 self.cookercfg = pickle.loads(data)
469 self.databuilder = bb.cookerdata.CookerDataBuilder(self.cookercfg, worker=True)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000470 self.databuilder.parseBaseConfiguration(worker=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500471 self.data = self.databuilder.data
472
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500473 def handle_extraconfigdata(self, data):
474 self.extraconfigdata = pickle.loads(data)
475
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500476 def handle_workerdata(self, data):
477 self.workerdata = pickle.loads(data)
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500478 bb.build.verboseShellLogging = self.workerdata["build_verbose_shell"]
479 bb.build.verboseStdoutLogging = self.workerdata["build_verbose_stdout"]
Andrew Geissler82c905d2020-04-13 13:39:40 -0500480 bb.msg.loggerDefaultLogLevel = self.workerdata["logdefaultlevel"]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500481 bb.msg.loggerDefaultDomains = self.workerdata["logdefaultdomain"]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600482 for mc in self.databuilder.mcdata:
483 self.databuilder.mcdata[mc].setVar("PRSERV_HOST", self.workerdata["prhost"])
Brad Bishopa34c0302019-09-23 22:34:48 -0400484 self.databuilder.mcdata[mc].setVar("BB_HASHSERVE", self.workerdata["hashservaddr"])
Patrick Williams92b42cb2022-09-03 06:53:57 -0500485 self.databuilder.mcdata[mc].setVar("__bbclasstype", "recipe")
Brad Bishop08902b02019-08-20 09:16:51 -0400486
487 def handle_newtaskhashes(self, data):
488 self.workerdata["newhashes"] = pickle.loads(data)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500489
490 def handle_ping(self, _):
491 workerlog_write("Handling ping\n")
492
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600493 logger.warning("Pong from bitbake-worker!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500494
495 def handle_quit(self, data):
496 workerlog_write("Handling quit\n")
497
498 global normalexit
499 normalexit = True
500 sys.exit(0)
501
502 def handle_runtask(self, data):
Andrew Geissler517393d2023-01-13 08:55:19 -0600503 runtask = pickle.loads(data)
504
505 fn = runtask['fn']
506 task = runtask['task']
507 taskname = runtask['taskname']
508
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509 workerlog_write("Handling runtask %s %s %s\n" % (task, fn, taskname))
510
Andrew Geissler517393d2023-01-13 08:55:19 -0600511 pid, pipein, pipeout = fork_off_task(self.cookercfg, self.data, self.databuilder, self.workerdata, self.extraconfigdata, runtask)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500512 self.build_pids[pid] = task
513 self.build_pipes[pid] = runQueueWorkerPipe(pipein, pipeout)
514
515 def process_waitpid(self):
516 """
517 Return none is there are no processes awaiting result collection, otherwise
518 collect the process exit codes and close the information pipe.
519 """
520 try:
521 pid, status = os.waitpid(-1, os.WNOHANG)
522 if pid == 0 or os.WIFSTOPPED(status):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500523 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500524 except OSError:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500525 return False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526
527 workerlog_write("Exit code of %s for pid %s\n" % (status, pid))
528
529 if os.WIFEXITED(status):
530 status = os.WEXITSTATUS(status)
531 elif os.WIFSIGNALED(status):
532 # Per shell conventions for $?, when a process exits due to
533 # a signal, we return an exit code of 128 + SIGNUM
534 status = 128 + os.WTERMSIG(status)
535
536 task = self.build_pids[pid]
537 del self.build_pids[pid]
538
539 self.build_pipes[pid].close()
540 del self.build_pipes[pid]
541
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600542 worker_fire_prepickled(b"<exitcode>" + pickle.dumps((task, status)) + b"</exitcode>")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500544 return True
545
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546 def handle_finishnow(self, _):
547 if self.build_pids:
548 logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600549 for k, v in iter(self.build_pids.items()):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550 try:
551 os.kill(-k, signal.SIGTERM)
552 os.waitpid(-1, 0)
553 except:
554 pass
555 for pipe in self.build_pipes:
556 self.build_pipes[pipe].read()
557
558try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600559 worker = BitbakeWorker(os.fdopen(sys.stdin.fileno(), 'rb'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560 if not profiling:
561 worker.serve()
562 else:
563 profname = "profile-worker.log"
564 prof = profile.Profile()
565 try:
566 profile.Profile.runcall(prof, worker.serve)
567 finally:
568 prof.dump_stats(profname)
569 bb.utils.process_profilelog(profname)
570except BaseException as e:
571 if not normalexit:
572 import traceback
573 sys.stderr.write(traceback.format_exc())
574 sys.stderr.write(str(e))
Andrew Geissler5199d832021-09-24 16:47:35 -0500575finally:
576 worker_thread_exit = True
577 worker_thread.join()
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500578
Andrew Geisslerd159c7f2021-09-02 21:05:58 -0500579workerlog_write("exiting")
Andrew Geissler5199d832021-09-24 16:47:35 -0500580if not normalexit:
581 sys.exit(1)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500582sys.exit(0)