mirror of
https://github.com/Cacti/spine.git
synced 2026-10-23 03:27:10 +09:00
## What Follow-up to #633 (already merged), from the code review findings on the equivalent 1.2.x change (#634): - **SOLAR_THREAD sleep budget**: `spine_reap_child_bounded()` has no `usleep()`-paced spin phase on `SOLAR_THREAD` builds - it sleeps a flat 1s per attempt. #633 raised the shared attempt budget (`NFT_PCLOSE_SPIN_ATTEMPTS`) to 100 for `nft_pclose()`'s brief allowance, which made it sleep up to **~100 seconds** on that platform instead of the intended ~20ms, reintroducing the exact poller stall #633 was fixing. `NFT_PCLOSE_SPIN_ATTEMPTS` is now platform-specific (100 elsewhere, 2 under `SOLAR_THREAD`). - **Unbounded EINTR retries**: bounded the `EINTR` retry loop in `spine_reap_child_bounded()` and in `nft_sweep_abandoned()` (which runs with `ListMutex` held), so a stream of caught signals can no longer spin either indefinitely. - **Kill before park on waitpid() error**: `nft_pclose()` now SIGKILLs the child before parking it when `waitpid()` itself errors (previously only done when the child was confirmed still running), so a still-running child isn't left outside the abandoned-pid sweep's reach. Preserves `waitpid()`'s errno across the call. - **Docs**: `nft_popen.h`'s `nft_pclose()` comment still described the old blocking-wait behavior and didn't mention `ETIMEDOUT` - #633 updated `nft_popen.c`'s comment but missed the header. Both now document the bounded-check/parked-child contract. ## Why These were flagged by review of #634 (the same fix ported to 1.2.x, where the affected code showed up as new rather than a diff against pre-existing lines). Fixing here too since develop has the identical code. ## What was tested - `./bootstrap && ./configure --enable-warnings && make` - clean build, no new warnings. - `make check` - all 11 suites pass. - `cppcheck --enable=all --std=c11` on `nft_popen.c` - no new errors. Closes #632. --------- Signed-off-by: TheWitness <thewitness@cacti.net>
175 lines
6.9 KiB
C
175 lines
6.9 KiB
C
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
| |
|
|
| 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. |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
| This code is designed, written, and maintained by the Cacti Group. See |
|
|
| about.php and/or the AUTHORS file for specific developer information. |
|
|
+-------------------------------------------------------------------------+
|
|
| http://www.cacti.net/ |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifndef SPINE_NFT_POPEN_H
|
|
#define SPINE_NFT_POPEN_H
|
|
/******************************************************************************
|
|
ex: set tabstop=4 shiftwidth=4 autoindent:
|
|
*
|
|
* (C) Copyright Xenadyne, Inc. 2002 All rights reserved.
|
|
*
|
|
* Permission to use, copy, modify and distribute this software for
|
|
* any purpose and without fee is hereby granted, provided that the
|
|
* above copyright notice appears in all copies.
|
|
*
|
|
* XENADYNE INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
|
* IN NO EVENT SHALL XENADYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* File: nft_popen.h
|
|
*
|
|
* PURPOSE
|
|
*
|
|
* Thread-safe substitute for popen() that doesn't use stdio streams.
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include <spawn.h>
|
|
|
|
/*!
|
|
* The nft_popen() function forks a command in a child process, and returns
|
|
* a pipe that is connected to the child's standard input and output. It is
|
|
* like the standard popen() call, except that it returns the file descriptor,
|
|
* instead of a stdio stream created by fdopen(). The file descriptor can be
|
|
* used with select() or poll(), or the caller can use fdopen() if a stdio
|
|
* FILE* is preferable.
|
|
*
|
|
* The mode argument is defined as in standard popen().
|
|
*
|
|
* On success, returns a file descriptor, or -1 on error.
|
|
* On failure, returns -1, with errno set to one of:
|
|
* EINVAL The mode argument is incorrect.
|
|
* EMFILE pipe() failed.
|
|
* ENFILE pipe() failed.
|
|
* ENOMEM malloc() failed.
|
|
* EAGAIN fork() failed.
|
|
*/
|
|
extern int nft_popen(const char * command, const char * mode);
|
|
|
|
/*!
|
|
* nft_pchild
|
|
*
|
|
* Get the pid of the child process for an fd created by ntf_popen().
|
|
*
|
|
* On success, the pid of the child process is returned.
|
|
* On failure, nft_pchild() returns -1, with errno set to:
|
|
*
|
|
* EBADF The fd is not an active nft_popen() file descriptor.
|
|
*/
|
|
extern int nft_pchild(int fd);
|
|
|
|
/*!
|
|
* nft_pclose
|
|
*
|
|
* Close the pipe and check the child's status with a brief, bounded,
|
|
* non-blocking waitpid(). A child still running past that point, or one
|
|
* whose waitpid() call itself failed, is killed and parked for later,
|
|
* asynchronous reaping rather than waited for here.
|
|
*
|
|
* On success, the exit status of the child process is returned.
|
|
* On failure, nft_pclose() returns -1, with errno set to:
|
|
*
|
|
* EBADF The fd is not an active popen() file descriptor.
|
|
* ETIMEDOUT The child had not exited by the end of the bounded
|
|
* check; it has been killed and parked for later reaping.
|
|
* (other) The waitpid() call itself failed for a reason other than
|
|
* ECHILD, which is treated as a successful reap; may be EINTR
|
|
* if the bounded EINTR retry budget was exhausted.
|
|
*/
|
|
extern int nft_pclose(int fd);
|
|
|
|
/*!
|
|
* spine_set_cloexec
|
|
*
|
|
* Mark a descriptor close-on-exec.
|
|
*
|
|
* Returns 0 on success, -1 on failure with errno set by fcntl().
|
|
*/
|
|
extern int spine_set_cloexec(int fd);
|
|
|
|
/* Duplicate a descriptor and mark the duplicate close-on-exec. On failure,
|
|
* no descriptor is returned and errno describes dup() or fcntl(). */
|
|
extern int spine_dup_cloexec(int fd);
|
|
|
|
/*!
|
|
* spine_open_pipe_cloexec
|
|
*
|
|
* Open a pipe whose descriptors are not inherited across exec. Spine spawns
|
|
* children from several threads, so a descriptor left inheritable is held by
|
|
* an unrelated child and the reader never sees EOF.
|
|
*
|
|
* Returns TRUE on success. On failure the descriptors are closed and FALSE is
|
|
* returned, so the caller owns nothing.
|
|
*/
|
|
extern int spine_open_pipe_cloexec(int pdes[2]);
|
|
|
|
/* Restore SIGPIPE's default disposition in posix_spawned children while the
|
|
* Spine parent handles broken pipes itself. */
|
|
extern int spine_spawnattr_sigpipe_default(posix_spawnattr_t *attr);
|
|
|
|
/*!
|
|
* spine_reap_child_bounded
|
|
*
|
|
* Reap a child with WNOHANG, sleeping between attempts, so a wedged script
|
|
* cannot pin a poller thread indefinitely.
|
|
*
|
|
* Returns 0 when the child was reaped - including ECHILD, where another
|
|
* waiter already collected it and pstat is set to 0 - and 1 when it is
|
|
* still running after attempts. Returns -1 on a waitpid() error, leaving
|
|
* errno as waitpid() set it; EINTR is retried within a bounded per-attempt
|
|
* budget, so it too surfaces as -1 with errno == EINTR once that budget is
|
|
* exhausted.
|
|
*/
|
|
extern int spine_reap_child_bounded(pid_t pid, int *pstat, int attempts);
|
|
|
|
/*!
|
|
* The cap on parked pids. Past it a child is logged and dropped, because an
|
|
* unbounded list would trade a pid leak for a memory leak.
|
|
*/
|
|
#define NFT_ABANDONED_MAX 64
|
|
|
|
/*!
|
|
* nft_abandon_child
|
|
*
|
|
* Record a child that outlived nft_pclose()'s kill budget. Nothing else in
|
|
* spine reaps, so a dropped child would stay a zombie for the daemon's
|
|
* lifetime; parked pids are swept on the next script poll. The pid and the
|
|
* reason are logged either way.
|
|
*/
|
|
extern void nft_abandon_child(pid_t pid, const char *reason);
|
|
|
|
/*!
|
|
* nft_abandoned_pending
|
|
*
|
|
* Sweep the parked pids with WNOHANG and return how many are still running.
|
|
* Zero means nothing is leaking.
|
|
*/
|
|
extern int nft_abandoned_pending(void);
|
|
|
|
#endif /* SPINE_NFT_POPEN_H */
|