Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
#!/bin/sh
#
# $NetBSD: rc,v 1.175 2020/09/08 16:10:53 martin Exp $
#
# rc --
#	Run the scripts in /etc/rc.d with rcorder, and log output
#	to /var/run/rc.log.

#	System startup script run by init(8) on autoboot or after single-user.
#	Output and error are redirected to console by init, and the console
#	is the controlling terminal.

export HOME=/
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
umask 022

if [ -e ./rc.subr ] ; then
	. ./rc.subr # for testing
else
	. /etc/rc.subr
fi
. /etc/rc.conf
_rc_conf_loaded=true

: ${RC_LOG_FILE:="/var/run/rc.log"}

# rc.subr redefines echo and printf.  Undo that here.
unset echo ; unalias echo
unset printf ; unalias printf

if ! checkyesno rc_configured; then
	echo "/etc/rc.conf is not configured.  Multiuser boot aborted."
	exit 1
fi

if [ "$1" = autoboot ]; then
	autoboot=yes
	rc_fast=yes	# run_rc_command(): do fast booting
fi

#
# Completely ignore INT and QUIT at the outer level.  The rc_real_work()
# function should do something different.
#
trap '' INT QUIT

/usr/pkg/bin/bash /lockdoc/boot.init.bash.sh

#
# This string will be used to mark lines of meta-data sent over the pipe
# from the rc_real_work() function to the rc_postprocess() function.  Lines
# not so marked are assumed to be output from rc.d scripts.
#
# This string is long and unique to ensure that it does not accidentally
# appear in output from any rc.d script.  It must not contain any
# characters that are special to glob expansion ('*', '?', '[', or ']').
#
rc_metadata_prefix="$0:$$:metadata:";

# Child scripts may sometimes want to print directly to the original
# stdout and stderr, bypassing the pipe to the postprocessor.  These
# _rc_*_fd variables are private, shared with /etc/rc.subr, but not
# intended to be used directly by child scripts.  (Child scripts
# may use rc.subr's no_rc_postprocess function.)
#
_rc_original_stdout_fd=7; export _rc_original_stdout_fd
_rc_original_stderr_fd=8; export _rc_original_stderr_fd
eval "exec ${_rc_original_stdout_fd}>&1"
eval "exec ${_rc_original_stderr_fd}>&2"
fdflags -s +cloexec 7 8

#
# rc_real_work
#	Do the real work.  Output from this function will be piped into
#	rc_postprocess(), and some of the output will be marked as
#	metadata.
#
# The body of this function is defined using (...), not {...}, to force
# it to run in a subshell.
#
rc_real_work()
(
	stty status '^T'

	# print_rc_metadata() wants to be able to print to the pipe
	# that goes to our postprocessor, even if its in a context
	# with redirected output.
	#
	_rc_postprocessor_fd=9 ; export _rc_postprocessor_fd
	_rc_pid=$$ ; export _rc_pid
	eval "exec ${_rc_postprocessor_fd}>&1"
	fdflags -s +cloexec 9

	# Print a metadata line when we exit
	#
	trap 'es=$?; print_rc_metadata "exit:$es"; trap "" 0; exit $es' 0

	#	Set shell to ignore SIGINT, but children will not ignore it.
	#	Shell catches SIGQUIT and returns to single user.
	#
	trap : INT
	trap '_msg="Boot interrupted at $(date)";
	      print_rc_metadata "interrupted:${_msg}";
	      exit 1' QUIT

	print_rc_metadata "start:$(date)"

	#
	# The stop_boot() function in rc.subr may kill $RC_PID.  We want
	# it to kill the subshell running this rc_real_work() function,
	# rather than killing the parent shell, because we want the
	# rc_postprocess() function to be able to log the error
	# without being killed itself.
	#
	# "$$" is the pid of the top-level shell, not the pid of the
	# subshell that's executing this function.  The command below
	# tentatively assumes that the parent of the "/bin/sh -c ..."
	# process will be the current subshell, and then uses "kill -0
	# ..." to check the result.  If the "/bin/sh -c ..." process
	# fails, or returns the pid of an ephemeral process that exits
	# before the "kill" command, then we fall back to using "$$".
	#
	RC_PID=$(/bin/sh -c 'ps -p $$ -o ppid=') || RC_PID=$$
	kill -0 $RC_PID >/dev/null 2>&1 || RC_PID=$$

	#
	# As long as process $RC_PID is still running, send a "nop"
	# metadata message to the postprocessor every few seconds.
	# This should help flush partial lines that may appear when
	# rc.d scripts that are NOT marked with "KEYWORD: interactive"
	# nevertheless attempt to print prompts and wait for input.
	#
	(
	    # First detach from tty, to avoid intercepting SIGINFO.
	    eval "exec ${_rc_original_stdout_fd}<&-"
	    eval "exec ${_rc_original_stderr_fd}<&-"
	    exec </dev/null >/dev/null 2>&1
	    while kill -0 $RC_PID ; do
		print_rc_metadata "nop"
		sleep 3
	    done
	) &

	#
	# Get a list of all rc.d scripts, and use rcorder to choose
	# what order to execute them.
	#
	# For testing, allow RC_FILES_OVERRIDE from the environment to
	# override this.
	#
	print_rc_metadata "cmd-name:rcorder"
	scripts=$(for rcd in ${rc_directories:-/etc/rc.d}; do
		test -d ${rcd} && echo ${rcd}/*;
	done)
	files=$(rcorder -s nostart ${rc_rcorder_flags} ${scripts})
	print_rc_metadata "cmd-status:rcorder:$?"

	if [ -n "${RC_FILES_OVERRIDE}" ]; then
		files="${RC_FILES_OVERRIDE}"
	fi

	#
	# Run the scripts in order.
	#
	for _rc_elem in $files; do
		print_rc_metadata "cmd-name:$_rc_elem"
		run_rc_script $_rc_elem start
		print_rc_metadata "cmd-status:$_rc_elem:$?"
	done

	print_rc_metadata "end:$(date)"
	exit 0
)

#
# rc_postprocess
#	Post-process the output from the rc_real_work() function.  For
#	each line of input, we have to decide whether to print the line
#	to the console, print a twiddle on the console, print a line to
#	the log, or some combination of these.
#
#	If rc_silent is true, then suppress most output, instead running
#	rc_silent_cmd (typically "twiddle") for each line.
#
# The body of this function is defined using (...), not {...}, to force
# it to run in a subshell.
#
# We have to deal with the following constraints:
#
#  * There may be no writable file systems early in the boot, so
#    any use of temporary files would be problematic.
#
#  * Scripts run during the boot may clear /tmp and/var/run, so even
#    if they are writable, using those directories too early may be
#    problematic.  We assume that it's safe to write to our log file
#    after the CRITLOCALMOUNTED script has run.
#
#  * /usr/bin/tee cannot be used because the /usr file system may not
#    be mounted early in the boot.
#
#  * All calls to the rc_log_message and rc_log_flush functions must be
#    from the same subshell, otherwise the use of a shell variable to
#    buffer log messages will fail.
#
rc_postprocess()
(
	local line
	local before after
	local IFS=''

	# Try quite hard to flush the log to disk when we exit.
	trap 'es=$?; rc_log_flush FORCE; trap "" 0; exit $es' 0

	yesno_to_truefalse rc_silent 2>/dev/null

	while read -r line ; do
		case "$line" in
		"${rc_metadata_prefix}"*)
			after="${line#*"${rc_metadata_prefix}"}"
			rc_postprocess_metadata "${after}"
			;;
		*"${rc_metadata_prefix}"*)
			# magic string is present, but not at the start of
			# the line.  Treat it as a partial line of
			# ordinary data, followed by a line of metadata.
			before="${line%"${rc_metadata_prefix}"*}"
			rc_postprocess_partial_line "${before}"
			after="${line#*"${rc_metadata_prefix}"}"
			rc_postprocess_metadata "${after}"
			;;
		*)
			rc_postprocess_plain_line "${line}"
			;;
		esac
	done

	# If we get here, then the rc_real_work() function must have
	# exited uncleanly.  A clean exit would have been accompanied by
	# a line of metadata that would have prevented us from getting
	# here.
	#
	exit 1
)

#
# rc_postprocess_plain_line string
#	$1 is a string representing a line of output from one of the
#	rc.d scripts.  Append the line to the log, and also either
#	display the line on the console, or run $rc_silent_cmd,
#	depending on the value of $rc_silent.
#
rc_postprocess_plain_line()
{
	local line="$1"
	rc_log_message "${line}"
	if $rc_silent; then
		eval "$rc_silent_cmd"
	else
		printf "%s\n" "${line}"
	fi
}

#
# rc_postprocess_partial_line string
#	This is just like rc_postprocess_plain_line, except that
#	a newline is not appended to the string.
#
rc_postprocess_partial_line()
{
	local line="$1"
	rc_log_message_n "${line}"
	if $rc_silent; then
		eval "$rc_silent_cmd"
	else
		printf "%s" "${line}"
	fi
}

#
# rc_postprocess_metadata string
#	$1 is a string containing metadata from the rc_real_work()
#	function.  The rc_metadata_prefix marker should already
#	have been removed before the string is passed to this function.
#	Take appropriate action depending on the content of the string.
#
rc_postprocess_metadata()
{
	local metadata="$1"
	local keyword args
	local msg
	local IFS=':'

	# given metadata="bleep:foo bar:baz",
	# set keyword="bleep", args="foo bar:baz",
	# $1="foo bar", $2="baz"
	#
	keyword="${metadata%%:*}"
	args="${metadata#*:}"
	set -- $args

	case "$keyword" in
	start)
		# Marks the start of the entire /etc/rc script.
		# $args contains a date/time.
		rc_log_message "[$0 starting at $args]"
		if ! $rc_silent; then
			printf "%s\n" "$args"
		fi
		;;
	cmd-name)
		# Marks the start of a child script (usually one of
		# the /etc/rc.d/* scripts).
		rc_log_message "[running $1]"
		;;
	cmd-status)
		# Marks the end of a child script.
		# $1 is a command name, $2 is the command's exit status.
		# If the command failed, report it, and add it to a list.
		if [ "$2" != 0 ]; then
			rc_failures="${rc_failures}${rc_failures:+ }$1"
			msg="$1 $(human_exit_code $2)"
			rc_log_message "$msg"
			if ! $rc_silent; then
				printf "%s\n" "$msg"
			fi
		fi
		# After the CRITLOCALMOUNTED script has finished, it's
		# OK to flush the log to disk
		case "$1" in
		*/CRITLOCALMOUNTED)
			rc_log_flush OK
			;;
		esac
		;;
	nop)
		# Do nothing.
		# This has the side effect of flushing partial lines,
		# and the echo() and printf() functions in rc.subr take
		# advantage of this.
		;;
	note)
		# Unlike most metadata messages, which should be used
		# only by /etc/rc and rc.subr, the "note" message may be
		# used directly by /etc.rc.d/* and similar scripts.
		# It adds a note to the log file, without displaying
		# it to stdout.
		rc_log_message "[NOTE: $args]"
		;;
	end)
		# Marks the end of processing, after the last child script.
		# If any child scripts (or other commands) failed, report them.
		#
		if [ -n "$rc_failures" ]; then
			rc_log_message "[failures]"
			msg="The following components reported failures:"
			msg="${msg}${nl}$( echo "    ${rc_failures}" | fmt )"
			msg="${msg}${nl}See ${RC_LOG_FILE} for more information."
			rc_log_message "${msg}"
			printf "%s\n" "${msg}"
		fi
		#
		# Report the end date/time, even in silent mode
		#
		rc_log_message "[$0 finished at $args]"
		printf "%s\n" "$args"
		;;
	exit)
		# Marks an exit from the rc_real_work() function.
		# This may be a normal or abnormal exit.
		#
		rc_log_message "[$0 exiting with status $1]"
		exit $1
		;;
	interrupted)
		# Marks an interrupt trapped by the rc_real_work() function.
		# $args is a human-readable message.
		rc_log_message "$args"
		printf "%s\n" "$args"
		;;
	*)
		# an unrecognised line of metadata
		rc_log_message "[metadata:${metadata}]"
		;;
	esac
}

#
# rc_log_message string [...]
#	Write a message to the log file, or buffer it for later.
#	This function appends a newline to the message.
#
rc_log_message()
{
	_rc_log_buffer="${_rc_log_buffer}${*}${nl}"
	rc_log_flush
}

#
# rc_log_message_n string [...]
#	Just like rc_log_message, except without appending a newline.
#
rc_log_message_n()
{
	_rc_log_buffer="${_rc_log_buffer}${*}"
	rc_log_flush
}

#
# rc_log_flush [OK|FORCE]
#	save outstanding messages from $_rc_log_buffer to $RC_LOG_FILE.
#
# The log file is expected to reside in the /var/run directory, which
# may not be writable very early in the boot sequence, and which is
# erased a little later in the boot sequence.  We therefore avoid
# writing to the file until we believe it's safe to do so.  We also
# assume that it's reasonable to always append to the file, never
# truncating it.
#
# Optional argument $1 may be "OK" to report that writing to the log
# file is expected to be safe from now on, or "FORCE" to force writing
# to the log file even if it may be unsafe.
#
# Returns a non-zero status if messages could not be written to the
# file.
#
rc_log_flush()
{
	#
	# If $_rc_log_flush_ok is false, then it's probably too early to
	# write to the log file, so don't do it, unless $1 is "FORCE".
	#
	: ${_rc_log_flush_ok=false}
	case "$1:$_rc_log_flush_ok" in
	OK:*)
		_rc_log_flush_ok=true
		;;
	FORCE:*)
		: OK just this once
		;;
	*:true)
		: OK
		;;
	*)
		# it's too early in the boot sequence, so don't flush
		return 1
		;;
	esac

	#
	# Now append the buffer to the file.  The buffer should already
	# contain a trailing newline, so don't add an extra newline.
	#
	if [ -n "$_rc_log_buffer" ]; then
		if { printf "%s" "${_rc_log_buffer}" >>"${RC_LOG_FILE}" ; } \
			2>/dev/null
		then
			_rc_log_buffer=""
		else
			return 1
		fi
	fi
	return 0
}

#
# Most of the action is in the rc_real_work() and rc_postprocess()
# functions.
#
rc_real_work "$@" 2>&1 | rc_postprocess
exit $?