mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 02:49:02 -04:00
Secure the popen use
This commit is contained in:
parent
39522d06d9
commit
af840808bc
5 changed files with 437 additions and 126 deletions
|
|
@ -32,6 +32,11 @@ void os_signal_init(void)
|
|||
/****************************************************************************/
|
||||
/* os */
|
||||
|
||||
char* os_fgets(char* s, int size, OS_FILE* stream)
|
||||
{
|
||||
return fgets(s, size, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit codes.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -240,6 +240,17 @@
|
|||
#define HAVE_LOCKFILE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Flags for open()
|
||||
*/
|
||||
#ifndef O_PATH
|
||||
#ifdef O_SEARCH
|
||||
#define O_PATH O_SEARCH /* macOS */
|
||||
#else
|
||||
#define O_PATH O_RDONLY /* POSIX */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Includes specific support for Windows or Linux.
|
||||
*/
|
||||
|
|
@ -306,6 +317,71 @@ void os_abort(void) __noreturn;
|
|||
*/
|
||||
void os_clear(void);
|
||||
|
||||
/**
|
||||
* Opaque handle tracking a process execution stream and its process ID.
|
||||
*
|
||||
* This structure encapsulates a standard buffered I/O stream tied to the
|
||||
* redirected standard output of a child process spawned via os_popen().
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
#define OS_FILE FILE
|
||||
#else
|
||||
typedef struct OS_FILE {
|
||||
FILE* fp; /**< Standard I/O buffered stream wrapper. */
|
||||
pid_t pid; /**< Process ID of the child process. */
|
||||
} OS_FILE;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Spawns a child process and opens a buffered stream to read its stdout.
|
||||
*
|
||||
* This function acts as a safer alternative to standard popen(), accepting an
|
||||
* explicit argument vector rather than evaluating a shell command string.
|
||||
*
|
||||
* The child process is completely isolated in its own process group (via setpgid)
|
||||
* with its stdin and stderr redirected to /dev/null. The underlying pipe is
|
||||
* created with O_CLOEXEC set to prevent leakage across concurrent forks.
|
||||
*
|
||||
* @param argv A NULL-terminated array of strings representing the argument vector.
|
||||
* argv[0] must contain the absolute path to the verified executable.
|
||||
* @param extra_args A mutable string containing additional arguments separated by
|
||||
* spaces, or NULL if no extra arguments are needed.
|
||||
* @return A pointer to an initialized OS_FILE structure on success, or NULL on failure.
|
||||
* On failure, errno is set appropriately.
|
||||
*/
|
||||
OS_FILE* os_popen(const char** argv, char* extra_args);
|
||||
|
||||
/**
|
||||
* Reads a line from an OS execution stream.
|
||||
*
|
||||
* Reads characters from the specified stream into the buffer string until
|
||||
* (size - 1) characters are read, a newline character is read and transferred,
|
||||
* or an end-of-file (EOF) condition is encountered. A terminating null
|
||||
* character is appended.
|
||||
*
|
||||
* @param s Pointer to an array of chars where the string read is stored.
|
||||
* @param size Maximum number of characters to be read (including the null character).
|
||||
* @param stream Pointer to the OS_FILE tracking context.
|
||||
*
|
||||
* @return On success, returns the buffer pointer @p s. If EOF is reached or a read
|
||||
* error occurs before any characters are read, returns NULL.
|
||||
*/
|
||||
char* os_fgets(char* s, int size, OS_FILE* stream);
|
||||
|
||||
/**
|
||||
* Closes an execution stream and reaps the associated child process.
|
||||
*
|
||||
* Closes the underlying buffered standard I/O stream, releases the allocated
|
||||
* tracking context container, and blocks until the associated child process
|
||||
* terminates to prevent the creation of zombie processes.
|
||||
*
|
||||
* @param stream Pointer to the active OS_FILE context to close and free.
|
||||
*
|
||||
* @return The termination status of the child process on success, or -1 on failure.
|
||||
* On failure, errno is set appropriately.
|
||||
*/
|
||||
int os_pclose(OS_FILE* stream);
|
||||
|
||||
/****************************************************************************/
|
||||
/* app */
|
||||
|
||||
|
|
|
|||
|
|
@ -963,6 +963,107 @@ unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* d
|
|||
return mac;
|
||||
}
|
||||
|
||||
unsigned argsplit(const char** split_map, unsigned split_max, char* str)
|
||||
{
|
||||
unsigned mac = 0;
|
||||
|
||||
/* skip leading whitespace */
|
||||
while (*str != 0 && isspace((unsigned char)*str))
|
||||
++str;
|
||||
|
||||
while (*str != 0 && mac < split_max) {
|
||||
char* dst = str;
|
||||
split_map[mac++] = dst;
|
||||
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
* cmd.exe rules:
|
||||
* Unquoted: '^' escapes next char; '"' opens a quoted block.
|
||||
* Quoted: '"' closes the block; '^' is an ordinary character.
|
||||
*/
|
||||
int quoted = 0;
|
||||
|
||||
while (*str != 0 && (quoted || !isspace((unsigned char)*str))) {
|
||||
if (quoted) {
|
||||
if (*str == '"') {
|
||||
quoted = 0;
|
||||
++str;
|
||||
} else {
|
||||
*dst++ = *str++;
|
||||
}
|
||||
} else {
|
||||
if (*str == '^' && *(str + 1) != 0) {
|
||||
++str;
|
||||
*dst++ = *str++;
|
||||
} else if (*str == '"') {
|
||||
quoted = 1;
|
||||
++str;
|
||||
} else {
|
||||
*dst++ = *str++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
/*
|
||||
* bash rules:
|
||||
* Single-quoted block: everything literal, no exceptions.
|
||||
* Double-quoted block: '\' only escapes $, `, ", '\', newline;
|
||||
* other backslashes are copied verbatim.
|
||||
* Unquoted '\': always escapes the immediately following char.
|
||||
*/
|
||||
|
||||
/* set of characters that '\' can escape inside double quotes */
|
||||
static const char dq_escapable[] = "$`\"\\\n";
|
||||
|
||||
while (*str != 0 && !isspace((unsigned char)*str)) {
|
||||
|
||||
if (*str == '\'') {
|
||||
/* single-quoted block: truly everything is literal */
|
||||
++str;
|
||||
while (*str != 0 && *str != '\'')
|
||||
*dst++ = *str++;
|
||||
if (*str == '\'')
|
||||
++str;
|
||||
|
||||
} else if (*str == '"') {
|
||||
/* double-quoted block */
|
||||
++str;
|
||||
while (*str != 0 && *str != '"') {
|
||||
if (*str == '\\' && strchr(dq_escapable, *(str + 1))) {
|
||||
++str;
|
||||
}
|
||||
*dst++ = *str++;
|
||||
}
|
||||
if (*str == '"')
|
||||
++str;
|
||||
|
||||
} else if (*str == '\\') {
|
||||
/* unquoted backslash: next character always literal */
|
||||
++str;
|
||||
if (*str != 0)
|
||||
*dst++ = *str++;
|
||||
|
||||
} else {
|
||||
*dst++ = *str++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (*str != 0)
|
||||
++str; /* step past the delimiter */
|
||||
|
||||
/* skip whitespace before the next token */
|
||||
while (*str != 0 && isspace((unsigned char)*str))
|
||||
++str;
|
||||
|
||||
/* null-terminate after str has already moved on */
|
||||
*dst = 0;
|
||||
}
|
||||
|
||||
return mac;
|
||||
}
|
||||
|
||||
char* strtrim(char* str)
|
||||
{
|
||||
char* begin;
|
||||
|
|
@ -1009,7 +1110,7 @@ char* worddigitstr(const char* haystack, const char* needle)
|
|||
/* left boundary */
|
||||
if (s == haystack || isspace((unsigned char)s[-1]) || isdigit((unsigned char)s[-1])) {
|
||||
/* right boundary */
|
||||
if (s[len] == '\0' || isspace((unsigned char)s[len]) || isdigit((unsigned char)s[len])) {
|
||||
if (s[len] == 0 || isspace((unsigned char)s[len]) || isdigit((unsigned char)s[len])) {
|
||||
return (char*)s;
|
||||
}
|
||||
}
|
||||
|
|
@ -1236,7 +1337,7 @@ int pathncmp(const char* a, const char* b, size_t n)
|
|||
if (la != lb)
|
||||
return la - lb;
|
||||
|
||||
if (la == '\0')
|
||||
if (la == 0)
|
||||
return 0;
|
||||
|
||||
--n;
|
||||
|
|
@ -2087,7 +2188,7 @@ int snumber(const char* str, const char* prefix, uint64_t* value)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int smartctl_attribute(FILE* f, const char* file, const char* name, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* inter)
|
||||
int smartctl_attribute(OS_FILE* f, const char* file, const char* name, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* inter)
|
||||
{
|
||||
unsigned i;
|
||||
int inside;
|
||||
|
|
@ -2135,7 +2236,7 @@ int smartctl_attribute(FILE* f, const char* file, const char* name, struct smart
|
|||
uint64_t raw;
|
||||
char* s;
|
||||
|
||||
s = fgets(buf, sizeof(buf), f);
|
||||
s = os_fgets(buf, sizeof(buf), f);
|
||||
if (s == 0)
|
||||
break;
|
||||
|
||||
|
|
@ -2455,14 +2556,14 @@ int smartctl_attribute(FILE* f, const char* file, const char* name, struct smart
|
|||
return 0;
|
||||
}
|
||||
|
||||
int smartctl_flush(FILE* f, const char* file, const char* name)
|
||||
int smartctl_flush(OS_FILE* f, const char* file, const char* name)
|
||||
{
|
||||
/* read the file */
|
||||
while (1) {
|
||||
char buf[256];
|
||||
char* s;
|
||||
|
||||
s = fgets(buf, sizeof(buf), f);
|
||||
s = os_fgets(buf, sizeof(buf), f);
|
||||
if (s == 0)
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -276,11 +276,69 @@ static inline const char* esc_shell(const char* str, char* buffer)
|
|||
char* strpolish(char* s);
|
||||
|
||||
/**
|
||||
* Split a string in multiple tokens separated by delimiters.
|
||||
* Split a string into tokens in-place.
|
||||
*
|
||||
* Multiple delimiters are grouped together.
|
||||
* Scans @str for tokens separated by any character in @delimiters,
|
||||
* null-terminates each token by overwriting its trailing delimiter,
|
||||
* and stores a pointer to each token's start in @split_map.
|
||||
*
|
||||
* Leading and consecutive delimiters are silently skipped, so the
|
||||
* output never contains empty tokens.
|
||||
*
|
||||
* The input string is modified in-place; @split_map entries point
|
||||
* directly into @str and are only valid for its lifetime.
|
||||
*
|
||||
* \param split_map Output array receiving pointers to each token.
|
||||
* \param split_max Capacity of @split_map; at most this many tokens are stored.
|
||||
* \param str Input string to tokenise (modified in-place).
|
||||
* \param delimiters Null-terminated set of delimiter characters.
|
||||
* \return Number of tokens stored in @split_map.
|
||||
*
|
||||
* Example:
|
||||
* char buf[] = " one,two,,three ";
|
||||
* char* map[8];
|
||||
* unsigned n = strsplit(map, 8, buf, " ,");
|
||||
* // n == 3, map[] == { "one", "two", "three" }
|
||||
*/
|
||||
unsigned strsplit(char** split_map, unsigned split_max, char* line, const char* delimiters);
|
||||
unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters);
|
||||
|
||||
/**
|
||||
* Split a command-line string into an argument vector,
|
||||
* applying the quoting rules of the platform's native shell.
|
||||
*
|
||||
* The input string is modified in-place: quotes and escape characters
|
||||
* are stripped and each token is null-terminated. Whitespace is the
|
||||
* implicit token delimiter on both platforms.
|
||||
*
|
||||
* bash quoting (Linux / macOS):
|
||||
* 'single quotes' - Everything literal; no escapes processed inside.
|
||||
* "double quotes" - '\' only escapes $, `, ", '\', and newline;
|
||||
* all other backslashes are kept as-is.
|
||||
* \x (unquoted) - Next character taken literally (even whitespace).
|
||||
*
|
||||
* cmd.exe quoting (Windows):
|
||||
* "double quotes" - Toggle quoted mode; inside, '^' is NOT special.
|
||||
* ^x (unquoted) - Next character taken literally.
|
||||
* Single quotes and '\' carry no special meaning in either mode.
|
||||
*
|
||||
* \param split_map Output array receiving pointers to each argument.
|
||||
* \param split_max Capacity of @split_map; at most this many tokens are stored.
|
||||
* \param str Input command-line string (modified in-place).
|
||||
* \return Number of arguments stored in @split_map.
|
||||
*
|
||||
* Example (bash):
|
||||
* char buf[] = "one 'two three' \"say \\\"hi\\\"\" back\\slash";
|
||||
* char* map[8];
|
||||
* unsigned n = argsplit(map, 8, buf);
|
||||
* // n==4, map[] == { "one", "two three", "say \"hi\"", "backslash" }
|
||||
*
|
||||
* Example (cmd.exe):
|
||||
* char buf[] = "one \"two three\" ^&safe ^\"quoted^\"";
|
||||
* char* map[8];
|
||||
* unsigned n = argsplit(map, 8, buf);
|
||||
* // n==4, map[] == { "one", "two three", "&safe", "\"quoted\"" }
|
||||
*/
|
||||
unsigned argsplit(const char** split_map, unsigned split_max, char* str);
|
||||
|
||||
/**
|
||||
* Trim spaces from the start and the end
|
||||
|
|
@ -476,12 +534,12 @@ void malloc_fail(size_t size);
|
|||
* Read smartctl attributes from a stream.
|
||||
* Return 0 on success.
|
||||
*/
|
||||
int smartctl_attribute(FILE* f, const char* file, const char* name, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* interf);
|
||||
int smartctl_attribute(OS_FILE* f, const char* file, const char* name, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* interf);
|
||||
|
||||
/**
|
||||
* Flush smartctl output from a stream.
|
||||
*/
|
||||
int smartctl_flush(FILE* f, const char* file, const char* name);
|
||||
int smartctl_flush(OS_FILE* f, const char* file, const char* name);
|
||||
|
||||
/**
|
||||
* Extract the temperature from the SMART info.
|
||||
|
|
|
|||
301
cmdline/unix.c
301
cmdline/unix.c
|
|
@ -82,7 +82,6 @@ void os_signal_init(void)
|
|||
/****************************************************************************/
|
||||
/* exec */
|
||||
|
||||
#if HAVE_LINUX_DEVICE
|
||||
/*
|
||||
* Securely verify and open an executable file.
|
||||
*
|
||||
|
|
@ -273,34 +272,11 @@ static int verify_executable(const char* exec_path, char* resolved_path)
|
|||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a string for shell metacharacters.
|
||||
* Returns -1 if dangerous characters are detected, 0 otherwise.
|
||||
*/
|
||||
static int validate_shell_input(const char* str)
|
||||
{
|
||||
/*
|
||||
* Define characters that can be used to inject commands or alter logic
|
||||
* Includes: ; | & $ > < ` ' " ( ) [ * ? \ ~ and whitespace
|
||||
*/
|
||||
const char* danger_chars = ";|&$><`'\"()[]*?\\~\r\n ";
|
||||
|
||||
/* check for any of the forbidden characters */
|
||||
if (strpbrk(str, danger_chars) != 0)
|
||||
return -1;
|
||||
|
||||
/* check if the string starts with a hyphen to prevent flag injection */
|
||||
if (str[0] == '-')
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a string for exec.
|
||||
* Returns -1 if dangerous characters are detected, 0 otherwise.
|
||||
*/
|
||||
static int validate_exec_input(const char* str)
|
||||
int validate_exec_input(const char* str)
|
||||
{
|
||||
/* reject paths trying to go up levels */
|
||||
if (strstr(str, "..") != 0)
|
||||
|
|
@ -356,7 +332,7 @@ bail:
|
|||
}
|
||||
|
||||
/*
|
||||
* and execute a verified executable, capturing stdout.
|
||||
* Fork and execute a verified executable, capturing stdout.
|
||||
*
|
||||
* Spawns @argv[0] in a new process with stdout connected to a pipe whose
|
||||
* read end is returned in @stdout_fd. stdin and stderr are redirected to
|
||||
|
|
@ -378,7 +354,7 @@ bail:
|
|||
*
|
||||
* Returns the child PID on success, or -1 on failure.
|
||||
*/
|
||||
pid_t os_spawn_stdout(const char** argv, int* stdout_fd)
|
||||
static pid_t os_spawn_stdout(const char** argv, int* stdout_fd)
|
||||
{
|
||||
char resolved_path[PATH_MAX];
|
||||
int out_pipe[2];
|
||||
|
|
@ -466,11 +442,13 @@ pid_t os_spawn_stdout(const char** argv, int* stdout_fd)
|
|||
/* parent */
|
||||
close(fd);
|
||||
|
||||
#ifdef F_SETPIPE_SZ /* not available on macOS */
|
||||
/* set the pipe buffer to the minimum to improve responsiveness */
|
||||
if (fcntl(out_pipe[0], F_SETPIPE_SZ, 4096) == -1) {
|
||||
log_error(errno, "Failed to set pipe size. %s.", strerror(errno));
|
||||
/* log non-fatal error or ignore */
|
||||
}
|
||||
#endif
|
||||
|
||||
close(out_pipe[1]);
|
||||
|
||||
|
|
@ -478,7 +456,7 @@ pid_t os_spawn_stdout(const char** argv, int* stdout_fd)
|
|||
return pid;
|
||||
}
|
||||
|
||||
int os_wait(pid_t pid, int* status)
|
||||
static int os_wait(pid_t pid, int* status)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -599,7 +577,77 @@ int os_spawn_and_wait(const char** argv)
|
|||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ARGV_MAX 64
|
||||
|
||||
OS_FILE* os_popen(const char** argv, char* extra_args)
|
||||
{
|
||||
const char* combined_argv[ARGV_MAX];
|
||||
|
||||
unsigned argc = 0;
|
||||
while (argv[argc] && argc < ARGV_MAX) {
|
||||
combined_argv[argc] = argv[argc];
|
||||
++argc;
|
||||
}
|
||||
|
||||
if (extra_args)
|
||||
argc += argsplit(combined_argv + argc, ARGV_MAX - argc, extra_args);
|
||||
|
||||
if (argc >= ARGV_MAX) {
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
combined_argv[argc] = 0;
|
||||
|
||||
int stdout_fd = -1;
|
||||
pid_t pid = os_spawn_stdout(combined_argv, &stdout_fd);
|
||||
if (pid < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE* fp = fdopen(stdout_fd, "r");
|
||||
if (!fp) {
|
||||
/* LCOV_EXCL_START */
|
||||
int saved_errno = errno;
|
||||
close(stdout_fd);
|
||||
int status;
|
||||
os_wait(pid, &status);
|
||||
errno = saved_errno;
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
OS_FILE* os_file = malloc_nofail(sizeof(OS_FILE));
|
||||
|
||||
os_file->fp = fp;
|
||||
os_file->pid = pid;
|
||||
|
||||
return os_file;
|
||||
}
|
||||
|
||||
char* os_fgets(char* s, int size, OS_FILE* stream)
|
||||
{
|
||||
return fgets(s, size, stream->fp);
|
||||
}
|
||||
|
||||
int os_pclose(OS_FILE* stream)
|
||||
{
|
||||
/* leep handles locally to free container block immediately */
|
||||
FILE* fp = stream->fp;
|
||||
pid_t pid = stream->pid;
|
||||
free(stream);
|
||||
|
||||
/* close buffered stream, releasing the underlying fd */
|
||||
fclose(fp);
|
||||
|
||||
int status = 0;
|
||||
if (os_wait(pid, &status) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit codes.
|
||||
|
|
@ -1284,7 +1332,6 @@ static int devdereference_bcachefs(uint64_t device, const char* dir, tommy_list*
|
|||
static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char* uuid, size_t uuid_size, char* mount_point, size_t mount_point_size)
|
||||
{
|
||||
char resolved[PATH_MAX];
|
||||
char cmd[PATH_MAX * 2 + 64];
|
||||
|
||||
const char* zfs = find_zfs();
|
||||
if (!zfs) {
|
||||
|
|
@ -1300,9 +1347,18 @@ static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char
|
|||
}
|
||||
|
||||
/* list all ZFS filesystems in one shot */
|
||||
snprintf(cmd, sizeof(cmd), "%s list -H -o name,guid,mountpoint -t filesystem 2>/dev/null", zfs);
|
||||
const char* argv[] = {
|
||||
zfs,
|
||||
"list",
|
||||
"-H",
|
||||
"-o",
|
||||
"name,guid,mountpoint",
|
||||
"-t",
|
||||
"filesystem",
|
||||
0
|
||||
};
|
||||
|
||||
FILE* fp = popen(cmd, "r");
|
||||
OS_FILE* fp = os_popen(argv, 0);
|
||||
if (!fp) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
|
|
@ -1315,7 +1371,7 @@ static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char
|
|||
char* map[3];
|
||||
unsigned mac;
|
||||
|
||||
char* s = fgets(buf, sizeof(buf), fp);
|
||||
char* s = os_fgets(buf, sizeof(buf), fp);
|
||||
if (s == 0)
|
||||
break;
|
||||
|
||||
|
|
@ -1344,7 +1400,7 @@ static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char
|
|||
}
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
os_pclose(fp);
|
||||
|
||||
if (best_len == 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1358,7 +1414,6 @@ static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char
|
|||
static int devdereference_zfs(uint64_t device, const char* dir, tommy_list* devlist)
|
||||
{
|
||||
char pool[PATH_MAX];
|
||||
char cmd[PATH_MAX * 2 + 64];
|
||||
|
||||
const char* zpool = find_zpool();
|
||||
if (!zpool) {
|
||||
|
|
@ -1378,15 +1433,15 @@ static int devdereference_zfs(uint64_t device, const char* dir, tommy_list* devl
|
|||
if (slash)
|
||||
*slash = 0;
|
||||
|
||||
if (validate_shell_input(pool) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
const char* argv[] = {
|
||||
zpool,
|
||||
"status",
|
||||
"-P",
|
||||
pool,
|
||||
0
|
||||
};
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "%s status -P %s 2>/dev/null", zpool, pool);
|
||||
|
||||
FILE* fp = popen(cmd, "r");
|
||||
OS_FILE* fp = os_popen(argv, 0);
|
||||
if (!fp) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
|
|
@ -1397,7 +1452,7 @@ static int devdereference_zfs(uint64_t device, const char* dir, tommy_list* devl
|
|||
while (1) {
|
||||
char buf[PATH_MAX * 2 + 64];
|
||||
|
||||
char* s = fgets(buf, sizeof(buf), fp);
|
||||
char* s = os_fgets(buf, sizeof(buf), fp);
|
||||
if (s == 0)
|
||||
break;
|
||||
|
||||
|
|
@ -1425,7 +1480,7 @@ static int devdereference_zfs(uint64_t device, const char* dir, tommy_list* devl
|
|||
++count;
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
os_pclose(fp);
|
||||
|
||||
if (count == 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -3396,9 +3451,9 @@ static int devstat(dev_t device, uint64_t* count)
|
|||
#if HAVE_LINUX_DEVICE
|
||||
static int devsmart(dev_t device, const char* name, const char* smartctl, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* interface)
|
||||
{
|
||||
char cmd[PATH_MAX + 64];
|
||||
char extra_args[PATH_MAX];
|
||||
char file[PATH_MAX];
|
||||
FILE* f;
|
||||
OS_FILE* f;
|
||||
int ret;
|
||||
const char* x;
|
||||
|
||||
|
|
@ -3418,48 +3473,51 @@ static int devsmart(dev_t device, const char* name, const char* smartctl, struct
|
|||
}
|
||||
|
||||
/* if there is a custom smartctl command */
|
||||
const char* args = "-a";
|
||||
if (smartctl[0]) {
|
||||
char option[PATH_MAX];
|
||||
snprintf(option, sizeof(option), smartctl, file);
|
||||
snprintf(cmd, sizeof(cmd), "%s -a %s", x, option);
|
||||
pathprint(extra_args, sizeof(extra_args), smartctl, file);
|
||||
const char* argv[] = {
|
||||
x,
|
||||
"-a",
|
||||
0
|
||||
};
|
||||
log_tag("smartctl:%s:%s:run: %s %s %s\n", file, name, x, args, extra_args);
|
||||
f = os_popen(argv, extra_args);
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), "%s -a %s", x, file);
|
||||
pathprint(extra_args, sizeof(extra_args), "%s", file);
|
||||
const char* argv[] = {
|
||||
x,
|
||||
"-a",
|
||||
file,
|
||||
0
|
||||
};
|
||||
log_tag("smartctl:%s:%s:run: %s %s %s\n", file, name, x, args, extra_args);
|
||||
f = os_popen(argv, 0);
|
||||
}
|
||||
|
||||
log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
|
||||
|
||||
f = popen(cmd, "r");
|
||||
if (!f) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
|
||||
log_tag("device:%s:%s:spawn\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s %s %s'.\n", x, args, extra_args);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (smartctl_attribute(f, file, name, smart, info, serial, family, model, interface) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
pclose(f);
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
os_pclose(f);
|
||||
log_tag("device:%s:%s:spawn\n", file, name);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
ret = pclose(f);
|
||||
ret = os_pclose(f);
|
||||
|
||||
log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
|
||||
|
||||
if (!WIFEXITED(ret)) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:abort\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (WEXITSTATUS(ret) == 127) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s %s %s' (not exited).\n", x, args, extra_args);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
@ -3546,9 +3604,9 @@ static void devattr(dev_t device, uint64_t* info, char* serial, char* family, ch
|
|||
#if HAVE_LINUX_DEVICE
|
||||
static int devprobe(dev_t device, const char* name, const char* smartctl, int* power, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* interface)
|
||||
{
|
||||
char cmd[PATH_MAX + 64];
|
||||
char extra_args[PATH_MAX];
|
||||
char file[PATH_MAX];
|
||||
FILE* f;
|
||||
OS_FILE* f;
|
||||
int ret;
|
||||
const char* x;
|
||||
|
||||
|
|
@ -3568,48 +3626,55 @@ static int devprobe(dev_t device, const char* name, const char* smartctl, int* p
|
|||
}
|
||||
|
||||
/* if there is a custom smartctl command */
|
||||
const char* args = "-n standby,3 -a";
|
||||
if (smartctl[0]) {
|
||||
char option[PATH_MAX];
|
||||
snprintf(option, sizeof(option), smartctl, file);
|
||||
snprintf(cmd, sizeof(cmd), "%s -n standby,3 -a %s", x, option);
|
||||
pathprint(extra_args, sizeof(extra_args), smartctl, file);
|
||||
const char* argv[] = {
|
||||
x,
|
||||
"-n",
|
||||
"standby,3",
|
||||
"-a",
|
||||
0
|
||||
};
|
||||
log_tag("smartctl:%s:%s:run: %s %s %s\n", file, name, x, args, extra_args);
|
||||
f = os_popen(argv, extra_args);
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), "%s -n standby,3 -a %s", x, file);
|
||||
pathprint(extra_args, sizeof(extra_args), "%s", file);
|
||||
const char* argv[] = {
|
||||
x,
|
||||
"-n",
|
||||
"standby,3",
|
||||
"-a",
|
||||
file,
|
||||
0
|
||||
};
|
||||
log_tag("smartctl:%s:%s:run: %s %s %s\n", file, name, x, args, extra_args);
|
||||
f = os_popen(argv, 0);
|
||||
}
|
||||
|
||||
log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
|
||||
|
||||
f = popen(cmd, "r");
|
||||
if (!f) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
|
||||
log_tag("device:%s:%s:spawn\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s %s %s'.\n", x, args, extra_args);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (smartctl_attribute(f, file, name, smart, info, serial, family, model, interface) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
pclose(f);
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
os_pclose(f);
|
||||
log_tag("device:%s:%s:spawn\n", file, name);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
ret = pclose(f);
|
||||
ret = os_pclose(f);
|
||||
|
||||
log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
|
||||
|
||||
if (!WIFEXITED(ret)) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:abort\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (WEXITSTATUS(ret) == 127) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s %s %s' (not exited).\n", x, args, extra_args);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
@ -3636,9 +3701,9 @@ static int devprobe(dev_t device, const char* name, const char* smartctl, int* p
|
|||
#if HAVE_LINUX_DEVICE
|
||||
static int devdown(dev_t device, const char* name, const char* smartctl)
|
||||
{
|
||||
char cmd[PATH_MAX + 64];
|
||||
char extra_args[PATH_MAX];
|
||||
char file[PATH_MAX];
|
||||
FILE* f;
|
||||
OS_FILE* f;
|
||||
int ret;
|
||||
const char* x;
|
||||
|
||||
|
|
@ -3658,55 +3723,61 @@ static int devdown(dev_t device, const char* name, const char* smartctl)
|
|||
}
|
||||
|
||||
/* if there is a custom smartctl command */
|
||||
const char* args = "-s standby,now";
|
||||
if (smartctl[0]) {
|
||||
char option[PATH_MAX];
|
||||
snprintf(option, sizeof(option), smartctl, file);
|
||||
snprintf(cmd, sizeof(cmd), "%s -s standby,now %s", x, option);
|
||||
pathprint(extra_args, sizeof(extra_args), smartctl, file);
|
||||
const char* argv[] = {
|
||||
x,
|
||||
"-s",
|
||||
"standby,now",
|
||||
0
|
||||
};
|
||||
log_tag("smartctl:%s:%s:run: %s %s %s\n", file, name, x, args, extra_args);
|
||||
f = os_popen(argv, extra_args);
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), "%s -s standby,now %s", x, file);
|
||||
pathprint(extra_args, sizeof(extra_args), "%s", file);
|
||||
const char* argv[] = {
|
||||
x,
|
||||
"-s",
|
||||
"standby,now",
|
||||
file,
|
||||
0
|
||||
};
|
||||
log_tag("smartctl:%s:%s:run: %s %s %s\n", file, name, x, args, extra_args);
|
||||
f = os_popen(argv, 0);
|
||||
}
|
||||
|
||||
log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
|
||||
|
||||
f = popen(cmd, "r");
|
||||
if (!f) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
|
||||
log_tag("device:%s:%s:spawn\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s %s %s'.\n", x, args, extra_args);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (smartctl_flush(f, file, name) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
pclose(f);
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
os_pclose(f);
|
||||
log_tag("device:%s:%s:spawn\n", file, name);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
ret = pclose(f);
|
||||
ret = os_pclose(f);
|
||||
|
||||
log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
|
||||
|
||||
if (!WIFEXITED(ret)) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:abort\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (WEXITSTATUS(ret) == 127) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:shell\n", file, name);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s %s %s' (not exited).\n", x, args, extra_args);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (WEXITSTATUS(ret) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_tag("device:%s:%s:exit:%d\n", file, name, WEXITSTATUS(ret));
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s' with return code %xh.\n", cmd, WEXITSTATUS(ret));
|
||||
log_fatal(EEXTERNAL, "Failed to run '%s %s %s' with return code %xh.\n", x, args, extra_args, WEXITSTATUS(ret));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue