#!/usr/bin/perl
# dh_sysuser --- debhelper to create system users

# Copyright (C) 2016—2019 Dmitry Bogatov <kaction@sagulo>
# Copyright (C) 2020-2024 Lorenzo Puliti <plorenzo@disroot.org>

# Author: Dmitry Bogatov <kaction@sagulo>

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

use 5.014;
use strict;
use Debian::Debhelper::Dh_Lib;
use File::Find;
use File::stat;
use feature 'signatures';
use feature 'switch';
no warnings 'experimental::signatures';
no warnings 'experimental::smartmatch';

our $VERSION = "1.6.0";

init();

sub parse_options($conf, $options, $user) {
    foreach my $opt (split(/,/, $options)) {
        for ($opt) {
            if (/^home=(.*)$/)  { $conf->{home} = $1; }
            elsif (/^home$/)       {
                my $normal = $user;
                $normal =~ s/^_+//;         # strip leading
                $normal =~ s/_+$//;         # and trailing underscore
                $normal =~ s/^[Dd]ebian-//; # and discouraged debian- prefix
                $conf->{home} = "/var/lib/$normal";
            }
            elsif (/^defaults$/)   { "do nothing"; }
            else               { error("unknown option `$opt'"); }
        }
    }
}

foreach my $pkg (@{$dh{DOPACKAGES}}) {
    my $tmp = tmpdir($pkg);
    my $sysusersdir = "$tmp/usr/lib/sysusers.d";
    my $minsysusers = pkgfile($pkg, 'minsysusers');

    if ($minsysusers eq '') {
    # start DEPRECATED section, remove this after trixie release
    my @entries = ();
    if (@ARGV) {
        while (@ARGV) {
            (my $user, my $opt) = splice(@ARGV, 0, 2);
            push @entries, [$user, $opt];
        }
    } elsif (my $cfg = pkgfile($pkg, 'sysuser')) {
        @entries = filedoublearray($cfg);
    };
    next unless @entries;
    foreach my $entry (@entries) {
        warning("using legacy and deprecated sysuser format, please switch to the new");
        warning("minsysusers format or use debhelper's dh_installsysusers instead");
        warning("support for the legacy sysuser format is scheduled for removal after trixie release");
        (my $user, my $opts) = @$entry;
        $opts ||= 'defaults';
        my %conf = (home => '/nonexistent');
        parse_options(\%conf, $opts, $user);
        foreach my $script (qw/postrm postinst/) {
            autoscript($pkg, $script, "$script-sysuser",
                       sub { s/%HOME%/$conf{home}/;
                             s/%PACKAGE%/$pkg/;
                             s/%USERNAME%/$user/;});
        }
        #systemd sysusers.d compat layer
        #my $tmp = tmpdir($pkg);
        #my $sysusersdir = "$tmp/usr/lib/sysusers.d";
        my $sysusersdconf = "$sysusersdir/$pkg.conf";
        install_dir($sysusersdir);
        open(my $fh, '>>', $sysusersdconf) or die $!;
            print $fh "u  $user  -  \"created by dh_sysuser for $pkg \"  $conf{home}\n";
        close $fh;
    }
    # 1.3.3 is the first version compatible with current (1.4.1) version
    # >=1.4.0 would be better but since recent versions of the package
    # imposed  <<.1.4.0, a dependency on a more relaxed version is needed
    addsubstvar($pkg, 'misc:Depends', 'sysuser-helper', '>= 1.3.3');
    #end DEPRECATED section
    } else {
        install_dir($sysusersdir);
        install_file($minsysusers, "$sysusersdir/$pkg.conf");
        autoscript($pkg, 'postinst', 'postinst-minsysusers', sub { s/#CONFILE_SYSUSERS#/$pkg.conf/;});
        #1.6= first version with minsysusers format
        addsubstvar($pkg, 'misc:Depends', 'sysuser-helper', '>= 1.6');
    }
}

# PROMISE: DH NOOP WITHOUT pkgfile(sysuser) pkgfile(minsysusers)
=head1 NAME

dh_sysuser - manage system users required for package operation

=head1 SYNOPSIS

B<dh_sysuser> [S<I<debhelper options>>] [I<username> I<options>] ...

=head1 DESCRIPTION

B<dh_sysuser> is an alternative to the more popular dh_installsysusers
addon; dh_sysuser is a debhelper addon providing a simple way to create
system users required for package operation (for example, to run a service
with dropped privileges).

Compared to dh_installsysusers, B<dh_sysuser> injects a different dependency
that plays nice with alternative init systems, non-linux ports or initless systems.
Packages builded with dh_sysuser will still work fine under systemd at the cost of
an additional dependency (sysuser-helper).

B<dh_sysuser> should not be used when the upstream source provides
a sysusers.d conf file already installed in /usr/lib/sysusers.d/; for such cases
please use dh_installsysusers.

The user creation itself is delegated to a systemd-sysusers provider, with
a fallback to the minsysusers(8) utility for systems where a systemd-sysuser
provider is not available.

=over

=item *

The primary group of the new user is created with the same name as the
user. The new users will not be a member of any other group except the
primary one.

=item *

New users have the F</etc/shadow> password field set to '!', making it
impossible to log in.

=item *

By default new users have the shell set to F</usr/sbin/nologin>. It is still possible
to get a new user's shell with I<su -s>.

=item *

The default home directory is set to /; if a different home is chosen, the home directory
is created (see below), its permissions are adjusted according to the B<SYS_DIR_MODE>
variable in F</etc/adduser.conf>. By default, this results in the mode 0755 for the home
directory.Files from F</etc/skel> are I<NOT> copied.

B<WARNING:> The data stored in new user's home directory are world-readable.
If you (as package maintainer) need full control over home directory permissions,
please file a bug.

=item *

It's possible to override the default setting for user's home and shell, add
a GECOS comment and set the user's UID. Please see sysusers.d(5) format.

=back

B<dh_sysuser> looks for a  F<debian/I<package>.F<minsysusers>> file,
if one exists, and installs it as F</usr/lib/sysusers.d/package.conf>; then
it adds a postinstall snippet code to make sure that the user is created at
package postinstall.
The  F<debian/I<package>.F<minsysusers>> file is expected to follow
the sysusers.d(5) conf file specification.

=over

=back

=head2 CRUFT OF SYSTEM USERS

Creating a system user (or a user in general) is easy, but safely removing one
is hard. Former version of this package used to remove users on purge when
home was set to /nonexistent or was empty; however a user may be allowed to
write files outside his home, and since UIDs are reusable, this may represent a
security risk.
With the current version of this package users are never removed automatically.

=head1 EXAMPLES

With the following F<debian/I<package>.F<minsysusers>> control file, you get
respectively:

    u     foo     -  "foo user"   /nonexistent      -
    g     bar     -
    m    baz  gname

a system user B<foo> with a gecos comment "foo user" and home set to
/nonexistent: the B<foo> group will be also created;
a system group B<bar>;
add gname group as supplementary group of baz user.

=head1 SEE ALSO

useradd(8), groupadd(8), usermod(8)

=cut
