diff --git a/.gitignore b/.gitignore index 27df0d4cc4..c067fadad1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ .deps/ .libs/ .inst/ +/_install_pkgprotodir/ Makefile Makefile.in ## Parent directory only @@ -19,6 +20,7 @@ Makefile.in /config.log /config.status /config.sub +/config.cache /configure /cscope.* /depcomp @@ -33,6 +35,10 @@ common/.dirstamp # Dist /nut-*.*.*/ /nut-*.tar.gz +/nut*.rpm +/NUT*.local.gz +/NUT*.p5i +/NUT*.depot # Official dist /nut-*.tar.gz.md5 diff --git a/.travis.yml b/.travis.yml index b65ff9cd54..1637a06e24 100644 --- a/.travis.yml +++ b/.travis.yml @@ -128,6 +128,15 @@ matrix: apt: packages: - *deps_driverlibs + - env: + - BUILD_TYPE=default-tgt:distcheck-light + - NO_PKG_CONFIG=true + os: linux + sudo: true + addons: + apt: + packages: + - *deps_driverlibs before_install: - if [ $TRAVIS_OS_NAME == "osx" ] ; then brew update; brew install binutils asciidoc docbook-xsl ; XML_CATALOG_FILES=/usr/local/etc/xml/catalog ; export XML_CATALOG_FILES ; fi diff --git a/Makefile.am b/Makefile.am index 7c35d2e124..66991ec46a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -121,7 +121,7 @@ install-dirs: @echo "Warning: 'make install-dirs' is deprecated." @echo "Use 'make installdirs' instead." @echo $(WARN) - make installdirs + $(MAKE) installdirs cgi build-cgi install-cgi install-cgi-dir install-cgi-bin \ install-cgi-man install-cgi-conf install-cgi-html: @echo "Error: 'make $@' no longer exists." @@ -137,23 +137,30 @@ snmp build-snmp install-snmp install-snmp-mgr install-snmp-man: @echo "Use './configure --with-snmp' instead." setver: @echo "Error: 'make setver' no longer exists." - @echo "Edit configure.in to set version number." + @echo "Edit configure.ac to set version number." package: - if test `uname -s` = "HP-UX"; then \ - cd scripts/HP-UX; \ - make package; \ - mv NUT_HPUX_package.depot NUT_HPUX_package@PACKAGE_VERSION@.depot; \ - elif test `uname -s` = "SunOS"; then \ - make; \ - rm -rf @prefix@; \ - make install; \ - cd scripts/Solaris; \ - make package; \ - make uninstall; \ - rm -rf @prefix@; \ - elif test `uname -s` = "AIX"; then \ - make dist; \ - cp scripts/Aix/nut-aix.spec /usr/src/packages/SPECS; \ - cp scripts/Aix/nut.init nut-@PACKAGE_VERSION@.tar.gz /usr/src/packages/SOURCES; \ - rpm -ba /usr/src/packages/SPECS/nut-aix.spec; \ - fi; + DESTDIR="$(abs_builddir)/_install_pkgprotodir" ; export DESTDIR; \ + rm -rf "$$DESTDIR"; \ + case "`uname -s`" in \ + "HP-UX") \ + ( cd scripts/HP-UX && \ + $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$DESTDIR" package && \ + mv NUT_HPUX_package.depot $(abs_top_builddir)/NUT_HPUX_package@PACKAGE_VERSION@.depot ) ;; \ + "SunOS") \ + $(MAKE) $(AM_MAKEFLAGS) && \ + $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$DESTDIR" install && \ + ( cd scripts/Solaris && \ + $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$DESTDIR" package ) && \ + $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$DESTDIR" uninstall && \ + rm -rf "$$DESTDIR" || \ + { echo "FAILED to produce SunOS packages, inspect '$$DESTDIR' for clues" >&2 ; exit 1; } ;; \ + "AIX") \ + if test -d /usr/src/packages/SPECS -a -w /usr/src/packages/SPECS ; then : ; else echo "Can not write to /usr/src/packages/SPECS" >&2 ; exit 1; fi ; \ + if test -d /usr/src/packages/SOURCES -a -w /usr/src/packages/SOURCES ; then : ; else echo "Can not write to /usr/src/packages/SOURCES" >&2 ; exit 1; fi ; \ + $(MAKE) $(AM_MAKEFLAGS) dist && \ + cp scripts/Aix/nut-aix.spec /usr/src/packages/SPECS && \ + cp scripts/Aix/nut.init nut-@PACKAGE_VERSION@.tar.gz /usr/src/packages/SOURCES && \ + rpm -ba /usr/src/packages/SPECS/nut-aix.spec && \ + mv /usr/src/packages/RPMS/nut*rpm $(abs_top_builddir)/ ;; \ + *) echo "Unsupported OS for 'make $@' (no recipe bound)" >&2; exit 1;; \ + esac diff --git a/README b/README index 247dc0a166..685bdbd9d4 100644 --- a/README +++ b/README @@ -120,8 +120,9 @@ The entry in `ups.conf` looks like this: driver = apcsmart port = /dev/ttyS1 -To start and stop drivers, use upsdrvctl. By default, it will start or -stop every UPS in the config file: +To start and stop drivers, use upsdrvctl of upsdrvsvcctl (installed on +operating systems with a service management framework supported by NUT). +By default, it will start or stop every UPS in the config file: /usr/local/ups/sbin/upsdrvctl start /usr/local/ups/sbin/upsdrvctl stop @@ -131,6 +132,17 @@ However, you can also just start or stop one by adding its name: /usr/local/ups/sbin/upsdrvctl start sparky /usr/local/ups/sbin/upsdrvctl stop sparky +On operating systems with a supported service management framework, +you might wrap your NUT drivers into individual services instances +with: + + /usr/local/ups/sbin/upsdrvsvcctl resync + +and then manage those service instances with commands like: + + /usr/local/ups/sbin/upsdrvsvcctl start sparky + /usr/local/ups/sbin/upsdrvsvcctl stop sparky + To find the driver name for your device, refer to the section below called "HARDWARE SUPPORT TABLE". diff --git a/ci_build.sh b/ci_build.sh index dd7b3f9ab0..bd59c1b6f5 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -186,7 +186,18 @@ default|default-alldrv|default-spellcheck|default-nodoc|default-withdoc|"default [ -z "$CI_TIME" ] || echo "`date`: Starting build of currently tested project..." CCACHE_BASEDIR=${PWD} export CCACHE_BASEDIR + + # Note: modern auto(re)conf requires pkg-config to generate the configure + # script, so to stage the situation of building without one (as if on an + # older system) we have to remove it when we already have the script. + # This matches the use-case of distro-building from release tarballs that + # include all needed pre-generated files to rely less on OS facilities. $CI_TIME ./autogen.sh 2> /dev/null + if [ "$NO_PKG_CONFIG" == "true" ] ; then + echo "NO_PKG_CONFIG==true : BUTCHER pkg-config for this test case" >&2 + sudo dpkg -r --force all pkg-config + fi + $CI_TIME ./configure "${CONFIG_OPTS[@]}" case "$BUILD_TYPE" in diff --git a/common/Makefile.am b/common/Makefile.am index 2bfc6e0dc9..e2a4acb697 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -12,6 +12,6 @@ libparseconf_la_SOURCES = parseconf.c libcommon_la_SOURCES = common.c state.c str.c upsconf.c libcommonclient_la_SOURCES = common.c state.c str.c # ensure inclusion of local implementation of missing systems functions -# using LTLIBOBJS. Refer to configure.in -> AC_REPLACE_FUNCS +# using LTLIBOBJS. Refer to configure.in/.ac -> AC_REPLACE_FUNCS libcommon_la_LIBADD = libparseconf.la @LTLIBOBJS@ libcommonclient_la_LIBADD = libparseconf.la @LTLIBOBJS@ diff --git a/configure.ac b/configure.ac index 48e4b457d0..29bd1debca 100644 --- a/configure.ac +++ b/configure.ac @@ -27,7 +27,23 @@ dnl Use "./configure --enable-maintainer-mode" to keep Makefile.in and Makefile dnl in sync after Git updates. AM_MAINTAINER_MODE -dnl PKG_PROG_PKG_CONFIG +dnl Some systems have older autotools without direct macro support for PKG_CONF* +have_PKG_CONFIG=yes +AC_PATH_PROG(dummy_PKG_CONFIG, pkg-config) +AS_IF([test x"$dummy_PKG_CONFIG" = xno || test -z "$dummy_PKG_CONFIG"], + [have_PKG_CONFIG=no], + [AC_MSG_NOTICE([checking for autoconf macro support of pkg-config]) + PKG_PROG_PKG_CONFIG + dummy_RES=$? + AS_IF([test $dummy_RES = 0], + [AC_MSG_NOTICE([checking for autoconf macro support of pkg-config module checker]) + PKG_CHECK_MODULES([dummy_PKG_CONFIG], [pkg-config], + [], [have_PKG_CONFIG=no]) + ], [have_PKG_CONFIG=no])] +) +AS_IF([test x"$have_PKG_CONFIG" = xno], + [AC_MSG_WARN([pkg-config is needed to look for further dependencies (will be skipped)])]) + dnl Various version related processing dnl ---------------------------------- @@ -311,13 +327,14 @@ dnl Solaris 10/11 USB handling (need librt and libusb runtime path) dnl HPUX, since v11, needs an explicit activation of pthreads case ${target_os} in solaris2.1* ) - echo Checking for Solaris 10 / 11 specific configuration for usb drivers + AC_MSG_CHECKING([for Solaris 10 / 11 specific configuration for usb drivers]) AC_SEARCH_LIBS(nanosleep, rt) LIBUSB_LIBS="-R/usr/sfw/lib ${LIBUSB_LIBS}" dnl FIXME: Sun's libusb doesn't support timeout (so blocks notification) dnl and need to call libusb close upon reconnection AC_DEFINE(SUN_LIBUSB, 1, [Define to 1 for Sun version of the libusb.]) SUN_LIBUSB=1 + AC_MSG_RESULT([${LIBUSB_LIBS}]) ;; hpux11*) CFLAGS="${CFLAGS} -lpthread" @@ -1180,7 +1197,90 @@ else fi AM_CONDITIONAL(WITH_PKG_CONFIG, test -n "${pkgconfigdir}") -PKG_PROG_PKG_CONFIG +AC_MSG_CHECKING(whether to install Solaris SMF files) +solarissmf="auto" +AC_ARG_WITH([solaris-smf], + AS_HELP_STRING([--with-solaris-smf=(yes|auto|no)], [Enable installation of NUT scripts and manifests for Solaris Service Management Framework (auto)]), +[ + case "${withval}" in + auto|"") + solarissmf="auto" + ;; + yes|no) + solarissmf="${withval}" + ;; + *) + AC_MSG_ERROR([Unexpected argument for --with-solaris-smf=${withval}]) + ;; + esac +], []) + +if test x"$solarissmf" = xauto ; then + if test -x /usr/sbin/svcadm && test -x /usr/sbin/svccfg && test -x /usr/bin/svcs ; then + solarissmf="yes" + else + solarissmf="no" + fi +fi +AC_MSG_RESULT([${solarissmf}]) +AM_CONDITIONAL(WITH_SOLARIS_SMF, test x"$solarissmf" = x"yes") + +AC_MSG_CHECKING(whether to make Solaris SVR4 packages) +solarispkg_svr4="auto" +AC_ARG_WITH([solaris-pkg-svr4], + AS_HELP_STRING([--with-solaris-pkg-svr4=(yes|auto|no)], [Enable construction of Solaris SVR4 packages (auto)]), +[ + case "${withval}" in + auto|"") + solarispkg_svr4="auto" + ;; + yes|no) + solarispkg_svr4="${withval}" + ;; + *) + AC_MSG_ERROR([Unexpected argument for --with-solaris-pkg-svr4=${withval}]) + ;; + esac +], []) + +if test x"$solarispkg_svr4" = xauto ; then + if test -x /usr/bin/pkgtrans && test -x /usr/bin/pkgmk && test -x /usr/bin/pkgproto ; then + solarispkg_svr4="yes" + else + solarispkg_svr4="no" + fi +fi +AC_MSG_RESULT([${solarispkg_svr4}]) +AM_CONDITIONAL(WITH_SOLARIS_PKG_SVR4, test x"$solarispkg_svr4" = x"yes") + +AC_MSG_CHECKING(whether to make Solaris IPS packages) +solarispkg_ips="auto" +AC_ARG_WITH([solaris-pkg-ips], + AS_HELP_STRING([--with-solaris-pkg-ips=(yes|auto|no)], [Enable construction of Solaris IPS packages (auto)]), +[ + case "${withval}" in + auto|"") + solarispkg_ips="auto" + ;; + yes|no) + solarispkg_ips="${withval}" + ;; + *) + AC_MSG_ERROR([Unexpected argument for --with-solaris-pkg-ips=${withval}]) + ;; + esac +], []) + +if test x"$solarispkg_ips" = xauto ; then + if test -x /usr/bin/pkg && test -x /usr/bin/pkgmogrify && test -x /usr/bin/pkgdepend ; then + solarispkg_ips="yes" + else + solarispkg_ips="no" + fi +fi +AC_MSG_RESULT([${solarispkg_ips}]) +AM_CONDITIONAL(WITH_SOLARIS_PKG_IPS, test x"$solarispkg_ips" = x"yes") + AC_MSG_CHECKING(whether to install systemd files) AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files (auto)]), @@ -1262,14 +1362,20 @@ AC_LANG_POP([C++]) have_cppunit="no" CPPUNIT_NUT_CXXFLAGS="" -AS_IF([test "${HAVE_CXX11}" = 1], - [PKG_CHECK_MODULES(CPPUNIT, cppunit, have_cppunit=yes, have_cppunit=no) - if test "${have_cppunit}" != "yes" ; then - AC_MSG_WARN([libcppunit not found - those C++ tests will not be built.]) - else - AS_IF([test -n "$CXX"],[AS_IF([$CXX --version 2>&1 | grep 'Free Software Foundation' > /dev/null], - [CPPUNIT_NUT_CXXFLAGS="-g -O0"])]) - fi]) +AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AS_IF([test "${HAVE_CXX11}" = 1], + [PKG_CHECK_MODULES(CPPUNIT, cppunit, have_cppunit=yes, have_cppunit=no) + AS_IF([test "${have_cppunit}" != "yes"], + [AC_MSG_WARN([libcppunit not found - those C++ tests will not be built.]) + have_cppunit=no], + [AS_IF([test -n "$CXX"], + [AS_IF([$CXX --version 2>&1 | grep 'Free Software Foundation' > /dev/null], + [CPPUNIT_NUT_CXXFLAGS="-g -O0"])]) + ]) + ]) + ], [AC_MSG_WARN([pkg-config not found, can not look properly for libcppunit - those C++ tests will not be built.]) + have_cppunit=no] +) AM_CONDITIONAL(HAVE_CPPUNIT, test "${have_cppunit}" = "yes") AC_DEFINE_UNQUOTED(CPPUNIT_NUT_CXXFLAGS, $CPPUNIT_NUT_CXXFLAGS, [Compiler flags for cppunit tests]) @@ -1451,7 +1557,9 @@ else AC_MSG_RESULT(no) fi -dnl expand ${sysconfdir} and write it out +dnl expand ${sysconfdir} and write it out - note that most packages +dnl override it to be /etc/nut, /etc/ups or similar, while the +dnl autotools default would be $prefix/etc conftemp="${sysconfdir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" @@ -1463,6 +1571,7 @@ conftemp="${datadir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" DATADIR=${conftemp} +NUT_DATADIR=${conftemp} AC_DEFINE_UNQUOTED(DATADIR, "${conftemp}", [Default path for data files]) dnl same for bindir @@ -1486,6 +1595,13 @@ eval conftemp=\"${conftemp}\" LIBDIR=${conftemp} AC_DEFINE_UNQUOTED(LIBDIR, "${conftemp}", [Default path for system libraries]) +dnl same for libexecdir +conftemp="${libexecdir}" +eval conftemp=\"${conftemp}\" +eval conftemp=\"${conftemp}\" +LIBEXECDIR=${conftemp} +AC_DEFINE_UNQUOTED(LIBEXECDIR, "${conftemp}", [Default path for system exec-libraries]) + dnl checks related to --with-snmp enabled on command-line @@ -1577,6 +1693,8 @@ AC_SUBST(STATEPATH) AC_SUBST(CONFPATH) AC_SUBST(BINDIR) AC_SUBST(LIBDIR) +AC_SUBST(NUT_DATADIR, [`eval echo "${DATADIR}"`]) +AC_SUBST(NUT_LIBEXECDIR, [`eval echo "${LIBEXECDIR}"`]) AC_SUBST(DRVPATH) AC_SUBST(SBINDIR) AC_SUBST(PORT) @@ -1630,19 +1748,35 @@ AC_OUTPUT([ scripts/devd/nut-usb.conf scripts/hotplug/Makefile scripts/hotplug/libhidups + scripts/Aix/nut.init scripts/HP-UX/nut.psf scripts/HP-UX/postinstall scripts/python/Makefile + scripts/upsdrvsvcctl/Makefile + scripts/upsdrvsvcctl/nut-driver-enumerator.sh + scripts/upsdrvsvcctl/upsdrvsvcctl scripts/systemd/Makefile - scripts/systemd/nut-driver.service + scripts/systemd/nut-driver@.service scripts/systemd/nut-monitor.service scripts/systemd/nut-server.service + scripts/systemd/nut-driver-enumerator.service + scripts/systemd/nut-driver-enumerator.path scripts/systemd/nutshutdown + scripts/Solaris/nut-driver-enumerator.xml + scripts/Solaris/nut-driver.xml + scripts/Solaris/nut-monitor.xml + scripts/Solaris/nut-server.xml + scripts/Solaris/nut.xml + scripts/Solaris/svc-nut-server + scripts/Solaris/svc-nut-monitor scripts/Solaris/Makefile scripts/Solaris/pkginfo + scripts/Solaris/preinstall scripts/Solaris/postinstall scripts/Solaris/preremove - scripts/Solaris/nut + scripts/Solaris/postremove + scripts/Solaris/preproto.pl + scripts/Solaris/nut scripts/udev/Makefile scripts/udev/nut-ipmipsu.rules scripts/udev/nut-usbups.rules diff --git a/data/driver.list.in b/data/driver.list.in index eb790ac91f..624116a922 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -513,7 +513,36 @@ "LDLC" "ups" "2" "UPS-1200D" "" "blazer_usb langid_fix=0x4095" -"Legrand" "ups" "2" "Keor Multiplug" "" "nutdrv_qx" +"Legrand" "ups" "2" "Keor SPX" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor Multiplug" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Daker DK / DK Plus" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Daker DK / DK Plus" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Niky / Niky S" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Niky / Niky S" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor LP/S/Line RT" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Keor S/Line RT" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor PDU" "USB" "usbhid-ups" +"Legrand" "ups" "2" "Keor SP" "USB" "usbhid-ups" +"Legrand" "ups" "4" "WHAD 800" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 1000" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 1250" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 1500" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 2000" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 2500" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD CAB 1250" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD CAB 2500" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD HE 800" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD HE 1000" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD HE 1500" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 1250" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 2500" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 3750" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 5000" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 5000 /2" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 6250 /2" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 7500 /2" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 8750 /2" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 10000 /2" "Serial" "metasys" "Lestar" "ups" "2" "MD-800E" "" "blazer_ser" @@ -538,48 +567,50 @@ "Mecer" "ups" "2" "ME-1000-WTU" "USB" "nutdrv_qx" # http://www.comx-computers.co.za/download/mecer/ME-1000-WTU.pdf "Mecer" "ups" "2" "ME-2000" "" "blazer_ser" -"Meta System" "ups" "1" "HF Line" "1..4 boards" "metasys" -"Meta System" "ups" "1" "HF Line \/2" "5..8 boards" "metasys" -"Meta System" "ups" "1" "HF Millennium 810" "" "metasys" -"Meta System" "ups" "1" "HF Millennium 820" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 910" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 920" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 930" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 940" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 950" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 960" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 970" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 980" "" "metasys" -"Meta System" "ups" "1" "ECO Network 750" "" "metasys" -"Meta System" "ups" "1" "ECO Network 1000" "" "metasys" -"Meta System" "ups" "1" "ECO Network 1050" "" "metasys" -"Meta System" "ups" "1" "ECO Network 1500" "" "metasys" -"Meta System" "ups" "1" "ECO Network 1800" "" "metasys" -"Meta System" "ups" "1" "ECO Network 2000" "" "metasys" -"Meta System" "ups" "1" "ECO Network 2100" "" "metasys" -"Meta System" "ups" "1" "ECO Network 2500" "" "metasys" -"Meta System" "ups" "1" "ECO Network 3000" "" "metasys" -"Meta System" "ups" "1" "ECO 305" "" "metasys" -"Meta System" "ups" "1" "ECO 308" "" "metasys" -"Meta System" "ups" "1" "ECO 311" "" "metasys" -"Meta System" "ups" "1" "ECO 511" "" "metasys" -"Meta System" "ups" "1" "ECO 516" "" "metasys" -"Meta System" "ups" "1" "ECO 519" "" "metasys" -"Meta System" "ups" "1" "ECO 522" "" "metasys" -"Meta System" "ups" "1" "ally HF 800" "" "metasys" -"Meta System" "ups" "1" "ally HF 1000" "" "metasys" -"Meta System" "ups" "1" "ally HF 1250" "" "metasys" -"Meta System" "ups" "1" "ally HF 1600" "" "metasys" -"Meta System" "ups" "1" "ally HF 2000" "" "metasys" -"Meta System" "ups" "1" "ally HF 2500" "" "metasys" -"Meta System" "ups" "1" "Megaline 1250" "" "metasys" -"Meta System" "ups" "1" "Megaline 2500" "" "metasys" -"Meta System" "ups" "1" "Megaline 3750" "" "metasys" -"Meta System" "ups" "1" "Megaline 5000" "" "metasys" -"Meta System" "ups" "1" "Megaline 6250" "" "metasys" -"Meta System" "ups" "1" "Megaline 7500" "" "metasys" -"Meta System" "ups" "1" "Megaline 8750" "" "metasys" -"Meta System" "ups" "1" "Megaline 10000" "" "metasys" +"Meta System" "ups" "1" "HF Line" "1..4 boards" "metasys" +"Meta System" "ups" "1" "HF Line /2" "5..8 boards" "metasys" +"Meta System" "ups" "1" "HF Millennium 810" "" "metasys" +"Meta System" "ups" "1" "HF Millennium 820" "" "metasys" +"Meta System" "ups" "1" "HF TOP Line 910" "" "metasys" +"Meta System" "ups" "1" "HF TOP Line 920" "" "metasys" +"Meta System" "ups" "1" "HF TOP Line 930" "" "metasys" +"Meta System" "ups" "1" "HF TOP Line 940" "" "metasys" +"Meta System" "ups" "1" "HF TOP Line 950" "" "metasys" +"Meta System" "ups" "1" "HF TOP Line 960" "" "metasys" +"Meta System" "ups" "1" "HF TOP Line 970" "" "metasys" +"Meta System" "ups" "1" "HF TOP Line 980" "" "metasys" +"Meta System" "ups" "1" "ECO Network 750" "" "metasys" +"Meta System" "ups" "1" "ECO Network 1000" "" "metasys" +"Meta System" "ups" "1" "ECO Network 1050" "" "metasys" +"Meta System" "ups" "1" "ECO Network 1500" "" "metasys" +"Meta System" "ups" "1" "ECO Network 1800" "" "metasys" +"Meta System" "ups" "1" "ECO Network 2000" "" "metasys" +"Meta System" "ups" "1" "ECO Network 2100" "" "metasys" +"Meta System" "ups" "1" "ECO Network 2500" "" "metasys" +"Meta System" "ups" "1" "ECO Network 3000" "" "metasys" +"Meta System" "ups" "1" "ECO 305" "" "metasys" +"Meta System" "ups" "1" "ECO 308" "" "metasys" +"Meta System" "ups" "1" "ECO 311" "" "metasys" +"Meta System" "ups" "1" "ECO 511" "" "metasys" +"Meta System" "ups" "1" "ECO 516" "" "metasys" +"Meta System" "ups" "1" "ECO 519" "" "metasys" +"Meta System" "ups" "1" "ECO 522" "" "metasys" +"Meta System" "ups" "4" "ally HF 800" "" "metasys" +"Meta System" "ups" "4" "ally HF 1000" "" "metasys" +"Meta System" "ups" "4" "ally HF 1250" "" "metasys" +"Meta System" "ups" "4" "ally HF 1600" "" "metasys" +"Meta System" "ups" "4" "ally HF 2000" "" "metasys" +"Meta System" "ups" "4" "ally HF 2500" "" "metasys" +"Meta System" "ups" "4" "Megaline 1250" "" "metasys" +"Meta System" "ups" "4" "Megaline 2500" "" "metasys" +"Meta System" "ups" "4" "Megaline 3750" "" "metasys" +"Meta System" "ups" "4" "Megaline 5000" "" "metasys" +"Meta System" "ups" "4" "Megaline 6250" "" "metasys" +"Meta System" "ups" "4" "Megaline 7500" "" "metasys" +"Meta System" "ups" "4" "Megaline 8750" "" "metasys" +"Meta System" "ups" "4" "Megaline 10000" "" "metasys" +"Meta System" "ups" "4" "DHEA 1000" "Serial" "metasys" +"Meta System" "ups" "4" "DHEA 1500" "Serial" "metasys" "MGE Office Protection Systems" "ups" "5" "Protection Center 500/675 VA" "USB" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Protection Station 500/650/800 VA" "USB" "usbhid-ups" diff --git a/docs/FAQ.txt b/docs/FAQ.txt index 6649bed64b..3a14105db9 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -272,6 +272,12 @@ Note: if you jumped in with both feet and didn't follow the INSTALL.nut document, you probably started upsd by itself. You have to run 'upsdrvctl start' to start the drivers after configuring ups.conf. +On operating systems with a supported service management framework, +you might wrap your NUT drivers into individual services instances +with 'upsdrvsvcctl resync' and then manage those with commands like +'upsdrvsvcctl stop' and 'upsdrvsvcctl start' (note that on other +systems this tool may be not pre-installed via packaging). + == Why don't the pathnames in your documentation match the package I installed? Each distribution has conventions for where specific file types should be diff --git a/docs/config-notes.txt b/docs/config-notes.txt index 822b6da046..18c8f3894f 100644 --- a/docs/config-notes.txt +++ b/docs/config-notes.txt @@ -104,7 +104,7 @@ quotes spanning multiple lines. Basic configuration ------------------- -This chapter describe the base configuration to establish communication with +This chapter describes the base configuration to establish communication with the device. This will be sufficient for PDU. But for UPS and SCD, you will also need to @@ -112,6 +112,13 @@ configure <>. image:images/simple.png[] +On operating systems with service management frameworks (such as Linux systemd +and Solaris/illumos SMF), the driver, data server and monitoring client daemons' +life-cycle is managed respectively by `nut-driver` (multi-instance), `nut-server` +and `nut-monitor` services. These are in turn wrapped by an "umbrella" service +(or systemd "target") conveniently called `nut` which allows to start or stop +those of the bundled services, which are enabled on a particular deployment. + [[Driver_configuration]] Driver configuration ~~~~~~~~~~~~~~~~~~~~ @@ -156,7 +163,8 @@ linkman:usbhid-ups[8] Starting the driver(s) ~~~~~~~~~~~~~~~~~~~~~~ -Start the driver(s) for your hardware: +Generally, you can just start the driver(s) for your hardware (all sections +defined in 'ups.conf') using the following command: /usr/local/ups/sbin/upsdrvctl start @@ -184,6 +192,67 @@ state path probably isn't writable by the driver. Check the After making changes, try the <> step again. +On operating systems with init-scripts managing life-cycle of the operating +environment, the `upsdrvctl` program is also commonly used in those scripts. +It has a few downsides, such as that if the device was not accessible during +OS startup and the driver connection timed out, it would remain not-started +until an administrator (or some other script) "kicks" the driver to retry +startup. Also, startup of the `upsd` data server daemon and its clients +like `upsmon` is delayed until all the NUT drivers complete their startup +(or time out trying). + +This can be a big issue on systems which monitor multiple devices, such as +big servers with multiple power sources, or administrative workstations +which monitor a datacenter full of UPSes. + +For this reason, NUT starting with version 2.7.5 supports startup of its +drivers as independent instances of a `nut-driver` service under the Linux +systemd and Solaris/illumos SMF service-management frameworks (corresponding +files and scripts may be not pre-installed in packaging for other systems). + +Such service instances have their own and independent life-cycle, including +parallel driver start and stop processing, and retries of startup in case of +failure as implemented by the service framework in the OS. The Linux systemd +solution also includes a `nut-driver.target` as a checkpoint that all defined +drivers have indeed started up (as well as being a singular way to enable or +disable startup of drivers). + +In both cases, a service named `nut-driver-enumerator` is registered, and +when it is (re-)started it scans the currently defined device sections in +`ups.conf` and the currently defined instances of `nut-driver` service, +and brings them in sync (adding or removing service instances), and if +there were changes -- it restarts the corresponding drivers (via service +instances) as well as the data server which only reads the list of sections +at its startup. This helper service should be triggered whenever your system +(re-)starts the `nut-server` service, so that it runs against an up-to-date +list of NUT driver processes. + +A service-oriented solution also allows to consider that different drivers +have different dependencies -- such as that networked drivers should begin +startup after IP addresses have been assigned, while directly-connected +devices might need nothing beside a mounted filesystem (or an activated +USB stack service or device rule, in case of Linux). Likewise, systems +administrators can define further local dependencies between services and +their instances as needed on particular deployments. + +This solution also adds the `upsdrvsvcctl` script to manage NUT drivers as +system service instances, whose CLI mimics that of `upsdrvctl` program. +One addition is the `resync` argument to trigger `nut-driver-enumerator`, +another is a `list` argument to display current mappings of service +instances to NUT driver sections. Also, original tool's arguments such +as the `-u` (user to run the driver as) or `-D` (debug of the driver) +do not make sense in the service context -- the accounts to use and +other arguments to the driver process are part of service setup (and +an administrator can manage it there). + +Note that while this solution tries to register service instances with same +names as NUT configuration sections for the devices, this can not always be +possible due to constraints such as syntax supported by a particular service +management framework. In this case, the enumerator falls back to MD5 hashes +of such section names, and the `upsdrvsvcctl` script supports this to map +the user-friendly NUT configuration section names to actual service names +that it would manage. + References: man pages: linkman:nutupsdrv[8], linkman:upsdrvctl[8] @@ -247,6 +316,9 @@ is stale, then your ups.conf is not configured correctly, or you have a driver that isn't working properly. You must fix this before going on to the next step. +On operating systems with service management frameworks, the data server +life-cycle is managed by `nut-server` service. + Reference: man page: linkman:upsd[8] Check the UPS data @@ -522,6 +594,9 @@ Start upsmon If it complains about something, then check your configuration. +On operating systems with service management frameworks, the monitoring client +life-cycle is managed by `nut-monitor` service. + Checking upsmon ^^^^^^^^^^^^^^^ diff --git a/docs/developers.txt b/docs/developers.txt index f989aa9970..113c280f25 100644 --- a/docs/developers.txt +++ b/docs/developers.txt @@ -581,7 +581,7 @@ to be locally installed on your system, including asciidoc, a2x, xsltproc, dblatex and any additional XSL stylesheets. Running +make distcheck-light+ is especially important if you have added or -removed files, or updated configure.in or some Makefile.am. Remember: simply +removed files, or updated configure.ac or some Makefile.am. Remember: simply adding a file to Git does not mean it will be distributed. To distribute a file, you must update the corresponding Makefile.am. diff --git a/docs/features.txt b/docs/features.txt index eef38b6d21..be67292908 100644 --- a/docs/features.txt +++ b/docs/features.txt @@ -137,9 +137,14 @@ supported by the UPS hardware. - All drivers are started and stopped with one common program. Starting one is as easy as starting ten: 'upsdrvctl start'. +- For operating systems with a supported service management framework, you can +manage the NUT drivers wrapped into independent service instances using the +'upsdrvsvcctl' instead, and gain the benefits of automated restart as well as +possibility to define further dependencies between your OS components. + - Shutdowns and other procedures may be tested without stressing actual UPS hardware by simulating status values with the dummy-ups pseudo-driver. Anything -which can happen in a driver can be replicated with dummy-ups. +which can happen in a driver can be replicated with dummy-ups. Monitoring diagrams ------------------- diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 94a16b4d9c..0e1f59ccd5 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -47,10 +47,12 @@ HTML_CONF_MANS = \ SRC_CLIENT_PAGES = \ nutupsdrv.txt \ + nut-driver-enumerator.txt \ upsc.txt \ upscmd.txt \ upsd.txt \ upsdrvctl.txt \ + upsdrvsvcctl.txt \ upslog.txt \ upsmon.txt \ upsrw.txt \ @@ -59,10 +61,12 @@ SRC_CLIENT_PAGES = \ if WITH_MANS MAN_CLIENT_PAGES = \ nutupsdrv.8 \ + nut-driver-enumerator.8 \ upsc.8 \ upscmd.8 \ upsd.8 \ upsdrvctl.8 \ + upsdrvsvcctl.8 \ upslog.8 \ upsmon.8 \ upsrw.8 \ @@ -73,10 +77,12 @@ man8_MANS = $(MAN_CLIENT_PAGES) HTML_CLIENT_MANS = \ nutupsdrv.html \ + nut-driver-enumerator.html \ upsc.html \ upscmd.html \ upsd.html \ upsdrvctl.html \ + upsdrvsvcctl.html \ upslog.html \ upsmon.html \ upsrw.html \ @@ -177,7 +183,7 @@ SRC_DEV_PAGES = \ if WITH_MANS # NOTE: nutclient_*.3 has no source counterpart (libnutclient_*.txt) -LIBNUTCLIENT_MISC_DEPS= +LIBNUTCLIENT_MISC_DEPS= \ nutclient_authenticate.3 \ nutclient_logout.3 \ nutclient_device_login.3 \ @@ -210,7 +216,7 @@ MAN3_DEV_PAGES = \ libnutclient_misc.3 \ libnutclient_tcp.3 \ libnutclient_variables.3 \ - $(LIBNUTCLIENT_MISC_DEPS) + $(LIBNUTCLIENT_MISC_DEPS) \ nutclient_destroy.3 \ nutclient_execute_device_command.3 \ nutclient_get_device_command_description.3 \ @@ -350,7 +356,6 @@ SRC_SERIAL_PAGES = \ mge-utalk.txt \ oneac.txt \ microdowell.txt \ - nutdrv_qx.txt \ optiups.txt \ powercom.txt \ powerpanel.txt \ @@ -391,7 +396,6 @@ MAN_SERIAL_PAGES = \ metasys.8 \ mge-shut.8 \ mge-utalk.8 \ - nutdrv_qx.8 \ oneac.8 \ microdowell.8 \ optiups.8 \ @@ -438,7 +442,6 @@ HTML_SERIAL_MANS = \ metasys.html \ mge-shut.html \ mge-utalk.html \ - nutdrv_qx.html \ oneac.html \ microdowell.html \ optiups.html \ @@ -472,7 +475,6 @@ SRC_USB_LIBUSB_PAGES = \ blazer-common.txt \ blazer_usb.txt \ nutdrv_atcl_usb.txt \ - nutdrv_qx.txt \ richcomm_usb.txt \ riello_usb.txt \ tripplite_usb.txt \ @@ -483,7 +485,6 @@ MAN_USB_LIBUSB_PAGES = \ bcmxcp_usb.8 \ blazer_usb.8 \ nutdrv_atcl_usb.8 \ - nutdrv_qx.8 \ richcomm_usb.8 \ riello_usb.8 \ tripplite_usb.8 \ @@ -497,13 +498,32 @@ endif HTML_USB_LIBUSB_MANS = \ bcmxcp_usb.html \ blazer_usb.html \ - nutdrv_qx.html \ nutdrv_atcl_usb.html \ richcomm_usb.html \ riello_usb.html \ tripplite_usb.html \ usbhid-ups.html +# (--with-serial / --with-usb) +SRC_SERIAL_USB_PAGES = \ + nutdrv_qx.txt + +if WITH_MANS +MAN_SERIAL_USB_PAGES = \ + nutdrv_qx.8 +endif + +if WITH_SERIAL + man8_MANS += $(MAN_SERIAL_USB_PAGES) +else +if WITH_USB + man8_MANS += $(MAN_SERIAL_USB_PAGES) +endif +endif + +HTML_SERIAL_USB_MANS = \ + nutdrv_qx.html + # (--with-neon) SRC_NETXML_PAGES = netxml-ups.txt if WITH_MANS @@ -568,7 +588,7 @@ MAN_LINUX_I2C_PAGES = asem.8 endif if WITH_LINUX_I2C - man8_MANS += $(LINUX_I2C_PAGES) + man8_MANS += $(MAN_LINUX_I2C_PAGES) endif HTML_LINUX_I2C_MANS = asem.html @@ -589,6 +609,7 @@ MAN_MANS += \ $(MAN_SERIAL_PAGES) \ $(MAN_SNMP_PAGES) \ $(MAN_USB_LIBUSB_PAGES) \ + $(MAN_SERIAL_USB_PAGES) \ $(MAN_NETXML_PAGES) \ $(MAN_POWERMAN_PAGES) \ $(MAN_IPMIPSU_PAGES) \ @@ -608,6 +629,7 @@ SRC_ALL_PAGES = \ $(SRC_SERIAL_PAGES) \ $(SRC_SNMP_PAGES) \ $(SRC_USB_LIBUSB_PAGES) \ + $(SRC_SERIAL_USB_PAGES) \ $(SRC_NETXML_PAGES) \ $(SRC_POWERMAN_PAGES) \ $(SRC_IPMIPSU_PAGES) \ @@ -647,6 +669,7 @@ HTML_MANS = \ $(HTML_SERIAL_MANS) \ $(HTML_SNMP_MANS) \ $(HTML_USB_LIBUSB_MANS) \ + $(HTML_SERIAL_USB_MANS) \ $(HTML_NETXML_MANS) \ $(HTML_POWERMAN_MANS) \ $(HTML_IPMIPSU_MANS) \ diff --git a/docs/man/index.txt b/docs/man/index.txt index 4121178186..d32175db31 100644 --- a/docs/man/index.txt +++ b/docs/man/index.txt @@ -51,6 +51,8 @@ Drivers ~~~~~~~ - linkman:upsdrvctl[8] +- linkman:upsdrvsvcctl[8] +- linkman:nut-driver-enumerator[8] - linkman:al175[8] - linkman:apcsmart[8] diff --git a/docs/man/nut-driver-enumerator.txt b/docs/man/nut-driver-enumerator.txt new file mode 100644 index 0000000000..094926dfc1 --- /dev/null +++ b/docs/man/nut-driver-enumerator.txt @@ -0,0 +1,123 @@ +NUT-DRIVER-ENUMERATOR(8) +======================== + +NAME +---- + +nut-driver-enumerator - tool to map NUT device entries to service instances + +SYNOPSIS +-------- +*nut-driver-enumerator.sh* -h + +*nut-driver-enumerator.sh* (no args) + +*nut-driver-enumerator.sh* [--COMMAND] + + +DESCRIPTION +----------- + +*nut-driver-enumerator.sh* implements the set-up and querying of the +mapping between NUT driver configuration sections for each individual +monitored device, and the operating system service management framework +service instances into which such drivers are wrapped for independent +execution and management (on platforms where NUT currently supports +this integration -- currently this covers Linux distributions with +systemd and systems derived from Solaris 10 codebase, including +proprietary Sun/Oracle Solaris and numerous open-source illumos +distributions with SMF). It may be not installed in packaging for +other operating systems. + +This script provides a uniform interface for further NUT tools +such as linkman:upsdrvsvcctl[8] to implement their logic as +platform-independently as was possible and practical. It is not +currently intended for end-user consumption (and so is located in +the 'libexec' directory), with *upsdrvsvcctl* exposing the most +useful data and actions with its 'list' and 'resync' arguments. + +One part of the platform complexity that *nut-driver-enumerator.sh* +hides is the difference of rules for valid service instance names +in various frameworks, as well as system tools and naming patterns +involved. + +COMMANDS +-------- + +*nut-driver-enumerator.sh (no args)*:: +Update wrapping of devices into services + +*nut-driver-enumerator.sh --get-service-framework*:: +Print the detected service management framework in this OS + +*nut-driver-enumerator.sh --list-devices*:: +Print list of devices in NUT config + +*nut-driver-enumerator.sh --list-services*:: +Print list of service instances which wrap registered +NUT devices (full name of service unit) + +*nut-driver-enumerator.sh --list-instances*:: +Print list of service instances which wrap registered +NUT devices (just instance suffix) + +*nut-driver-enumerator.sh --get-service-for-device DEV*:: +Print the full name of service unit which wraps a NUT +device named `DEV` + +*nut-driver-enumerator.sh --get-device-for-service SVC*:: +Print the NUT device name for full or instance-suffix name of +a service unit `SVC` which wraps it + +*nut-driver-enumerator.sh --list-services-for-devices*:: +Print a TAB-separated list of service units and corresponding +NUT device names which each such unit wraps + +ENVIRONMENT VARIABLES +--------------------- + +By default *nut-driver-enumerator.sh* executed without arguments would +automatically start any newly registered service instances wrapping the +NUT devices, and would also restart the `nut-server` service if the +configuration was changed. Environment variable `AUTO_START=no` disables +this default part of the action. + +Also see below for environment variable `REPORT_RESTART_42=no` value. + +DIAGNOSTICS +----------- + +*nut-driver-enumerator.sh* will return a zero exit code if it had nothing +to do (all currently defined drivers match all of the currently defined +service instances, one-to-one) and if it had no errors in its operation. + +Other codes can be returned as a result of re-synchronization of mappings: + +*42*:: +NUT device sections and system service instances differed before, but +now match up -- so now the caller should likely restart some services. +Note that the drivers' service instances may have been started or stopped +as required (by `AUTO_START=yes`) -- but maybe the upsmon or upssched +services should restart. +If you pass environment variable `REPORT_RESTART_42=no` then +this codepath would return 0 (as a non-error exit code). +In default mode, such non-null reconfiguration should cause +the nut-driver-enumerator service to restart and this would +propagate to other NUT services that depend on it. + +*13*:: +Sections and services differed, and still do not match up + +*1*:: +Bad inputs, e.g. unrecognized service management framework + +*2*:: +Absent or unreadable `ups.conf` file + +SEE ALSO +-------- +linkman:upsdrvsvcctl[8], linkman:ups.conf[5] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ +The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsdrvctl.txt b/docs/man/upsdrvctl.txt index 68a4b8c473..7a788c41c6 100644 --- a/docs/man/upsdrvctl.txt +++ b/docs/man/upsdrvctl.txt @@ -22,6 +22,9 @@ whenever possible. When used properly, upsdrvctl lets you maintain identical startup scripts across multiple systems with different UPS configurations. +Note: For operating systems with service management frameworks, such as +Solaris SMF or Linux systemd, the *upsdrvsvcctl* may be a better choice. + OPTIONS ------- @@ -97,7 +100,7 @@ background. SEE ALSO -------- -linkman:nutupsdrv[8], linkman:upsd[8], linkman:ups.conf[5] +linkman:upsdrvsvcctl[8], linkman:nutupsdrv[8], linkman:upsd[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/man/upsdrvsvcctl.txt b/docs/man/upsdrvsvcctl.txt new file mode 100644 index 0000000000..89cdded97b --- /dev/null +++ b/docs/man/upsdrvsvcctl.txt @@ -0,0 +1,197 @@ +UPSDRVSVCCTL(8) +=============== + +NAME +---- + +upsdrvsvcctl - UPS driver service instance controller + +SYNOPSIS +-------- +*upsdrvsvcctl* -h + +*upsdrvsvcctl* ['OPTIONS'] {start | stop } ['ups'] + +DESCRIPTION +----------- + +*upsdrvsvcctl* provides a uniform interface for controlling your UPS +drivers wrapped into service instances on platforms which support that +(currently this covers Linux distributions with systemd and systems +derived from Solaris 10 codebase, including proprietary Sun/Oracle +Solaris and numerous open-source illumos distributions with SMF). +It may be not installed in packaging for other operating systems. + +When used properly, upsdrvsvcctl lets you maintain identical startup +scripts across multiple systems with different UPS configurations. + +The goal of this solution is to allow the services of *upsd* data +server to start up even if some of the power devices are currently +not accessible, and for NUT drivers to be automatically restarted +by the system in case of problems (driver bug, startup failure). +It also allows for faster startup of systems which monitor several +devices, by letting each driver to start in parallel with others, +and not with a sequential loop like was done previously. + +Independent service instances for each NUT driver also allow one +to configure further dependencies, such as that networking must be +available for SNMP and similar drivers (but is not needed for +local-medium drivers such as serial or USB). + +The old monolithic "all or nothing" solution requiring that all +drivers must be running, which sufficed for deployments with a few +UPSes, did not really work well for monitoring larger deployments. +It was also not easy to strike a pre-packaged balance between early +UPS protection for USB/serial home setups vs. waiting for network +on larger ones. + +*upsdrvsvcctl* is a script which mimics the operation of *upsdrvctl* +program (where possible) to provide similar end-user experience when +manipulating drivers wrapped into service instances rather than as +directly executed daemons. It relies on *nut-driver-enumerator.sh* +for a large part of actual operations. + +You should use upsdrvsvcctl instead of direct calls to the drivers +and daemon-based management with *upsdrvctl* whenever possible (that +is, for "production" use on compatible OSes). Otherwise (testing, +other OSes) the *upsdrvctl* is a recommended option. + +OPTIONS +------- + +*-h*:: +Display the help text. + +*-t*:: +Enable testing mode. Testing mode makes upsdrvsvcctl display the actions +it would execute without actually doing them. + + + +OPTIONS OF UPSDRVCTL NOT (CURRENTLY) APPLICABLE TO UPSDRVSVCCTL +--------------------------------------------------------------- + +Options like '-r', '-u' or '-D' could be handled by properties of the +service instances themselves, with this script helping to configure +them (assuming proper privileges of the user who called it). This is +not a "production" use case, though, to change such options on a +configured system -- so for experiments and troubleshooting, it may +be better to stop the service instance and play with *upsdrvctl* +directly. + +*-r* 'directory':: +If starting a driver, this value will direct it to *chroot*(2) into +'directory'. This can be useful when securing systems. + +This may be set in the ups.conf with "chroot" in the global section. + +*-u* 'username':: +If starting a driver, this value will direct it to *setuid*(2) to +the user id associated with 'username'. + +If the driver is started as root without specifying this value, it will +use the username that was compiled into the binary. This defaults to +"nobody", and is far from ideal. + +This may be set in ups.conf with "user" in the global section. + +*-D*:: +Raise the driver debug level. Use this multiple times for additional +details. + +COMMANDS +-------- + +*upsdrvsvcctl* supports three of the commands processed by *upsdrvctl* -- +start, stop and shutdown. They take an optional argument which is a UPS +name from linkman:ups.conf[5]. Without that argument, they operate on +every UPS that is currently configured. + +Note: shutdown is currently supported by stopping the driver service +instances to release the potentially held ports etc., calling the +*upsdrvctl* directly for issuing the shutdown command, and restarting +the driver service instances to reconnect when the device comes back +online. + +*start*:: +Start the UPS driver(s). In case of failure, further attempts may be executed +by using the 'maxretry' and 'retrydelay' options - see linkman:ups.conf[5]. + +*stop*:: +Stop the UPS driver(s). + +*upsdrvsvcctl* also supports further operations for troubleshooting the +mapping of NUT driver section names to the service instance names (which +may differ due to limitations of various systems). + +*list*:: +list the currently active mapping of service instances to device sections + +*resync*:: +update the mapping of service instances for NUT drivers to device section +names used in 'ups.conf' (register new instances, tear down obsoleted ones). + + +COMMANDS OF UPSDRVCTL NOT (CURRENTLY) APPLICABLE TO UPSDRVSVCCTL +---------------------------------------------------------------- + +*shutdown*:: +Command the UPS driver(s) to run their shutdown sequence. Drivers are +stopped according to their sdorder value - see linkman:ups.conf[5]. + +WARNING: this will probably power off your computers, so don't +play around with this option. Only use it when your systems are prepared +to lose power. + +NOTE: refer to linkman:ups.conf[5] for using the *nowait* parameter. + +ENVIRONMENT VARIABLES +--------------------- + +*NUT_CONFPATH* is the path name of the directory that contains +`upsd.conf` and other configuration files. If this variable is not set, +*upsdrvsvcctl* (or rather *nut-driver-enumerator.sh*) would use a built-in +default, which is often `/usr/local/ups/etc`. + +DIAGNOSTICS +----------- + +upsdrvsvcctl will return a nonzero exit code if it encounters an error +while performing the desired operation. This will also happen if a +driver takes longer than the 'maxstartdelay' period to enter the +background. + +Any messages issued by the *upsdrvctl* program used to start the NUT +drivers as part of the service instances' implementations, or by the +drivers themselves, will be logged by the service management framework +facilities and will not appear in your interactive terminal used to +manage the driver. + +Use `upsdrvsvcctl list` or `upsdrvsvcctl list NUT-device` to find out +the service instance name for the NUT driver (section name) you are +interested in. Then look up the service logs (where the outputs of the +service implementation program as well as the framework messages about +this service are stored), as suggested below: + +*Linux systemd*:: +Messages will normally be kept in the service journal, so: + + journalctl -lu nut-driver@instance-name + +Note that your local system configuration may be impacted by such +nuances as passing the journal data to a standard syslog server, +and/or by having a small cache for locally stored journal messages +(so older entries would disappear). There may also be or not be a +copy of the journals stored in the filesystem. + +*Solaris SMF*:: +Look for `/var/svc/log/system-power-nut-driver:instance-name.log` file. + +SEE ALSO +-------- +linkman:upsdrvctl[8], linkman:nutupsdrv[8], linkman:upsd[8], +linkman:nut-driver-enumerator[8], linkman:ups.conf[5] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ +The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 17ecf094a1..87286acc44 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -47,7 +47,7 @@ NUT network protocol, over the time: |=============================================================================== NOTE: any new version of the protocol implies an update of NUT_NETVERSION -in *configure.in*. +in *configure.ac*. GET diff --git a/docs/nut.dict b/docs/nut.dict index ac437c6bb6..4a54a2d276 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2422 utf-8 +personal_ws-1.1 en 2431 utf-8 AAS ACFAIL ACFREQ @@ -145,6 +145,7 @@ CERTREQUEST CERTVERIF CEST CHRG +CLI CLOCAL CMDDESC CMDSCRIPT @@ -438,8 +439,8 @@ KNutClient KNutSetting KOLFF KRT -Kaminski Kain +Kaminski Kanji Kazutoshi Kebo @@ -898,6 +899,7 @@ SKU SL SMALLBUF SMBUS +SMF SMK SMT SMTP @@ -1339,6 +1341,7 @@ cmdname cmds cmdvartab codebase +codepath coldstarts collectd colspan @@ -1653,6 +1656,7 @@ ivtscd jNUT jNut jNutWebAPI +journalctl jpg jpgraph json @@ -1679,6 +1683,7 @@ libaugeas libc libcommon libdir +libexec libhid libhidups libi @@ -1730,6 +1735,7 @@ lowruntime lowvoltsout lr lsusb +lu lvo lxml lxyz @@ -2187,6 +2193,7 @@ sudo suid superset sv +svc svn sw symlink @@ -2201,6 +2208,7 @@ syscalls sysconfdir sysconfig syslog +systemctl systemd systemdsystemunitdir systemhours @@ -2301,6 +2309,7 @@ upsdebugx upsdev upsdrv upsdrvctl +upsdrvsvcctl upserror upsfetch upsgone diff --git a/drivers/Makefile.am b/drivers/Makefile.am index e04989cf83..20d006eb71 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -34,21 +34,21 @@ SERIAL_DRIVERLIST = al175 bcmxcp belkin belkinunv bestfcom \ gamatronic genericups isbmex liebert liebert-esp2 masterguard metasys \ mge-utalk microdowell mge-shut oneac optiups powercom rhino \ safenet skel solis tripplite tripplitesu upscode2 victronups powerpanel \ - blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old apcupsd-ups riello_ser \ - nutdrv_qx + blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old apcupsd-ups riello_ser SNMP_DRIVERLIST = snmp-ups USB_LIBUSB_DRIVERLIST = usbhid-ups bcmxcp_usb tripplite_usb \ blazer_usb richcomm_usb riello_usb \ - nutdrv_atcl_usb \ - nutdrv_qx + nutdrv_atcl_usb USB_DRIVERLIST = $(USB_LIBUSB_DRIVERLIST) +SERIAL_USB_DRIVERLIST = \ + nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups MODBUS_DRIVERLIST = phoenixcontact_modbus LINUX_I2C_DRIVERLIST = asem # distribute all drivers, even ones that are not built by default -EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(SNMP_DRIVERLIST) $(USB_DRIVERLIST) $(NEONXML_DRIVERLIST) $(MACOSX_DRIVERLIST) +EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(SNMP_DRIVERLIST) $(USB_DRIVERLIST) $(SERIAL_USB_DRIVERLIST) $(NEONXML_DRIVERLIST) $(MACOSX_DRIVERLIST) # construct the list of drivers to build if SOME_DRIVERS @@ -56,7 +56,11 @@ if SOME_DRIVERS else driverexec_PROGRAMS = if WITH_SERIAL - driverexec_PROGRAMS += $(SERIAL_DRIVERLIST) + driverexec_PROGRAMS += $(SERIAL_DRIVERLIST) $(SERIAL_USB_DRIVERLIST) +else +if WITH_USB + driverexec_PROGRAMS += $(SERIAL_USB_DRIVERLIST) +endif endif if WITH_SNMP driverexec_PROGRAMS += $(SNMP_DRIVERLIST) diff --git a/include/Makefile.am b/include/Makefile.am index 41f47baebb..527d0995b3 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -13,7 +13,7 @@ nut_version.h: FORCE echo '/* This file was generated by "make". */' >> _nut_version.h ; \ if [ -z "$$GITREV" ]; \ then NUT_VERSION="$(PACKAGE_VERSION)"; \ - echo '/* The version number is set by AC_INIT in configure.in. */' >> _nut_version.h ; \ + echo '/* The version number is set by AC_INIT in configure.ac. */' >> _nut_version.h ; \ else NUT_VERSION="$$GITREV"; \ echo '/* The version number is determined by the most recent Git tag. */' >> _nut_version.h ; \ fi ; \ diff --git a/scripts/Aix/.gitignore b/scripts/Aix/.gitignore index ce505768bf..038fae6802 100644 --- a/scripts/Aix/.gitignore +++ b/scripts/Aix/.gitignore @@ -1 +1,2 @@ nut-aix.spec +nut.init diff --git a/scripts/Aix/nut.init b/scripts/Aix/nut.init deleted file mode 100644 index a54f85899d..0000000000 --- a/scripts/Aix/nut.init +++ /dev/null @@ -1,163 +0,0 @@ -#! /bin/sh -# -# ups: Starts the Network UPS Tools -# -# chkconfig: - 26 74 -# description: Network UPS Tools is a collection of programs which provide a common \ -# interface for monitoring and administering UPS hardware. -# processname: upsd -# config: /usr/local/ups/etc -# config: /etc/rc.ups -# -### BEGIN INIT INFO -# Provides: ups -# Required-Start: $syslog $network $named -# Required-Stop: $local_fs -# Default-Stop: 0 1 6 -# Short-Description: Starts the Network UPS tools -# Description: Network UPS Tools is a collection of programs which provide a common \ -# interface for monitoring and administering UPS hardware. -### END INIT INFO - -success() { - echo OK -} - -failure() { - echo FAILED -} - -# Resolve what processes should run -SERVER="no" -CLIENT="no" - -if [ -f /usr/local/ups/etc/nut.conf ]; then - . /usr/local/ups/etc/nut.conf - - case $MODE in - standalone|netserver) - SERVER="yes" - ;; - esac - - rpm -q nut-client >/dev/null 2>&1 && CLIENT="yes" -fi - -do_start() { - if [ "$SERVER" = "yes" ]; then - echo "Starting UPS driver controller: \c" - /usr/local/ups/sbin/upsdrvctl start >/dev/null 2>&1 && success || failure - RETVAL=$? - - echo "Starting upsd: \c" - /usr/local/ups/sbin/upsd $UPSD_OPTIONS >/dev/null 2>&1 && success || failure - if [ "$RETVAL" = 0 ]; then - RETVAL=$? - fi - fi - - if [ "$CLIENT" = "yes" ]; then - echo "Starting UPS monitor: \c" - /usr/local/ups/sbin/upsmon >/dev/null 2>&1 && success || failure - if [ "$RETVAL" = 0 ]; then - RETVAL=$? - fi - fi - - [ "$RETVAL" = 0 ] && touch /var/locks/ups -} - -do_stop() { - if test -e /var/run/nut/upsmon.pid; then - echo "Stopping UPS monitor: \c" - PID=`cat /var/run/nut/upsmon.pid` - kill $PID && success || failure - rm /var/run/nut/upsmon.pid - fi - - if [ "$SERVER" = "yes" ]; then - if test -e /var/run/nut/upsd.pid; then - echo "Stopping upsd: \c" - PID=`cat /var/run/nut/upsd.pid` - kill -9 $PID && success || failure - rm /var/run/nut/upsd.pid - fi - RETVAL=$? - - echo "Shutting down UPS driver controller: \c" - /usr/local/ups/sbin/upsdrvctl stop > /dev/null 2>&1 && success || failure - if [ "$RETVAL" = 0 ]; then - RETVAL=$? - fi - fi - [ "$RETVAL" = 0 ] && rm -f /var/locks/ups -} - -do_restart() { - do_stop - waitmore=5 - while [ -n "$(ls /var/run/nut/)" -a $waitmore -ge 1 ] - do - sleep 1 - waitmore=$((waitmore-1)) - done - do_start -} - -do_reload() { - # FIXME: upsd and upsmon always return 0 - # => can't tell if reload was successful - if [ "$SERVER" = "yes" ]; then - echo "Reloading upsd" - /usr/local/ups/sbin/upsd -c reload - RETVAL=$? - fi - - echo "Reloading upsmon" - /usr/local/ups/sbin/upsmon -c reload - if [ "$RETVAL" = 0 ]; then - RETVAL=$? - fi -} - -# See how we are called. -case "$1" in - start) - do_start ;; - - stop) - do_stop ;; - - restart) - do_restart ;; - - try-restart) - [ -f /var/locks/ups ] && do_restart || true - ;; - - reload) - do_reload ;; - - force-reload) - do_restart ;; - - status) - if [ "$SERVER" = "yes" ]; then - if test -f /var/locks/ups; then - echo "upsd is running with PID" `cat /var/run/nut/upsd.pid` - fi - fi - - if test -e /var/run/nut/upsmon.pid; then - echo "upsmon is running with PID" `cat /var/run/nut/upsmon.pid` - elif rpm -q nut-client >/dev/null 2>&1; then - echo "upsmon isn't running" - fi - ;; - - *) - echo "Usage: $0 {start|stop|restart|try-restart|reload|force-reload|status}" - RETVAL=3 -esac - -exit $RETVAL diff --git a/scripts/Aix/nut.init.in b/scripts/Aix/nut.init.in new file mode 100755 index 0000000000..018777efe6 --- /dev/null +++ b/scripts/Aix/nut.init.in @@ -0,0 +1,171 @@ +#! /bin/sh +# +# ups: Starts the Network UPS Tools +# +# chkconfig: - 26 74 +# description: Network UPS Tools is a collection of programs which provide a common \ +# interface for monitoring and administering UPS hardware. +# processname: upsd +# config: /usr/local/ups/etc +# config: /etc/rc.ups +# +### BEGIN INIT INFO +# Provides: ups +# Required-Start: $syslog $network $named +# Required-Stop: $local_fs +# Default-Stop: 0 1 6 +# Short-Description: Starts the Network UPS tools +# Description: Network UPS Tools is a collection of programs which provide a common \ +# interface for monitoring and administering UPS hardware. +### END INIT INFO + +success() { + echo OK +} + +failure() { + echo FAILED +} + +# Resolve what processes should run +SERVER="no" +CLIENT="no" + +NUT_DIR="@prefix@" +NUT_SBIN_DIR="${NUT_DIR}/sbin" +NUT_LIB_DIR="${NUT_DIR}/lib" +NUT_RUN_DIR="@PIDPATH@/nut" +CONFIG="@CONFPATH@/nut.conf" +NUTUSER="@RUN_AS_USER@" +NUTGROUP="@RUN_AS_GROUP@" +NUT_VAR_LOCK="/var/locks/ups" + +if [ -f "$CONFIG" ] ; then + . "$CONFIG" + + case "$MODE" in + standalone|netserver) + SERVER="yes" + ;; + esac + + rpm -q nut-client >/dev/null 2>&1 && CLIENT="yes" +fi + +do_start() { + RETVAL=0 + + if [ ! -d "$NUT_RUN_DIR" ]; then + mkdir -p "$NUT_RUN_DIR" && \ + chown "root:$NUTGROUP" "$NUT_RUN_DIR" && \ + chmod 770 "$NUT_RUN_DIR" + RETVAL=$? + fi + + if [ "$SERVER" = "yes" ]; then + echo "Starting UPS driver controller: \c" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsdrvctl start >/dev/null 2>&1 && success || { RETVAL=$?; failure; } + + echo "Starting upsd: \c" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsd $UPSD_OPTIONS >/dev/null 2>&1 && success || { RETVAL=$?; failure; } + fi + + if [ "$CLIENT" = "yes" ]; then + echo "Starting UPS monitor: \c" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsmon >/dev/null 2>&1 && success || { RETVAL=$?; failure; } + fi + + [ "$RETVAL" = 0 ] && touch "${NUT_VAR_LOCK}" + return $RETVAL +} + +do_stop() { + RETVAL=0 + if test -e "${NUT_RUN_DIR}"/upsmon.pid; then + echo "Stopping UPS monitor: \c" + PID="`cat "${NUT_RUN_DIR}"/upsmon.pid`" + kill -15 $PID && success || { RETVAL=$?; failure; } + rm "${NUT_RUN_DIR}"/upsmon.pid + fi + + if [ "$SERVER" = "yes" ]; then + if test -e "${NUT_RUN_DIR}"/upsd.pid; then + echo "Stopping upsd: \c" + PID="`cat "${NUT_RUN_DIR}"/upsd.pid`" + kill -15 $PID && success || { RETVAL=$?; failure; } + rm "${NUT_RUN_DIR}"/upsd.pid + fi + + echo "Shutting down UPS driver controller: \c" + "${NUT_SBIN_DIR}"/upsdrvctl stop > /dev/null 2>&1 && success || { RETVAL=$?; failure; } + fi + [ "$RETVAL" = 0 ] && rm -f "${NUT_VAR_LOCK}" + return $RETVAL +} + +do_restart() { + do_stop + waitmore=5 + while [ -n "$(ls "${NUT_RUN_DIR}"/)" -a $waitmore -ge 1 ] + do + sleep 1 + waitmore="$(expr $waitmore - 1)" + done + do_start +} + +do_reload() { + # FIXME: upsd and upsmon always return 0 + # => can't tell if reload was successful + RETVAL=0 + if [ "$SERVER" = "yes" ]; then + echo "Reloading upsd" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsd -c reload && success || { RETVAL=$?; failure; } + fi + + echo "Reloading upsmon" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsmon -c reload && success || { RETVAL=$?; failure; } + return $RETVAL +} + +# See how we are called. +case "$1" in + start) + do_start ;; + + stop) + do_stop ;; + + restart) + do_restart ;; + + try-restart) + [ -f "${NUT_VAR_LOCK}" ] && do_restart || true + ;; + + reload) + do_reload ;; + + force-reload) + do_restart ;; + + status) + if [ "$SERVER" = "yes" ]; then + if test -f "${NUT_VAR_LOCK}"; then + echo "upsd is running with PID" `cat "${NUT_RUN_DIR}"/upsd.pid` + fi + fi + + if test -e "${NUT_RUN_DIR}"/upsmon.pid; then + echo "upsmon is running with PID" `cat "${NUT_RUN_DIR}"/upsmon.pid` + elif rpm -q nut-client >/dev/null 2>&1; then + echo "upsmon isn't running" + fi + ;; + + *) + echo "Usage: $0 {start|stop|restart|try-restart|reload|force-reload|status}" + RETVAL=3 +esac + +exit $RETVAL diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 8e84e24b5d..2d54b5b085 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -25,4 +25,4 @@ EXTRA_DIST = README \ Windows/halt.c \ Windows/Makefile -SUBDIRS = augeas devd hotplug python systemd udev Solaris +SUBDIRS = augeas devd hotplug python systemd udev Solaris upsdrvsvcctl diff --git a/scripts/Solaris/.gitignore b/scripts/Solaris/.gitignore index 92334788dc..4bfd7c9314 100644 --- a/scripts/Solaris/.gitignore +++ b/scripts/Solaris/.gitignore @@ -1,4 +1,15 @@ /nut /pkginfo +/preinstall /postinstall /preremove +/postremove +/preproto.pl +/svc-nut-server +/svc-nut-monitor +/nut-driver.xml +/nut-driver-enumerator.xml +/nut-monitor.xml +/nut-server.xml +/nut.xml +/NUT*.local.gz diff --git a/scripts/Solaris/Makefile.am b/scripts/Solaris/Makefile.am index 15075b0617..baa4ee3713 100644 --- a/scripts/Solaris/Makefile.am +++ b/scripts/Solaris/Makefile.am @@ -1,17 +1,83 @@ -EXTRA_DIST = makelocal.sh - -package: makelocal.sh pkginfo - $ cd @prefix@; $ find . -print | pkgproto > prototype1 - $ cp makelocal.sh precheck.py pkginfo nut preinstall postinstall preremove postremove preproto.pl @prefix@ - $ cd @prefix@; perl preproto.pl - $ cd @prefix@; python precheck.py - $ cd @prefix@; rm -f prototype1 - $ cd @prefix@; ./makelocal.sh - $ cp @prefix@/*.gz $(srcdir) - if test `uname -p` = "i386"; then \ - mv NUT_solaris_package.local.gz NUT_solaris_i386_package@PACKAGE_VERSION@.local.gz; \ - else \ - if test `uname -p` = "sparc"; then \ - mv NUT_solaris_package.local.gz NUT_solaris_sparc_package@PACKAGE_VERSION@.local.gz; \ - fi; \ - fi; +EXTRA_DIST = makelocal.sh README +PROTOTYPE_DIR = $(DESTDIR)@prefix@ +SOLARIS_CHECK_TARGETS = + +SOLARIS_SMF_MANIFESTS = \ + nut.xml \ + nut-server.xml \ + nut-monitor.xml \ + nut-driver.xml \ + nut-driver-enumerator.xml + +SOLARIS_SMF_METHODSCRIPTS = \ + svc-nut-server \ + svc-nut-monitor + +if WITH_SOLARIS_SMF +# OS equivalent of /lib/svc/method and /var/svc/manifest/application +# but we can just use then from this location +solarissmfmethoddir = @datadir@/solaris-smf/method +solarissmfmanifestdir = @datadir@/solaris-smf/manifest +solarissmfmethod_SCRIPTS = $(SOLARIS_SMF_METHODSCRIPTS) +solarissmfmanifest_DATA = $(SOLARIS_SMF_MANIFESTS) + +libexec_SCRIPTS = ../upsdrvsvcctl/nut-driver-enumerator.sh + +sbin_SCRIPTS = ../upsdrvsvcctl/upsdrvsvcctl + +SOLARIS_CHECK_TARGETS += check-local-solaris-smf +endif + +solarisinitscriptdir = @datadir@/solaris-init +solarisinitscript_SCRIPTS = nut + +SOLARIS_PACKAGE_TARGETS = + +if WITH_SOLARIS_PKG_IPS +SOLARIS_PACKAGE_TARGETS += package-solaris-ips +endif + +if WITH_SOLARIS_PKG_SVR4 +SOLARIS_PACKAGE_TARGETS += package-solaris-svr4 +endif + +package: $(SOLARIS_PACKAGE_TARGETS) + +# TODO: Reduce build dependencies (implicit!) on python and perl +# by shelling the scripts used below +# NOTE: This assumes the rest of the product has already been built +# and installed under PROTOTYPE_DIR, but declares no explicit +# dependency on that +SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS = makelocal.sh precheck.py preproto.pl +SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS = preinstall postinstall preremove postremove +SOLARIS_PACKAGE_SVR4_INSTALLDATA = pkginfo +package-solaris-svr4: $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLDATA) + if test -n "@auglensdir@" && test -d "$(DESTDIR)@auglensdir@" ; then \ + mkdir -p "$(DESTDIR)@datadir@/augeas-lenses" && \ + cd "$(DESTDIR)@auglensdir@" && \ + ( cp -prf ./ "$(DESTDIR)@datadir@/augeas-lenses/" || cp -rf ./ "$(DESTDIR)@datadir@/augeas-lenses/" ) ; fi + cd $(PROTOTYPE_DIR) && find . -print | pkgproto > prototype1 + cp $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLDATA) $(PROTOTYPE_DIR) + cd $(PROTOTYPE_DIR) && chmod +x $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) + cd $(PROTOTYPE_DIR) && perl preproto.pl + cd $(PROTOTYPE_DIR) && python precheck.py + cd $(PROTOTYPE_DIR) && rm -f prototype1 + cd $(PROTOTYPE_DIR) && ./makelocal.sh + cp $(PROTOTYPE_DIR)/*.gz $(builddir) + UNAME_P="`uname -p`" && case "$${UNAME_P}" in \ + i386|sparc) \ + mv -f NUT_solaris_package.local.gz "$(abs_top_builddir)/NUT_solaris_$${UNAME_P}_package@PACKAGE_VERSION@.local.gz" ;; \ + esac + +# TODO: Define support for IPS packaging (provide p5m files and make rules) +package-solaris-ips: + @echo "SKIPPED : Target $@ is not implemented yet" + +check-local: $(SOLARIS_CHECK_TARGETS) + +check-local-solaris-smf: $(SOLARIS_SMF_MANIFESTS) + @[ -x /usr/sbin/svccfg ] || { echo "WARNING : Target $@ skipped due to absent /usr/sbin/svccfg" >&2; return 0; } ; \ + RES=0 ; for F in $^ ; do \ + echo " SVCCFG-VALIDATE $$F"; \ + /usr/sbin/svccfg validate "$$F" || RES=$$? ; \ + done; exit $$RES diff --git a/scripts/Solaris/README b/scripts/Solaris/README new file mode 100644 index 0000000000..9053ff1cea --- /dev/null +++ b/scripts/Solaris/README @@ -0,0 +1,10 @@ +This directory contains init-scripts and SMF manifests and methods +for integration of NUT services with Solaris and descendant OSes. + +This also includes the nut-driver-enumerator.sh (service and implementation +method) and upsdrvsvcctl (tool) to manage NUT drivers as service instances, +which are stored in `../systemd/` subdirectory (portable codebase shared +with Linux systemd). + +Init-script solution contributed by numerous authors +SMF solution contributed by Jim Klimov diff --git a/scripts/Solaris/makelocal.sh b/scripts/Solaris/makelocal.sh index 639e780036..f017c43cdd 100755 --- a/scripts/Solaris/makelocal.sh +++ b/scripts/Solaris/makelocal.sh @@ -1,5 +1,11 @@ #!/bin/sh -pkgmk -o -d `pwd` -pkgtrans `pwd` `pwd`/NUT_solaris_package.local -gzip `pwd`/NUT_solaris_package.local +# Creates the package file from current-directory contents +# Called by Makefile starting from installed prototype directory + +echo "Making Solaris SVR4 package metadata..." && \ +pkgmk -o -d "`pwd`" && \ +echo "Making Solaris SVR4 package archive file..." && \ +( yes "" | pkgtrans "`pwd`" "`pwd`/NUT_solaris_package.local" ) && \ +echo "Compressing Solaris SVR4 package archive file..." && \ +gzip "`pwd`/NUT_solaris_package.local" diff --git a/scripts/Solaris/nut-driver-enumerator.xml.in b/scripts/Solaris/nut-driver-enumerator.xml.in new file mode 100644 index 0000000000..bc6d90dcb9 --- /dev/null +++ b/scripts/Solaris/nut-driver-enumerator.xml.in @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/nut-driver.xml.in b/scripts/Solaris/nut-driver.xml.in new file mode 100644 index 0000000000..676fd24641 --- /dev/null +++ b/scripts/Solaris/nut-driver.xml.in @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/nut-monitor.xml.in b/scripts/Solaris/nut-monitor.xml.in new file mode 100644 index 0000000000..c3118d3557 --- /dev/null +++ b/scripts/Solaris/nut-monitor.xml.in @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/nut-server.xml.in b/scripts/Solaris/nut-server.xml.in new file mode 100644 index 0000000000..dd5fa71f73 --- /dev/null +++ b/scripts/Solaris/nut-server.xml.in @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/nut.in b/scripts/Solaris/nut.in old mode 100644 new mode 100755 index ab7ec94efa..1c39603c37 --- a/scripts/Solaris/nut.in +++ b/scripts/Solaris/nut.in @@ -3,29 +3,31 @@ #init.d script to start nut services NUT_DIR="@prefix@" -CONFIG=$NUT_DIR/etc/nut.conf +NUT_SBIN_DIR="${NUT_DIR}/sbin" +NUT_LIB_DIR="${NUT_DIR}/lib" +CONFIG="@CONFPATH@/nut.conf" -if [ -f $CONFIG ] ; then - . $CONFIG +if [ -f "$CONFIG" ] ; then + . "$CONFIG" fi ups_stop () { pkill -n upsmon pkill -n upsd - ${NUT_DIR}/sbin/upsdrvctl stop > /dev/null 2>&1 + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsdrvctl" stop > /dev/null 2>&1 } ups_start () { if [ "$MODE" = "none" ];then - echo No mode set + echo "No NUT mode set, not starting anything" >&2 exit 1 fi - if [ ! "$MODE" = "netclient" ];then - $NUT_DIR/sbin/upsdrvctl start #> /dev/null 2>&1 - $NUT_DIR/sbin/upsd #> /dev/null 2>&1 + if [ "$MODE" != "netclient" ] ; then + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsdrvctl" start #> /dev/null 2>&1 + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsd" #> /dev/null 2>&1 fi - $NUT_DIR/sbin/upsmon #> /dev/null 2>&1 + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsmon" #> /dev/null 2>&1 } case $1 in @@ -46,10 +48,11 @@ case $1 in ups_start ;; 'poweroff') - $NUT_DIR/sbin/upsmon -K >/dev/null 2>&1 + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsmon" -K >/dev/null 2>&1 if [ $? = 0 ]; then - echo "Shutting down the UPS ..." - #$NUT_DIR/sbin/upsdrvctl shutdown + echo "Shutting down the UPS(es) ..." + echo "WARNING: UPS shutdown is currently disabled, please uncomment it in the init-script if desired" >&2 + #${NUT_SBIN_DIR}/upsdrvctl shutdown fi ;; *) @@ -57,7 +60,7 @@ case $1 in echo "Usage: '$0' {start | stop | restart }" echo "" exit 64 - ;; + ;; esac exit $? diff --git a/scripts/Solaris/nut.xml.in b/scripts/Solaris/nut.xml.in new file mode 100644 index 0000000000..b06b2b151c --- /dev/null +++ b/scripts/Solaris/nut.xml.in @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/pkginfo.in b/scripts/Solaris/pkginfo.in index d99bdbec53..39667f0a79 100644 --- a/scripts/Solaris/pkginfo.in +++ b/scripts/Solaris/pkginfo.in @@ -1,6 +1,6 @@ PKG="NUT" NAME="Network UPS Tools" -ARCH="@OS_NAME@" +ARCH="@target_cpu@" VERSION="@PACKAGE_VERSION@" CATEGORY="application" VENDOR="http://www.networkupstools.org" diff --git a/scripts/Solaris/postinstall.in b/scripts/Solaris/postinstall.in old mode 100644 new mode 100755 index 136e07719c..a8d0c683c4 --- a/scripts/Solaris/postinstall.in +++ b/scripts/Solaris/postinstall.in @@ -3,63 +3,106 @@ #Postinstall script NUT_DIR="@prefix@" +prefix="@prefix@" # expanded as part of some autoconf macros below # make sure the nut user exists and has correct memberships -res=`getent group nut` +res="`getent group @RUN_AS_GROUP@`" || res="" if [ -z "$res" ]; then - groupadd nut + /usr/sbin/groupadd "@RUN_AS_GROUP@" fi -res=`getent passwd nut` +res="`getent passwd @RUN_AS_USER@`" || res="" if [ -z "$res" ]; then - useradd -g nut -G root -d ${NUT_DIR}/bin nut + /usr/sbin/useradd -c "Network UPS Tools" -g "@RUN_AS_GROUP@" -G root -d "@STATEPATH@" -s /bin/false @RUN_AS_USER@ fi -res=`groups nut | grep -w nut` +res="`groups "@RUN_AS_GROUP@" | grep -w "@RUN_AS_USER@"`" || res="" if [ -z "$res" ]; then - usermod -g nut -G root nut + /usr/sbin/usermod -g "@RUN_AS_GROUP@" -G root "@RUN_AS_USER@" fi # make sure that conffiles are secured and have the correct ownerships -if [ -d @CONFPATH@ ] ; then - chown root:nut @CONFPATH@ +if [ -d "@CONFPATH@" ] ; then + chown "root:@RUN_AS_GROUP@" "@CONFPATH@" fi -for file in nut.conf ups.conf upsd.conf upsmon.conf upsd.users upssched.conf; do - if [ -f @CONFPATH@/$file ] ; then - chown root:nut @CONFPATH@/$file - chmod 640 @CONFPATH@/$file +for file in nut.conf ups.conf upsd.conf upsmon.conf upsd.users upssched.conf nut-driver-enumerator.conf; do + if [ -f "@CONFPATH@/$file" ] ; then + chown "root:@RUN_AS_GROUP@" "@CONFPATH@/$file" + chmod 640 "@CONFPATH@/$file" fi done # make sure that /var/run/nut exists and has the correct ownerships -if [ ! -d @PIDPATH@/nut ] ; then - mkdir -p @PIDPATH@/nut +if [ ! -d "@PIDPATH@/nut" ] ; then + mkdir -p "@PIDPATH@/nut" fi -if [ -d @PIDPATH@/nut ] ; then - chown root:nut @PIDPATH@/nut - chmod 770 @PIDPATH@/nut +if [ -d "@PIDPATH@/nut" ] ; then + chown "root:@RUN_AS_GROUP@" "@PIDPATH@/nut" + chmod 770 "@PIDPATH@/nut" fi # make sure that /var/state/ups exists and has the correct ownerships -if [ ! -d @STATEPATH@ ] ; then - mkdir -p @STATEPATH@ +if [ ! -d "@STATEPATH@" ] ; then + mkdir -p "@STATEPATH@" fi -if [ -d @STATEPATH@ ] ; then - chown root:nut @STATEPATH@ - chmod 770 @STATEPATH@ +if [ -d "@STATEPATH@" ] ; then + chown "root:@RUN_AS_GROUP@" "@STATEPATH@" + chmod 770 "@STATEPATH@" fi -# Put init script in /etc/init.d - -cp $NUT_DIR/nut /etc/init.d -chmod 744 /etc/init.d/nut +if [ -n "@auglensdir@" ] && [ -d "@auglensdir@" ] && [ -d "@datadir@/augeas-lenses" ] ; then + ( cd "@datadir@/augeas-lenses" && cp -prf ./ "@auglensdir@"/ ) +fi -ln -s /etc/init.d/nut /etc/rc3.d/S100nut > /dev/null 2>&1 -ln -s /etc/init.d/nut /etc/rc3.d/K100nut > /dev/null 2>&1 +if test -x /usr/sbin/svcadm && test -x /usr/sbin/svccfg && test -x /usr/bin/svcs ; then + echo "Register SMF services..." + for S in nut-driver-enumerator nut-driver nut-server nut-monitor nut ; do + echo "Importing NUT service manifest: $S..." + /usr/sbin/svccfg import "@datadir@/solaris-smf/manifest/$S.xml" + done + # Enable services if the system already has a configuration (e.g. upgrade) + if test -s "@CONFPATH@/ups.conf" ; then + echo "Stopping NUT drivers, if any (in case of upgrade)..." + @SBINDIR@/upsdrvsvcctl stop + @SBINDIR@/upsdrvctl -DDDDD stop + sleep 5 + echo "Register NUT drivers (if any)..." + AUTO_START=no "@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" + sleep 2 + echo "Enable NUT drivers (if any)..." + /usr/sbin/svcadm enable -s nut-driver-enumerator || \ + { /usr/sbin/svcadm clear nut-driver-enumerator ; \ + /usr/sbin/svcadm enable -s nut-driver-enumerator; } + @SBINDIR@/upsdrvsvcctl start + else + echo "NOT ENABLING nut-driver-enumerator at this time : missing @CONFPATH@/ups.conf" >&2 + fi + if test -s "@CONFPATH@/ups.conf" && test -s "@CONFPATH@/upsd.conf" && test -s "@CONFPATH@/upsd.users" ; then + echo "Enable NUT upsd data server..." + /usr/sbin/svcadm enable -s nut-server + else + echo "NOT ENABLING nut-server at this time : missing at least one of : @CONFPATH@/ups.conf @CONFPATH@/upsd.conf @CONFPATH@/upsd.users" >&2 + fi + if test -s "@CONFPATH@/upsmon.conf" ; then + echo "Enable NUT upsmon client..." + /usr/sbin/svcadm enable -s nut-monitor + else + echo "NOT ENABLING nut-monitor at this time : missing @CONFPATH@/upsmon.conf" >&2 + fi + echo "Enable NUT umbrella service..." + /usr/sbin/svcadm enable -s nut +else + echo "Put init script in /etc/init.d..." + cp -pf "@NUT_DATADIR@/solaris-init/nut" /etc/init.d + chown root:bin /etc/init.d/nut + chmod 744 /etc/init.d/nut -# Start nut services + ln -s ../init.d/nut /etc/rc3.d/S90nut > /dev/null 2>&1 + ln -s ../init.d/nut /etc/rc3.d/K10nut > /dev/null 2>&1 -#echo "Starting nut services" -#$NUT_DIR/sbin/upsdrvctl start #> /dev/null 2>&1 -#$NUT_DIR/sbin/upsd #> /dev/null 2>&1 -#$NUT_DIR/sbin/upsmon #> /dev/null 2>&1 + # Start nut services + #echo "Starting nut services" + #$NUT_DIR/sbin/upsdrvctl start #> /dev/null 2>&1 + #$NUT_DIR/sbin/upsd #> /dev/null 2>&1 + #$NUT_DIR/sbin/upsmon #> /dev/null 2>&1 +fi diff --git a/scripts/Solaris/postremove b/scripts/Solaris/postremove deleted file mode 100755 index 99a0842bc3..0000000000 --- a/scripts/Solaris/postremove +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -# Remove nut group and user - -/usr/sbin/userdel nut - -/usr/sbin/groupdel nut - -# Remove init script from /etc/init.d - -rm /etc/init.d/nut -rm /etc/rc3.d/S100nut -rm /etc/rc3.d/K100nut - diff --git a/scripts/Solaris/postremove.in b/scripts/Solaris/postremove.in new file mode 100755 index 0000000000..0fdb9f79b5 --- /dev/null +++ b/scripts/Solaris/postremove.in @@ -0,0 +1,17 @@ +#!/bin/sh + +# Remove init script from /etc/init.d - created by scripts not packaging + +rm -f /etc/init.d/nut +rm -f /etc/rc3.d/S90nut +rm -f /etc/rc3.d/K10nut + +# Remove nut group and user + +/usr/sbin/userdel "@RUN_AS_USER@" + +/usr/sbin/groupdel "@RUN_AS_GROUP@" + +# Remove /var/run/nut + +rm -rf "@PIDPATH@/nut" diff --git a/scripts/Solaris/preinstall b/scripts/Solaris/preinstall deleted file mode 100755 index a63ade7c5f..0000000000 --- a/scripts/Solaris/preinstall +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -# Create group nut - -grep -w "nut" /etc/group -if [ $? -eq 1 ]; then - /usr/sbin/groupadd nut -fi - -# Create user for installing "Network UPS Tools" - -grep -w "nut" /etc/passwd -if [ $? -eq 1 ]; then - /usr/sbin/useradd -c "Network UPS Tools" -d /export/home/nut -m nut -fi diff --git a/scripts/Solaris/preinstall.in b/scripts/Solaris/preinstall.in new file mode 100755 index 0000000000..b3156db566 --- /dev/null +++ b/scripts/Solaris/preinstall.in @@ -0,0 +1,22 @@ +#!/bin/sh + +NUT_DIR="@prefix@" + +# Create group nut + +grep -w "@RUN_AS_GROUP@" /etc/group +if [ "$?" != 0 ]; then + /usr/sbin/groupadd "@RUN_AS_GROUP@" +fi + +# Create user for installing "Network UPS Tools" + +grep -w "@RUN_AS_USER@" /etc/passwd +if [ "$?" != 0 ]; then + /usr/sbin/useradd -c "Network UPS Tools" -g "@RUN_AS_GROUP@" -G root -d "@STATEPATH@" -s /bin/false "@RUN_AS_USER@" +fi + +res="`groups "@RUN_AS_GROUP@" | grep -w "@RUN_AS_USER@"`" || res="" +if [ -z "$res" ]; then + /usr/sbin/usermod -g "@RUN_AS_GROUP@" -G root "@RUN_AS_USER@" +fi diff --git a/scripts/Solaris/prepackage.py b/scripts/Solaris/prepackage.py deleted file mode 100755 index f8d987b36b..0000000000 --- a/scripts/Solaris/prepackage.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -import sys -import commands - -# checkinstall script creation - -platform = commands.getoutput('uname -s') -architecture = commands.getoutput('uname -p') - -fp=open("checkinstall","w") -fp.write("#!/bin/sh\n") -fp.write("\nexpected_platform=SunOS\n") -if platform == "SunOS" and architecture == "i386": - fp.write("expected_architecture=i386\n") -elif platform == "SunOS" and architecture == "sparc": - fp.write("expected_architecture=sparc\n") - -fp.write("platform=`uname -s`\n") -fp.write("architecture=`uname -p`\n\n") - -fp.write("if [ ${platform} -eq ${expected_platform} ]; then\n") -fp.write("\tif [ ${architecture} -eq ${expected_architecture} ]; then\n") -fp.write("\t\techo \"Checkinstall complete\"\n") -fp.write("\telse\n") -fp.write("\t\techo \"This is not Solaris $architecture machine\"\n") -fp.write("\t\texit 1\n") -fp.write("\tfi\n") -fp.write("else\n") -fp.write("\techo \"This is not Solaris machine\"\n") -fp.write("\texit 1\n") -fp.write("fi\n") -fp.write("exit 0\n") - -fp.close() - - diff --git a/scripts/Solaris/preproto.pl b/scripts/Solaris/preproto.pl.in similarity index 86% rename from scripts/Solaris/preproto.pl rename to scripts/Solaris/preproto.pl.in index 5068859704..ac52c16b29 100755 --- a/scripts/Solaris/preproto.pl +++ b/scripts/Solaris/preproto.pl.in @@ -8,6 +8,8 @@ $postinstall = "postinstall"; $preremove = "preremove"; $postremove = "postremove"; +$nutuser = "@RUN_AS_USER@"; +$nutgroup = "@RUN_AS_GROUP@"; open (PREPROTO,"< $temp") || die "Unable to read prototype information ($!)\n"; open (PROTO,"> $prototype") || die "Unable to write file prototype ($!)\n"; @@ -26,12 +28,12 @@ } elsif ($thisline =~ "^[fd] ") { # Change the ownership for files and directories ($dir, $none, $file, $mode, $user, $group) = split / /,$thisline; - print PROTO "$dir $none $file=$file $mode nut nut\n"; + print PROTO "$dir $none $file=$file $mode $nutuser $nutgroup\n"; } else { # Symlinks and other stuff should be printed as well ofcourse print PROTO "$thisline\n"; } } -print PROTO "f $none nut $mode root nut\n"; +#print PROTO "f $none nut $mode root $nutgroup\n"; close PROTO; close PREPROTO; diff --git a/scripts/Solaris/preremove.in b/scripts/Solaris/preremove.in old mode 100644 new mode 100755 index dc4a1a19b7..b74eb2edfd --- a/scripts/Solaris/preremove.in +++ b/scripts/Solaris/preremove.in @@ -1,8 +1,45 @@ -#!bin/sh +#!/bin/sh # Stop all nut services NUT_DIR="@prefix@" +prefix="@prefix@" # expanded as part of some autoconf macros below -#$NUT_DIR/sbin/upsdrvctl stop +if test -x /usr/sbin/svcadm && test -x /usr/sbin/svccfg && test -x /usr/bin/svcs ; then + # Unconfigure SMF services + for S in nut nut-monitor nut-driver-enumerator nut-server ; do + echo "Stopping NUT service: $S..." + /usr/sbin/svcadm clear "$S" + /usr/sbin/svcadm disable -s "$S" + echo "Removing NUT service: $S..." + /usr/sbin/svccfg delete "$S" || \ + /usr/sbin/svccfg -s "$S" delete || \ + /usr/sbin/svccfg -s "$S" delete default + done + echo "Stopping NUT drivers, if any..." + @SBINDIR@/upsdrvsvcctl stop + @SBINDIR@/upsdrvctl -DDDDD stop + sleep 5 + for S in `/usr/bin/svcs -H -o fmri '*/nut-driver:*'` ; do + echo "Stopping NUT service: $S..." + /usr/sbin/svcadm clear "$S" + /usr/sbin/svcadm disable "$S" + done + sleep 5 + for S in `/usr/bin/svcs -H -o fmri '*/nut-driver:*' | grep -wv default` ; do + echo "Removing NUT service: $S..." + /usr/sbin/svccfg -s "nut-driver" delete -f "$S" || \ + /usr/sbin/svccfg delete "$S" + done + S="nut-driver" && \ + echo "Removing NUT service: $S..." && \ + /usr/sbin/svccfg delete "$S" || \ + /usr/sbin/svccfg -s "$S" delete || \ + /usr/sbin/svccfg -s "$S" delete default +else + [ -x /etc/init.d/nut ] && /etc/init.d/nut stop +fi +if [ -n "@auglensdir@" ] && [ -d "@auglensdir@" ] && [ -d "@datadir@/augeas-lenses" ] ; then + ( cd "@datadir@/augeas-lenses" && find . -type f -exec rm -f "@auglensdir@"/'{}' \; ) +fi diff --git a/scripts/Solaris/svc-nut-monitor.in b/scripts/Solaris/svc-nut-monitor.in new file mode 100755 index 0000000000..a7dd0dc91d --- /dev/null +++ b/scripts/Solaris/svc-nut-monitor.in @@ -0,0 +1,56 @@ +#!/sbin/sh + +# Trivial (better is yet to come) SMF method script to start nut services +# Adapted for OpenIndiana userland from init.d script template in NUT sources +# Adaptation copyright (C) 2016-2017 Jim Klimov + +if [ -z "$SMF_FMRI" ]; then + echo "$0 must be called in SMF context!" >&2 + exit 1 +fi + +# smf(5) +. /lib/svc/share/smf_include.sh || exit + +prefix="@prefix@" +NUT_DIR="@prefix@" +NUT_SBIN_DIR="${NUT_DIR}/sbin" +NUT_LIB_DIR="${NUT_DIR}/lib" +NUT_RUN_DIR="@PIDPATH@/nut" +CONFIG="@CONFPATH@/nut.conf" +NUTUSER="@RUN_AS_USER@" +NUTGROUP="@RUN_AS_GROUP@" + +if [ -f "$CONFIG" ] ; then + . "$CONFIG" +fi + +ups_start () { + if [ "$MODE" = "none" ];then + echo "No NUT mode set, not starting anything" >&2 + exit $SMF_EXIT_ERR_CONFIG + fi + + # Default rights inspired by NUT scripts/Solaris/postinstall.in + mkdir -p "$NUT_RUN_DIR" && \ + chown "root:$NUTGROUP" "$NUT_RUN_DIR" && \ + chmod 770 "$NUT_RUN_DIR" \ + || exit $SMF_EXIT_ERR_FATAL + + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsmon #> /dev/null 2>&1 +} + +case "$1" in +'start') + ups_start + ;; + +*) + echo "" + echo "Usage: '$0' {start}" + echo "" + exit $SMF_EXIT_ERR_CONFIG + ;; +esac + +exit $? diff --git a/scripts/Solaris/svc-nut-server.in b/scripts/Solaris/svc-nut-server.in new file mode 100755 index 0000000000..9a826036e4 --- /dev/null +++ b/scripts/Solaris/svc-nut-server.in @@ -0,0 +1,60 @@ +#!/sbin/sh + +# Trivial (better is yet to come) SMF method script to start nut services +# Adapted for OpenIndiana userland from init.d script template in NUT sources +# Adaptation copyright (C) 2016 Jim Klimov + +if [ -z "$SMF_FMRI" ]; then + echo "$0 must be called in SMF context!" >&2 + exit 1 +fi + +# smf(5) +. /lib/svc/share/smf_include.sh || exit + +prefix="@prefix@" +NUT_DIR="@prefix@" +NUT_SBIN_DIR="$NUT_DIR/sbin" +NUT_LIB_DIR="${NUT_DIR}/lib" +NUT_RUN_DIR="@PIDPATH@/nut" +CONFIG="@CONFPATH@/nut.conf" +NUTUSER="@RUN_AS_USER@" +NUTGROUP="@RUN_AS_GROUP@" + +if [ -f "$CONFIG" ] ; then + . "$CONFIG" +fi + +ups_start () { + # Default rights inspired by NUT scripts/Solaris/postinstall.in + mkdir -p "$NUT_RUN_DIR" && \ + chown "root:$NUTGROUP" "$NUT_RUN_DIR" && \ + chmod 770 "$NUT_RUN_DIR" \ + || exit $SMF_EXIT_ERR_FATAL + + if [ "$MODE" = "none" ];then + echo "No NUT mode set, not starting anything" >&2 + exit 1 + fi + + if [ "$MODE" != "netclient" ] ; then + # In this distribution, UPS drivers are wrapped by service instances + #LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsdrvctl" start #> /dev/null 2>&1 + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsd" #> /dev/null 2>&1 + fi +} + +case "$1" in +'start') + ups_start + ;; + +*) + echo "" + echo "Usage: '$0' {start}" + echo "" + exit $SMF_EXIT_ERR_CONFIG + ;; +esac + +exit $? diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index 8ce9631f34..7d8b70041a 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -330,8 +330,7 @@ if [ -z "$NUMWALKFILE" ]; then fi # hostname is also mandatory while [ -z "$HOSTNAME" ]; do - echo " - Please enter the SNMP host IP address or name." + printf "\n\tPlease enter the SNMP host IP address or name.\n" read -p "SNMP host IP name or address: " HOSTNAME < /dev/tty if echo $HOSTNAME | egrep -q '[^a-zA-Z0-9]'; then echo "Please use only letters and digits" diff --git a/scripts/systemd/.gitignore b/scripts/systemd/.gitignore index 4164cd086c..a08b94f5ef 100644 --- a/scripts/systemd/.gitignore +++ b/scripts/systemd/.gitignore @@ -1,4 +1,8 @@ /nut-driver.service +/nut-driver@.service +/nut-driver.target /nut-monitor.service /nut-server.service /nutshutdown +/nut-driver-enumerator.service +/nut-driver-enumerator.path diff --git a/scripts/systemd/Makefile.am b/scripts/systemd/Makefile.am index 72e9a1c0cd..3547f3e49c 100644 --- a/scripts/systemd/Makefile.am +++ b/scripts/systemd/Makefile.am @@ -3,14 +3,25 @@ EXTRA_DIST = README if HAVE_SYSTEMD systemdsystemunit_DATA = \ - nut-driver.service \ + nut-driver-enumerator.service \ + nut-driver-enumerator.path \ + nut-driver@.service \ nut-monitor.service \ - nut-server.service + nut-server.service \ + nut-driver.target \ + nut.target + +EXTRA_DIST += nut-driver.target systemdshutdown_SCRIPTS = nutshutdown +libexec_SCRIPTS = ../upsdrvsvcctl/nut-driver-enumerator.sh + +sbin_SCRIPTS = ../upsdrvsvcctl/upsdrvsvcctl + else -EXTRA_DIST += nut-driver.service.in nut-monitor.service.in \ - nut-server.service.in nutshutdown.in +EXTRA_DIST += nut-driver@.service.in nut-monitor.service.in \ + nut-server.service.in nutshutdown.in nut-driver.target nut.target \ + nut-driver-enumerator.path.in nut-driver-enumerator.service.in endif diff --git a/scripts/systemd/README b/scripts/systemd/README index c8930ab958..b0063581d7 100644 --- a/scripts/systemd/README +++ b/scripts/systemd/README @@ -4,5 +4,10 @@ Service Manager. These files are automatically installed, upon detection (at configure time) of a systemd enabled system. +This also uses the nut-driver-enumerator.sh (service and implementation +method) and upsdrvsvcctl (tool) to manage NUT drivers as service instances +located in ../upsdrvsvcctl/ source subdirectory. + Contributed by Michal Hlavinka +Updated 2016-2018 by Michal Hrusecky and Jim Klimov diff --git a/scripts/systemd/nut-driver-enumerator.path.in b/scripts/systemd/nut-driver-enumerator.path.in new file mode 100644 index 0000000000..13b0b5e055 --- /dev/null +++ b/scripts/systemd/nut-driver-enumerator.path.in @@ -0,0 +1,7 @@ +# Trigger restart of nut-driver-enumerator.service whenever ups.conf is edited + +[Path] +PathModified=@CONFDIR@/ups.conf + +[Install] +WantedBy=nut.target diff --git a/scripts/systemd/nut-driver-enumerator.service.in b/scripts/systemd/nut-driver-enumerator.service.in new file mode 100644 index 0000000000..809a16f79b --- /dev/null +++ b/scripts/systemd/nut-driver-enumerator.service.in @@ -0,0 +1,29 @@ +[Unit] +# This unit starts early in system lifecycle to set up nut-driver instances. +# End-user may also restart this unit after editing ups.conf to automatically +# un-register or add new instances as appropriate. +Description=Network UPS Tools - enumeration of configure-file devices into systemd unit instances +After=local-fs.target +Before=nut-driver.target +PartOf=nut.target + +[Service] +### Script needs privileges to restart units +#User=@RUN_AS_USER@ +#Group=@RUN_AS_GROUP@ +User=root +# it is expected that the process has to exit before systemd starts follow-up units +Type=oneshot +# the service shall be considered active even when all its processes exited +RemainAfterExit=yes +# Currently systemd does not support restarting of oneshot services, and does +# not seem to guarantee that other services would only start after the script +# completes, for a non-oneshot case. The script itself handles restarting of +# nut-server which is the primary concerned dependency at the moment, so we +# don't want it to fail the unit (when it can't restart). +Environment=REPORT_RESTART_42=no +ExecStart=@NUT_LIBEXECDIR@/nut-driver-enumerator.sh +ExecReload=@NUT_LIBEXECDIR@/nut-driver-enumerator.sh + +[Install] +WantedBy=nut.target diff --git a/scripts/systemd/nut-driver.service.in b/scripts/systemd/nut-driver.service.in deleted file mode 100644 index f81ac6996f..0000000000 --- a/scripts/systemd/nut-driver.service.in +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Network UPS Tools - power device driver controller -After=local-fs.target network.target -StopWhenUnneeded=yes - -[Service] -ExecStart=@SBINDIR@/upsdrvctl start -ExecStop=@SBINDIR@/upsdrvctl stop -Type=forking - diff --git a/scripts/systemd/nut-driver.target b/scripts/systemd/nut-driver.target new file mode 100644 index 0000000000..d994ab9882 --- /dev/null +++ b/scripts/systemd/nut-driver.target @@ -0,0 +1,8 @@ +[Unit] +Description=Network UPS Tools - target for power device drivers on this system +After=local-fs.target +# network.target +PartOf=nut.target + +[Install] +WantedBy=nut.target diff --git a/scripts/systemd/nut-driver@.service.in b/scripts/systemd/nut-driver@.service.in new file mode 100644 index 0000000000..2560033f3c --- /dev/null +++ b/scripts/systemd/nut-driver@.service.in @@ -0,0 +1,42 @@ +[Unit] +Description=Network UPS Tools - device driver for %I +After=local-fs.target +PartOf=nut-driver.target +# Note: The choice of "network.target" allows to schedule this unit +# roughly when the network stack of this OS is ready (e.g. that the +# subsequent `upsd` will have a `0.0.0.0` or a `localhost` to bind +# to); however this target does not ensure availability of a real +# connection or final IP addresses. Drivers that require network as +# a media for interaction with UPSes (snmp-ups, netxml-ups, ipmi etc.) +# may want to extend this unit with `Requires=network-online.target` +# instead. Also note that *generally* this should not be a problem, +# since the drivers have a few retries with timeouts during startup, +# and typically by the time the box gets an IP address, the driver +# is still retrying to start and will succeed. +# Extending the unit does not require *this* file to be edited, you +# can instead drop in an additional piece of configuration, e.g. add +# a `/etc/systemd/system/nut-driver@.service.d/network.conf` with: +# [Unit] +# Requires=network-online.target +# After=network-online.target +# If your `upsd` requires specific IP addresses to be available before +# starting, a `/etc/systemd/system/nut-driver.target.d/network.conf` +# can be used in a similar manner. +# Finally note that "nut-driver-enumerator.service" should take care of this. + +[Service] +ExecStart=/bin/sh -c "@SBINDIR@/upsdrvctl start `@NUT_LIBEXECDIR@/nut-driver-enumerator.sh --get-device-for-service %i`" +ExecStop=/bin/sh -c "@SBINDIR@/upsdrvctl stop `@NUT_LIBEXECDIR@/nut-driver-enumerator.sh --get-device-for-service %i`" +Type=forking +# Note: If you customize the "maxstartdelay" in ups.conf or in your +# NUT compilation defaults, so it exceeds the default systemd unit +# startup timeout (typically 90 sec), then make sure to set a slightly +# longer systemd timeout for the nut-driver unit instances. You can +# do this by populating a drop-in configuration, so it is not later +# overwritten by updates to your NUT package -- create a dir+file: +# /etc/systemd/system/nut-driver@.service.d/timeout.conf with lines: +# [Service] +# TimeoutStartSec=190s + +[Install] +WantedBy=nut-driver.target diff --git a/scripts/systemd/nut-monitor.service.in b/scripts/systemd/nut-monitor.service.in index 8429bf280c..d19bb28c81 100644 --- a/scripts/systemd/nut-monitor.service.in +++ b/scripts/systemd/nut-monitor.service.in @@ -1,6 +1,19 @@ [Unit] Description=Network UPS Tools - power device monitor and shutdown controller After=local-fs.target network.target nut-server.service +# Note: We do not specify Requires nut-server.service because +# the `upsd` daemon(s) may be running on a different machine +# (connected to the UPSes) than the `upsmon` shutdown protector. +# The "Wants" directive would try to start the nut-server but +# would not abort if that attempt fails for whatever reason. +Wants=nut-server.service +# Extending the unit does not require *this* file to be edited, you +# can instead drop in an additional piece of configuration, e.g. add +# a `/etc/systemd/system/nut-monitor.service.d/network.conf` with: +# [Unit] +# Requires=network-online.target +# After=network-online.target +PartOf=nut.target [Service] ExecStart=@SBINDIR@/upsmon @@ -8,4 +21,4 @@ PIDFile=@PIDPATH@/upsmon.pid Type=forking [Install] -WantedBy=multi-user.target +WantedBy=nut.target diff --git a/scripts/systemd/nut-server.service.in b/scripts/systemd/nut-server.service.in index 7a786701f8..54122a59dc 100644 --- a/scripts/systemd/nut-server.service.in +++ b/scripts/systemd/nut-server.service.in @@ -1,11 +1,21 @@ [Unit] Description=Network UPS Tools - power devices information server -After=local-fs.target network.target nut-driver.service +After=local-fs.target network.target nut-driver.target # We don't Require drivers to be successfully started! This would be # a change of behavior compared to init SysV, and could prevent from # accessing successfully started, at least to audit a system. -Wants=nut-driver.service +Wants=nut-driver.target +# The `upsd` is a networked service (even if bound to a `localhost`) +# so it requires that the OS has some notion of networking already. +# Extending the unit does not require *this* file to be edited, you +# can instead drop in an additional piece of configuration, e.g. add +# a `/etc/systemd/system/nut-server.service.d/network.conf` with: +# [Unit] +# Requires=network-online.target +# After=network-online.target +Requires=network.target Before=nut-monitor.service +PartOf=nut.target [Service] ExecStart=@SBINDIR@/upsd @@ -13,4 +23,4 @@ ExecReload=@SBINDIR@/upsd -c reload Type=forking [Install] -WantedBy=multi-user.target +WantedBy=nut.target diff --git a/scripts/systemd/nut.target b/scripts/systemd/nut.target new file mode 100644 index 0000000000..f744d29c55 --- /dev/null +++ b/scripts/systemd/nut.target @@ -0,0 +1,8 @@ +[Unit] +Description=Network UPS Tools - target for power device drivers, data server and monitoring client (if enabled) on this system +After=local-fs.target nut-driver.target nut-server.service nut-monitor.service +Wants=local-fs.target nut-driver.target nut-server.service nut-monitor.service +# network.target + +[Install] +WantedBy=multi-user.target diff --git a/scripts/systemd/nutshutdown.in b/scripts/systemd/nutshutdown.in old mode 100644 new mode 100755 diff --git a/scripts/upsdrvsvcctl/.gitignore b/scripts/upsdrvsvcctl/.gitignore new file mode 100644 index 0000000000..3c24b9a908 --- /dev/null +++ b/scripts/upsdrvsvcctl/.gitignore @@ -0,0 +1,2 @@ +/nut-driver-enumerator.sh +/upsdrvsvcctl diff --git a/scripts/upsdrvsvcctl/Makefile.am b/scripts/upsdrvsvcctl/Makefile.am new file mode 100644 index 0000000000..7d7a0f235e --- /dev/null +++ b/scripts/upsdrvsvcctl/Makefile.am @@ -0,0 +1,12 @@ +EXTRA_DIST = README + +if HAVE_SYSTEMD +EXTRA_DIST += nut-driver-enumerator.sh upsdrvsvcctl +else +if WITH_SOLARIS_SMF +EXTRA_DIST += nut-driver-enumerator.sh upsdrvsvcctl +endif +endif + +EXTRA_DIST += nut-driver-enumerator.sh.in upsdrvsvcctl.in + diff --git a/scripts/upsdrvsvcctl/README b/scripts/upsdrvsvcctl/README new file mode 100644 index 0000000000..b2a9db64a8 --- /dev/null +++ b/scripts/upsdrvsvcctl/README @@ -0,0 +1,11 @@ +This directory contains the shared NUT support files for Linux systemd (the +System and Service Manager) and Solaris SMF (Service Management Framework). +It includes the nut-driver-enumerator.sh (service and implementation method) +and upsdrvsvcctl (tool) to manage NUT drivers as service instances. + +These files are automatically installed into SBINDIR/upsdrvsvcctl and +LIBEXECDIR/nut-driver-enumerator.sh, upon detection (at configure time) +of a systemd or SMF enabled system, with Makefiles of the ../systemd/ and +../Solaris/ source directories respectively. + +Contributed 2016-2018 by Jim Klimov diff --git a/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in new file mode 100755 index 0000000000..c6de73186a --- /dev/null +++ b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in @@ -0,0 +1,797 @@ +#!/bin/sh +# +# NOTE: This script is intentionally written with portable shell constructs +# with the aim and hope to work in different interpreters, so it is a +# bit dumber and less efficient than could be achieved with the more +# featured shells in the spectrum. +# +# Copyright (C) 2016-2018 Eaton +# +# 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 2 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +#! \file nut-driver-enumerator.sh(.in) +# \author Jim Klimov +# \brief Enumerate NUT devices for service-unit instance configuration +# \details This script allows to enumerate UPSes in order to produce the +# individual service unit instances for each defined configuration. +# It assumes the user has adequate permissions to inspect and create +# services (e.g. is a root or has proper RBAC profiles to do so). +# It helps service frameworks such as Linux systemd and Solaris SMF. +# When executed, this script looks for all configured ups.conf +# sections and registered service instances, and makes these two +# lists match up. +# FIXME: Does not track changes inside sections ATM. +# Returns exit codes: +# 0 Sections and services already match up +# 42 Sections and services differed, but now match up - +# now the caller should likely restart some services. +# Note that the drivers' service instances were started or +# stopped as required (by AUTO_START=yes) - but maybe the +# upsd or upsmon services should restart. If you pass envvar +# REPORT_RESTART_42=no then this codepath would return 0. +# In default mode, such non-null reconfiguration should cause +# the nut-driver-enumerator service to restart and this would +# propagate to other NUT services that depend on it. +# 13 Sections and services differed, and still do not match up +# 1 Bad inputs, e.g. unrecognized service management framework +# 2 Absent or unreadable ups.conf file +# \todo Complete the sample basic support for Solaris SMF (needs support +# for the service definitions similar to systemd NUT units to be +# added to that platform first). Parts in this script are finished. +# + +# NOTE: Currently found caveats that might be solved later but require +# considerable effort: +# * Solaris SMF constrains the syntax of valid strings for instance names +# (e.g. not starting with a digit, no period chars) which blocks creation +# of some UPS driver instances. This might be worked around by hashing +# the device name e.g. to base64 (and un-hashing instance name when calling +# upsdrvctl), but is not quite user-friendly. Also can store device name +# in a service attribute while mangling the instance name to a valid subset. +# Comparisons (if devices are already wrapped) becomes more complicated in +# the script in either case, as well as in the service startup method. +# ** The `+` `/` `=` characters from base64 are also invalid for SMF instance +# name, but the first two can be sed'ed to `-` `_` and back, for example. +# Some prefix word is also needed (avoid starting with a digit). +# The trailing padding `=` can be dropped, and added until we get a +# non-empty decode. Conversions can be done by +# `echo "$string" | openssl base64 -e|-d` +# * Dummy-UPS services that "proxy" another locally defined section are +# essentially a circular dependency for upsd. While the system might +# start-up lacking a driver, there should be some timer to re-enable +# failed not-disabled drivers (would be useful in any case though). + +# Directory where NUT configs are located, e.g. /etc/nut or /etc/ups +# Set at package configuration, compiled into daemons and drivers +prefix="@prefix@" +[ -n "${NUT_CONFPATH-}" ] || NUT_CONFPATH="@sysconfdir@" + +# Third-party services to depend on (can be overridden by config file) +### Note that for systemd+udev integration, it may be better to set up +### triggers in udev, see e.g. +### http://stackoverflow.com/questions/18463755/linux-start-daemon-on-connected-usb-serial-dongle +### Also can tune whether a driver "Wants" another service (would consider +### ordering if that one is enabled, but live if it is disabled), or if it +### "Requires" that (would cause that to start). +DEPSVC_USB_SYSTEMD="systemd-udev.service systemd-udev-settle.service" +DEPREQ_USB_SYSTEMD="Wants" +DEPSVC_NET_FULL_SYSTEMD="network-online.target" +DEPREQ_NET_FULL_SYSTEMD="Wants" +DEPSVC_NET_LOCAL_SYSTEMD="network.target" +DEPREQ_NET_LOCAL_SYSTEMD="Wants" +SVCNAME_SYSTEMD="nut-driver" + +# Some or all of these FMRIs may be related to dynamically changing hardware +# require_all) ;; # All cited services are running (online or degraded) +# require_any) ;; # At least one of the cited services is running +# optional_all) ;; # (All) cited services are running or would not run +# # without administrative action (disabled, maintenance, +# # not present, or are waiting for dependencies which do +# # not start without administrative action). +DEPSVC_USB_SMF="svc:/system/hotplug:default svc:/system/dbus:default svc:/system/hal:default svc:/milestone/devices:default" +DEPREQ_USB_SMF="optional_all" +# By default there are several physical network FMRIs shipped and at most +# only one is enabled on a particular system (e.g. :default or :nwam) +DEPSVC_NET_FULL_SMF="svc:/network/physical" +DEPREQ_NET_FULL_SMF="optional_all" +DEPSVC_NET_LOCAL_SMF="svc:/network/loopback:default" +DEPREQ_NET_LOCAL_SMF="optional_all" +SVCNAME_SMF="svc:/system/power/nut-driver" + +[ -z "${NUT_DRIVER_ENUMERATOR_CONF-}" ] && \ + NUT_DRIVER_ENUMERATOR_CONF="${NUT_CONFPATH}/nut-driver-enumerator.conf" + +[ -s "${NUT_DRIVER_ENUMERATOR_CONF}" ] && \ + echo "Sourcing config file: ${NUT_DRIVER_ENUMERATOR_CONF}" && \ + . "${NUT_DRIVER_ENUMERATOR_CONF}" + +[ -z "${UPSCONF-}" ] && \ + UPSCONF="${NUT_CONFPATH}/ups.conf" + +# Start a freshly-registered unit? +[ -z "${AUTO_START-}" ] && AUTO_START=yes + +# We avoid regex '\t' which gets misinterpreted by some tools +TABCHAR="`printf '\t'`" || TABCHAR=' ' + +if [ -z "${SERVICE_FRAMEWORK-}" ] ; then + [ -x /usr/sbin/svcadm ] && [ -x /usr/sbin/svccfg ] && [ -x /usr/bin/svcs ] && \ + SERVICE_FRAMEWORK="smf" + [ -z "${SERVICE_FRAMEWORK-}" ] && \ + [ -x /bin/systemctl ] && \ + SERVICE_FRAMEWORK="systemd" +fi + +# Cache needed bits of ups.conf to speed up later parsing. Note that these +# data are needed for most operations, and populated by upslist_readFile() +UPSCONF_DATA="" + +# List of configured UPSes in the config-file +UPSLIST_FILE="" +# List of configured service instances for UPS drivers +UPSLIST_SVCS="" +hook_registerInstance="" +hook_unregisterInstance="" +hook_listInstances="" +hook_listInstances_raw="" +hook_validInstanceName="" +hook_validFullUnitName="" +hook_validInstanceSuffixName="" +hook_restart_upsd="" + +case "${SERVICE_FRAMEWORK-}" in + smf) + hook_registerInstance="smf_registerInstance" + hook_unregisterInstance="smf_unregisterInstance" + hook_listInstances="smf_listInstances" + hook_listInstances_raw="smf_listInstances_raw" + hook_validInstanceName="smf_validInstanceName" + hook_validFullUnitName="smf_validFullUnitName" + hook_validInstanceSuffixName="smf_validInstanceSuffixName" + hook_restart_upsd="smf_restart_upsd" + ;; + systemd) + hook_registerInstance="systemd_registerInstance" + hook_unregisterInstance="systemd_unregisterInstance" + hook_listInstances="systemd_listInstances" + hook_listInstances_raw="systemd_listInstances_raw" + hook_validInstanceName="systemd_validInstanceName" + hook_validFullUnitName="systemd_validFullUnitName" + hook_validInstanceSuffixName="systemd_validInstanceSuffixName" + hook_restart_upsd="systemd_restart_upsd" + ;; + "") + echo "Error detecting the service-management framework on this OS" >&2 + exit 1 + ;; + *) + echo "Error: User provided an unknown service-management framework '$SERVICE_FRAMEWORK'" >&2 + exit 1 + ;; +esac + +common_isFiled() { + [ -n "$UPSLIST_FILE" ] && \ + for UPSF in $UPSLIST_FILE ; do + [ "$1" = "$UPSF" ] && return 0 + [ "`$hook_validInstanceName "$UPSF"`" = "$1" ] && return 0 + done + return 1 +} + +common_isRegistered() { + [ -n "$UPSLIST_SVCS" ] && \ + for UPSS in $UPSLIST_SVCS ; do + [ "$1" = "$UPSS" ] && return 0 + [ "`$hook_validInstanceName "$1"`" = "$UPSS" ] && return 0 + done + return 1 +} + +upslist_equals() { + # Compare pre-sorted list of DEVICES ($1) and SVCINSTs ($2) including + # the possible mangling for service names. Return 0 if lists describe + # exactly same set of devices and their services. + # FIXME: This currently only checks the names, not the contents of + # the sections, so re-definitions of an existing device configuration + # would not trigger a service restart at this time. Such deeper check + # belongs in a different routine really, and requires a way to track + # old and new state of configs (maybe hashed upsconf_getSection output + # as a service configuration variable?) on all supported frameworks. + + # Trivial case 1: equal strings + [ "$1" = "$2" ] && return 0 + # Trivial case 2: different amount of entries + [ "`echo "$1" | wc -l`" = "`echo "$2" | wc -l`" ] || return $? + + _TMP_DEV_SVC="" + for _DEV in $1 ; do + for _SVC in $2 ; do + [ "$_DEV" = "$_SVC" ] \ + || [ "`$hook_validInstanceName "$_DEV"`" = "$_SVC" ] \ + && { [ -z "$_TMP_DEV_SVC" ] \ + && _TMP_DEV_SVC="$_DEV = $_SVC" \ + || _TMP_DEV_SVC="$_TMP_DEV_SVC +$_DEV = $_SVC" ; } + done + done + + # Exit code : is the built mapping as long as the source list(s)? + [ "`echo "$1" | wc -l`" = "`echo "$_TMP_DEV_SVC" | wc -l`" ] +} + +upsconf_getSection() { + # "$1" = name of ups.conf section to display in whole + # + # NOTE (TODO?): This routine finds the one NUT device section, prints it + # and returns when the section is over. It currently does not cover (in + # a way allowing to do it efficiently) selection of several sections, + # or storing each section content in some array or dynamic variables + # (as would be better fit for portable shells) to later address them + # quickly without re-parsing the file or big envvar many times. + # + [ -n "$1" ] || return $? + + if [ -n "$UPSCONF" ] && [ -f "$UPSCONF" ] && [ -r "$UPSCONF" ]; then + [ ! -s "$UPSCONF" ] \ + && echo "WARNING: The '$UPSCONF' file exists but is empty" >&2 \ + && return 3 + else + echo "FATAL: The '$UPSCONF' file does not exist or is not readable" >&2 + return 2 + fi + + CURR_SECTION="" + RES=1 + while read LINE ; do + case "$LINE" in + \["$1"\]) + if [ "$RES" = 0 ]; then + # We have already displayed a section, here is a new one, + # and this routine only displays one (TODO: toggle?) + return 0 + fi + CURR_SECTION="$1" ; echo "$LINE"; RES=0 ;; + \[*\]) + [ "$CURR_SECTION" = "$1" ] && break + CURR_SECTION="other" + ;; + "") ;; + *) if [ "$CURR_SECTION" = "$1" ]; then + echo "$LINE" + fi + ;; + esac + done < "$UPSCONF" + + [ "$RES" = 0 ] || echo "ERROR: Section [$1] was not found in the '$UPSCONF' file" >&2 + return $RES +} + +upsconf_getValue() { + # "$1" = name of ups.conf section; $2 = name of config key; echo the value + [ -n "$1" ] || return $? + [ -n "$2" ] || return $? + CURR_SECTION="" + RES=0 + LINE="`upsconf_getSection "$1" | egrep '^[ \t]*'"$2"'[ \t]*='`" || RES=$? + [ "$RES" = 0 ] || echo "ERROR: Section [$1] or key '$2' in it was not found in the '$UPSCONF' file" >&2 + + VALUE="$(echo "$LINE" | sed -e 's,^[ '"$TABCHAR"']*'"$2[ $TABCHAR]"'*=[ '"$TABCHAR"']*\(.*\)['"$TABCHAR"'\ ]*.*$,\1,' -e 's,^\"\(.*\)\"$,\1,' -e "s,^'\(.*\)'$,\1,")" || RES=$? + echo "$VALUE" + + [ "$RES" = 0 ] || echo "ERROR: Section [$1] or key '$2' in it was not found in the '$UPSCONF' file" >&2 + return $RES +} + +upsconf_getDriver() { + # "$1" = name of ups.conf section; return (echo) the driver name used there + # In the context this function is used, UPSCONF exists and section is there + upsconf_getValue "$1" "driver" + return $? +} + +upsconf_getPort() { + # "$1" = name of ups.conf section; return (echo) the "port" name used there + # In the context this function is used, UPSCONF exists and section is there + upsconf_getValue "$1" "port" + return $? +} + +upsconf_getDriverMedia() { + # "$1" = name of ups.conf section; return (echo) name and type of driver as + # needed for dependency evaluation (what services we must depend on for this + # unit), newline-separated (drvnametype). Empty type for unclassified + # results, assuming no known special dependencies (note that depending on + # particular system's physics, both serial and network media may need USB). + CURR_DRV="`upsconf_getDriver "$1"`" || return $? + case "$CURR_DRV" in + *netxml*|*snmp*|*ipmi*|*powerman*|*-mib*|*avahi*|*apcupsd*) + printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + *usb*) + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; + *dummy*|*clone*) # May be networked (proxy to remote NUT) + CURR_PORT="`upsconf_getPort "$1"`" || CURR_PORT="" + case "$CURR_PORT" in + *@localhost|*@|*@127.0.0.1|*@::1) + printf '%s\n%s\n' "$CURR_DRV" "network-localhost" ; return ;; + *@*) + printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + *) + printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac + ;; + *) printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac +} + +upsconf_getMedia() { + _DRVMED="`upsconf_getDriverMedia "$1"`" || return + echo "$_DRVMED" | tail -n +2 + return 0 +} + +upsconf_debug() { + _DRV="`upsconf_getDriver "$1"`" + _PRT="`upsconf_getPort "$1"`" + _MED="`upsconf_getMedia "$1"`" + echo "INST: [$1]: DRV='$_DRV' PORT='$_PRT' MEDIA='$_MED'" +} + +calc_md5() { + _MD5="" + _MD5="`echo "$1" | md5sum 2>/dev/null | awk '{print $1}'`" && [ -n "$_MD5" ] || \ + { _MD5="`echo "$1" | openssl dgst -md5 2>/dev/null | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + return 1 + echo "$_MD5" +} + +smf_validFullUnitName() { + case "$1" in + *:*) echo "$1" ;; + *) echo "$SVCNAME_SMF:$1" ;; + esac +} +smf_validInstanceName() { + echo "MD5_`calc_md5 "$1"`" +} +smf_validInstanceSuffixName() { + case "$1" in + *:*) echo "$1" | sed 's,^.*:\([^:]*\)$,\1,' ;; + *) echo "$1" ;; + esac +} +smf_registerInstance() { + DEVICE="$1" + SVCINST="$1" + /usr/sbin/svccfg -s nut-driver add "$DEVICE" || \ + { SVCINST="`smf_validInstanceName "$1"`" && \ + /usr/sbin/svccfg -s nut-driver add "$SVCINST" || return ; } + echo "Added instance: 'nut-driver:$SVCINST' for NUT configuration section '$DEVICE'" >&2 + + DEPSVC="" + DEPREQ="" + _MED="`upsconf_getMedia "$DEVICE"`" + case "$_MED" in + usb) + DEPSVC="$DEPSVC_USB_SMF" + DEPREQ="$DEPREQ_USB_SMF" ;; + network-localhost) + DEPSVC="$DEPSVC_NET_LOCAL_SMF" + DEPREQ="$DEPREQ_NET_LOCAL_SMF" ;; + network) + DEPSVC="$DEPSVC_NET_FULL_SMF" + DEPREQ="$DEPREQ_NET_FULL_SMF" ;; + '') ;; + *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + esac + + TARGET_FMRI="nut-driver:$SVCINST" + if [ -n "$DEPSVC" ]; then + [ -n "$DEPREQ" ] || DEPREQ="optional_all" + echo "Adding '$DEPREQ' dependency for '$SVCINST' on '$DEPSVC'..." + + DEPPG="nut-driver-enumerator-generated" + RESTARTON="refresh" + /usr/sbin/svccfg -s "$TARGET_FMRI" addpg "$DEPPG" dependency && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/grouping = astring: "$DEPREQ" && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/restart_on = astring: "$RESTARTON" && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/type = astring: service && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/entities = fmri: "($DEPSVC)" && \ + echo "OK" || echo "FAILED to define the dependency" >&2 + fi + + /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return + if [ "$AUTO_START" = yes ] ; then + /usr/sbin/svcadm clear "${TARGET_FMRI}" 2>/dev/null || true + /usr/sbin/svcadm enable "${TARGET_FMRI}" || return + echo "Started instance: 'nut-driver:$SVCINST' for NUT configuration section '$DEVICE'" >&2 + fi +} +smf_unregisterInstance() { + echo "Removing instance: 'nut-driver:$1' ..." >&2 + /usr/sbin/svcadm disable -ts 'nut-driver:'"$1" || false + /usr/sbin/svccfg -s nut-driver delete "$1" +} +smf_listInstances_raw() { + # Newer versions have pattern matching; older SMF might not have this luxury + /usr/bin/svcs -a -H -o fmri | egrep '/nut-driver:' +} +smf_listInstances() { + smf_listInstances_raw | sed 's/^.*://' | sort -n +} +smf_restart_upsd() { + echo "Restarting NUT data server to make sure it knows new configuration..." + /usr/sbin/svcadm clear "nut-server" 2>/dev/null + /usr/sbin/svcadm restart "nut-server" +} + +systemd_validFullUnitName() { + case "$1" in + *@*) echo "$1" ;; + *) echo "$SVCNAME_SYSTEMD@$1.service" ;; + esac +} +systemd_validInstanceName() { + echo "MD5_`calc_md5 "$1"`" +} +systemd_validInstanceSuffixName() { + echo "$1" | sed -e 's,^.*@,,' -e 's,\.service$,,' +} +systemd_registerInstance() { + DEVICE="$1" + SVCINST="$1" + /bin/systemctl enable 'nut-driver@'"$DEVICE" || \ + { SVCINST="`systemd_validInstanceName "$1"`" && \ + /bin/systemctl enable 'nut-driver@'"$SVCINST" || return ; } + echo "Enabled instance: 'nut-driver@$SVCINST' for NUT configuration section '$DEVICE'" >&2 + + DEPSVC="" + DEPREQ="" + _MED="`upsconf_getMedia "$DEVICE"`" + case "$_MED" in + usb) + DEPSVC="$DEPSVC_USB_SYSTEMD" + DEPREQ="$DEPREQ_USB_SYSTEMD" ;; + network-localhost) + DEPSVC="$DEPSVC_NET_LOCAL_SYSTEMD" + DEPREQ="$DEPREQ_NET_LOCAL_SYSTEMD" ;; + network) + DEPSVC="$DEPSVC_NET_FULL_SYSTEMD" + DEPREQ="$DEPREQ_NET_FULL_SYSTEMD" ;; + '') ;; + *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + esac + if [ -n "$DEPSVC" ]; then + [ -n "$DEPREQ" ] || DEPREQ="#Wants" + echo "Adding '$DEPREQ'+After dependency for '$SVCINST' on '$DEPSVC'..." + mkdir -p "/etc/systemd/system/nut-driver@$1.service.d" && \ + cat > "/etc/systemd/system/nut-driver@$SVCINST.service.d/nut-driver-enumerator-generated.conf" <&2 + fi + + if [ "$AUTO_START" = yes ] ; then + /bin/systemctl start --no-block 'nut-driver@'"$SVCINST" || return + echo "Started instance: 'nut-driver@$SVCINST' for NUT configuration section '$DEVICE'" >&2 + fi +} +systemd_unregisterInstance() { + echo "Removing instance: 'nut-driver@$1' ..." >&2 + /bin/systemctl stop 'nut-driver@'"$1" || false + /bin/systemctl disable 'nut-driver@'"$1" + rm -rf "/etc/systemd/system/nut-driver@$1.service.d" +} +systemd_listInstances_raw() { + /bin/systemctl show 'nut-driver@*' -p Id | egrep '=nut-driver' | sed 's,^Id=,,' +} +systemd_listInstances() { + systemd_listInstances_raw | sed -e 's/^.*@//' -e 's/\.service$//' | sort -n +} +systemd_restart_upsd() { + echo "Restarting NUT data server to make sure it knows new configuration..." + /bin/systemctl restart "nut-server" +} + +upslist_readFile() { + # Read the ups.conf file and find all defined sections (names of + # configuration blocks for drivers that connect to a certain device + # using specified protocol and media) + if [ -n "$UPSCONF" ] && [ -f "$UPSCONF" ] && [ -r "$UPSCONF" ]; then + [ -s "$UPSCONF" ] && \ + UPSCONF_DATA="$(egrep '[\[\=]' < "$UPSCONF" | sed -e 's,^['"$TABCHAR"'\ ]*,,' -e 's,^\#.*$,,' -e 's,['"$TABCHAR"'\ ]*$,,' -e 's,['"$TABCHAR"'\ ]*=['"$TABCHAR"'\ ]*,=,g' -e 's,=\"\([^\ '"$TABCHAR"']*\)\"$,=\1,' | egrep '^(\[.*\]|driver=|port=)')" \ + || UPSCONF_DATA="" + else + echo "FATAL: The '$UPSCONF' file does not exist or is not readable" >&2 + return 2 + fi + + [ -n "$UPSCONF_DATA" ] && \ + UPSLIST_FILE="$(echo "$UPSCONF_DATA" | egrep '^\[.*\]$' | sed 's,^\[\(.*\)\]$,\1,' | sort -n)" \ + || UPSLIST_FILE="" + if [ -z "$UPSLIST_FILE" ] ; then + echo "Error reading the '$UPSCONF' file or it does not declare any device configurations" >&2 + # Ok to continue - we may end up removing all instances + fi +} + +upslist_readSvcs() { + UPSLIST_SVCS="`$hook_listInstances`" || UPSLIST_SVCS="" + if [ -z "$UPSLIST_SVCS" ] && [ "$1" != "-" ] ; then + EXPLAIN="" + [ -z "$1" ] || EXPLAIN=" - $1" + echo "Error reading the list of ${SERVICE_FRAMEWORK-} service instances for UPS drivers, or none are defined${EXPLAIN}" >&2 + # Ok to continue - we may end up defining new instances + fi +} + +upslist_debug() { + for UPSF in $UPSLIST_FILE ; do + upsconf_debug "$UPSF" + done +} + +upslist_addSvcs() { + # FIXME: Support redefined pre-existing sections - update state data... + for UPSF in $UPSLIST_FILE ; do + if ! common_isRegistered "$UPSF" ; then + echo "Adding new ${SERVICE_FRAMEWORK} service instance for power device [${UPSF}]..." >&2 + $hook_registerInstance "$UPSF" + fi + done +} + +upslist_delSvcs() { + for UPSS in $UPSLIST_SVCS ; do + if ! common_isFiled "$UPSS" ; then + echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] which is no longer in config file..." >&2 + $hook_unregisterInstance "$UPSS" + fi + done +} + +nut_driver_enumerator_main() { + ################# MAIN PROGRAM by default + + upslist_readFile || exit $? + #upslist_debug + upslist_readSvcs "before manipulations" + + # Quickly exit if there's nothing to do; note the lists are pre-sorted + # Otherwise a non-zero exit will be done below + # FIXME: Implement testing in detail whether section definitions were + # changed since last run, as a second step after checking that name + # lists are still the same. + upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" && return 0 + + if [ -n "$UPSLIST_FILE" ]; then + upslist_addSvcs + upslist_readSvcs "after checking for new config sections to define service instances" + fi + + if [ -n "$UPSLIST_SVCS" ]; then + upslist_delSvcs + fi + + upslist_readSvcs + if [ -n "$UPSLIST_SVCS" ] ; then + echo "=== The currently defined service instances are:" + echo "$UPSLIST_SVCS" + fi + + if [ -n "$UPSLIST_FILE" ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" + echo "$UPSLIST_FILE" + fi + + # We had some changes to the config file; upsd must be made aware + if [ "$AUTO_START" = yes ] ; then + $hook_restart_upsd + fi + + # Return 42 if there was a change applied succesfully + # (but e.g. some services should restart - upsd, maybe upsmon) + if upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" ; then + [ "${REPORT_RESTART_42-}" = no ] && return 0 || return 42 + fi + return 13 +} + +# By default, update wrapping of devices into services +if [ $# = 0 ]; then + nut_driver_enumerator_main ; exit $? +fi + +usage() { + cat << EOF +$0 (no args) + Update wrapping of devices into services +$0 --get-service-framework + Print the detected service + management framework in this OS +$0 --list-devices + Print list of devices in NUT config +$0 --list-services + Print list of service instances which wrap registered + NUT devices (full name of service unit) +$0 --list-instances + Print list of service instances which wrap registered + NUT devices (just instance suffix) +$0 --get-service-for-device DEV + Print the full name of service unit which wraps a NUT + device named "DEV" +$0 --get-device-for-service SVC + Print the NUT device name for full or instance-suffix name of + a service unit which wraps it +$0 --list-services-for-devices + Print a TAB-separated list of service units and corresponding + NUT device names which each such unit wraps +$0 --show-config DEV +$0 --show-device-config DEV + Show configuration block of the specified NUT device +$0 --show-device-config-value DEV KEY + Show single configuration key of the specified NUT device +EOF +} + +while [ $# -gt 0 ]; do + case "$1" in + --help|-h|-help) usage; exit 0 ;; + --get-service-framework) echo "${SERVICE_FRAMEWORK}" ; exit 0 ;; + --list-devices) + upslist_readFile && \ + if [ -n "$UPSLIST_FILE" ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" >&2 + echo "$UPSLIST_FILE" + exit 0 + fi + echo "No devices detected in '$UPSCONF'" >&2 + exit 1 + ;; + --list-services) + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && \ + if [ -n "$UPSLIST_SVCS_RAW" ] ; then + echo "=== The currently defined service units are:" >&2 + echo "$UPSLIST_SVCS_RAW" + exit 0 + fi + echo "No service units detected" >&2 + exit 1 + ;; + --list-instances) + upslist_readSvcs "by user request" && \ + if [ -n "$UPSLIST_SVCS" ] ; then + echo "=== The currently defined service instances are:" >&2 + echo "$UPSLIST_SVCS" + exit 0 + fi + echo "No service instances detected" >&2 + exit 1 + ;; + --get-service-for-device) [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 + DEV="$2" + upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ + || { echo "No service instances detected" >&2 ; exit 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ + || { echo "No service units detected" >&2 ; exit 1; } + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + echo "$UNIT" + exit 0 + fi + done + fi + done + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + echo "$UNIT" + exit 0 + fi + done + fi + done + echo "No service instances detected that match device '$2'" >&2 + exit 1 + ;; + --get-device-for-service) [ -z "$2" ] && echo "Service (instance) name argument required" >&2 && exit 1 + # Instance name can be a hash or "native" NUT section name + SVC="`$hook_validInstanceSuffixName "$2"`" + case "$SVC" in + MD5_*) ;; # fall through to the bulk of code + *) echo "$SVC" ; exit 0 ;; + esac + FINAL_RES=0 + OUT="`"$0" --list-services-for-devices`" && [ -n "$OUT" ] || FINAL_RES=$? + if [ "$FINAL_RES" = 0 ]; then + echo "$OUT" | grep "$SVC" | ( \ + while read _SVC _DEV ; do + _SVC="`$hook_validInstanceSuffixName "${_SVC}"`" || exit + [ "${_SVC}" = "${SVC}" ] && echo "$_DEV" && exit 0 + done ; exit 1 ) && exit 0 + fi + echo "No service instance '$2' was detected that matches a NUT device" >&2 + exit 1 + ;; + --list-services-for-devices) + FINAL_RES=0 + upslist_readFile && [ -n "$UPSLIST_FILE" ] \ + || { echo "No devices detected in '$UPSCONF'" >&2 ; exit 1 ; } + upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ + || { echo "No service instances detected" >&2 ; exit 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ + || { echo "No service units detected" >&2 ; exit 1; } + for DEV in $UPSLIST_FILE ; do + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + echo "WARNING: no service instances detected that match device '$DEV'" >&2 + FINAL_RES=1 + done + exit $FINAL_RES + ;; + --show-config|--show-device-config) + [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 + DEV="$2" + upsconf_getSection "$DEV" + exit $? + ;; + --show-config-value|--show-device-config-value) + [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 + [ -z "$3" ] && echo "Configuration key name argument required" >&2 && exit 1 + DEV="$2" + KEY="$3" + upslist_readFile && \ + upsconf_getValue "$DEV" "$KEY" + exit $? + ;; + *) echo "Unrecognized argument: $1" >&2 ; exit 1 ;; + esac + shift +done diff --git a/scripts/upsdrvsvcctl/upsdrvsvcctl.in b/scripts/upsdrvsvcctl/upsdrvsvcctl.in new file mode 100755 index 0000000000..ac71ddf67e --- /dev/null +++ b/scripts/upsdrvsvcctl/upsdrvsvcctl.in @@ -0,0 +1,184 @@ +#!/bin/sh +# +# Copyright (C) 2016-2018 Eaton +# +# 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 2 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +#! \file upsdrvsvcctl(.in) +# \author Jim Klimov +# \brief Manage NUT devices registered as service-unit instances +# + +if [ -z "${SERVICE_FRAMEWORK-}" ] ; then + [ -x /usr/sbin/svcadm ] && [ -x /usr/sbin/svccfg ] && [ -x /usr/bin/svcs ] && \ + SERVICE_FRAMEWORK="smf" + [ -z "${SERVICE_FRAMEWORK-}" ] && \ + [ -x /bin/systemctl ] && \ + SERVICE_FRAMEWORK="systemd" +fi + +VERB="" +CMD="" +CMDARG="" +ENUMERATOR="" +case "$SERVICE_FRAMEWORK" in + smf) CMD="/usr/sbin/svcadm" + ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" + ;; + systemd) CMD="/bin/systemctl" + ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" + ;; + *) echo "Unrecognized SERVICE_FRAMEWORK: $SERVICE_FRAMEWORK" >&2 ; exit ;; +esac + + +usage() { + # Note: version header differs from UPS_VERSION in binaries that + # might also have the git-version suffixed during build time + cat << EOF +Network UPS Tools - UPS driver controller ${PACKAGE_VERSION} +Starts and stops UPS drivers via system service instances, see +the $ENUMERATOR +script for more details. + +usage: $0 [OPTIONS] (start | stop | shutdown) [] + +Options: + -h display this help + -t testing mode - prints actions without doing them + -D raise debugging level + start start all UPS drivers in ups.conf + start only start driver for UPS + stop stop all UPS drivers in ups.conf + stop only stop driver for UPS + +Note: the "shutdown" options from original upsdrvctl are not currently +directly supported by this service management framework wrapper; instead +they are passed to the native upsdrvctl binary (your current user account +should have sufficient permissions to do that all): + shutdown shutdown all UPS drivers in ups.conf + shutdown only shutdown UPS + +usage: $0 [OPTIONS] resync + resync call $ENUMERATOR + to update the mapping of service instances for + NUT drivers to device sections in 'ups.conf' + list call $ENUMERATOR + to list the mapping of service instances to device sections + list (optionally return the service instance name for one device) + show-config output config section from ups.conf for device +EOF +} + +ACTION="" +SVCINST="" +DRYRUN="" +DEBUG=0 +# Note: DEBUG is UNUSED_PARAM so far +while [ $# -gt 0 ]; do + case "$1" in + resync) eval $DRYRUN $ENUMERATOR ; exit $? ;; + list) + if [ -n "$2" ] ; then + eval $ENUMERATOR --get-service-for-device "$2" ; exit $? + else + eval $ENUMERATOR --list-services-for-devices ; exit $? + fi + ;; + show-config) + if [ -n "$2" ] ; then + eval $ENUMERATOR --show-device-config "$2" ; exit $? + else + echo "ERROR: Argument required" >&2; exit 1 + fi + ;; + start|stop) + ACTION="$1" + if [ -n "$2" ] ; then + SVCINST="`$ENUMERATOR --get-service-for-device "$2"`" || exit + shift + fi + ;; + shutdown) + echo "NOTE: Action '$1' is not implemented via services currently, will call upsdrvctl" >&2 + echo "Stopping the driver service instance(s) to release exclusive resources, if any..." >&2 + RES=0 + $0 stop $2 + @SBINDIR@/upsdrvctl shutdown $2 || RES=$? + echo "Starting the driver service instance(s) so they can reconnect when the UPS returns..." >&2 + $0 start $2 + exit $RES + ;; + -t) DRYRUN="echo" ;; + -h) usage; exit 0 ;; + -d) DEBUG="`expr $DEBUG + 1`" ;; + -r|-u) echo "Option '$1 $2' is not implemented via services currently" >&2 ; shift;; + *) echo "Unrecognized argument: $1" >&2 ; exit ;; + esac + shift +done + +if [ -z "$ENUMERATOR" ] || [ ! -s "$ENUMERATOR" ] || [ ! -x "$ENUMERATOR" ] ; then + echo "ENUMERATOR script (nut-driver-enumerator.sh) not found!" >&2 + exit 1 +fi + +if [ -z "$ACTION" ]; then + echo "No action was requested!" >&2 + exit 1 +fi + +if [ -z "$SVCINST" ]; then + SVCINST="`$ENUMERATOR --list-services`" || exit +fi + +# TODO: Support shutdown of one or all UPSes by stopping its service +# and then calling the original upsdrvctl on it? +case "$ACTION" in + start) + VERB="Starting" + case "$SERVICE_FRAMEWORK" in + smf) CMDARG="enable -t" ;; + systemd) CMDARG="start" ;; + esac + ;; + stop) + VERB="Stopping" + case "$SERVICE_FRAMEWORK" in + smf) CMDARG="disable -t" ;; + systemd) CMDARG="stop" ;; + esac + ;; + *) echo "Unrecognized ACTION: $ACTION" >&2 ; exit ;; +esac + +for INST in $SVCINST ; do + echo "$VERB $INST ..." >&2 + $DRYRUN $CMD $CMDARG "$INST" & +done +wait + +case "$SERVICE_FRAMEWORK" in + smf) + sleep 1 + echo "Post-process clearing services that failed early..." >&2 + for INST in $SVCINST ; do + echo "Clearing $INST (if it got broken) ..." >&2 + $DRYRUN $CMD clear "$INST" & + done + ;; +esac + +wait diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 9fea9276c4..8b41a3dbf9 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -246,8 +246,22 @@ int main(int argc, char *argv[]) xml_sec.usec_timeout = -1; /* Override with the "timeout" common setting later */ xml_sec.peername = NULL; + /* Parse command line options -- First loop: only get debug level */ + /* Suppress error messages, for now -- leave them to the second loop. */ + opterr = 0; + while((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) + if (opt_ret == 'D') + nut_debug_level++; + + nutscan_init(); + display_func = nutscan_display_ups_conf; + /* Parse command line options -- Second loop: everything else */ + /* Restore error messages... */ + opterr = 1; + /* ...and index of the item to be processed by getopt(). */ + optind = 1; /* Note: the getopts print an error message about unknown arguments * or arguments which need a second token and that is missing now */ while((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL))!=-1) { @@ -278,7 +292,7 @@ int main(int argc, char *argv[]) cidr = strdup(optarg); break; case 'D': - nut_debug_level++; + /* nothing to do, here */ break; case 'c': if(!nutscan_avail_snmp) { @@ -466,12 +480,6 @@ int main(int argc, char *argv[]) /* BEWARE: allow_all does not include allow_eaton_serial! */ } - /* TODO: nutscan_init() should consider (via args? shared global vars?) - * which scan types we desire at this run, and not try to load irrelevant - * libraries. - */ - nutscan_init(); - /* TODO/discuss : Should the #else...#endif code below for lack of pthreads * during build also serve as a fallback for pthread failure at runtime? */