3

I have an autotools project which successfully builds and tests an app (https://github.com/goglecm/AutoBrightnessCam). The app is installed in the bin directory (preceded by any prefix the user specifies). That's pretty straightforward. I now need to make a systemd service to start it at boot time. I've created the service file and ran it manually and it works fine.

The last bit is to tell configure.ac and Makefile.am to patch a *.service.in file with the correct path for the app (just like config.h is created from config.h.in).

Will using AC_CONFIG_HEADERS be appropriate to patch *.service.in into *.service? Is there another macro used for "non-headers" perhaps?

Also, how do I specify that the service file should land (i.e. installed) in /etc/systemd/system?

Is there perhaps a better way of starting this app at boot time without systemd?

John
  • 1,012
  • 14
  • 22
  • Regarding your comment, *"for some reason it replaces @bindir@ with ${exec_prefix}/bin"*, that is what is supposed to happen. See the [GNU Coding Standards](https://www.gnu.org/prep/standards/), and [7.2.5 Variables for Installation Directories](https://www.gnu.org/prep/standards/html_node/Directory-Variables.html). You can specify different values for `--prefix` and `--exec_prefix` (or `--sysconfdir`), but the root cause of your problem is different. The problem is due to limitations in the Coding Standards and Autotools. It is not equipped to handle both System and User Installed software. – jww Dec 26 '19 at 02:37
  • Maybe you need two makefiles. The first build and installs your software in `/usr/local` or `/opt/`. The second installs system timers and services in `/etc`. I don't know if Autotools will handle dual makefiles any better though. Autotools is just too limited and too buggy. Consider, Fedora cut-over to Systemd in 2013 and Debian in 2015. The Autotools folks have had 7 years to address this problem, but they have done absolutely nothing. – jww Dec 26 '19 at 02:38
  • 1
    Also see [Install binaries into /bin, /sbin, /usr/bin and /usr/sbin, interactions with --prefix and DESTDIR](https://stackoverflow.com/q/8669303/608639), which seems to be very similar to your problem. @WilliamPursell's answer looks like it takes the "split" approach, but I have not studied it in detail. In the end, dual makefiles, Purcell's hacks and other gyrations you come up with are due to inadequacies in the Coding Standards and bugs in Autotools. If everything were completely well specified, you would not have these troubles. – jww Dec 26 '19 at 02:57
  • 1
    Also see [Can I install a systemd file during distcheck using $dc_install_base?](https://stackoverflow.com/q/26980634/608639) – jww Dec 26 '19 at 05:30
  • 1
    I know you've already fixed this in your own repo, but for the benefit of future question-readers: You shouldn't install systemd unit files in `/etc/systemd/` at all. They should be installed in the systemd unit dir, most likely `${libdir}/systemd/system/` and `${libdir}/systemd/user/`. The `systemctl enable` command will make the symlink into `/etc/systemd/(system|user)`. If `systemd.pc` was installed you can query the paths using `pkg-config --variable=systemd_system_unit_dir systemd` and `pkg-config --variable=systemd_user_unit_dir systemd`, or their autotools equivalents. – FeRD Jul 01 '21 at 21:47
  • It's also since been pointed out to me that the underscored versions of those pkg-config variables are relatively recent additions, so if you need compatibility with older systemd it might be better to look up the paths using `--variable=systemdsystemunitdir` and `--variable=systemduserunitdir`, as unreadable as those versions are. – FeRD Jul 12 '21 at 22:30
  • Those are some good points you're making, @FeRD – John Jul 14 '21 at 09:06

2 Answers2

9

How do I specify that the service file should land (i.e. installed) in /etc/systemd/system?

According to Systemd's daemon man page:

<BEGINQUOTE>

Installing systemd Service Files

At the build installation time (e.g. make install during package build), packages are recommended to install their systemd unit files in the directory returned by pkg-config systemd --variable=systemdsystemunitdir (for system services) or pkg-config systemd --variable=systemduserunitdir (for user services). This will make the services available in the system on explicit request but not activate them automatically during boot. Optionally, during package installation (e.g. rpm -i by the administrator), symlinks should be created in the systemd configuration directories via the enable command of the systemctl(1) tool to activate them automatically on boot.

Packages using autoconf(1) are recommended to use a configure script excerpt like the following to determine the unit installation path during source configuration:

PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
     [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
     [with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
     def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)

     AS_IF([test "x$def_systemdsystemunitdir" = "x"],
   [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
    [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
    with_systemdsystemunitdir=no],
   [with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
      [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])

This snippet allows automatic installation of the unit files on systemd machines, and optionally allows their installation even on machines lacking systemd. (Modification of this snippet for the user unit directory is left as an exercise for the reader.)

Additionally, to ensure that make distcheck continues to work, it is recommended to add the following to the top-level Makefile.am file in automake(1)-based projects:

AM_DISTCHECK_CONFIGURE_FLAGS = \
  --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

Finally, unit files should be installed in the system with an automake excerpt like the following:

if HAVE_SYSTEMD
systemdsystemunit_DATA = \
  foobar.socket \
  foobar.service
endif

...
</ENDQUOTE>

So it appears you should use systemdsystemunitdir and systemduserunitdir. How well Autotools supports it, well...

A quick grep on Fedora 31 using grep systemdsystemunitdir /bin/autoconf and grep -IR systemdsystemunitdir /usr/share shows no Autotools support yet. 7 years and counting...


Is there perhaps a better way of starting this app at boot time without systemd?

Systemd should be OK to start your app. Simply use systemctl(1) to enable and start them as you normally would.

Based on your GitHub and autobrightnesscam.service.in, I would not dick around with Autotools for this. You can waste copious amounts of time working around Autotols short comings (speaking from experience).

My configure.ac script (which is just a shell script) would copy autobrightnesscam.service.in to autobrightnesscam.service, and then use sed to copy-in the correct directories and files. Then, I would copy the updated autobrightnesscam.service to its proper location in AC_CONFIG_COMMANDS_POST. Maybe something like:

SERVICE_FILE=autobrightnesscam.service
SYSTEMD_DIR=`pkg-config systemd --variable=systemdsystemunitdir`

# Use default if SYSTEMD_DIR is empty
if test x"$SYSTEMD_DIR" = "x"; then
    SYSTEMD_DIR=/etc/systemd/system
fi

AC_CONFIG_COMMANDS_POST([cp "$SERVICE_FILE" "$SYSTEMD_DIR"])
AC_CONFIG_COMMANDS_POST([systemctl enable "$SYSTEMD_DIR/$SERVICE_FILE"])
AC_CONFIG_COMMANDS_POST([systemctl start "$SERVICE_FILE"])
jww
  • 97,681
  • 90
  • 411
  • 885
4

Will using AC_CONFIG_HEADERS be appropriate to patch *.service.in into *.service? Is there another macro used for "non-headers" perhaps?

No. AC_CONFIG_HEADERS is for setting up configuration headers to support your build. It is rarely used for anything other than building a config.h recording the results of certain tests that Autoconf performs, and it is not as flexible as other options in this area.

If you have additional files that you want Autoconf to build from templates then you should tell Autoconf about them via AC_CONFIG_FILES. Example:

AC_CONFIG_FILES([Makefile AutoBrightnessCam.service])

But if some of the data with which you are filling that template are installation directories then Autoconf is probably not the right place to do this at all, because it makes provision for the installation prefix to be changed by arguments to make. You would at least need to work around that, but the best thing to do is to roll with it instead, and build the .service file under make's control. It's not that hard, and there are several technical advantages, some applying even if there aren't any installation directory substitutions to worry about.

You can do it the same way that configure does, by running the very same template you're already using through sed, with an appropriate script. Something like this would appear in your Makefile.am:

SERVICE_SUBS = \
    s,[@]VARIABLE_NAME[@],$(VARIABLE_NAME),g; \
    s,[@]OTHER_VARIABLE[@],$(OTHER_VARIABLE),g

AutoBrightnessCam.service: AutoBrightnessCam.service.in
    $(SED) -e '$(SERVICE_SUBS)' < $< > $@

Also, how do I specify that the service file should land (i.e. installed) in /etc/systemd/system?

You use Automake's standard mechanism for specifying custom installation locations. Maybe something like this:

sytemdsysdir = $(sysconfdir)/systemd/system

systemdsys_DATA = AutoBrightnessCam.service

Is there perhaps a better way of starting this app at boot time without systemd?

On a systemd-based machine, systemd is in control of what starts at boot. If you want the machine to start your application automatically at boot, then I think your options are limited to

  • Configuring systemd to start it
  • Configuring something in a chain of programs ultimately started by systemd to start it
  • Hacking the bootloader or kernel to start it

There is room for diverging opinions here, but I think the first of those is cleanest and most future-proof, and I cannot recommend the last.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Using make to build the service file with sed sounds like a very good idea. So far I managed to tell autoconf to do the substitutions but for some reason it replaces @bindir@ with ${exec_prefix}/bin, which I can then fix through make. (or just use @prefix@/bin, though this is not so portable). – John Sep 20 '19 at 13:45
  • Yes, @John, that's precisely what I was talking about. – John Bollinger Sep 21 '19 at 00:32
  • Thanks for the link to the uniform naming scheme. That's what I needed to define a custom installation directory for my script. – sonofanickel Apr 12 '21 at 15:14