#!/usr/bin/perl

=head1 NAME

dh_openrc - install OpenRC service registration into package maintainer scripts

=cut

use strict;
use warnings;
use Debian::Debhelper::Dh_Lib;

our $VERSION = '1.0.2';

=head1 SYNOPSIS

B<dh_openrc> [S<I<debhelper options>>] [B<-n>] [S<I<service options>> ...]

=head1 DESCRIPTION

B<dh_openrc> is a debhelper program that registers OpenRC services using
L<rc-update(8)> and L<rc-service(8)> in package maintainer scripts.

It is intended to be used with B<dh --with openrc>.  In that mode,
L<dh_installinit(1)> still installs F<debian/I<package>.init> (and related
files) into F</etc/init.d/>, but does B<not> emit sysvinit
L<update-rc.d(8)> snippets.  B<dh_openrc> supplies the OpenRC equivalents
according to F<debian/I<package>.openrc>.

=head1 FILES

=over 4

=item debian/I<package>.openrc

Lists OpenRC services to register.  Each non-comment line has the form:

  I<service> [I<options>]

where I<options> is a comma-separated list (same style as L<dh_runit(1)>).

If this file is absent, B<dh_openrc> does nothing for that package.

=back

=head1 OPTIONS IN debian/I<package>.openrc

=over 4

=item B<defaults>

Explicit no-op placeholder.

=item B<runlevel=>I<runlevel>

OpenRC runlevel(s) passed to B<rc-update add>.  Default: B<default>.
Multiple runlevels may be separated by spaces inside the value if quoted
via a single token without commas, e.g. C<runlevel=default> (one runlevel).

=item B<name=>I<name>

Service / F</etc/init.d> script name.  Defaults to the first field on the
line.

=item B<disable>

Do not enable the service (skip B<rc-update add>).

=item B<nostart>

Do not start/stop/restart via B<rc-service>; only manage runlevel
membership.

=item B<onupgrade=>B<restart>|B<stop>|B<nostop>

Upgrade policy (default B<restart>):

=over 4

=item B<restart>

Restart (or start on fresh install) after upgrade; do not stop in
F<preinst> during upgrade (like B<dh_installinit --restart-after-upgrade>).

=item B<stop>

Stop before upgrade and start after (classic prerm/postinst).

=item B<nostop>

Do not stop or restart on upgrade; still stop on remove.

=back

=item B<noscripts>

Do not modify maintainer scripts for this entry.

=back

=head1 EXAMPLES

  # debian/rsyslog.openrc
  rsyslog runlevel=default

  # debian/netbase.openrc
  networking runlevel=sysinit,nostart

  # debian/foo.openrc
  foo defaults
  foo-extra name=foo-extra,runlevel=default,disable

=head1 OPTIONS

=over 4

=item B<-n>, B<--no-scripts>

Do not modify maintainer scripts (global).

=back

=cut

init(options => {});

sub parse_options {
	my ($opts) = @_;
	my $conf = {
		enable      => 1,
		nostart     => 0,
		noscripts   => 0,
		onupgrade   => 'restart',
		runlevel    => 'default',
	};
	$opts //= '';
	$opts =~ s/^\s+|\s+$//g;
	return $conf if $opts eq '';

	for my $opt (split(/,/, $opts)) {
		$opt =~ s/^\s+|\s+$//g;
		next if $opt eq '';
		if ($opt eq 'defaults') {
			# no-op
		} elsif ($opt eq 'disable') {
			$conf->{enable} = 0;
		} elsif ($opt eq 'nostart') {
			$conf->{nostart} = 1;
		} elsif ($opt eq 'noscripts') {
			$conf->{noscripts} = 1;
		} elsif ($opt =~ /^name=(.+)$/) {
			$conf->{name} = $1;
		} elsif ($opt =~ /^runlevel=(.+)$/) {
			$conf->{runlevel} = $1;
		} elsif ($opt =~ /^onupgrade=(.+)$/) {
			my $v = $1;
			error("unknown onupgrade value `$v' (want restart|stop|nostop)")
				unless $v =~ /^(?:restart|stop|nostop)$/;
			$conf->{onupgrade} = $v;
		} else {
			error("unknown option `$opt' in openrc config");
		}
	}
	return $conf;
}

my %snippet_options = ('snippet-order' => 'service');

foreach my $package (@{$dh{DOPACKAGES}}) {
	next if is_udeb($package);

	my $pkgfile = pkgfile($package, 'openrc');
	next unless $pkgfile;

	my @entries = filedoublearray($pkgfile);
	next unless @entries;

	my $tmp = tmpdir($package);
	my $added_scripts = 0;

	for my $entry (@entries) {
		my ($svc_field, $opts) = @$entry;
		error("empty service name in $pkgfile") unless defined $svc_field && length $svc_field;

		my $conf = parse_options($opts // '');
		my $script = $conf->{name} // $svc_field;

		my $init_path = "$tmp/etc/init.d/$script";
		warning("$package: $init_path not found in package staging area; OpenRC snippets will still be generated")
			unless -e $init_path;

		next if $conf->{noscripts} || $dh{NOSCRIPTS};

		my $runlevel = $conf->{enable} ? $conf->{runlevel} : '';
		my $replace = {
			'SCRIPT'         => $script,
			'RUNLEVEL'       => $runlevel,
			'ERROR_HANDLER'  => $dh{ERROR_HANDLER} // 'exit 1',
		};

		# Always restore +x on reinstall of a removed-but-not-purged package
		autoscript($package, 'preinst', 'preinst-openrc-chmod', $replace, \%snippet_options);

		if (!$conf->{nostart}) {
			if ($conf->{onupgrade} eq 'restart') {
				autoscript($package, 'postinst', 'postinst-openrc-restart', $replace, \%snippet_options);
				autoscript($package, 'prerm', 'prerm-openrc-norestart', $replace, \%snippet_options);
			} elsif ($conf->{onupgrade} eq 'stop') {
				autoscript($package, 'postinst', 'postinst-openrc', $replace, \%snippet_options);
				autoscript($package, 'preinst', 'preinst-openrc-stop', $replace, \%snippet_options);
				autoscript($package, 'prerm', 'prerm-openrc-norestart', $replace, \%snippet_options);
			} else {  # nostop
				# No stop before upgrade; postinst uses start (not restart).
				autoscript($package, 'postinst', 'postinst-openrc', $replace, \%snippet_options);
				autoscript($package, 'prerm', 'prerm-openrc-norestart', $replace, \%snippet_options);
			}
		} else {
			autoscript($package, 'postinst', 'postinst-openrc-nostart', $replace, \%snippet_options);
		}

		autoscript($package, 'postrm', 'postrm-openrc',
			{ 'SCRIPT' => $script, 'ERROR_HANDLER' => $dh{ERROR_HANDLER} // 'exit 1' },
			\%snippet_options);

		$added_scripts = 1;
	}

	if ($added_scripts) {
		# Like runit-helper for dh-runit: depend on a small helper package
		# instead of openrc itself, so service packages do not block openrc
		# upgrades in apt.
		addsubstvar($package, 'misc:Depends', 'openrc-helper', '>= 1.0.2~');
	}
}

=head1 SEE ALSO

L<debhelper(7)>, L<dh_installinit(1)>, L<rc-update(8)>, L<rc-service(8)>,
L<dh_runit(1)>

=head1 AUTHOR

OpenRC packaging helpers for Devuan-style systems.

=cut
