The officially official Devuan Forum!

You are not logged in.

#1 2020-07-30 15:04:50

little
Member
Registered: 2017-06-08
Posts: 19  

Best Practices for preinst / postinst dpkg Compatibility

So I have .deb install scripts on a package (zoneminder) that I'm looking to get changed. They are built for systemd, and look something like this:

#! /bin/sh

set +e

if [ "$1" = "configure" ]; then

  . /etc/zm/zm.conf
  for CONFFILE in /etc/zm/conf.d/*.conf; do
    . "$CONFFILE"
  done

  # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
  chown www-data:root /var/log/zm
  chown www-data:www-data /var/lib/zm
  if [ -z "$2" ]; then
    chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/*
  fi
  if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ] && [ "$(command -v a2enmod)" != "" ]; then
    echo "The cgi module is not enabled in apache2.  I am enabling it using a2enmod cgi."
    a2enmod cgi
  fi
  if [ ! -e "/etc/apache2/mods-enabled/rewrite.load" ] && [ "$(command -v a2enmod)" != "" ]; then
    echo "The rewrite module is not enabled in apache2.  I am enabling it using a2enmod rewrite."
    a2enmod rewrite
  fi

  if [ "$ZM_DB_HOST" = "localhost" ]; then

    if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ] || [ -e "/etc/init.d/mysql" ]; then
      # Ensure zoneminder is stopped
      deb-systemd-invoke stop zoneminder.service || exit $?

      #
      # Get mysql started if it isn't running
      #

      if [ -e "/lib/systemd/system/mariadb.service" ]; then
        DBSERVICE="mariadb.service"
      else
        DBSERVICE="mysql.service"
      fi
      echo "Detected db service is $DBSERVICE"
      if systemctl is-failed --quiet $DBSERVICE; then
        echo "$DBSERVICE is in a failed state; it will not be started."
        echo "If you have already resolved the problem preventing $DBSERVICE from running,"
        echo "run sudo systemctl restart $DBSERVICE then run sudo dpkg-reconfigure zoneminder."
        exit 1
      fi

      if ! systemctl is-active --quiet mysql.service mariadb.service; then
        # Due to /etc/init.d service autogeneration, mysql.service always returns the status of mariadb.service
        # However, mariadb.service will not return the status of mysql.service.
        deb-systemd-invoke start $DBSERVICE
      fi
 # Make sure systemctl status exit code is 0; i.e. the DB is running
      if systemctl is-active --quiet "$DBSERVICE"; then
        mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
        # test if database if already present...
        if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
            echo "Creating zm db"
            cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
            if [ $? -ne 0 ]; then
              echo "Error creating db."
              exit 1;
            fi
            # This creates the user.
            echo "CREATE USER '${ZM_DB_USER}'@localhost IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
        fi
        echo "Updating permissions"
        echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute  on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql

        zmupdate.pl --nointeractive
        zmupdate.pl --nointeractive -f

        # Add any new PTZ control configurations to the database (will not overwrite)
        zmcamtool.pl --import >/dev/null 2>&1
        echo "Done Updating; starting ZoneMinder."
      else
        echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'
      fi
    else
      echo 'MySQL/MariaDB not found; assuming remote server.'
    fi

  else
    echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)."
  fi
  deb-systemd-invoke restart zoneminder.service

fi
# Automatically added by dh_systemd_enable/12.1.1
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
        if deb-systemd-helper debian-installed 'zoneminder.service'; then
                # This will only remove masks created by d-s-h on package removal.
                deb-systemd-helper unmask 'zoneminder.service' >/dev/null || true

                if deb-systemd-helper --quiet was-enabled 'zoneminder.service'; then
                        # Create new symlinks, if any.
                        deb-systemd-helper enable 'zoneminder.service' >/dev/null || true
                fi
        fi

        # Update the statefile to add new symlinks (if any), which need to be cleaned
        # up on purge. Also remove old symlinks.
        deb-systemd-helper update-state 'zoneminder.service' >/dev/null || true
fi
# End automatically added section
# Automatically added by dh_installinit/12.1.1
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
        # In case this system is running systemd, we need to ensure that all
        # necessary tmpfiles (if any) are created before starting.
        if [ -d /run/systemd/system ] ; then
                systemd-tmpfiles --create zoneminder.conf >/dev/null || true
        fi
fi
# End automatically added section
# Automatically added by dh_installdeb/12.1.1
dpkg-maintscript-helper rm_conffile /etc/zm/apache.conf 1.28.1-5~ -- "$@"
# End automatically added section
# Automatically added by dh_installinit/12.1.1
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
        if [ -x "/etc/init.d/zoneminder" ]; then
                update-rc.d zoneminder defaults >/dev/null
                invoke-rc.d zoneminder start || exit 1
        fi
fi
# End automatically added section

What breaks compatibility is the specific commands such as deb-systemd-helper or  deb-systemd-invoke restart zoneminder.service for example.

What's the best practice for adding support for sysvinit or devuan as well? Should I search - if exists - /etc/devuan_version then do devuan section... or is there a better way that is not limited to devuan (i.e. would also support any distro with sysvinit).

I should mention that, in terms of simplicity, I would not want to just add on something like

  else
    echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)."
  fi
<added section>
if (init is detected as sysvinit)
then
service restart zoneminder
else
</added section>
  deb-systemd-invoke restart zoneminder.service

And layer more commands in each section, as that would make maintenance difficult.

I would instinctively want to separate out the configs, so that the sysvinit is in its own section to make everything less entangled and more modular.  My thought was to have it be
if sysvinit detected then
sysvinitsection with break to end script
else
systemd section


give a man an init, he takes an os

Offline

#2 2020-07-30 18:36:45

Head_on_a_Stick
Member
From: London
Registered: 2019-03-24
Posts: 1,064  
Website

Re: Best Practices for preinst / postinst dpkg Compatibility

little wrote:

What's the best practice for adding support for sysvinit or devuan as well?

The posted script seems to already support sysvinit:

 if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
        if [ -x "/etc/init.d/zoneminder" ]; then
                update-rc.d zoneminder defaults >/dev/null
                invoke-rc.d zoneminder start || exit 1
        fi
fi

Black Lives Matter

Offline

#3 2020-07-30 20:04:14

Marjorie
Member
From: Teignmouth, UK
Registered: 2019-06-09
Posts: 89  

Re: Best Practices for preinst / postinst dpkg Compatibility

As I understand it the correct way is to re-build the .deb package from the source using deb_helper.
There a tutorial on the overall process here: https://wiki.debian.org/BuildingTutorial.
Though the tutorial uses an example where you want to change the source code (and recompile) not just the packaging.

Essentially to create a Devuan compatible .deb you need to:
1) add a suitable init script to the files tree (your /etc/init.d/zoneminder). As has been pointed out by Head_on_a_Stick you can generate one from the systemd service file using the sysv2d.sh script.
2) Adding a Debian rule to generate the sysvinit additions to the postinst and postrm scripts.
The rule will be a parametrisation of the deb helper program dh_installinit.
3) Change the version number and annotate the change.
4) If you then want to submit as a bug to Debian create a diff on the original and submit  it with the bug report.

When you've recreated your .deb you might see in your postinst file something like this :

# Automatically added by dh_installinit/12.1.1
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
	if [ -x "/etc/init.d/chrony" ]; then
		update-rc.d chrony defaults >/dev/null
		if [ -n "$2" ]; then
			_dh_action=restart
		else
			_dh_action=start
		fi
		invoke-rc.d --skip-systemd-native chrony $_dh_action || exit 1
	fi
fi
# End automatically added section

This example (for chrony) enables (using update-rc.d) and starts (using invoke-rc.d) the init.

The corresponding addition to postrm is:

# Automatically added by dh_installinit/12.1.1
if [ "$1" = "purge" ] ; then
	update-rc.d chrony remove >/dev/null
fi
# End automatically added section

However some maintainers don't provide an init file and don't run dh_installinit, which is where we get problems.

Here is an example of the rules for another deb (nftables) that doesn't. As you can see, dh_installinit is called but just with the -n parameter and so doesn'r run. Run without any parameters it will create the postinst code to enable and start the init and the postrm code to remove it. For some programs you might not want to have enable or start (in this case for dh_installsystemd they are turned off by parameters --no-enable- -no-start).

#!/usr/bin/make -f

ifeq (,$(filter terse,$(DEB_BUILD_OPTIONS)))
export DH_VERBOSE=1
endif
export PYBUILD_NAME = nftables
export DEB_BUILD_MAINT_OPTIONS = hardening=+all

configure_opts	:= --with-xtables --with-json --with-python-bin=/usr/bin/python3

override_dh_auto_configure:
	dh_auto_configure -- $(configure_opts) --

%:
	dh $@ --with python3

override_dh_fixperms:
	dh_fixperms
	chmod a+x debian/nftables/etc/nftables.conf

override_dh_installsystemd:
	dh_installsystemd --no-enable --no-start

override_dh_installinit:
	# dh_installinit will try to mess with /etc/init.d/nftables in
	# the maintainer scripts, but we don't ship it
	dh_installinit -n

override_dh_installexamples:
	dh_installexamples -XMakefile
	# upstream examples are installed in by the 'install' target to '/etc/nftables'
	mv debian/tmp/etc/nftables/*.nft debian/nftables/usr/share/doc/nftables/examples/
	rm -rf debian/nftables/etc/nftables

Last edited by Marjorie (2020-07-30 21:10:28)

Offline

#4 2020-07-30 20:12:39

little
Member
Registered: 2017-06-08
Posts: 19  

Re: Best Practices for preinst / postinst dpkg Compatibility

Per Headonastick: There's other commands in the script that fail to run on devuan, and that snippet is only for the startup defaults. If you read the remaining parts of the script, you will see what I mean.

I should probably get around to reading one of the dpkg books, so I apologize if this information is obvious to package maintainers.

Per Marjorie, thanks, that seems to point me in the right direction. I'll look into the rules, along with the commands you listed and should be able to figure it out.

Last edited by little (2020-07-30 20:49:33)


give a man an init, he takes an os

Offline

Board footer