Skip to content

Commit 9ebdf52

Browse files
authored
Merge pull request #2761 from jimklimov/issue-1298
Makefile.am, configure.ac, NEWS.adoc: implement `make install-as-root` [#1298]
2 parents 72ea426 + 665e5c1 commit 9ebdf52

File tree

7 files changed

+299
-27
lines changed

7 files changed

+299
-27
lines changed

INSTALL.nut.adoc

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,30 @@ Installation
306306

307307
[NOTE]
308308
=====================================================================
309+
You should now gain privileges for installing software if necessary, e.g.:
309310
310-
you should now gain privileges for installing software if necessary:
311+
su -
311312
312-
su
313+
or prefix installation (not build/check!) commands with `sudo`, e.g.:
313314
315+
sudo make install ...
316+
=====================================================================
317+
318+
[NOTE]
319+
=====================================================================
320+
If you install NUT for direct consumption on the system that has just
321+
built it from source, you might be aided for some of the steps listed
322+
and explained below by running (as `root`):
323+
324+
make install-as-root
325+
326+
This should not only install the software, but also create directories
327+
such as the state path, assign ownership and access permissions to
328+
certain directories and sample configuration files which NUT user
329+
and/or group accounts should be able to read and/or write, and, on
330+
some platforms, also (re-)start the NUT daemons/services.
331+
332+
See above "System User creation", that should be done first!
314333
=====================================================================
315334

316335
Install the files to a system level directory:
@@ -328,7 +347,7 @@ have created.
328347

329348
If you are packaging this software, then you will probably want to
330349
use the DESTDIR variable to redirect the build into another place,
331-
i.e.:
350+
also known as a "prototype directory" or a "staging area", i.e.:
332351

333352
make DESTDIR=/tmp/package install
334353
make DESTDIR=/tmp/package install-conf
@@ -337,9 +356,12 @@ i.e.:
337356
State path creation
338357
^^^^^^^^^^^^^^^^^^^
339358

359+
NOTE: See above about `make install-as-root`, if you use that -- skip
360+
this step here.
361+
340362
Create the state path directory for the driver(s) and server to use
341363
for storing UPS status data and other auxiliary files, and make it
342-
group-writable by the group of the system user you created.
364+
group-writable by the group of the system user you created, e.g.:
343365

344366
mkdir -p /var/state/ups
345367
chmod 0770 /var/state/ups

Makefile.am

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,201 @@ setver:
526526
@echo "Error: 'make setver' no longer exists."
527527
@echo "Edit configure.ac to set version number."
528528

529+
# Adjust permissions when installing as `root` into the actual system.
530+
# We honour DESTDIR anyway, as someone can install into a chroot etc.
531+
# NOTE: Might be an 'install-data-hook' (for dirs) and/or 'install-exec-hook'
532+
# (for service restart) but better not force this on everyone?
533+
# It is also up to the end-user making such an installation to remove (or not)
534+
# dirs and files made below.
535+
# To err on the safe side in cross builds, we ignore Windows builds and those
536+
# not built for the same system as the build host.
537+
install-data-hook:
538+
@case "@target_os@" in *mingw*) exit 0;; esac ; \
539+
if [ x"@host_os@" != x"@build_os@" ]; then exit 0 ; fi ; \
540+
if [ x"@target_os@" != x"@build_os@" ]; then exit 0 ; fi ; \
541+
if (command -v id) && [ x"`id -u`" = x0 ] && [ x"$(DESTDIR)" = x -o x"$(DESTDIR)" = x/ ] ; then \
542+
echo "================================================================================" >&2 ; \
543+
echo "| NUT data files have been installed into the system, now consider running |" >&2 ; \
544+
echo "| '(sudo) make install-as-root' to apply permissions and service state changes |" >&2 ; \
545+
echo "================================================================================" >&2 ; \
546+
fi
547+
548+
if HAVE_SYSTEMD
549+
HAVE_SYSTEMD = true
550+
else
551+
HAVE_SYSTEMD = false
552+
endif
553+
554+
if WITH_SYSTEMD_TMPFILES
555+
WITH_SYSTEMD_TMPFILES = true
556+
else
557+
WITH_SYSTEMD_TMPFILES = false
558+
endif
559+
560+
if WITH_SYSTEMD_PRESET
561+
WITH_SYSTEMD_PRESET = true
562+
else
563+
WITH_SYSTEMD_PRESET = false
564+
endif
565+
566+
if WITH_CGI
567+
WITH_CGI = true
568+
else
569+
WITH_CGI = false
570+
endif
571+
572+
if WITH_SOLARIS_SMF
573+
WITH_SOLARIS_SMF = true
574+
else
575+
WITH_SOLARIS_SMF = false
576+
endif
577+
578+
if WITH_SOLARIS_INIT
579+
WITH_SOLARIS_INIT = true
580+
else
581+
WITH_SOLARIS_INIT = false
582+
endif
583+
584+
# TODO: Actually move this into scripts like Solaris/postinstall
585+
# using OS-specific `useradd`/`groupadd`, etc.
586+
# Note that as we stop services, we may be dealing with (older)
587+
# distros that do not follow current naming in NUT code base.
588+
install-as-root:
589+
@+echo "$@: starting (no-op if not root)" >&2 ; \
590+
case "@target_os@" in *mingw*) exit 0;; esac ; \
591+
if [ x"@host_os@" != x"@build_os@" ]; then exit 0 ; fi ; \
592+
if [ x"@target_os@" != x"@build_os@" ]; then exit 0 ; fi ; \
593+
prefix="@prefix@"; \
594+
if (command -v id) && [ x"`id -u`" = x0 ] ; then \
595+
if [ x"$(DESTDIR)" = x -o x"$(DESTDIR)" = x/ ] ; then \
596+
if $(HAVE_SYSTEMD) ; then \
597+
echo "$@: Stop NUT services, if any" >&2 ; \
598+
@SYSTEMD_SYSTEMCTL_PROGRAM@ stop nut-monitor.service nut-server.service || true ; \
599+
@SYSTEMD_SYSTEMCTL_PROGRAM@ stop nut-driver.service || true ; \
600+
@SYSTEMD_SYSTEMCTL_PROGRAM@ stop nut-driver.target || true ; \
601+
@SYSTEMD_SYSTEMCTL_PROGRAM@ stop nut.target || true ; \
602+
fi ; \
603+
if $(WITH_SOLARIS_SMF) || $(WITH_SOLARIS_INIT) ; then \
604+
if $(WITH_SOLARIS_SMF) ; then \
605+
echo "$@: Stop NUT services, if any" >&2 ; \
606+
SMF_ACTIVE="`/usr/bin/svcs -a -Hostate,fmri | grep svc:/system/power/ | grep -v disabled | awk '{print $$2}'`" ; \
607+
for S in $$SMF_ACTIVE ; do \
608+
/usr/sbin/svcadm disable -ts $$S || true ; \
609+
done ; \
610+
fi ; \
611+
$(top_builddir)/scripts/Solaris/preremove \
612+
|| exit ; \
613+
fi ; \
614+
fi ; \
615+
$(MAKE) $(AM_FLAGS) DESTDIR="$(DESTDIR)" install || exit ; \
616+
if [ x"$(DESTDIR)" = x -o x"$(DESTDIR)" = x/ ] ; then \
617+
if $(WITH_SOLARIS_SMF) || $(WITH_SOLARIS_INIT) ; then \
618+
$(top_builddir)/scripts/Solaris/preinstall && \
619+
$(top_builddir)/scripts/Solaris/postinstall ; \
620+
exit ; \
621+
fi ; \
622+
fi ; \
623+
echo " MKDIR $(DESTDIR)/@STATEPATH@ $(DESTDIR)/@STATEPATH@/upssched" >&2 ; \
624+
$(MKDIR_P) "$(DESTDIR)/@STATEPATH@/upssched" && \
625+
for D in "@PIDPATH@" "@ALTPIDPATH@" "@ALTSTATEPATH@" "@CONFPATH@" ; do \
626+
case x"$$D" in \
627+
x|x@*) ;; \
628+
*) echo " MKDIR $(DESTDIR)/$$D" >&2 ; \
629+
$(MKDIR_P) "$(DESTDIR)/$$D" \
630+
|| exit ;; \
631+
esac ; \
632+
done ; \
633+
if (command -v chmod) ; then \
634+
echo " CHMOD(0770) $(DESTDIR)/@STATEPATH@/upssched" >&2 ; \
635+
chmod 0770 "$(DESTDIR)/@STATEPATH@/upssched" \
636+
|| exit ; \
637+
for D in "@STATEPATH@" "@PIDPATH@" "@ALTPIDPATH@" "@ALTSTATEPATH@" ; do \
638+
case x"$$D" in \
639+
x|x@*|x/run|x/var/run|x/tmp|x/var/tmp|x/dev/shm|x/etc|x/var|x/usr|x/usr/local|x/usr/local/etc|x/usr/etc) ;; \
640+
*) echo " CHMOD(0770) $(DESTDIR)/$$D" >&2 ; \
641+
chmod 0770 "$(DESTDIR)/$$D" \
642+
|| exit ;; \
643+
esac ; \
644+
done ; \
645+
case x"@CONFPATH@" in \
646+
x|x@*|x/run|x/var/run|x/tmp|x/var/tmp|x/dev/shm|x/etc|x/var|x/usr|x/usr/local|x/usr/local/etc|x/usr/etc) ;; \
647+
*) echo " CHMOD(0751) $(DESTDIR)/@CONFPATH@" >&2 ; \
648+
chmod 0751 "$(DESTDIR)/@CONFPATH@" \
649+
|| exit ;; \
650+
esac ; \
651+
for F in hosts.conf.sample upsstats-single.html.sample upsstats.html.sample upsset.conf.sample ; do \
652+
echo " CHMOD(0644) CGI: $(DESTDIR)/@CONFPATH@/$$F" >&2 ; \
653+
chmod 0644 "$(DESTDIR)/@CONFPATH@/$$F" \
654+
|| { if $(WITH_CGI) ; then exit 1 ; else true ; fi ; } ; \
655+
done ; \
656+
for F in nut.conf.sample ups.conf.sample upsd.conf.sample upsd.users.sample upsmon.conf.sample upssched.conf.sample ; do \
657+
echo " CHMOD(0640) $(DESTDIR)/@CONFPATH@/$$F" >&2 ; \
658+
chmod 0640 "$(DESTDIR)/@CONFPATH@/$$F" \
659+
|| exit ; \
660+
done ; \
661+
else \
662+
echo "$@: WARNING: Can not CHMOD created locations!" >&2 ; \
663+
fi ; \
664+
if (command -v chown) && test 0 -lt "`id -u '@RUN_AS_USER@'`" \
665+
&& ( test 0 -lt "`getent group '@RUN_AS_GROUP@' | awk -F: '{print $$3}'`" || test 0 -lt "`id -g '@RUN_AS_GROUP@'`" ) \
666+
; then \
667+
echo " CHOWN(@RUN_AS_USER@:@RUN_AS_GROUP@) $(DESTDIR)/@STATEPATH@/upssched" >&2 ; \
668+
chown "@RUN_AS_USER@:@RUN_AS_GROUP@" "$(DESTDIR)/@STATEPATH@/upssched" \
669+
|| exit ; \
670+
for D in "@STATEPATH@" "@PIDPATH@" "@ALTPIDPATH@" "@ALTSTATEPATH@" ; do \
671+
case x"$$D" in \
672+
x|x@*|x/run|x/var/run|x/tmp|x/var/tmp|x/dev/shm|x/etc|x/var|x/usr|x/usr/local|x/usr/local/etc|x/usr/etc) ;; \
673+
*) echo " CHOWN(@RUN_AS_USER@:@RUN_AS_GROUP@) $(DESTDIR)/$$D" >&2 ; \
674+
chown "@RUN_AS_USER@:@RUN_AS_GROUP@" "$(DESTDIR)/$$D" \
675+
|| exit ;; \
676+
esac ; \
677+
done ; \
678+
case x"@CONFPATH@" in \
679+
x|x@*|x/run|x/var/run|x/tmp|x/var/tmp|x/dev/shm|x/etc|x/var|x/usr|x/usr/local|x/usr/local/etc|x/usr/etc) ;; \
680+
*) echo " CHOWN(root:@RUN_AS_GROUP@) $(DESTDIR)/@CONFPATH@" >&2 ; \
681+
chown "root:@RUN_AS_GROUP@" "$(DESTDIR)/@CONFPATH@" \
682+
|| exit ;; \
683+
esac ; \
684+
for F in hosts.conf.sample upsstats-single.html.sample upsstats.html.sample upsset.conf.sample ; do \
685+
echo " CHOWN(root:@RUN_AS_GROUP@) CGI: $(DESTDIR)/@CONFPATH@/$$F" >&2 ; \
686+
chown "root:@RUN_AS_GROUP@" "$(DESTDIR)/@CONFPATH@/$$F" \
687+
|| { if $(WITH_CGI) ; then exit 1 ; else true ; fi ; } ; \
688+
done ; \
689+
for F in nut.conf.sample ups.conf.sample upsd.conf.sample upsd.users.sample upsmon.conf.sample upssched.conf.sample ; do \
690+
echo " CHOWN(root:@RUN_AS_GROUP@) $(DESTDIR)/@CONFPATH@/$$F" >&2 ; \
691+
chown "root:@RUN_AS_GROUP@" "$(DESTDIR)/@CONFPATH@/$$F" \
692+
|| exit ; \
693+
done ; \
694+
else \
695+
echo "$@: WARNING: Can not CHOWN created locations!" >&2 ; \
696+
fi ; \
697+
if [ x"$(DESTDIR)" = x -o x"$(DESTDIR)" = x/ ] ; then \
698+
if $(HAVE_SYSTEMD) ; then \
699+
echo "$@: Activate default systemd layout, restart services:" >&2 ; \
700+
if $(WITH_SYSTEMD_TMPFILES) ; then \
701+
echo "$@: Apply systemd-tmpfiles presets" >&2 ; \
702+
@SYSTEMD_TMPFILES_PROGRAM@ --create || exit ; \
703+
fi ; \
704+
echo "$@: Learn systemd definition changes" >&2 ; \
705+
@SYSTEMD_SYSTEMCTL_PROGRAM@ daemon-reload || exit ; \
706+
if $(WITH_SYSTEMD_PRESET) ; then \
707+
echo "$@: Apply systemd enabled/disabled service presets" >&2 ; \
708+
@SYSTEMD_SYSTEMCTL_PROGRAM@ preset-all || exit ; \
709+
else \
710+
echo "$@: Apply systemd enabled/disabled service defaults" >&2 ; \
711+
@SYSTEMD_SYSTEMCTL_PROGRAM@ disable nut.target nut-driver.target nut-monitor nut-server nut-driver-enumerator.path nut-driver-enumerator.service || exit ; \
712+
@SYSTEMD_SYSTEMCTL_PROGRAM@ enable nut.target nut-driver.target nut-monitor nut-server nut-driver-enumerator.path nut-driver-enumerator.service || exit ; \
713+
fi ; \
714+
echo "$@: Reconfigure nut-driver-enumerator (service instance wrapping)" >&2 ; \
715+
@SYSTEMD_SYSTEMCTL_PROGRAM@ restart udev || true ; \
716+
$(top_builddir)/scripts/upsdrvsvcctl/nut-driver-enumerator.sh --reconfigure || { RES=$$?; if [ $$RES != 42 ] ; then exit $$RES ; fi ; } ; \
717+
echo "$@: Restart NUT services" >&2 ; \
718+
@SYSTEMD_SYSTEMCTL_PROGRAM@ restart nut-driver-enumerator.service nut-monitor.service nut-server.service || exit ; \
719+
fi ; \
720+
fi ; \
721+
echo "$@: finished" >&2 ; \
722+
fi
723+
529724
# Clean the dist tarball and packages
530725
MAINTAINERCLEANFILES_DISTBALL = nut-*.tar.gz
531726
# HP-UX:

NEWS.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,10 @@ relocated into new `shutdown.default` INSTCMD definitions. [#2670]
585585
- Introduced a simple experiment to expose NUT client readings as filesystem
586586
objects via FUSE, in `scripts/fuse/execfuse-nut` now. [#2591]
587587
588+
- Introduced `make install-as-root` to create directories not directly
589+
populated by `make install` and NUT build artifacts, apply permissions
590+
and (on some platforms) restart services involved with NUT. [#1298]
591+
588592
589593
Release notes for NUT 2.8.2 - what's new since 2.8.1
590594
----------------------------------------------------

configure.ac

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,20 @@ AS_IF([test x"${GETENT}" != x], [
294294
],[
295295
AS_IF([test x"${ID}" != x], [
296296
PROBE_OS_USER="${ID} -u "
297-
PROBE_OS_GROUP="${ID} -g "
297+
AS_IF([test -r "/etc/groups"], [
298+
dnl FIXME: For command prefix usage like used in this
299+
dnl script, we might be better off defining a shell
300+
dnl function and referring to it. Grep would catch
301+
dnl also user names that have a group as secondary!
302+
PROBE_OS_GROUP="cat /etc/groups | grep -w "
303+
], [
304+
dnl This shows groups of a USER with specified name!
305+
dnl But to probe for cases where string names are
306+
dnl same (e.g. "nut" or "ups") this might be good
307+
dnl enough.
308+
PROBE_OS_GROUP="${ID} -g "
309+
])
310+
AC_MSG_WARN([Can not PROPERLY check existence of group accounts on this system, but can try best-effort])
298311
],[
299312
AC_MSG_WARN([Can not check existence of user and group accounts on this system])
300313
])
@@ -307,7 +320,7 @@ dnl certain other configure options (e.g. "in-place replacement")
307320
RUN_AS_USER="nobody"
308321
RUN_AS_GROUP="nobody"
309322
AS_IF([test -n "`${PROBE_OS_GROUP} nogroup`" && ! test -n "`${PROBE_OS_GROUP} "${RUN_AS_GROUP}"`"],
310-
[RUN_AS_GROUP="nogroup"]
323+
[RUN_AS_GROUP="nogroup"]
311324
)
312325

313326
dnl NOTE: NUT legacy default, keep as is for least surprise
@@ -463,7 +476,11 @@ AS_IF([test x"$nut_enable_inplace_runtime" = xyes -a x"${NUT_VERSION_DEPLOYED-}"
463476
&& test x"${CONFIG_FLAGS_DEPLOYED}" != x \
464477
|| CONFIG_FLAGS_DEPLOYED=""
465478
466-
NUT_VERSION_DEPLOYED="`"${DEPLOYED_TOOL}" -DV 2>&1 | grep 'configured with flags:' | head -1 | sed 's,^.*Network UPS Tools version \(.*\) configured with flags:.*$,\1,'`" \
479+
dnl NOTE: Currntly NUT_VERSION_DEPLOYED is just informative (or a flag
480+
dnl that we've "reentered" the configuration script), so we tolerate a
481+
dnl better detailed string that is more than just a version, e.g.:
482+
dnl 2.8.2.1829-1829-g8f8a4f417 (development iteration after 2.8.2) built with gcc (Debian 10.2.1-6) 10.2.1 20210110
483+
NUT_VERSION_DEPLOYED="`"${DEPLOYED_TOOL}" -DV 2>&1 | grep 'configured with flags:' | head -1 | sed -e 's,^.*Network UPS Tools version \(.*\) configured with flags:.*$,\1,' -e 's, and *$,,'`" \
467484
&& test x"${NUT_VERSION_DEPLOYED}" != x \
468485
|| NUT_VERSION_DEPLOYED=""
469486
@@ -4035,6 +4052,8 @@ dnl This option is only provided so that make distcheck can override it,
40354052
dnl otherwise we ask pkg-config whenever --with-systemdsystemunitdir is
40364053
dnl given
40374054

4055+
AC_PATH_PROG([SYSTEMD_SYSTEMCTL_PROGRAM], [systemctl], [/usr/bin/systemctl])
4056+
40384057
dnl Similarly for presets (list of svcs enabled/disabled by default)
40394058
AC_MSG_CHECKING(whether to install systemd preset files)
40404059
AC_ARG_WITH([systemdsystempresetdir],
@@ -4075,6 +4094,7 @@ if test -n "${systemdsystempresetdir}"; then
40754094
else
40764095
AC_MSG_RESULT(no)
40774096
fi
4097+
AM_CONDITIONAL([WITH_SYSTEMD_PRESET], [test x"${systemdsystempresetdir}" != "x"])
40784098

40794099
dnl Similarly for shutdown integration hooks
40804100
AC_MSG_CHECKING(whether to install systemd shutdown files)
@@ -4115,6 +4135,7 @@ if test -n "${systemdshutdowndir}"; then
41154135
else
41164136
AC_MSG_RESULT(no)
41174137
fi
4138+
AM_CONDITIONAL([WITH_SYSTEMD_SHUTDOWN], [test x"${systemdshutdowndir}" != "x"])
41184139

41194140
dnl Note: if (systemd-)tmpfiles tech is present, it can be useful even for
41204141
dnl daemons starting not as systemd units, to pre-create /var/run/nut etc.
@@ -4153,6 +4174,7 @@ if test -n "${systemdtmpfilesdir}"; then
41534174
else
41544175
AC_MSG_RESULT(no)
41554176
fi
4177+
AM_CONDITIONAL([WITH_SYSTEMD_TMPFILES], [test x"${systemdtmpfilesdir}" != "x"])
41564178

41574179
dnl What pathname would we embed into unit files ExecStartPre?
41584180
dnl TODO? Any need to make it a --with-... argument?

docs/config-notes.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ the user you created in the preparation process.
327327
The suggested configuration is to `chown` it to `root`, `chgrp` it to the
328328
group you created, then make it readable by the group.
329329

330+
NOTE: If you installed NUT from source and used `make install-as-root`,
331+
or if your distribution packaging did, the sample configuration files
332+
would have the suggested ownership and permissions assigned, so if you
333+
use e.g. `cp -pf upsd.users.sample upsd.users` (as `root`) to start out
334+
with some annotated comments and adapt that to your deployment, the
335+
copied files should also get the expected safe permissions.
336+
330337
chown root:nut upsd.conf upsd.users
331338
chmod 0640 upsd.conf upsd.users
332339

@@ -660,6 +667,13 @@ The recommended setting is to have it owned by `root:nut`, then make it
660667
readable by the group and not by the world. This file contains passwords
661668
that could be used by an attacker to start a shutdown, so keep it secure.
662669

670+
NOTE: If you installed NUT from source and used `make install-as-root`,
671+
or if your distribution packaging did, the sample configuration files
672+
would have the suggested ownership and permissions assigned, so if you
673+
use e.g. `cp -pf upsmon.conf.sample upsmon.conf` (as `root`) to start out
674+
with some annotated comments and adapt that to your deployment, the
675+
copied files should also get the expected safe permissions.
676+
663677
chown root:nut upsmon.conf
664678
chmod 0640 upsmon.conf
665679

0 commit comments

Comments
 (0)