#!/usr/bin/perl

=head1 NAME

dh_s6 - install s6-rc service registration into package maintainer scripts

=cut

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

our $VERSION = '1.0.0';

=head1 SYNOPSIS

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

=head1 DESCRIPTION

B<dh_s6> registers s6-rc services by adding them to bundle F<contents.d>
directories and applying live state changes through L<s6-rc(8)> in package
maintainer scripts.

It is intended to be used with B<dh --with s6>.  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_s6> supplies the s6-rc equivalents
according to F<debian/I<package>.s6>.

=head1 FILES

=over 4

=item debian/I<package>.s6

Lists s6-rc 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_s6> does nothing for that package.

=back

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

=over 4

=item B<defaults>

Explicit no-op placeholder.

=item B<bundle=>I<bundle>

s6-rc bundle whose F<contents.d> receives the service (default: B<default>).

=item B<name=>I<name>

s6-rc service name.  Defaults to the first field on the line.

=item B<disable>

Do not add the service to the bundle (skip enable).

=item B<nostart>

Only manage bundle membership; do not start/stop/restart via B<s6-rc>.

=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.s6
  syslog-ng bundle=default

  # debian/openssh-server.s6
  sshd bundle=misc

  # debian/netbase.s6
  networking bundle=boot,nostart

  # debian/foo.s6
  foo defaults
  foo-extra name=foo-extra,bundle=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',
		bundle      => '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 =~ /^bundle=(.+)$/) {
			$conf->{bundle} = $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 s6 config");
		}
	}
	return $conf;
}

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

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

	my $pkgfile = pkgfile($package, 's6');
	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; s6 snippets will still be generated")
			unless -e $init_path;

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

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

		autoscript($package, 'preinst', 'preinst-s6-chmod', $replace, \%snippet_options);

		if (!$conf->{nostart}) {
			if ($conf->{onupgrade} eq 'restart') {
				autoscript($package, 'postinst', 'postinst-s6-restart', $replace, \%snippet_options);
				autoscript($package, 'prerm', 'prerm-s6-norestart', $replace, \%snippet_options);
			} elsif ($conf->{onupgrade} eq 'stop') {
				autoscript($package, 'postinst', 'postinst-s6', $replace, \%snippet_options);
				autoscript($package, 'preinst', 'preinst-s6-stop', $replace, \%snippet_options);
				autoscript($package, 'prerm', 'prerm-s6-norestart', $replace, \%snippet_options);
			} else {  # nostop
				autoscript($package, 'postinst', 'postinst-s6', $replace, \%snippet_options);
				autoscript($package, 'prerm', 'prerm-s6-norestart', $replace, \%snippet_options);
			}
		} else {
			autoscript($package, 'postinst', 'postinst-s6-nostart', $replace, \%snippet_options);
		}

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

		$added_scripts = 1;
	}

	if ($added_scripts) {
		addsubstvar($package, 'misc:Depends', 's6-helper', '>= 1.0.0~');
	}
}

=head1 SEE ALSO

L<debhelper(7)>, L<dh_installinit(1)>, L<s6-rc(8)>, L<s6-rc-compile(8)>,
L<s6-rc-update(8)>, L<dh_openrc(1)>, L<dh_runit(1)>

=head1 AUTHOR

s6 packaging helpers for Genuen-style systems.

=cut
