diff --git a/clients/upsmon.c b/clients/upsmon.c index 941b36d3b0..ab0245ffe1 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1279,7 +1279,7 @@ static int parse_conf_arg(size_t numargs, char **arg) } /* RUN_AS_USER */ - if (!strcmp(arg[0], "RUN_AS_USER")) { + if (!strcmp(arg[0], "RUN_AS_USER")) { free(run_as_user); run_as_user = xstrdup(arg[1]); return 1; @@ -1393,6 +1393,13 @@ static void loadconfig(void) fatalx(EXIT_FAILURE, "%s", ctx.errmsg); } + if (reload_flag == 1) { + /* if upsmon.conf added or changed + * (or commented away) the debug_min + * setting, detect that */ + nut_debug_level_global = -1; + } + while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", @@ -1419,6 +1426,15 @@ static void loadconfig(void) } } + if (reload_flag == 1) { + if (nut_debug_level_global > -1) { + upslogx(LOG_INFO, + "Applying debug_min=%d from upsmon.conf", + nut_debug_level_global); + nut_debug_level = nut_debug_level_global; + } + } + pconf_finish(&ctx); } @@ -1809,6 +1825,7 @@ static void help(const char *arg_progname) printf(" - fsd: shutdown all primary-mode UPSes (use with caution)\n"); printf(" - reload: reread configuration\n"); printf(" - stop: stop monitoring and exit\n"); + printf(" -P send the signal above to specified PID (bypassing PID file)\n"); printf(" -D raise debugging level (and stay foreground by default)\n"); printf(" -F stay foregrounded even if no debugging is enabled\n"); printf(" -B stay backgrounded even if debugging is bumped\n"); @@ -2042,7 +2059,8 @@ static void check_parent(void) int main(int argc, char *argv[]) { const char *prog = xbasename(argv[0]); - int i, cmd = 0, checking_flag = 0, foreground = -1; + int i, cmd = 0, cmdret = -1, checking_flag = 0, foreground = -1; + pid_t oldpid = -1; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); @@ -2053,7 +2071,7 @@ int main(int argc, char *argv[]) run_as_user = xstrdup(RUN_AS_USER); - while ((i = getopt(argc, argv, "+DFBhic:f:pu:VK46")) != -1) { + while ((i = getopt(argc, argv, "+DFBhic:P:f:pu:VK46")) != -1) { switch (i) { case 'c': if (!strncmp(optarg, "fsd", strlen(optarg))) @@ -2067,6 +2085,12 @@ int main(int argc, char *argv[]) if (cmd == 0) help(argv[0]); break; + + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help(argv[0]); + break; + case 'D': nut_debug_level++; break; @@ -2121,17 +2145,41 @@ int main(int argc, char *argv[]) } if (cmd) { - sendsignal(prog, cmd); - exit(EXIT_SUCCESS); + if (oldpid < 0) { + cmdret = sendsignal(prog, cmd); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } + /* exit(EXIT_SUCCESS); */ + exit((cmdret == 0)?EXIT_SUCCESS:EXIT_FAILURE); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ - if (sendsignal(prog, 0) == 0) { + if (oldpid < 0) { + cmdret = sendsignal(prog, 0); + } else { + cmdret = sendsignalpid(oldpid, 0); + } + switch (cmdret) { + case 0: printf("Fatal error: A previous upsmon instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); + + case -3: + case -2: + upslogx(LOG_WARNING, "Could not %s PID file " + "to see if previous upsmon instance is " + "already running!\n", + (cmdret == -3 ? "find" : "parse")); + break; + + case -1: + default: + /* Just failed to send signal, no competitor running */ + break; } argc -= optind; diff --git a/common/common.c b/common/common.c index 472d15ecac..bb00aafdc9 100644 --- a/common/common.c +++ b/common/common.c @@ -293,7 +293,9 @@ void writepid(const char *name) pidf = fopen(fn, "w"); if (pidf) { - fprintf(pidf, "%d\n", (int) getpid()); + intmax_t pid = (intmax_t)getpid(); + upsdebugx(1, "Saving PID %jd into %s", pid, fn); + fprintf(pidf, "%jd\n", pid); fclose(pidf); } else { upslog_with_errno(LOG_NOTICE, "writepid: fopen %s", fn); @@ -302,61 +304,97 @@ void writepid(const char *name) umask(mask); } -/* open pidfn, get the pid, then send it sig */ -int sendsignalfn(const char *pidfn, int sig) +/* send sig to pid, returns -1 for error, or + * zero for a successfully sent signal + */ +int sendsignalpid(pid_t pid, int sig) { - char buf[SMALLBUF]; - FILE *pidf; - pid_t pid = -1; int ret; - pidf = fopen(pidfn, "r"); - if (!pidf) { - upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn); + if (pid < 2 || pid > get_max_pid_t()) { + upslogx(LOG_NOTICE, + "Ignoring invalid pid number %" PRIdMAX, + (intmax_t) pid); return -1; } - if (fgets(buf, sizeof(buf), pidf) == NULL) { - upslogx(LOG_NOTICE, "Failed to read pid from %s", pidfn); - fclose(pidf); + /* see if this is going to work first */ + ret = kill(pid, 0); + + if (ret < 0) { + perror("kill"); return -1; } - { /* scoping */ - intmax_t _pid = strtol(buf, (char **)NULL, 10); /* assuming 10 digits for a long */ - if (_pid <= get_max_pid_t()) { - pid = (pid_t)_pid; - } else { - upslogx(LOG_NOTICE, "Received a pid number too big for a pid_t: %" PRIdMAX, _pid); + if (sig != 0) { + /* now actually send it */ + ret = kill(pid, sig); + + if (ret < 0) { + perror("kill"); + return -1; } } - if (pid < 2) { - upslogx(LOG_NOTICE, "Ignoring invalid pid number %" PRIdMAX, (intmax_t) pid); - fclose(pidf); - return -1; + return 0; +} + +/* parses string buffer into a pid_t if it passes + * a few sanity checks; returns -1 on error + */ +pid_t parsepid(const char *buf) +{ + pid_t pid = -1; + + /* assuming 10 digits for a long */ + intmax_t _pid = strtol(buf, (char **)NULL, 10); + if (_pid <= get_max_pid_t()) { + pid = (pid_t)_pid; + } else { + upslogx(LOG_NOTICE, "Received a pid number too big for a pid_t: %" PRIdMAX, _pid); } - /* see if this is going to work first */ - ret = kill(pid, 0); + return pid; +} - if (ret < 0) { - perror("kill"); +/* open pidfn, get the pid, then send it sig + * returns negative codes for errors, or + * zero for a successfully sent signal + */ +int sendsignalfn(const char *pidfn, int sig) +{ + char buf[SMALLBUF]; + FILE *pidf; + pid_t pid = -1; + int ret = -1; + + pidf = fopen(pidfn, "r"); + if (!pidf) { + upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn); + return -3; + } + + if (fgets(buf, sizeof(buf), pidf) == NULL) { + upslogx(LOG_NOTICE, "Failed to read pid from %s", pidfn); fclose(pidf); - return -1; + return -2; } + /* TOTHINK: Original code only closed pidf before + * exiting the method, on error or "normally". + * Why not here? Do we want an (exclusive?) hold + * on it while being active in the method? + */ - /* now actually send it */ - ret = kill(pid, sig); + /* this method actively reports errors, if any */ + pid = parsepid(buf); - if (ret < 0) { - perror("kill"); - fclose(pidf); - return -1; + if (pid >= 0) { + /* this method actively reports errors, if any */ + ret = sendsignalpid(pid, sig); } fclose(pidf); - return 0; + return ret; } int snprintfcat(char *dst, size_t size, const char *fmt, ...) diff --git a/conf/upsd.conf.sample b/conf/upsd.conf.sample index 9d6a9cd1f4..fb420e3a78 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -155,3 +155,14 @@ # running mode directly, and without need to edit init-scripts or service # unit definitions. Note that command-line option `-D` can only increase # this verbosity level. +# +# NOTE: if the running daemon receives a `reload` command, presence of the +# `DEBUG_MIN NUMBER` value in the configuration file can be used to tune +# debugging verbosity in the running service daemon (it is recommended to +# comment it away or set the minimum to explicit zero when done, to avoid +# huge journals and I/O system abuse). Keep in mind that for this run-time +# tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +# is applied instantly and overrides any previously set value, from file +# or CLI options, regardless of older logging level being higher or lower +# than the newly found number; a missing (or commented away) value however +# does not change the previously active logging verbosity. diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index dedbb92afb..aeec849eda 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -440,3 +440,14 @@ FINALDELAY 5 # running mode directly, and without need to edit init-scripts or service # unit definitions. Note that command-line option `-D` can only increase # this verbosity level. +# +# NOTE: if the running daemon receives a `reload` command, presence of the +# `DEBUG_MIN NUMBER` value in the configuration file can be used to tune +# debugging verbosity in the running service daemon (it is recommended to +# comment it away or set the minimum to explicit zero when done, to avoid +# huge journals and I/O system abuse). Keep in mind that for this run-time +# tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +# is applied instantly and overrides any previously set value, from file +# or CLI options, regardless of older logging level being higher or lower +# than the newly found number; a missing (or commented away) value however +# does not change the previously active logging verbosity. diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index 40f9786a5d..67e41dbf46 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -135,6 +135,17 @@ Optionally specify a minimum debug level for `upsd` data daemon, e.g. for troubleshooting a deployment, without impacting foreground or background running mode directly. Command-line option `-D` can only increase this verbosity level. ++ +NOTE: if the running daemon receives a `reload` command, presence of the +`DEBUG_MIN NUMBER` value in the configuration file can be used to tune +debugging verbosity in the running service daemon (it is recommended to +comment it away or set the minimum to explicit zero when done, to avoid +huge journals and I/O system abuse). Keep in mind that for this run-time +tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +is applied instantly and overrides any previously set value, from file +or CLI options, regardless of older logging level being higher or lower +than the newly found number; a missing (or commented away) value however +does not change the previously active logging verbosity. SEE ALSO -------- diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 771668a688..3dc2aebcf3 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -43,6 +43,12 @@ are: *reload*;; reread configuration files *stop*;; stop process and exit +*-P* 'pid':: +Send the command signal above using specified PID number, rather than +consulting the PID file. This can help define service units which +start `upsd` as a foreground process so it does not create a PID file. +See also `-FF` option as an alternative. + *-D*:: Raise the debugging level. upsd will run in the foreground by default, and will print information on stdout about the monitoring process. @@ -50,6 +56,7 @@ Use this option multiple times for more details. *-F*:: upsd will run in the foreground, regardless of debugging settings. +Specify twice (`-FF` or `-F -F`) to save the PID file even in this mode. *-B*:: upsd will run in the background, regardless of debugging settings. diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index ac92401a60..8aa44443cc 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -399,6 +399,17 @@ Optionally specify a minimum debug level for `upsmon` daemon, e.g. for troubleshooting a deployment, without impacting foreground or background running mode directly. Command-line option `-D` can only increase this verbosity level. ++ +NOTE: if the running daemon receives a `reload` command, presence of the +`DEBUG_MIN NUMBER` value in the configuration file can be used to tune +debugging verbosity in the running service daemon (it is recommended to +comment it away or set the minimum to explicit zero when done, to avoid +huge journals and I/O system abuse). Keep in mind that for this run-time +tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +is applied instantly and overrides any previously set value, from file +or CLI options, regardless of older logging level being higher or lower +than the newly found number; a missing (or commented away) value however +does not change the previously active logging verbosity. SEE ALSO -------- diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 5006d40e49..2f226d5f42 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -11,7 +11,7 @@ SYNOPSIS *upsmon* -h -*upsmon* -c 'command' +*upsmon* -c 'command' [-P 'pid'] *upsmon* [-D] [-F | -B] [-K] [-p] [-u 'user'] @@ -44,6 +44,12 @@ commands are: *reload*;; reread linkman:upsmon.conf[5] configuration file. See "reloading nuances" below if this doesn't work. +*-P* 'pid':: +Send the command signal above using specified PID number, rather than +consulting the PID file. This can help define service units which +start main `upsmon` as a foreground process so it does not have to +rely on a PID file. + *-D*:: Raise the debugging level. upsmon will run in the foreground by default, and will print information on stdout about the monitoring process. diff --git a/include/common.h b/include/common.h index 67deaff905..7358faeec8 100644 --- a/include/common.h +++ b/include/common.h @@ -103,6 +103,10 @@ void chroot_start(const char *path); /* write a pid file - is a full pathname *or* just the program name */ void writepid(const char *name); +/* parses string buffer into a pid_t if it passes + * a few sanity checks; returns -1 on error */ +pid_t parsepid(const char *buf); + /* send a signal to another running process */ int sendsignal(const char *progname, int sig); @@ -112,7 +116,17 @@ int snprintfcat(char *dst, size_t size, const char *fmt, ...) /* Report maximum platform value for the pid_t */ pid_t get_max_pid_t(void); -/* open , get the pid, then send it */ +/* send sig to pid after some sanity checks, returns + * -1 for error, or zero for a successfully sent signal */ +int sendsignalpid(pid_t pid, int sig); + +/* open , get the pid, then send it + * returns zero for successfully sent signal, + * negative for errors: + * -3 PID file not found + * -2 PID file not parsable + * -1 Error sending signal + */ int sendsignalfn(const char *pidfn, int sig); const char *xbasename(const char *file); diff --git a/scripts/systemd/nut-server.service.in b/scripts/systemd/nut-server.service.in index 0ad8a5e092..713f5c4273 100644 --- a/scripts/systemd/nut-server.service.in +++ b/scripts/systemd/nut-server.service.in @@ -20,8 +20,10 @@ PartOf=nut.target [Service] EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N +# Note: foreground mode by default skips writing a PID file (and +# needs Type=simple); can use "-FF" here to create one anyway: ExecStart=@SBINDIR@/upsd -F -ExecReload=@SBINDIR@/upsd -c reload +ExecReload=@SBINDIR@/upsd -c reload -P $MAINPID [Install] WantedBy=nut.target diff --git a/server/conf.c b/server/conf.c index de973bdc1c..8bb694e1a8 100644 --- a/server/conf.c +++ b/server/conf.c @@ -333,6 +333,13 @@ void load_upsdconf(int reloading) return; } + if (reloading) { + /* if upsd.conf added or changed + * (or commented away) the debug_min + * setting, detect that */ + nut_debug_level_global = -1; + } + while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", @@ -359,6 +366,15 @@ void load_upsdconf(int reloading) } + if (reloading) { + if (nut_debug_level_global > -1) { + upslogx(LOG_INFO, + "Applying debug_min=%d from upsd.conf", + nut_debug_level_global); + nut_debug_level = nut_debug_level_global; + } + } + pconf_finish(&ctx); } diff --git a/server/upsd.c b/server/upsd.c index 68e7e7ba85..4a709aa8c1 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1173,8 +1173,10 @@ static void help(const char *arg_progname) printf(" commands:\n"); printf(" - reload: reread configuration files\n"); printf(" - stop: stop process and exit\n"); + printf(" -P send the signal above to specified PID (bypassing PID file)\n"); printf(" -D raise debugging level (and stay foreground by default)\n"); printf(" -F stay foregrounded even if no debugging is enabled\n"); + printf(" -FF stay foregrounded and still save the PID file\n"); printf(" -B stay backgrounded even if debugging is bumped\n"); printf(" -h display this help\n"); printf(" -r chroots to \n"); @@ -1251,6 +1253,7 @@ int main(int argc, char **argv) char *chroot_path = NULL; const char *user = RUN_AS_USER; struct passwd *new_uid = NULL; + pid_t oldpid = -1; progname = xbasename(argv[0]); @@ -1263,7 +1266,7 @@ int main(int argc, char **argv) printf("Network UPS Tools %s %s\n", progname, UPS_VERSION); - while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:DFB")) != -1) { + while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:P:DFB")) != -1) { switch (i) { case 'p': case 'i': @@ -1301,11 +1304,21 @@ int main(int argc, char **argv) help(progname); break; + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help(progname); + break; + case 'D': nut_debug_level++; break; case 'F': - foreground = 1; + if (foreground > 0) { + /* specified twice to save PID file anyway */ + foreground = 2; + } else { + foreground = 1; + } break; case 'B': foreground = 0; @@ -1334,17 +1347,41 @@ int main(int argc, char **argv) } if (cmd) { - cmdret = sendsignalfn(pidfn, cmd); + if (oldpid < 0) { + cmdret = sendsignalfn(pidfn, cmd); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } exit((cmdret == 0)?EXIT_SUCCESS:EXIT_FAILURE); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ - if (sendsignalfn(pidfn, 0) == 0) { + if (oldpid < 0) { + cmdret = sendsignalfn(pidfn, 0); + } else { + cmdret = sendsignalpid(oldpid, 0); + } + switch (cmdret) { + case 0: printf("Fatal error: A previous upsd instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); + + case -3: + case -2: + upslogx(LOG_WARNING, "Could not %s PID file '%s' " + "to see if previous upsd instance is " + "already running!\n", + (cmdret == -3 ? "find" : "parse"), + pidfn); + break; + + case -1: + default: + /* Just failed to send signal, no competitor running */ + break; } argc -= optind; @@ -1442,7 +1479,13 @@ int main(int argc, char **argv) background(); writepid(pidfn); } else { - memset(pidfn, 0, sizeof(pidfn)); + if (foreground == 2) { + upslogx(LOG_WARNING, "Running as foreground process, but saving a PID file anyway"); + writepid(pidfn); + } else { + upslogx(LOG_WARNING, "Running as foreground process, not saving a PID file"); + memset(pidfn, 0, sizeof(pidfn)); + } } /* initialize SSL (keyfile must be readable by nut user) */