File: openrc

package info (click to toggle)
unit-translator 0.8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 632 kB
  • sloc: sh: 909; makefile: 28
file content (452 lines) | stat: -rw-r--r-- 14,759 bytes parent folder | download
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
# openrc output backend
# shellcheck shell=bash

# add openrc shebang
#
# rely on in-built variables and functions available via the
# openrc-run interpreter
gen_openrc_script_shebang() {
	printf '%s\n' '#!/sbin/openrc-run'
}

# Return the complement of the supplied capabilities
cap_complement() {
    declare -A avail="( $(setpriv --list-caps | awk 'BEGIN{ORS=" ";} { print "CAP_" toupper($0), NR }') )"
    for cap do
	unset 'avail[${cap}]'
    done
    echo "${!avail[@]}"
}
#cap_complement CAP_CHOWN CAP_LL CAP_ANOTHET

# generate variables from the values defined in service[key].
#
gen_openrc_script_variables() {
	local ssd_args
	# add service description
	printf '%s\n' "description=\"${unit[Description]:-None provided}.\""
	if [ "${unit[Documentation]:-}" ]; then
		printf '# %s:\n' 'Documentation'
		while read -r doc; do
			# shellcheck disable=SC2086
			printf '#  %s\n' ${doc} # Unquoted to split whitespace
		done <<<"${unit[Documentation]}"
	fi
	printf '\n'

	if [[ "${service[Type]}" != "forking" && "${service[Type]}" != oneshot ]]; then
		if [[ "${service[Restart]:-}" != no ]]; then
			# TODO: supervise-daemon doesn't distinguish Restart categories
			print_directive supervisor supervise-daemon
		fi
	fi

	gen_openrc_script_exec_event --start "${service[ExecStart]:-}"

	print_directive umask "${service[UMask]:-}"
	print_directive directory "${service[WorkingDirectory]:+"${service[WorkingDirectory]#-}"}" # Ignore '-' prefix
	if [[ -n "${service[RootDirectory]:-}" ]]; then
		ssd_args="--chroot ${service[RootDirectory]} "
	fi

	if [[ -n "${service[Nice]:-}" ]]; then
		ssd_args+="--nicelevel ${service[Nice]} "
	fi

	if [[ "$(uname -s)" == 'Linux' ]] ; then
		if [[ -n "${service[IOSchedulingClass]:-}" || -n "${service[IOSchedulingPriority]:-}" ]]; then
			ssd_args+="--iosched ${service[IOSchedulingClass]:-best-effort}:${service[IOSchedulingPriority]:-4} "
		fi

		if [[ -n "${service[NoNewPrivileges]:-}" ]] && is_true "${service[NoNewPrivileges]}"; then
			ssd_args+="--no-new-privs "
		fi

		if [[ -n "${service[SecureBits]:-}" ]]; then
		    local secbits=0
		    for sbit in ${service[SecureBits]}; do
			case $sbit in
			    # See linux/securebits.h
			    noroot) ((secbits |= 1)) ;;
			    noroot-locked) ((secbits |= 1 << 1)) ;;
			    no-setuid-fixup) ((secbits |= 1 << 2)) ;;
			    no-setuid-fixup-locked) ((secbits  |= 1 << 3)) ;;
			    keep-caps) ((secbits |= 1 << 4)) ;;
			    keep-caps-locked) ((secbits |= 1 << 5)) ;;
			    *) ;;
			esac
		    done
		    ssd_args+="--secbits ${secbits} "
		fi

		if [[ -n "${service[OOMScoreAdjust]:-}" ]]; then
			ssh_args+="--oom-score-adj ${service[OOMScoreAdjust]} "
		fi

		if [[ "${service[CapabilityBoundingSet]+defined}" ]] ; then
			# openrc --capabilities use cap_iab_from_text(3) which doesn't
			# support all/none. So build the complement manually.
			caps=$(cap_complement ${service[CapabilityBoundingSet]#'~'})
			case ${service[CapabilityBoundingSet]} in
			    '') caps_args="!${caps//[$' \n\r\t']/,!}" ;;
			    ~*) caps_args="${caps//[$' \n\r\t']/,}"
				caps_args+="${service[CapabilityBoundingSet]//[$' ~\n\r\t']/,!}" ;;
			    *) caps_args="!${caps//[$' \n\r\t']/,!}"
			       caps_args+=",${service[CapabilityBoundingSet]//[$' \n\r\t']/,}" ;;
			esac
		fi
		if [[ "${service[AmbientCapabilities]+defined}" ]] ; then
			case ${service[AmbientCapabilities]} in
			    '') ;;
			    ~*) caps=$(cap_complement ${service[AmbientCapabilities]#'~'})
				caps_args+="${caps_args:+,}^${caps//[$' \n\r\t']/,^}" ;;
			    *) caps_args+="${caps_args:+,}^${service[AmbientCapabilities]//[$' \n\r\t']/,^}" ;;
			esac
		fi
		if [[ -n "${caps_args:-}" ]]; then
			ssd_args+="--capabilities $caps_args "
		fi
	fi

	print_directive start_stop_daemon_args "${ssd_args:-}"
}

# Replace insserv/LSB dependency with supplied expansion
replace_depend() {
	dep=$1
	key=$2
	shift 2

	# /etc/insserv.conf expansions are preceeded by +, i.e. optional, so move
	# Requires to Uses
	if [[ "$key" == Requires ]]; then
	    append_key=Uses
	else
	    append_key="$key"
	fi
	# Remove original
	remove_depend "$dep" "$key"
	# Add replacements
	add_depends "$append_key" "$@"
	# Remove circular dependencies
	remove_depend "${openrc_script}" "$key"
}

# depends[] is insserv/LSB style. Recursively expand virtual references.
expand_lsb_depends() {
	while [[ "${depends[*]}" =~ '$' ]]; do
		for key in Requires Wants After Before Uses; do
			for dep in ${depends[$key]:-}; do
				case "$dep" in
				    # TODO: taken from /etc/insserv.conf. Consider grep rather than harcoding.
				    \$local_fs) replace_depend "$dep" "$key" mountall mountall-bootclean mountoverflowtmp umountfs ;;
				    \$network) replace_depend "$dep" "$key" networking ifupdown ;;
				    \$named) replace_depend "$dep" "$key" named dnsmasq lwresd bind9 unbound pdns-recursor \$network ;;
				    \$remote_fs) replace_depend "$dep" "$key" \$local_fs mountnfs mountnfs-bootclean umountnfs sendsigs ;;
				    \$syslog) replace_depend "$dep" "$key" rsyslog sysklogd syslog-ng dsyslog inetutils-syslogd ;;
				    \$time) replace_depend "$dep" "$key" hwclock ;;
				    #  and /etc/insserv.conf.d
				    \$portmap) replace_depend "$dep" "$key" rpcbind ;;
				    \$*) echo "WARNING: ignoring unknown virtual dependency $dep" >&2
				    replace_depend "$dep" "$key" '' ;;
				esac
			done
		done
	done
}

# dependency resolution
#
# pending: special cases
gen_openrc_script_functions() {
	# if dependencies exist then check which dependency exists. according to the
	# check, use either one of `need`, `use`, `before` and `after`.
	if [[ -n "${depends[*]}"||
		-n "${install[Alias]:-}" ]]; then

		expand_lsb_depends

		printf '%s\n' 'depend() {'

		if [[ -n "${depends[Requires]:-}" ]]; then
			printf "%s\n" "    need ${depends[Requires]}"
		fi

		if [[ -n "${depends[Wants]:-}" ]]; then
			printf "%s\n" "    want ${depends[Wants]}"
		fi

		if [[ -n "${depends[Uses]:-}" ]]; then
			printf "%s\n" "    use ${depends[Uses]}"
		fi

		if [[ -n "${depends[Before]:-}" ]]; then
			printf "%s\n" "    before ${depends[Before]}"
		fi

		if [[ -n "${depends[After]:-}" ]]; then
			printf "%s\n" "    after ${depends[After]}"
		fi

		if [[ -n "${install[Alias]:-}" ]]; then
			alias=${install[Alias]//.service}
			printf "%s\n" "    provide ${alias#$}" # Could be lsb/insserv style; remove any leading $
		fi
		printf '%s\n' '}'
	fi

	for t in Stop Start-Pre Start-Post Stop-Pre Stop-Post Reload ; do
		gen_openrc_script_exec_event "--${t@L}" "${service[Exec${t/-/}]:-}"
	done
}

# generate configuration required by the service
gen_openrc_script_conf() {
	gen_environment

	if [[ "${service[ExecStop]:-}${service[ExecReload]:-}" =~ '$MAINPID' ]]; then
		print_directive MAINPID '$([ ! -f "/run/supervise-${RC_SVCNAME}.pid" ] || cat "/run/supervise-${RC_SVCNAME}.pid")'
	fi
	if [[ "${service[KillMode]:-control-group}" = 'control-group' ]]; then
		print_directive rc_cgroup_cleanup YES
		print_directive rc_send_sighup "${service[SendSIGHUP]:-no}"
	fi
	while read -r tmp ; do
		ulimit+="$tmp "
	done < <(gen_ulimit_args)
	print_directive rc_ulimit "${ulimit:-}"
}

# a generic function that handles all events of type: start, stop, reload,
# start_pre, stop_pre, start_post, stop_post.
#
# this function accepts two arguments: first one is the type of execution
# (start, stop, etc) and the second is the actual command performing said
# execution.
gen_openrc_script_exec_event() {
	local exec_name exec_args exec_type exec_event runas

	# type is {start,stop,restart,reload} and event is the actual $type command
	exec_type="${1}"
	if [[ "${service[Type]}" == oneshot || "${exec_type}" == "--*-*" ]]; then
		runas=runas
	fi
	exec_event=$(handle_exec_prefixes "${2}" "${runas:-}")

	# start generating the script
	case "${exec_type}" in
		"--start")
			# Handle oneshot with a custom start(). Multiple ExecStart possible
			if [[ "${service[Type]}" == oneshot ]]; then
				print_sh_function start "${exec_event}"
			else
				# Everything before the first space
				exec_name=${exec_event%% *}
				# Everything not in $exec_name
				exec_args=${exec_event#"${exec_name}"}
				printf '%s\n' "command=\"${exec_name}\""
				printf '%s\n' "command_args=\"${exec_args# }\"" # trim initial whitespace
			fi

			# run as $user and $group if the systemd service provides one
			if [[ "${service[User]:-}" ]]; then
				local user
				user="${service[User]}"

				if [[ "${service[Group]:-}" ]]; then
					local group
					group="${service[Group]}"

					printf '%s\n' "command_user=\"${user}:${group}\""
				else
					printf '%s\n' "command_user=\"${user}\""
				fi
			fi

			# define PID if the systemd service provides one
			if [[ "${service[PIDFile]:-}" ]]; then
				local pid_file
				pid_file="${service[PIDFile]}"

				printf '%s\n' "pidfile=\"${pid_file}\""
			fi
			;;
		"--stop")
			print_sh_function stop "${exec_event}"
			;;
		"--start-pre")
		    chks=$(cat <<EOF
$(gen_pre_checks)
$(gen_instantiated_check)
$exec_event
EOF
			)
		    print_sh_function start_pre "${chks}"
			;;
		"--start-post")
			print_sh_function start_post "${exec_event}"
			;;
		"--stop-pre")
			print_sh_function stop_pre "${exec_event}"
			;;
		"--stop-post")
			print_sh_function stop_post "${exec_event}"
			;;
		"--reload")
		    	if [[ "${exec_event}" ]]; then
				printf '%s\n' 'extra_started_commands="reload"'
				print_sh_function reload 'ebegin "Reloading ${RC_SVCNAME}"'$'\n'"${exec_event}"
			fi
			;;
	esac
}

# Translate systemd.unit(5) specifiers.
# These are embedded in the openrc script, so POSIX only and be careful with quoting.
replace_specifiers() {
	sed --sandbox "s,%a,\$(arch),g;
	s,%A,\$(awk -F= '/^IMAGE_VERSION=/ {print \$2}' /etc/os-release),g;
	s,%b,\(cat /proc/sys/kernel/random/boot_id,g;
	s,%B,\$(awk -F= '/^BUILD_ID=/ {print \$2}' /etc/os-release),g;
	s,%C,\${XDG_CACHE_HOME:-/var/cache},g;
	s,%d,\${CREDENTIALS_DIRECTORY:-},g;
	s,%D,\${XDG_DATA_HOME:-/usr/share},g;
	s,%E,\${XDG_CONFIG_HOME:-/etc},g;
	s,%f,/\$(echo \${RC_SVCNAME#*.}|tr - /),g;
	s,%g,\$(getent group \$(id -g)|cut -d: -f1),g;
	s,%G,\$(id -g),ig;
	s,%h,\$(getent passwd \$USER|cut -d: -f6),g;
	s,%H,\$(hostname -f),g;
	s,%i,\${RC_SVCNAME#*.},g;
	s,%I,\$(echo \${RC_SVCNAME#*.}|tr - /),g;
	s,%j,\$(echo \${RC_SVCNAME%.*}|sed s/^.*-//),g;
	s,%J,\$(echo \${RC_SVCNAME%.*}|sed s/^.*-//| tr - /),g;
	s,%[lq],\$(hostname),g;
	s,%L,\${XDG_STATE_HOME:-/var}/log,g;
	s,%m,\$(cat /etc/machine-id),g;
	s,%M,\$(awk -F= '/^IMAGE_VERSION=/ {print \$2}' /etc/os-release),g;
	s,%n,\${RC_SVCNAME},g;
	s,%N,\${RC_SVCNAME%.*},g;
	s,%o,\$(awk -F= '/^IMAGE_VERSION=/ {print \$2}' /etc/os-release),g;
	s,%p,\${RC_SVCNAME%.*},g;
	s,%P,\$(echo \${RC_SVCNAME%.*}|tr - /),g;
	s,%s,\$(getent passwd \$USER|cut -d: -f7),g;
	s,%S,\${XDG_STATE_HOME:-/var/lib},g;
	s,%t,\${XDG_RUNTIME_DIR:-/run},g;
	s,%T,\${TMPDIR:-/tmp},g;
	s,%u,\$(whoami),g;
	s,%v,\$(uname -r),g;
	s,%V,\${TMPDIR:-/var/tmp},g;
	s,%w,\$(awk -F= '/^VERSION_ID=/ {print \$2}' /etc/os-release),g;
	s,%W,\$(awk -F= '/^VARIANT_ID=/ {print \$2}' /etc/os-release),g;
	s,%y,,gi; # Fragments unsupported
	s,%%,%,g;
	"
}

# read a boolean value and returns true or false
# usage: is_true val
# val		boolean value
is_true() { case "$1" in 1 | [Oo][Nn] | [Tt]* | [Yy]*) true ;; *) false ;; esac }

gen_instantiated_check() {
    	if [[ "${instantiated}" -eq 1 ]]; then
	    # shellcheck disable=SC2016
	    {
		printf '%s\n' 'if [ -z "${RC_SVCNAME#*.}" ]; then'
		printf '%s\n' '  eerror "${RC_SVCNAME} cannot be started directly."'
		printf '%s\n' '  eerror "You must make symbolic links to the instances you want to start."'
		printf '%s\n' '  return 1'
		printf '%s\n' 'fi'
	    }
	fi
}

gen_pre_checks() {
	for constraint in Assert Condition; do
		for test in ACPower Architecture Capability ControlGroupController CPUFeature CPUs DirectoryNotEmpty Environment FileIsExecutable FileNotEmpty Firmware FirstBoot Group Host KernelCommandLine KernelVersion Memory NeedsUpdate OSRelease PathExists PathExistsGlob PathIsDirectory PathIsEncrypted PathIsMountPoint PathIsReadWrite PathIsSymbolicLink Security User Virtualization; do
		    while
			read -r trigger
			read -r pre
			read -r p; do
			t=$(gen_test_case "$test" "$pre" "$p")
			if [ "${t}" ]; then
					if [ "${trigger}" ] ; then
					    triggers+=$'\n'"( $t ) ||"
					else
					    [ $constraint = 'Assert' ] && fail="echo \"Prohibited by ${constraint}${test} ${pre}${p}\"; exit 1" ||
						    fail="start() { einfo \"Skipped due to ${constraint}${test} ${pre}${p}\"; }"
					    echo "$t || $fail"
					fi
				else
					echo "WARNING: unsupported test: $constraint $test ${pre}${p}" >&2
					echo ": # WARNING: skipped unsupported ${constraint}${test} ${pre}${p}"
				fi
				unset t	trigger pre
			done < <(split_constraint "${unit[${constraint}${test}]:-}")
		done
	done
	if [[ "${triggers:-}" ]] ; then
	    printf '( # Triggering conditions\n'
	    # Remove the final trailing '||'
	    print_lines "${triggers%||}" ' '
	    printf ') || start() { einfo "Skipped due to no Triggering Conditions" ; }'
	fi
}

export_service() {
	systemd_unit=$1
	base_dir=$2
	if [[ "${unit_flavour}" == user ]]; then
	    base_dir+=/user
	fi
	openrc_init_dir="${base_dir}/init.d"
	mkdir -p "${openrc_init_dir}"

	openrc_script=$(basename "${systemd_unit%.*}")

	# indicate instantiated units
	if [[ -z "${openrc_script#*@}" ]]; then
	    instantiated=1
	    openrc_script="${openrc_script/\@/\.}"
	else
	    instantiated=0
	fi

	# Handle service Type specifics
	case "${service[Type]}" in
	    oneshot)
		if ! is_true "${service[RemainAfterExit]:-no}" ; then
		    service[ExecStartPost]+=$'\n!mark_service_stopped'
		fi ;;
	    notify-reload)
		service[ExecReload]='supervise-daemon "${RC_SVCNAME}" -s HUP' ;;
	esac

	# generate the "#!/sbin/openrc-run" shebang which is required to run
	# openrc scripts
	gen_openrc_script_shebang >"${openrc_init_dir}/${openrc_script}"

	# embed source and sha256
	gen_origin >>"${openrc_init_dir}/${openrc_script}"

	# generate and store necessary openrc variables in the appropriate file
	gen_openrc_script_variables | replace_specifiers >>"${openrc_init_dir}/${openrc_script}"

	# generate and store necessary openrc functions in the appropriate file
	gen_openrc_script_functions | replace_specifiers >>"${openrc_init_dir}/${openrc_script}"

	# make resulting script executable
	chmod +x "${openrc_init_dir}/${openrc_script}"

	# Handle directives for conf.d file
	conf_d_contents=$(gen_openrc_script_conf)
	if [[ -n "${conf_d_contents:-}" ]]; then
		openrc_conf_dir="${base_dir}/conf.d"
		mkdir -p "${openrc_conf_dir}"

		# generate and store the environment variables in the appropriate file
		gen_origin >"${openrc_conf_dir}/${openrc_script}"
		printf '%s\n' "$conf_d_contents" >>"${openrc_conf_dir}/${openrc_script}"
	fi
}