Skip to content

Commit

Permalink
Merge pull request networkupstools#2739 from jimklimov/issue-694
Browse files Browse the repository at this point in the history
Add support for STATEPATH via ups.conf
  • Loading branch information
jimklimov authored Jan 1, 2025
2 parents 4e9eb9a + 5817cec commit bdc139d
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 14 deletions.
4 changes: 4 additions & 0 deletions NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,10 @@ relocated into new `shutdown.default` INSTCMD definitions. [#2670]
by the `configure` script during build, but can override it with a
`NUT_PIDPATH` environment variable in certain use-cases (such as
tests). [#2407]
* allow drivers to set `STATEPATH` via `ups.conf` to match `upsd`
custom configuration ability; the data server would prefer the value
from `ups.conf` over the one in `upsd.conf`, if both are present.
Note that `NUT_STATEPATH` environment variable trumps both. [issue #694]
* introduced a check for daemons working with PID files to double-check
that if they can resolve the program name of a running process with
this identifier, that such name matches the current program (avoid
Expand Down
5 changes: 5 additions & 0 deletions UPGRADING.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ Changes from 2.8.2 to 2.8.3
a Git workspace, not tarball archives) may have to set it to `false` when
calling `make` for NUT. [#2510]
- Drivers should now be able to set `STATEPATH` via `ups.conf` to match `upsd`
custom configuration ability; in fact, the data server would prefer the
value from `ups.conf` over the one in `upsd.conf`, if both are present.
Note that `NUT_STATEPATH` environment variable trumps both. [issue #694]
- NUT products like `nut-scanner`, which dynamically load shared libraries
at run-time without persistent pre-linking, should now know the library
file names that were present during build (likely encumbered with version
Expand Down
21 changes: 17 additions & 4 deletions common/nutconf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ bool GenericConfiguration::writeTo(NutStream & ostream) const
}


bool GenericConfiguration::get(const std::string & section, const std::string & entry, ConfigParamList & params) const
bool GenericConfiguration::get(const std::string & section, const std::string & entry, ConfigParamList & params, bool caseSensitive) const
{
// Get section
SectionMap::const_iterator section_iter = sections.find(section);
Expand All @@ -938,9 +938,22 @@ bool GenericConfiguration::get(const std::string & section, const std::string &
const GenericConfigSection::EntryMap & entries = section_iter->second.entries;

GenericConfigSection::EntryMap::const_iterator entry_iter = entries.find(entry);
if (entry_iter == entries.end())
if (entry_iter == entries.end()) {
if (caseSensitive)
return false;

// Another pass, maybe slower and inefficient, for case-insensitive matching
// We are already at one end of the entries, so scroll back to beginning
GenericConfigSection::EntryMap::const_iterator entry_begin = entries.begin();
for (; entry_iter != entry_begin; entry_iter--) {
if (!(::strcasecmp(entry_iter->first.c_str(), entry.c_str())))
goto found;
}

return false;
}

found:
// Provide parameters values
params = entry_iter->second.values;

Expand Down Expand Up @@ -1025,13 +1038,13 @@ void GenericConfiguration::removeSection(const std::string & section)
}


std::string GenericConfiguration::getStr(const std::string & section, const std::string & entry) const
std::string GenericConfiguration::getStr(const std::string & section, const std::string & entry, bool caseSensitive) const
{
std::string str;

ConfigParamList params;

if (!get(section, entry, params))
if (!get(section, entry, params, caseSensitive))
return str;

if (params.empty())
Expand Down
9 changes: 9 additions & 0 deletions conf/ups.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@
#
# driverpath: OPTIONAL. Used for custom setups. See man page for details.
#
# statepath: OPTIONAL. Used for custom setups. Tell drivers to place their
# state sockets for communication with 'upsd' in 'path' rather
# than the default location that was compiled into the program.
# Note that the drivers must use the same path as 'upsd', so the
# data server would prefer this setting from `ups.conf` global
# section, if present, over its own in `upsd.conf`.
# Environment variable NUT_STATEPATH set by caller can override
# this setting.
#
# nowait: OPTIONAL. Tell upsdrvctl to not wait at all for the driver(s)
# to execute the requested command. Fire and forget.
#
Expand Down
3 changes: 3 additions & 0 deletions conf/upsd.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
#
# Tell upsd to look for the driver state sockets in 'path' rather
# than the default that was compiled into the program.
# Note that the drivers must use the same path, so `upsd` would prefer the
# same-named setting from `ups.conf` global section, if present, over its own.
# Environment variable NUT_STATEPATH set by caller can override this setting.

# =======================================================================
# LISTEN <IP address or name> [<port>]
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5544,6 +5544,7 @@ AC_CONFIG_FILES([
scripts/python/module/setup.py
scripts/upsdrvsvcctl/Makefile
scripts/systemd/Makefile
scripts/systemd/nut.target
scripts/systemd/nut-common-tmpfiles.conf
scripts/systemd/[email protected]
scripts/systemd/nut-monitor.service
Expand Down
13 changes: 13 additions & 0 deletions docs/man/ups.conf.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ Optional. Path name of the directory in which the UPS driver executables
reside. If you don't specify this, the programs look in a built-in default
directory, which is often /usr/local/ups/bin.

*statepath*::

Optional. Path name of the directory in which the UPS drivers should place
their state sockets for local communication with `upsd` data server, rather
than the default location that was compiled into the program, which is often
/var/state/ups.
+
Note that the drivers must use the same path as `upsd`, so the data server
would prefer this setting from `ups.conf` global section, if present, over
its own in `upsd.conf`.
+
Environment variable `NUT_STATEPATH` set by caller can override this setting.

*maxstartdelay*::

Optional. Same as the UPS field of the same name, but this is the
Expand Down
6 changes: 6 additions & 0 deletions docs/man/upsd.conf.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ used by init-scripts and service unit method scripts.

Tell upsd to look for the driver state sockets in 'path' rather
than the default that was compiled into the program.
+
Note that the drivers must use the same path, so `upsd` would prefer the
same-named setting from `ups.conf` global section, if present, over its own.
+
Environment variable `NUT_STATEPATH` set by caller (e.g. init script
or service method) can override this setting.

"LISTEN 'interface' 'port'"::

Expand Down
17 changes: 17 additions & 0 deletions drivers/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,23 @@ static void do_global_args(const char *var, const char *val)
return;
}

/* Most ups.conf vars are lower-case, but this one
* is shared with upsd which uses upper-case. */
if (!strcasecmp(var, "STATEPATH")) {
/* Is there a higher-priority envvar? */
char *s = getenv("NUT_STATEPATH");
if (s) {
if (strcmp(s, val)) {
upslogx(LOG_WARNING, "Environment variable NUT_STATEPATH='%s' overrides setting STATEPATH='%s'", s, val);
} /* else be quiet */
} else {
/* Just let any further consumers of dflt_statepath()
* know the desired value with minimal codebase impact
*/
setenv("NUT_STATEPATH", val, 1);
}
}

/* unrecognized */
}

Expand Down
41 changes: 35 additions & 6 deletions include/nutconf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -810,24 +810,26 @@ class GenericConfiguration : public BaseConfiguration, public Serialisable
* \param[in] section Section name
* \param[in] entry Entry name
* \param[out] params Configuration parameters
* \param[in] caseSensitive Use case-sensitive entry name matching? (default: true)
*
* \retval true if the entry was found
* \retval false otherwise
*/
bool get(const std::string & section, const std::string & entry, ConfigParamList & params) const;
bool get(const std::string & section, const std::string & entry, ConfigParamList & params, bool caseSensitive = true) const;

/**
* \brief Global scope configuration parameters getter
*
* \param[in] entry Entry name
* \param[out] params Configuration parameters
* \param[in] caseSensitive Use case-sensitive entry name matching? (default: true)
*
* \retval true if the entry was found
* \retval false otherwise
*/
inline bool get(const std::string & entry, ConfigParamList & params) const
inline bool get(const std::string & entry, ConfigParamList & params, bool caseSensitive = true) const
{
return get("", entry, params);
return get("", entry, params, caseSensitive);
}

/**
Expand Down Expand Up @@ -926,25 +928,50 @@ class GenericConfiguration : public BaseConfiguration, public Serialisable
*
* \param section Section name
* \param entry Entry name
* \param caseSensitive Use case-sensitive entry name matching? (default: true)
*
* \return Configuration parameter as string
*/
std::string getStr(
const std::string & section,
const std::string & entry) const;
const std::string & entry,
bool caseSensitive = true) const;

/**
* \brief Configuration string getter
*
* Empty string is returned if the section or entry doesn't exist.
*
* \param section Section name
* \param entry Entry name (as C char array or string literal)
* \param caseSensitive Use case-sensitive entry name matching? (default: true)
*
* \return Configuration parameter as string
*/
std::string getStr(
const std::string & section,
const char * entry,
bool caseSensitive = true) const
{
std::string sEntry{entry};
return getStr(section, sEntry, caseSensitive);
}

/**
* \brief Global scope configuration string getter
*
* Empty string is returned if the entry doesn't exist.
*
* \param entry Entry name
* \param caseSensitive Use case-sensitive entry name matching? (default: true)
*
* \return Configuration parameter as string
*/
inline std::string getStr(const std::string & entry) const
inline std::string getStr(
const std::string & entry,
bool caseSensitive = true) const
{
return getStr("", entry);
return getStr("", entry, caseSensitive);
}

/**
Expand Down Expand Up @@ -1666,6 +1693,7 @@ class UpsConfiguration : public GenericConfiguration

inline std::string getChroot() const { return getStr("chroot"); }
inline std::string getDriverPath() const { return getStr("driverpath"); }
inline std::string getStatePath() const { return getStr("statepath", false); } // NOTE: accept it case-insensitively
inline std::string getGroup() const { return getStr("group"); }
inline std::string getSynchronous() const { return getStr("synchronous"); }
inline std::string getUser() const { return getStr("user"); }
Expand All @@ -1682,6 +1710,7 @@ class UpsConfiguration : public GenericConfiguration

inline void setChroot(const std::string & path) { setStr("chroot", path); }
inline void setDriverPath(const std::string & path) { setStr("driverpath", path); }
inline void setStatePath(const std::string & path) { setStr("statepath", path); }
inline void setGroup(const std::string & group) { setStr("group", group); }
inline void setSynchronous(const std::string & val) { setStr("synchronous", val); }
inline void setUser(const std::string & user) { setStr("user", user); }
Expand Down
3 changes: 3 additions & 0 deletions scripts/augeas/nutupsconf.aug.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ let ups_global = "chroot"
| "user"
| "group"
| "debug_min"
| "STATEPATH"
| "statepath"

(* This expression did involve a lot of courtship around the parser *)
let ups_fields_re = /(default|override)\.[^:=#\r\t\n \/]+/
Expand All @@ -57,6 +59,7 @@ let ups_fields = "driver"
| "user"
| "group"
| "debug_min"
| "LIBUSB_DEBUG"
@SPECIFIC_DRV_VARS@

let ups_entry = IniFile.indented_entry (ups_global|ups_fields|ups_fields_re) ups_sep ups_comment
Expand Down
1 change: 1 addition & 0 deletions scripts/systemd/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Note: nut-common-tmpfiles.conf.in is also generated, by configure script
/nut-common-tmpfiles.conf.in
/nut-common-tmpfiles.conf
/nut.target
/nut-driver.service
/[email protected]
/nut-monitor.service
Expand Down
4 changes: 2 additions & 2 deletions scripts/systemd/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ endif !WITH_LIBSYSTEMD_INHIBITOR
systemdtmpfiles_DATA = \
nut-common-tmpfiles.conf

EXTRA_DIST += nut-driver.target nut.target nut-sleep.service
EXTRA_DIST += nut-driver.target nut-sleep.service

systemdshutdown_SCRIPTS = nutshutdown

Expand All @@ -35,7 +35,7 @@ sbin_SCRIPTS = ../upsdrvsvcctl/upsdrvsvcctl
else !HAVE_SYSTEMD
EXTRA_DIST += \
[email protected] nut-monitor.service.in nut-sleep.service \
nut-server.service.in nutshutdown.in nut-driver.target nut.target \
nut-server.service.in nutshutdown.in nut-driver.target nut.target.in \
nut-driver-enumerator.path.in nut-driver-enumerator.service.in \
nut-driver-enumerator-daemon-activator.path.in \
nut-driver-enumerator-daemon-activator.service.in \
Expand Down
File renamed without changes.
22 changes: 20 additions & 2 deletions server/conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg)
if (sp && strcmp(sp, arg[1])) {
/* Only warn if the two strings are not equal */
upslogx(LOG_WARNING,
"Ignoring STATEPATH='%s' from configuration file, "
"Ignoring STATEPATH='%s' from upsd.conf configuration file, "
"in favor of NUT_STATEPATH='%s' environment variable",
NUT_STRARG(arg[1]), NUT_STRARG(sp));
}
Expand Down Expand Up @@ -443,8 +443,26 @@ void do_upsconf_args(char *upsname, char *var, char *val)
{
ups_t *temp;

/* no "global" stuff for us */
/* almost no "global" stuff for us */
if (!upsname) {

/* STATEPATH <dir> (may be lower-case) */
if (!strcasecmp(var, "STATEPATH")) {
const char *sp = getenv("NUT_STATEPATH");
if (sp && strcmp(sp, val)) {
/* Only warn if the two strings are not equal */
upslogx(LOG_WARNING,
"Ignoring STATEPATH='%s' from ups.conf configuration file, "
"in favor of NUT_STATEPATH='%s' environment variable",
NUT_STRARG(val), NUT_STRARG(sp));
}
free(statepath);
statepath = xstrdup(sp ? sp : val);
/* This setting source keeps priority
* to best match up with the drivers */
setenv("NUT_STATEPATH", statepath, 1);
}

return;
}

Expand Down

0 comments on commit bdc139d

Please sign in to comment.