Skip to content

Add support for STATEPATH via ups.conf #2739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/nut-driver@.service
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
/nut-driver@.service
/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 += \
nut-driver@.service.in 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
Loading