Merge branch 'wrap_process'
This commit is contained in:
@@ -32,6 +32,7 @@ src/pv/format/name.c \
|
||||
src/pv/format/previousline.c \
|
||||
src/pv/format/progressbar.c \
|
||||
src/pv/format/rate.c \
|
||||
src/pv/format/ratio.c \
|
||||
src/pv/format/sgr.c \
|
||||
src/pv/format/timer.c \
|
||||
src/pv/loop.c \
|
||||
|
||||
@@ -13,6 +13,8 @@ pv \- monitor and manage the progress of data through a pipe
|
||||
\fBpv\fR \fB\-R\fR|\fB\-\-remote\fR \fIPID\fR [\fIOPTION\fR]...
|
||||
.PP
|
||||
\fBpv\fR \fB\-Q\fR|\fB\-\-query\fR \fIPID\fR [\fIOPTION\fR]...
|
||||
.PP
|
||||
\fBpv\fR [\fIOPTION\fR]... \fB\-M\fR|\fB\-\-monitor\fR \fISIDE\fR \-\- \fICOMMAND\fR [\fIARGS\fR]...
|
||||
.\"
|
||||
.SH DESCRIPTION
|
||||
Show the progress of data through a pipeline by giving information such as
|
||||
@@ -27,6 +29,9 @@ read.
|
||||
.PP
|
||||
In \*(lq\fB\-\-watchfd\fR\*(rq mode, inspect another process and show its
|
||||
progress through the files it has open.
|
||||
.PP
|
||||
In \*(lq\fB\-\-monitor\fR\*(rq mode, run a command and display the progress
|
||||
of data through both its standard input and its standard output.
|
||||
.\"
|
||||
.SH OPTIONS
|
||||
.\"
|
||||
@@ -409,6 +414,37 @@ they match those of that process - such as
|
||||
\*(lq\fB\-\-null\fR\*(rq, and
|
||||
\*(lq\fB\-\-average\-rate\-window\fR\*(rq.
|
||||
Data transfer modifiers will have no effect.
|
||||
.TP
|
||||
.BR \-M " \fISIDE\fR, " \-\-monitor " \fISIDE\fR"
|
||||
Run the command specified by the remaining arguments, and monitor its
|
||||
standard input, its standard output, or both, depending on whether
|
||||
\fISIDE\fR is \*(lq\fBin\fR\*(rq, \*(lq\fBout\fR\*(rq, or
|
||||
\*(lq\fBboth\fR\*(rq.
|
||||
Use \*(lq\fB\-\-\fR\*(rq after all of the \fBpv\fR options to keep the
|
||||
monitored command's options separate.
|
||||
.IP
|
||||
With a \fISIDE\fR of \*(lq\fBboth\fR\*(rq, two progress bars will be shown.
|
||||
All of \fBpv\fR's display switches, output modifiers, and data transfer
|
||||
modifiers will apply to both sides.
|
||||
.IP
|
||||
For example,
|
||||
\*(lq\fBpv\~\-\-cursor\~\-\-monitor\~both\~\-\-\~gzip\~\-9\fR\*(rq is
|
||||
equivalent to
|
||||
\*(lq\fBpv\~\-\-cursor\~|\~gzip\~\-9\~|\~pv\~\-\-cursor\fR\*(rq, except that
|
||||
the latter can't show the input:output ratio.
|
||||
.IP
|
||||
The \*(lq\fB\-\-format\fR\*(rq and \*(lq\fB\-\-name\fR\*(rq options may be
|
||||
specified twice, in which case the first one applies to the input side and
|
||||
the second applies to the output side.
|
||||
If no \*(lq\fB\-\-format\fR\*(rq options are specified, the default format
|
||||
based on the other display switches is used as normal, except that the
|
||||
ratio is added to the output side when \fISIDE\fR is \*(lq\fBboth\fR\*(rq.
|
||||
.IP
|
||||
The values \*(lq\fB0\fR\*(rq, \*(lq\fB1\fR\*(rq, and \*(lq\fB2\fR\*(rq may
|
||||
be used as synonyms for \*(lq\fBin\fR\*(rq, \*(lq\fBout\fR\*(rq, and
|
||||
\*(lq\fBboth\fR\*(rq.
|
||||
.IP
|
||||
This option cannot be used with \*(lq\fB\-\-store\-and\-forward\*(rq.
|
||||
.\"
|
||||
.SS "Other options"
|
||||
.TP
|
||||
@@ -498,6 +534,10 @@ Equivalent to \*(lq\fB\-\-bytes\fR\*(rq.
|
||||
If \*(lq\fB\-\-bits\fR\*(rq was specified, \*(lq\fB%b\fR\*(rq shows the bits
|
||||
transferred so far, not bytes.
|
||||
.TP
|
||||
.B %{ratio}
|
||||
Show the ratio of bytes transferred by the other side to the bytes
|
||||
transferred by this side, in \*(lq\fB\-\-monitor\~both\fR\*(rq mode.
|
||||
.TP
|
||||
.BR %T ", " %{buffer\-percent}
|
||||
Percentage of the transfer buffer in use.
|
||||
Equivalent to \*(lq\fB\-\-buffer\-percent\fR\*(rq.
|
||||
@@ -721,7 +761,8 @@ pv \-\-numeric \-\-format '{"elapsed":%t,"bytes":%b,"rate":%r,"percentage":%{pro
|
||||
.\"
|
||||
.SH EXIT STATUS
|
||||
An exit status of 1 indicates a problem with the \*(lq\fB\-\-remote\fR\*(rq,
|
||||
\*(lq\fB\-\-query\fR\*(rq, or \*(lq\fB\-\-pidfile\fR\*(rq options.
|
||||
\*(lq\fB\-\-query\fR\*(rq, \*(lq\fB\-\-pidfile\fR\*(rq, or
|
||||
\*(lq\fB\-\-monitor\fR\*(rq options.
|
||||
.PP
|
||||
Any other exit status is a bitmask of the following:
|
||||
.TP 5
|
||||
|
||||
@@ -51,3 +51,10 @@ typedef bool _Bool;
|
||||
#define PV_REMOTE_CONTROL 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef SPLINT
|
||||
/* Remove __attribute__(()) if not using GCC. */
|
||||
#ifndef __GNUC__
|
||||
#define __attribute__(x) /* GCC-only feature */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+19
-2
@@ -32,9 +32,22 @@ typedef enum {
|
||||
PV_ACTION_STORE_AND_FORWARD, /* store to file, then output from it */
|
||||
PV_ACTION_WATCHFD, /* watch process file descriptors */
|
||||
PV_ACTION_REMOTE_CONTROL, /* remotely control another pv */
|
||||
PV_ACTION_QUERY /* watch the state of another pv */
|
||||
PV_ACTION_QUERY, /* watch the state of another pv */
|
||||
PV_ACTION_MONITOR /* run a process, watch its stdin/out */
|
||||
} pvaction_t;
|
||||
|
||||
/*
|
||||
* Sides of a monitored command to monitor with PV_ACTION_MONITOR.
|
||||
*/
|
||||
typedef enum {
|
||||
PV_SIDE_NONE, /* don't monitor a command */
|
||||
PV_SIDE_IN, /* monitor only the input side */
|
||||
PV_SIDE_OUT, /* monitor only the output side */
|
||||
PV_SIDE_BOTH /* monitor both sides */
|
||||
} pvside_t;
|
||||
|
||||
typedef /*@null@*/ const char * argv_string;
|
||||
|
||||
/*
|
||||
* Structure describing run-time options.
|
||||
*
|
||||
@@ -46,14 +59,16 @@ struct opts_s {
|
||||
/*@keep@*/ const char *program_name; /* name the program is running as */
|
||||
/*@keep@*/ /*@null@*/ char *output; /* fd to write output to */
|
||||
/*@keep@*/ /*@null@*/ char *name; /* display name, if any */
|
||||
/*@keep@*/ /*@null@*/ char *name1; /* first name, if two were given */
|
||||
/*@keep@*/ /*@null@*/ char *default_bar_style; /* default bar style */
|
||||
/*@keep@*/ /*@null@*/ char *format; /* output format, if any */
|
||||
/*@keep@*/ /*@null@*/ char *format1; /* first format, if two were given */
|
||||
/*@keep@*/ /*@null@*/ char *pidfile; /* PID file, if any */
|
||||
/*@keep@*/ /*@null@*/ char *store_and_forward_file; /* store and forward file, if any */
|
||||
/*@keep@*/ /*@null@*/ char *extra_display; /* extra display specifier, if any */
|
||||
/*@keep@*/ /*@null@*/ pid_t *watchfd_pid; /* array of processes to watch fds of */
|
||||
/*@keep@*/ /*@null@*/ int *watchfd_fd; /* array of fds to watch in each one (0=all) */
|
||||
/*@keep@*/ /*@null@*/ const char **argv; /* array of non-option arguments */
|
||||
/*@keep@*/ /*@null@*/ argv_string *argv; /* array of non-option arguments */
|
||||
size_t lastwritten; /* show N bytes last written */
|
||||
off_t rate_limit; /* rate limit, in bytes per second */
|
||||
size_t buffer_size; /* buffer size, in bytes (0=default) */
|
||||
@@ -70,6 +85,7 @@ struct opts_s {
|
||||
unsigned int watchfd_count; /* number of watchfd items */
|
||||
unsigned int watchfd_length; /* allocated array size */
|
||||
pvaction_t action; /* the program action to perform */
|
||||
pvside_t side; /* which side of the monitored command to monitor */
|
||||
bool progress; /* progress bar flag */
|
||||
bool timer; /* timer flag */
|
||||
bool eta; /* ETA flag */
|
||||
@@ -77,6 +93,7 @@ struct opts_s {
|
||||
bool rate; /* rate counter flag */
|
||||
bool average_rate; /* average rate counter flag */
|
||||
bool bytes; /* bytes transferred flag */
|
||||
bool ratio; /* in:out ratio flag */
|
||||
bool bits; /* report transfer size in bits */
|
||||
bool decimal_units; /* decimal prefix flag */
|
||||
bool bufpercent; /* transfer buffer percentage flag */
|
||||
|
||||
@@ -26,6 +26,7 @@ extern "C" {
|
||||
#define RATE_GRANULARITY 100000000 /* nsec between -L rate chunks */
|
||||
#define RATE_BURST_WINDOW 5 /* rate burst window (multiples of rate) */
|
||||
#define REMOTE_INTERVAL 100000000 /* nsec between checks for -R and -Q */
|
||||
#define MONITOR_EXCHANGE_INTERVAL 100000000 /* nsec between "-M both" data exchanges */
|
||||
#define BUFFER_SIZE (size_t) 409600 /* default transfer buffer size */
|
||||
#define BUFFER_SIZE_MAX (size_t) 524288 /* max auto transfer buffer size */
|
||||
#define MAX_READ_AT_ONCE (size_t) 524288 /* max to read() in one go */
|
||||
@@ -201,22 +202,15 @@ struct pvstate_s {
|
||||
off_t size; /* total size of data */
|
||||
unsigned int skip_errors; /* skip read errors counter */
|
||||
int output_fd; /* fd to write output to */
|
||||
pid_t othermonitor_pid; /* pid of the other monitor, in "-M both" mode */
|
||||
int othermonitor_read_fd; /* fd to read transfer counts from other monitor */
|
||||
int othermonitor_write_fd; /* fd to write transfer counts to other monitor */
|
||||
unsigned int average_rate_window; /* time window in seconds for average rate calculations */
|
||||
unsigned int history_interval; /* seconds between each average rate calc history entry */
|
||||
pvdisplay_width_t width; /* screen width */
|
||||
unsigned int height; /* screen height */
|
||||
unsigned int extra_displays; /* bitmask of extra display destinations */
|
||||
struct { /* old-style format options (used by -R) */
|
||||
size_t lastwritten; /* --last-written (amount) */
|
||||
bool progress; /* --progress */
|
||||
bool timer; /* --timer */
|
||||
bool eta; /* --eta */
|
||||
bool fineta; /* --fineta */
|
||||
bool rate; /* --rate */
|
||||
bool average_rate; /* --average-rate */
|
||||
bool bytes; /* --bytes */
|
||||
bool bufpercent; /* --buffer-percent */
|
||||
} format_option;
|
||||
pvformatoptions_s format_option; /* old-style format options (used by -R) */
|
||||
bool force; /* display even if not on terminal */
|
||||
bool cursor; /* use cursor positioning */
|
||||
bool numeric; /* numeric output only */
|
||||
@@ -324,6 +318,7 @@ struct pvstate_s {
|
||||
|
||||
bool showing_timer; /* set if showing timer */
|
||||
bool showing_bytes; /* set if showing byte/line count */
|
||||
bool showing_ratio; /* set if showing monitoring in:out ratio */
|
||||
bool showing_rate; /* set if showing transfer rate */
|
||||
bool showing_last_written; /* set if displaying the last few bytes written */
|
||||
bool showing_previous_line; /* set if displaying the previously output line */
|
||||
@@ -427,6 +422,8 @@ struct pvstate_s {
|
||||
off_t total_written; /* total bytes or lines written */
|
||||
off_t transferred; /* amount transferred (written - unconsumed) */
|
||||
|
||||
off_t otherside_transferred; /* amount transferred by the other side ("-M both") */
|
||||
|
||||
/* Keep track of line positions to backtrack written_but_not_consumed. */
|
||||
/*@only@*/ /*@null@*/ off_t *line_positions; /* line separator write positions (circular buffer) */
|
||||
size_t line_positions_capacity; /* total size of line position array */
|
||||
@@ -606,6 +603,7 @@ pvdisplay_bytecount_t pv_formatter_fineta(pvformatter_args_t);
|
||||
pvdisplay_bytecount_t pv_formatter_rate(pvformatter_args_t);
|
||||
pvdisplay_bytecount_t pv_formatter_average_rate(pvformatter_args_t);
|
||||
pvdisplay_bytecount_t pv_formatter_bytes(pvformatter_args_t);
|
||||
pvdisplay_bytecount_t pv_formatter_ratio(pvformatter_args_t);
|
||||
pvdisplay_bytecount_t pv_formatter_buffer_percent(pvformatter_args_t);
|
||||
pvdisplay_bytecount_t pv_formatter_last_written(pvformatter_args_t);
|
||||
pvdisplay_bytecount_t pv_formatter_previous_line(pvformatter_args_t);
|
||||
|
||||
+20
-8
@@ -29,6 +29,7 @@ extern "C" {
|
||||
*/
|
||||
#define PV_ERROREXIT_REMOTE_OR_PID 1
|
||||
#define PV_ERROREXIT_SAF 1 /* store and forward error */
|
||||
#define PV_ERROREXIT_MONITOR 1 /* monitor mode error */
|
||||
#define PV_ERROREXIT_ACCESS 2
|
||||
#define PV_ERROREXIT_OUROBOROS 4
|
||||
#define PV_ERROREXIT_TRANSITION 8
|
||||
@@ -192,15 +193,24 @@ extern /*@null@*/ /*@only@*/ pvstate_t pv_state_alloc(void);
|
||||
extern void pv_state_reset(pvstate_t state);
|
||||
|
||||
/*
|
||||
* Set the formatting string, given a set of old-style formatting options.
|
||||
* Set the format options and use them to build a default formatting string.
|
||||
* The default string is used if no format string is explicitly set.
|
||||
*/
|
||||
extern void pv_state_set_format(pvstate_t state, bool progress,
|
||||
bool timer, bool eta,
|
||||
bool fineta, bool rate,
|
||||
bool average_rate, bool bytes,
|
||||
bool bufpercent,
|
||||
size_t lastwritten,
|
||||
/*@null@*/ const char *name);
|
||||
typedef struct {
|
||||
size_t lastwritten; /* --last-written (amount) */
|
||||
bool progress; /* --progress */
|
||||
bool timer; /* --timer */
|
||||
bool eta; /* --eta */
|
||||
bool fineta; /* --fineta */
|
||||
bool rate; /* --rate */
|
||||
bool average_rate; /* --average-rate */
|
||||
bool bytes; /* --bytes */
|
||||
bool bufpercent; /* --buffer-percent */
|
||||
} pvformatoptions_s;
|
||||
extern void pv_state_set_format_options(pvstate_t, pvformatoptions_s);
|
||||
|
||||
/* Append a string to the default format. */
|
||||
void pv_state_append_to_default_format(pvstate_t, /*@null@ */ const char *);
|
||||
|
||||
/*
|
||||
* Set the various options.
|
||||
@@ -238,6 +248,8 @@ extern void pv_state_extra_display_set(pvstate_t, /*@null@*/ const char *);
|
||||
extern void pv_state_output_set(pvstate_t, int, const char *);
|
||||
extern void pv_state_average_rate_window_set(pvstate_t, unsigned int);
|
||||
extern void pv_state_set_terminal_supports_utf8(pvstate_t, bool);
|
||||
extern void pv_state_othermonitor_set(pvstate_t, pid_t, int, int);
|
||||
extern void pv_state_cancel_output_if_empty_format_string(pvstate_t);
|
||||
|
||||
extern void pv_state_inputfiles(pvstate_t, unsigned int, const char **);
|
||||
extern void pv_state_watchfds(pvstate_t, unsigned int, const pid_t *, const int *);
|
||||
|
||||
@@ -372,6 +372,9 @@ void display_help(void)
|
||||
N_("show progress of process PID"),
|
||||
{ 0, 0, 0, 0} },
|
||||
#endif /* PV_REMOTE_CONTROL */
|
||||
{ "-M", "--monitor", "in|0|out|1|both|2",
|
||||
N_("run a command and monitor its standard input, output, or both"),
|
||||
{ 0, 0, 0, 0} },
|
||||
{ "", NULL, NULL, NULL, { 0, 0, 0, 0} },
|
||||
{ "-P", "--pidfile", N_("FILE"),
|
||||
N_("save process ID in FILE"),
|
||||
|
||||
+386
-13
@@ -19,6 +19,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef HAVE_LANGINFO_H
|
||||
#include <langinfo.h>
|
||||
#endif
|
||||
@@ -131,10 +132,12 @@ static int pv__set_output(pvstate_t state, opts_t opts, /*@null@ */ const char *
|
||||
return 0;
|
||||
|
||||
if (NULL == output_file || 0 == strcmp(output_file, "-")) {
|
||||
debug("%s", "setting output to stdout");
|
||||
pv_state_output_set(state, STDOUT_FILENO, "(stdout)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("%s: %s", "setting output", output_file);
|
||||
output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); /* flawfinder: ignore */
|
||||
/*
|
||||
* flawfinder rationale: the output filename has been
|
||||
@@ -160,7 +163,7 @@ static int pv__set_output(pvstate_t state, opts_t opts, /*@null@ */ const char *
|
||||
* with the input file list forced to be just the store-and-forward file.
|
||||
* Returns nonzero on error.
|
||||
*/
|
||||
static int pv__store_and_forward(pvstate_t state, opts_t opts, bool can_have_eta)
|
||||
static int pv__store_and_forward(pvstate_t state, opts_t opts, pvformatoptions_s format_options)
|
||||
{
|
||||
char tmp_filename[4096]; /* flawfinder: ignore */
|
||||
bool use_temporary_file;
|
||||
@@ -221,11 +224,10 @@ static int pv__store_and_forward(pvstate_t state, opts_t opts, bool can_have_eta
|
||||
if (0 != retcode)
|
||||
goto end_store_and_forward;
|
||||
|
||||
/* Reset the formatting to set the displayed name to "(input)". */
|
||||
/* Set the displayed name to "(input)" and trigger a format reparse. */
|
||||
/*@-mustfreefresh@ */
|
||||
pv_state_set_format(state, opts->progress, opts->timer, can_have_eta ? opts->eta : false,
|
||||
can_have_eta ? opts->fineta : false, opts->rate, opts->average_rate,
|
||||
opts->bytes, opts->bufpercent, opts->lastwritten, _("(input)"));
|
||||
pv_state_name_set(state, _("(input)"));
|
||||
pv_state_set_format_options(state, format_options);
|
||||
/*@+mustfreefresh@ *//* see below about gettext _() calls. */
|
||||
|
||||
/* Run the main loop as normal. */
|
||||
@@ -247,10 +249,12 @@ static int pv__store_and_forward(pvstate_t state, opts_t opts, bool can_have_eta
|
||||
/* Recalculate the input size. */
|
||||
pv_state_size_set(state, pv_calc_total_size(state));
|
||||
|
||||
/* Set the displayed name to whatever was requested. */
|
||||
pv_state_name_set(state, opts->name);
|
||||
/* Reset the format, since we might have been asked to show ETA. */
|
||||
pv_state_set_format(state, opts->progress, opts->timer, opts->eta,
|
||||
opts->fineta, opts->rate, opts->average_rate,
|
||||
opts->bytes, opts->bufpercent, opts->lastwritten, opts->name);
|
||||
format_options.eta = opts->eta;
|
||||
format_options.fineta = opts->fineta;
|
||||
pv_state_set_format_options(state, format_options);
|
||||
|
||||
/* Reset calculated values in the state. */
|
||||
pv_state_reset(state);
|
||||
@@ -267,6 +271,355 @@ static int pv__store_and_forward(pvstate_t state, opts_t opts, bool can_have_eta
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Run the main transfer loop for the given side of monitor mode, using the
|
||||
* "command_fd" file descriptor as the transfer loop's output (on the "in"
|
||||
* side, it's a pipe to the command) or as its input (on the "out" side,
|
||||
* it's a pipe from the command).
|
||||
*
|
||||
* If both sides are active, "othermonitor_pid" is the PID of the monitor on
|
||||
* the other side, "othermonitor_read_fd" is a pipe file descriptor to read
|
||||
* info from the other monitor, and "othermonitor_write_fd" is for writing
|
||||
* info to the other monitor.
|
||||
*
|
||||
* Returns the appropriate exit status.
|
||||
*
|
||||
* As a side effect, "command_fd" is closed.
|
||||
*/
|
||||
static int pv__run_monitor(const char *program_name, pvstate_t state, pvside_t side, int command_fd,
|
||||
pid_t othermonitor_pid, int othermonitor_read_fd, int othermonitor_write_fd)
|
||||
{
|
||||
const char *dummy_argv[1]; /* flawfinder: ignore */
|
||||
|
||||
/* flawfinder - the array length is passed along with the array. */
|
||||
|
||||
switch (side) {
|
||||
case PV_SIDE_NONE: /* fall through */
|
||||
case PV_SIDE_BOTH:
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
case PV_SIDE_IN:
|
||||
/* Replace stdout with the pipe to the command. */
|
||||
debug("replacing stdout with fd %d", command_fd);
|
||||
if (dup2(command_fd, STDOUT_FILENO) < 0) {
|
||||
fprintf(stderr, "%s: %s\n", program_name, strerror(errno));
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
}
|
||||
break;
|
||||
case PV_SIDE_OUT:
|
||||
/* Replace stdin with the pipe from the command. */
|
||||
debug("replacing stdin with fd %d", command_fd);
|
||||
if (dup2(command_fd, STDIN_FILENO) < 0) {
|
||||
fprintf(stderr, "%s: %s\n", program_name, strerror(errno));
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (close(command_fd) < 0) {
|
||||
fprintf(stderr, "%s: %s\n", program_name, strerror(errno));
|
||||
}
|
||||
|
||||
pv_state_othermonitor_set(state, othermonitor_pid, othermonitor_read_fd, othermonitor_write_fd);
|
||||
|
||||
/*@-observertrans@ */
|
||||
dummy_argv[0] = "-";
|
||||
/*@+observertrans@ */
|
||||
pv_state_inputfiles(state, 1, dummy_argv);
|
||||
|
||||
pv_state_cancel_output_if_empty_format_string(state);
|
||||
return pv_main_loop(state);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Monitor mode: run a process and run the main transfer loop on its input,
|
||||
* output, or both. Returns the appropriate exit status.
|
||||
*
|
||||
* When monitoring both sides, two pipes are set up between the monitor
|
||||
* processes for each side - in to out, and out to in - for them to exchange
|
||||
* information about the transfer, so both sides can see how many bytes the
|
||||
* other side has transferred. This is what allows the in:out ratio to be
|
||||
* displayed.
|
||||
*/
|
||||
static int pv__monitor(pvstate_t state, opts_t opts, pvformatoptions_s format_options)
|
||||
{
|
||||
int pipefd_cmd_in[2]; /* pipe from the monitor to the command */
|
||||
int pipefd_cmd_out[2]; /* pipe from the command to the monitor */
|
||||
int pipefd_in_to_out[2]; /* from in monitor to out monitor */
|
||||
int pipefd_out_to_in[2]; /* from out monitor to in monitor */
|
||||
int retcode, pid_status;
|
||||
pid_t command_pid, in_monitor_pid, out_monitor_pid, waited_pid;
|
||||
|
||||
/* Arguments check. */
|
||||
if ((NULL == opts->argv) || (opts->argc < 1) || (NULL == opts->argv[0])) {
|
||||
/*@-mustfreefresh@ */
|
||||
fprintf(stderr, "%s: -M: %s\n", opts->program_name, _("a command to run must be specified"));
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
/*@+mustfreefresh@ *//* see below about gettext _() calls. */
|
||||
}
|
||||
|
||||
retcode = 0;
|
||||
|
||||
/*
|
||||
* Create the pipes for communicating with the command.
|
||||
*/
|
||||
|
||||
pipefd_cmd_in[0] = -1;
|
||||
pipefd_cmd_in[1] = -1;
|
||||
pipefd_cmd_out[0] = -1;
|
||||
pipefd_cmd_out[1] = -1;
|
||||
|
||||
/* Pipe for the input side of the command, if we're monitoring it. */
|
||||
if ((PV_SIDE_IN == opts->side) || (PV_SIDE_BOTH == opts->side)) {
|
||||
if (0 != pipe(pipefd_cmd_in)) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
}
|
||||
debug("pipefd_cmd_in[]=(%d,%d)", pipefd_cmd_in[0], pipefd_cmd_in[1]);
|
||||
}
|
||||
|
||||
/* Pipe for the output side of the command, if we're monitoring it. */
|
||||
if ((PV_SIDE_OUT == opts->side) || (PV_SIDE_BOTH == opts->side)) {
|
||||
if (0 != pipe(pipefd_cmd_out)) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
if (-1 != pipefd_cmd_in[0])
|
||||
(void) close(pipefd_cmd_in[0]);
|
||||
if (-1 != pipefd_cmd_in[1])
|
||||
(void) close(pipefd_cmd_in[1]);
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
}
|
||||
debug("pipefd_cmd_out[]=(%d,%d)", pipefd_cmd_out[0], pipefd_cmd_out[1]);
|
||||
}
|
||||
|
||||
/* Common idiom to close an fd, and set it to -1, if it's open. */
|
||||
#define close_if_open(x) if (-1 != x) { \
|
||||
(void) close(x); \
|
||||
x = 1; \
|
||||
}
|
||||
|
||||
/* Create a process to run the command. */
|
||||
command_pid = (pid_t) fork();
|
||||
if (command_pid < 0) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
close_if_open(pipefd_cmd_in[0]);
|
||||
close_if_open(pipefd_cmd_in[1]);
|
||||
close_if_open(pipefd_cmd_out[0]);
|
||||
close_if_open(pipefd_cmd_out[1]);
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
} else if (0 == command_pid) {
|
||||
/* Command process. */
|
||||
|
||||
/* Close the write end of the "in" pipe. */
|
||||
close_if_open(pipefd_cmd_in[1]);
|
||||
|
||||
/* Close the read end of the "out" pipe. */
|
||||
close_if_open(pipefd_cmd_out[0]);
|
||||
|
||||
/* Put the read end of the "in" pipe on stdin. */
|
||||
if (-1 != pipefd_cmd_in[0]) {
|
||||
debug("replacing command stdin with fd %d", pipefd_cmd_in[0]);
|
||||
if (dup2(pipefd_cmd_in[0], STDIN_FILENO) < 0) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
(void) close(pipefd_cmd_in[0]);
|
||||
debug("replaced command stdin with fd %d", pipefd_cmd_in[0]);
|
||||
pipefd_cmd_in[0] = -1;
|
||||
}
|
||||
|
||||
/* Put the write end of the "out" pipe on stdout. */
|
||||
if (-1 != pipefd_cmd_out[1]) {
|
||||
debug("replacing command stdout with fd %d", pipefd_cmd_out[1]);
|
||||
if (dup2(pipefd_cmd_out[1], STDOUT_FILENO) < 0) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
(void) close(pipefd_cmd_out[1]);
|
||||
debug("replaced command stdout with fd %d", pipefd_cmd_out[1]);
|
||||
pipefd_cmd_out[1] = -1;
|
||||
}
|
||||
|
||||
/* Execute the command. */
|
||||
(void) execvp(opts->argv[0], (char *const *) (opts->argv)); /* flawfinder: ignore */
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
/*
|
||||
* flawfinder recommends using a library call instead of
|
||||
* executing another program with execvp(), but that isn't
|
||||
* appropriate here, the whole purpose of monitor mode is to
|
||||
* execute a process and monitor it.
|
||||
*/
|
||||
}
|
||||
|
||||
/* Main process, not the command process. */
|
||||
|
||||
/* Close the read end of the "in" pipe. */
|
||||
close_if_open(pipefd_cmd_in[0]);
|
||||
|
||||
/* Close the write end of the "out" pipe. */
|
||||
close_if_open(pipefd_cmd_out[1]);
|
||||
|
||||
/*
|
||||
* If monitoring both input and output, create pipes for the two
|
||||
* monitors to talk to each other in both directions.
|
||||
*/
|
||||
pipefd_in_to_out[0] = -1;
|
||||
pipefd_in_to_out[1] = -1;
|
||||
pipefd_out_to_in[0] = -1;
|
||||
pipefd_out_to_in[1] = -1;
|
||||
if (PV_SIDE_BOTH == opts->side) {
|
||||
if (0 != pipe(pipefd_in_to_out)) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
close_if_open(pipefd_cmd_in[1]);
|
||||
close_if_open(pipefd_cmd_out[0]);
|
||||
(void) kill(command_pid, SIGTERM);
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
}
|
||||
debug("pipefd_in_to_out[]=(%d,%d)", pipefd_in_to_out[0], pipefd_in_to_out[1]);
|
||||
if (0 != pipe(pipefd_out_to_in)) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
close_if_open(pipefd_in_to_out[0]);
|
||||
close_if_open(pipefd_in_to_out[1]);
|
||||
close_if_open(pipefd_cmd_in[1]);
|
||||
close_if_open(pipefd_cmd_out[0]);
|
||||
(void) kill(command_pid, SIGTERM);
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
}
|
||||
debug("pipefd_out_to_in[]=(%d,%d)", pipefd_out_to_in[0], pipefd_out_to_in[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* If monitoring both input and output, create a process for
|
||||
* monitoring the output side.
|
||||
*/
|
||||
in_monitor_pid = -1;
|
||||
out_monitor_pid = -1;
|
||||
if (PV_SIDE_BOTH == opts->side) {
|
||||
in_monitor_pid = (pid_t) getpid();
|
||||
out_monitor_pid = (pid_t) fork();
|
||||
if (out_monitor_pid < 0) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
close_if_open(pipefd_in_to_out[0]);
|
||||
close_if_open(pipefd_in_to_out[1]);
|
||||
close_if_open(pipefd_out_to_in[0]);
|
||||
close_if_open(pipefd_out_to_in[1]);
|
||||
close_if_open(pipefd_cmd_in[1]);
|
||||
close_if_open(pipefd_cmd_out[0]);
|
||||
(void) kill(command_pid, SIGTERM);
|
||||
return PV_ERROREXIT_MONITOR;
|
||||
} else if (0 == out_monitor_pid) {
|
||||
/* Output side monitoring process. */
|
||||
|
||||
/* Close the write end of the command "in" pipe. */
|
||||
close_if_open(pipefd_cmd_in[1]);
|
||||
|
||||
/* Close the write end of the in-to-out pipe. */
|
||||
close_if_open(pipefd_in_to_out[1]);
|
||||
|
||||
/* Close the read end of the out-to-in pipe. */
|
||||
close_if_open(pipefd_out_to_in[0]);
|
||||
|
||||
/* Add ratio to the default format on the out side. */
|
||||
if (PV_SIDE_BOTH == opts->side) {
|
||||
pv_state_append_to_default_format(state, "%{ratio}");
|
||||
}
|
||||
|
||||
retcode =
|
||||
pv__run_monitor(opts->program_name, state, PV_SIDE_OUT, pipefd_cmd_out[0], in_monitor_pid,
|
||||
pipefd_in_to_out[0], pipefd_out_to_in[1]);
|
||||
|
||||
/* Close the other ends of the intra-monitor pipes. */
|
||||
close_if_open(pipefd_in_to_out[0]);
|
||||
close_if_open(pipefd_out_to_in[1]);
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
/* Input side monitoring process. */
|
||||
|
||||
/* Close the read end of the in-to-out pipe. */
|
||||
close_if_open(pipefd_in_to_out[0]);
|
||||
|
||||
/* Close the write end of the out-to-in pipe. */
|
||||
close_if_open(pipefd_out_to_in[1]);
|
||||
}
|
||||
|
||||
/* Monitor the remaining side. */
|
||||
switch (opts->side) {
|
||||
case PV_SIDE_NONE:
|
||||
retcode = PV_ERROREXIT_MONITOR;
|
||||
break;
|
||||
case PV_SIDE_BOTH:
|
||||
/* Use name1, format1 for the "in" side. */
|
||||
if (NULL != opts->name1) {
|
||||
pv_state_name_set(state, opts->name1);
|
||||
}
|
||||
if (NULL != opts->format1) {
|
||||
pv_state_format_string_set(state, opts->format1);
|
||||
}
|
||||
/* Trigger a format reparse. */
|
||||
pv_state_set_format_options(state, format_options);
|
||||
/*@fallthrough@ */
|
||||
/* falling through as "out" is in another process (above). */
|
||||
#ifndef SPLINT
|
||||
__attribute__((fallthrough));
|
||||
#endif
|
||||
case PV_SIDE_IN:
|
||||
/* Close the read end of the "out" pipe. */
|
||||
close_if_open(pipefd_cmd_out[0]);
|
||||
retcode =
|
||||
pv__run_monitor(opts->program_name, state, PV_SIDE_IN, pipefd_cmd_in[1], out_monitor_pid,
|
||||
pipefd_out_to_in[0], pipefd_in_to_out[1]);
|
||||
break;
|
||||
case PV_SIDE_OUT:
|
||||
/* Close the write end of the "in" pipe. */
|
||||
close_if_open(pipefd_cmd_in[1]);
|
||||
retcode =
|
||||
pv__run_monitor(opts->program_name, state, PV_SIDE_OUT, pipefd_cmd_out[0], in_monitor_pid,
|
||||
pipefd_in_to_out[0], pipefd_out_to_in[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If monitoring the "in" side, close stdout to signal EOF,
|
||||
* otherwise when we wait for the monitored command, we'll wait
|
||||
* forever.
|
||||
*/
|
||||
if (PV_SIDE_IN == opts->side || PV_SIDE_BOTH == opts->side) {
|
||||
if (close(STDOUT_FILENO) < 0) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the monitored command to exit. */
|
||||
do {
|
||||
/*@-type@ */
|
||||
/* splint disagreement about __pid_t vs pid_t. */
|
||||
pid_status = 0;
|
||||
waited_pid = waitpid(command_pid, &pid_status, 0);
|
||||
/*@+type@ */
|
||||
} while (-1 == waited_pid && EINTR == errno);
|
||||
|
||||
/* If monitoring both sides, wait for the "out" side to exit. */
|
||||
if (PV_SIDE_BOTH == opts->side && -1 != out_monitor_pid) {
|
||||
/* Close our ends of the intra-monitor pipes. */
|
||||
close_if_open(pipefd_in_to_out[1]);
|
||||
close_if_open(pipefd_out_to_in[0]);
|
||||
/* Wait for the "out" side to exit. */
|
||||
do {
|
||||
/*@-type@ */
|
||||
/* splint disagreement about __pid_t vs pid_t. */
|
||||
pid_status = 0;
|
||||
waited_pid = waitpid(out_monitor_pid, &pid_status, 0);
|
||||
/*@+type@ */
|
||||
} while (-1 == waited_pid && EINTR == errno);
|
||||
}
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process command-line arguments and set option flags, then call functions
|
||||
* to initialise, and finally enter the main loop.
|
||||
@@ -278,6 +631,7 @@ int main(int argc, char **argv)
|
||||
int retcode = 0;
|
||||
bool can_have_eta = true;
|
||||
bool terminal_supports_utf8 = false;
|
||||
pvformatoptions_s format_options;
|
||||
|
||||
#if ! HAVE_SETPROCTITLE
|
||||
initproctitle(argc, argv);
|
||||
@@ -361,8 +715,11 @@ int main(int argc, char **argv)
|
||||
|
||||
/*
|
||||
* Put our list of input files into the PV internal state.
|
||||
*
|
||||
* Don't do this in monitor mode, since the rest of PV won't be
|
||||
* using the list in that case.
|
||||
*/
|
||||
if (NULL != opts->argv) {
|
||||
if ((NULL != opts->argv) && (PV_ACTION_MONITOR != opts->action)) {
|
||||
pv_state_inputfiles(state, opts->argc, (const char **) (opts->argv));
|
||||
}
|
||||
|
||||
@@ -469,6 +826,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: size calculation for monitor mode. */
|
||||
|
||||
/* Initialise the signal handling. */
|
||||
pv_sig_init(state);
|
||||
|
||||
@@ -529,9 +888,17 @@ int main(int argc, char **argv)
|
||||
pv_state_extra_display_set(state, opts->extra_display);
|
||||
pv_state_average_rate_window_set(state, opts->average_rate_window);
|
||||
|
||||
pv_state_set_format(state, opts->progress, opts->timer, can_have_eta ? opts->eta : false,
|
||||
can_have_eta ? opts->fineta : false, opts->rate, opts->average_rate,
|
||||
opts->bytes, opts->bufpercent, opts->lastwritten, opts->name);
|
||||
format_options.progress = opts->progress;
|
||||
format_options.timer = opts->timer;
|
||||
format_options.eta = can_have_eta ? opts->eta : false;
|
||||
format_options.fineta = can_have_eta ? opts->fineta : false;
|
||||
format_options.rate = opts->rate;
|
||||
format_options.average_rate = opts->average_rate;
|
||||
format_options.bytes = opts->bytes;
|
||||
format_options.bufpercent = opts->bufpercent;
|
||||
format_options.lastwritten = opts->lastwritten;
|
||||
|
||||
pv_state_set_format_options(state, format_options);
|
||||
|
||||
debug("%s: %s", "terminal_supports_utf8", terminal_supports_utf8 ? "true" : "false");
|
||||
pv_state_set_terminal_supports_utf8(state, terminal_supports_utf8);
|
||||
@@ -542,11 +909,13 @@ int main(int argc, char **argv)
|
||||
break;
|
||||
case PV_ACTION_TRANSFER:
|
||||
/* Normal "transfer data" mode. */
|
||||
pv_state_cancel_output_if_empty_format_string(state);
|
||||
retcode = pv_main_loop(state);
|
||||
break;
|
||||
case PV_ACTION_STORE_AND_FORWARD:
|
||||
/* Store-and-forward transfer mode. */
|
||||
retcode = pv__store_and_forward(state, opts, can_have_eta);
|
||||
pv_state_cancel_output_if_empty_format_string(state);
|
||||
retcode = pv__store_and_forward(state, opts, format_options);
|
||||
break;
|
||||
case PV_ACTION_WATCHFD:
|
||||
/* "Watch file descriptor(s) of another process" mode. */
|
||||
@@ -560,6 +929,10 @@ int main(int argc, char **argv)
|
||||
/* Query the progress of another running pv. */
|
||||
retcode = pv_query_loop(state, opts->query);
|
||||
break;
|
||||
case PV_ACTION_MONITOR:
|
||||
/* Run a process and monitor its input and output. */
|
||||
retcode = pv__monitor(state, opts, format_options);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear up the PID file, if one was written. */
|
||||
|
||||
+97
-3
@@ -64,8 +64,12 @@ void opts_free( /*@only@ */ opts_t opts)
|
||||
*/
|
||||
if (NULL != opts->name)
|
||||
free(opts->name);
|
||||
if (NULL != opts->name1)
|
||||
free(opts->name1);
|
||||
if (NULL != opts->format)
|
||||
free(opts->format);
|
||||
if (NULL != opts->format1)
|
||||
free(opts->format1);
|
||||
if (NULL != opts->pidfile)
|
||||
free(opts->pidfile);
|
||||
if (NULL != opts->output)
|
||||
@@ -88,12 +92,13 @@ void opts_free( /*@only@ */ opts_t opts)
|
||||
|
||||
/*
|
||||
* Add a filename to the list of non-option arguments, returning false on
|
||||
* error. The filename is not copied - the pointer is stored.
|
||||
* error. The filename is not copied - the pointer is stored. The list is
|
||||
* guaranteed to have a NULL after the last item.
|
||||
*/
|
||||
bool opts_add_file(opts_t opts, const char *filename)
|
||||
{
|
||||
/*@-branchstate@ */
|
||||
if ((opts->argc >= opts->argv_length) || (NULL == opts->argv)) {
|
||||
if (((1 + opts->argc) >= opts->argv_length) || (NULL == opts->argv)) {
|
||||
opts->argv_length = opts->argc + 10;
|
||||
/*@-keeptrans@ */
|
||||
opts->argv = realloc(opts->argv, opts->argv_length * sizeof(char *));
|
||||
@@ -117,6 +122,7 @@ bool opts_add_file(opts_t opts, const char *filename)
|
||||
*/
|
||||
|
||||
opts->argv[opts->argc++] = filename;
|
||||
opts->argv[opts->argc] = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -416,6 +422,27 @@ static bool opts_watchfd_parse(opts_t opts, const char *argument, /*@null@ */ co
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return true if the first string is at least as long as the second, and
|
||||
* its start matches the entirety of the second. Both strings must be
|
||||
* null-terminated.
|
||||
*/
|
||||
static bool string_starts_with(const char *string, const char *match)
|
||||
{
|
||||
size_t string_length, match_length;
|
||||
|
||||
string_length = strlen(string); /* flawfinder: ignore */
|
||||
match_length = strlen(match); /* flawfinder: ignore */
|
||||
/* flawfinder - these are null-terminated strings. */
|
||||
|
||||
if (string_length < match_length)
|
||||
return false;
|
||||
if (0 == strncmp(string, match, match_length))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_NFTW
|
||||
/*
|
||||
* Callback function for nftw() to add the size of the given file to the
|
||||
@@ -680,6 +707,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
{ "watchfd", 1, NULL, (int) 'd' },
|
||||
{ "output", 1, NULL, (int) 'o' },
|
||||
{ "average-rate-window", 1, NULL, (int) 'm' },
|
||||
{ "monitor", 1, NULL, (int) 'M' },
|
||||
#ifdef ENABLE_DEBUGGING
|
||||
{ "debug", 1, NULL, (int) '!' },
|
||||
#endif /* ENABLE_DEBUGGING */
|
||||
@@ -688,7 +716,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
/*@+nullassign@ */
|
||||
int option_index = 0;
|
||||
#endif /* HAVE_GETOPT_LONG */
|
||||
char *short_options = "hVpteIrab8kTA:fvnqcWD:s:gl0i:w:H:N:u:F:x:L:B:CEZ:SYKOXU:R:Q:P:d:m:o:"
|
||||
char *short_options = "hVpteIrab8kTA:fvnqcWD:s:gl0i:w:H:N:u:F:x:L:B:CEZ:SYKOXU:R:Q:P:d:m:o:M:"
|
||||
#ifdef ENABLE_DEBUGGING
|
||||
"!:"
|
||||
#endif
|
||||
@@ -736,6 +764,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
numopts = 0;
|
||||
|
||||
opts->action = PV_ACTION_TRANSFER;
|
||||
opts->side = PV_SIDE_NONE;
|
||||
opts->interval = 1;
|
||||
opts->delay_start = 0;
|
||||
opts->average_rate_window = 30;
|
||||
@@ -995,6 +1024,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
opts->height_set_manually = opts->height == 0 ? false : true;
|
||||
break;
|
||||
case 'N':
|
||||
opts->name1 = opts->name;
|
||||
opts->name = pv_strdup(optarg);
|
||||
if (NULL == opts->name) {
|
||||
fprintf(stderr, "%s: -N: %s\n", opts->program_name, strerror(errno));
|
||||
@@ -1069,6 +1099,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
opts->format1 = opts->format;
|
||||
opts->format = pv_strdup(optarg);
|
||||
if (NULL == opts->format) {
|
||||
fprintf(stderr, "%s: -F: %s\n", opts->program_name, strerror(errno));
|
||||
@@ -1102,6 +1133,33 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
case 'm':
|
||||
opts->average_rate_window = pv_getnum_count(optarg, opts->decimal_units);
|
||||
break;
|
||||
case 'M':
|
||||
opts->action = PV_ACTION_MONITOR;
|
||||
if (string_starts_with(optarg, "0")) {
|
||||
opts->side = PV_SIDE_IN;
|
||||
} else if (string_starts_with(optarg, "in")) {
|
||||
opts->side = PV_SIDE_IN;
|
||||
} else if (string_starts_with(optarg, "stdin")) {
|
||||
opts->side = PV_SIDE_IN;
|
||||
} else if (string_starts_with(optarg, "1")) {
|
||||
opts->side = PV_SIDE_OUT;
|
||||
} else if (string_starts_with(optarg, "out")) {
|
||||
opts->side = PV_SIDE_OUT;
|
||||
} else if (string_starts_with(optarg, "stdout")) {
|
||||
opts->side = PV_SIDE_OUT;
|
||||
} else if (string_starts_with(optarg, "2")) {
|
||||
opts->side = PV_SIDE_BOTH;
|
||||
} else if (string_starts_with(optarg, "both")) {
|
||||
opts->side = PV_SIDE_BOTH;
|
||||
} else {
|
||||
/*@-mustfreefresh@ *//* see above */
|
||||
fprintf(stderr, "%s: -M: %s: %s\n",
|
||||
opts->program_name, optarg, _("invalid side specification"));
|
||||
opts_free(opts);
|
||||
return NULL;
|
||||
/*@+mustfreefresh@ */
|
||||
}
|
||||
break;
|
||||
#ifdef ENABLE_DEBUGGING
|
||||
case '!':
|
||||
debugging_output_destination(optarg);
|
||||
@@ -1238,6 +1296,31 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
if (opts->error_skip_block > 0 && 0 == opts->skip_errors)
|
||||
opts->skip_errors = 1;
|
||||
|
||||
/*
|
||||
* Don't allow -R or -Q with -M.
|
||||
*/
|
||||
if ((PV_ACTION_MONITOR == opts->action) && ((0 != opts->remote) || (0 != opts->query))) {
|
||||
/*@-mustfreefresh@ *//* see above */
|
||||
fprintf(stderr, "%s: %s: %s\n", opts->program_name, 0 != opts->remote ? "-R" : "-Q",
|
||||
_("monitor mode cannot be specified with this option"));
|
||||
opts_free(opts);
|
||||
return NULL;
|
||||
/*@+mustfreefresh@ */
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't allow -U with -M.
|
||||
*/
|
||||
if ((PV_ACTION_STORE_AND_FORWARD == opts->action && PV_SIDE_NONE != opts->side)
|
||||
|| (PV_ACTION_MONITOR == opts->action && NULL != opts->store_and_forward_file)) {
|
||||
/*@-mustfreefresh@ *//* see above */
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name,
|
||||
_("monitor mode cannot be used with store-and-forward"));
|
||||
opts_free(opts);
|
||||
return NULL;
|
||||
/*@+mustfreefresh@ */
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't allow any non-option arguments with -R or -Q.
|
||||
*/
|
||||
@@ -1250,6 +1333,17 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
/*@+mustfreefresh@ */
|
||||
}
|
||||
|
||||
/*
|
||||
* At least one non-option argument is required with -M.
|
||||
*/
|
||||
if ((PV_ACTION_MONITOR == opts->action) && (optind >= (int) argc)) {
|
||||
/*@-mustfreefresh@ *//* see above */
|
||||
fprintf(stderr, "%s: -M: %s\n", opts->program_name, _("a command to run must be specified"));
|
||||
opts_free(opts);
|
||||
return NULL;
|
||||
/*@+mustfreefresh@ */
|
||||
}
|
||||
|
||||
/*
|
||||
* Store remaining command-line arguments.
|
||||
*/
|
||||
|
||||
@@ -533,6 +533,7 @@ pvdisplay_bytecount_t pv_formatter_segmentcontent(char *content, pvformatter_arg
|
||||
{ "b", &pv_formatter_bytes, false },
|
||||
{ "{bytes}", &pv_formatter_bytes, false },
|
||||
{ "{transferred}", &pv_formatter_bytes, false },
|
||||
{ "{ratio}", &pv_formatter_ratio, false },
|
||||
{ "T", &pv_formatter_buffer_percent, false },
|
||||
{ "{buffer-percent}", &pv_formatter_buffer_percent, false },
|
||||
{ "A", &pv_formatter_last_written, false },
|
||||
@@ -620,6 +621,7 @@ static void pv__format_init(pvprogramstatus_t status, readonly_pvcontrol_t contr
|
||||
|
||||
display->showing_timer = false;
|
||||
display->showing_bytes = false;
|
||||
display->showing_ratio = false;
|
||||
display->showing_rate = false;
|
||||
display->showing_last_written = false;
|
||||
display->showing_previous_line = false;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Formatter function for ratio of bytes transferred by the other monitored
|
||||
* side to bytes transferred by this side.
|
||||
*
|
||||
* Copyright 2026 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Ratio of bytes or lines transferred by the other monitored side ("-M
|
||||
* both") to the bytes or lines transferred by this side.
|
||||
*/
|
||||
pvdisplay_bytecount_t pv_formatter_ratio(pvformatter_args_t args)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
args->display->showing_ratio = true;
|
||||
|
||||
if (0 == args->buffer_size)
|
||||
return 0;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == args->transfer->otherside_transferred || 0 == args->transfer->transferred) {
|
||||
(void) pv_snprintf(content, sizeof(content), "%s:%s", "-", "-");
|
||||
} else if (args->transfer->otherside_transferred == args->transfer->transferred) {
|
||||
(void) pv_snprintf(content, sizeof(content), "%d:%d", 1, 1);
|
||||
} else {
|
||||
long double side1, side2, ratio;
|
||||
|
||||
side1 = (long double) (args->transfer->otherside_transferred);
|
||||
side2 = (long double) (args->transfer->transferred);
|
||||
|
||||
if (side1 > side2) {
|
||||
ratio = side1 / side2;
|
||||
(void) pv_snprintf(content, sizeof(content), "%.4Lg:%d", ratio, 1);
|
||||
} else {
|
||||
ratio = side2 / side1;
|
||||
(void) pv_snprintf(content, sizeof(content), "%d:%.4Lg", 1, ratio);
|
||||
}
|
||||
}
|
||||
|
||||
return pv_formatter_segmentcontent(content, args);
|
||||
}
|
||||
+129
-2
@@ -166,6 +166,121 @@ static long double pv__elapsed_transfer_time(const struct timespec *loop_start_t
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Exchange information with the other side of the "-M both" monitor about
|
||||
* the amount of data transferred.
|
||||
*/
|
||||
static void pv__monitor_exchange(pvstate_t state)
|
||||
{
|
||||
bool written_yet = false;
|
||||
|
||||
while ((state->control.othermonitor_read_fd >= 0) || (state->control.othermonitor_write_fd >= 0)) {
|
||||
struct timeval tv;
|
||||
fd_set readfds;
|
||||
fd_set writefds;
|
||||
fd_set exceptfds;
|
||||
int max_fd;
|
||||
int result;
|
||||
|
||||
max_fd = -1;
|
||||
if (state->control.othermonitor_read_fd > max_fd)
|
||||
max_fd = state->control.othermonitor_read_fd;
|
||||
if ((!written_yet) && (state->control.othermonitor_write_fd > max_fd))
|
||||
max_fd = state->control.othermonitor_write_fd;
|
||||
|
||||
memset(&tv, 0, sizeof(tv));
|
||||
|
||||
#if SPLINT
|
||||
/* splint doesn't like FD_ZERO, FD_SET, FD_ISSET. */
|
||||
/* Refer to src/pv/transfer.c for more details. */
|
||||
memset(&readfds, 0, sizeof(readfds));
|
||||
memset(&writefds, 0, sizeof(writefds));
|
||||
memset(&exceptfds, 0, sizeof(exceptfds));
|
||||
#else /* !SPLINT */
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&exceptfds);
|
||||
if (state->control.othermonitor_read_fd >= 0)
|
||||
FD_SET(state->control.othermonitor_read_fd, &readfds);
|
||||
if ((!written_yet) && (state->control.othermonitor_write_fd >= 0))
|
||||
FD_SET(state->control.othermonitor_write_fd, &writefds);
|
||||
#endif /* !SPLINT */
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
result = select(max_fd + 1, &readfds, &writefds, &exceptfds, &tv);
|
||||
|
||||
if (result <= 0)
|
||||
break;
|
||||
|
||||
if ((state->control.othermonitor_read_fd >= 0)
|
||||
#ifndef SPLINT
|
||||
&& (FD_ISSET(state->control.othermonitor_read_fd, &readfds))
|
||||
#endif
|
||||
) {
|
||||
off_t otherside_transferred;
|
||||
ssize_t nread;
|
||||
|
||||
otherside_transferred = 0;
|
||||
|
||||
nread = read(state->control.othermonitor_read_fd, &otherside_transferred, /* flawfinder: ignore */
|
||||
sizeof(otherside_transferred));
|
||||
/*
|
||||
* flawfinder rationale: no buffer overflow possible
|
||||
* as we read a fixed size to a fixed point.
|
||||
*/
|
||||
|
||||
if (0 == nread) {
|
||||
/* EOF - close. */
|
||||
debug("%d: %s", state->control.othermonitor_read_fd, "EOF - closing");
|
||||
(void) close(state->control.othermonitor_read_fd);
|
||||
state->control.othermonitor_read_fd = -1;
|
||||
} else if (nread < 0) {
|
||||
debug("%d: %s: %s", state->control.othermonitor_read_fd, "error on read",
|
||||
strerror(errno));
|
||||
} else if (nread != (ssize_t) (sizeof(otherside_transferred))) {
|
||||
debug("%d: %d: %s", state->control.othermonitor_read_fd, (int) nread,
|
||||
"incorrect byte count - ignoring");
|
||||
} else {
|
||||
debug("%s: %lu", "other side transfer amount", (unsigned long) otherside_transferred);
|
||||
state->transfer.otherside_transferred = otherside_transferred;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!written_yet) && (state->control.othermonitor_write_fd >= 0)
|
||||
#ifndef SPLINT
|
||||
&& (FD_ISSET(state->control.othermonitor_write_fd, &writefds))
|
||||
#endif
|
||||
) {
|
||||
ssize_t nwritten;
|
||||
|
||||
nwritten =
|
||||
write(state->control.othermonitor_write_fd, &(state->transfer.transferred),
|
||||
sizeof(state->transfer.transferred));
|
||||
|
||||
if ((nwritten < 0) && ((EINTR == errno) || (EAGAIN == errno))) {
|
||||
debug("%d: %s: %s", state->control.othermonitor_write_fd, "transient write error",
|
||||
strerror(errno));
|
||||
} else if (nwritten < 0) {
|
||||
debug("%d: %s: %s", state->control.othermonitor_write_fd, "write error - closing",
|
||||
strerror(errno));
|
||||
(void) close(state->control.othermonitor_write_fd);
|
||||
state->control.othermonitor_write_fd = -1;
|
||||
} else if (nwritten != (ssize_t) (sizeof(state->transfer.transferred))) {
|
||||
debug("%d: %d: %s", state->control.othermonitor_write_fd, (int) nwritten,
|
||||
"incorrect byte count written - closing");
|
||||
(void) close(state->control.othermonitor_write_fd);
|
||||
state->control.othermonitor_write_fd = -1;
|
||||
} else {
|
||||
written_yet = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pipe data from a list of files to standard output, giving information
|
||||
* about the transfer on standard error according to the given options.
|
||||
@@ -180,7 +295,7 @@ int pv_main_loop(pvstate_t state)
|
||||
long double target;
|
||||
bool eof_in, eof_out, final_update;
|
||||
struct timespec start_time, next_update, next_ratecheck, cur_time;
|
||||
struct timespec next_remotecheck;
|
||||
struct timespec next_remotecheck, next_monitor_exchange;
|
||||
int input_fd, output_fd;
|
||||
unsigned int file_idx;
|
||||
bool output_is_pipe;
|
||||
@@ -213,7 +328,7 @@ int pv_main_loop(pvstate_t state)
|
||||
/*@-type@ */
|
||||
if ((sb.st_mode & S_IFMT) == S_IFIFO) {
|
||||
output_is_pipe = true;
|
||||
debug("%s", "output is a pipe");
|
||||
debug("%s (fd %d)", "output is a pipe", output_fd);
|
||||
}
|
||||
/*@+type@ *//* splint says st_mode is __mode_t, not mode_t */
|
||||
} else {
|
||||
@@ -250,10 +365,12 @@ int pv_main_loop(pvstate_t state)
|
||||
|
||||
memset(&next_ratecheck, 0, sizeof(next_ratecheck));
|
||||
memset(&next_remotecheck, 0, sizeof(next_remotecheck));
|
||||
memset(&next_monitor_exchange, 0, sizeof(next_monitor_exchange));
|
||||
memset(&next_update, 0, sizeof(next_update));
|
||||
|
||||
pv_elapsedtime_copy(&next_ratecheck, &cur_time);
|
||||
pv_elapsedtime_copy(&next_remotecheck, &cur_time);
|
||||
pv_elapsedtime_copy(&next_monitor_exchange, &cur_time);
|
||||
pv_elapsedtime_copy(&next_update, &cur_time);
|
||||
if ((state->control.delay_start > 0)
|
||||
&& (state->control.delay_start > state->control.interval)) {
|
||||
@@ -338,6 +455,16 @@ int pv_main_loop(pvstate_t state)
|
||||
pv_elapsedtime_add_nsec(&next_remotecheck, REMOTE_INTERVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange messages with the other side of the monitor
|
||||
* every short while, in monitor mode.
|
||||
*/
|
||||
if ((state->control.othermonitor_pid > 0)
|
||||
&& (pv_elapsedtime_compare(&cur_time, &next_monitor_exchange) > 0)) {
|
||||
pv__monitor_exchange(state);
|
||||
pv_elapsedtime_add_nsec(&next_monitor_exchange, MONITOR_EXCHANGE_INTERVAL);
|
||||
}
|
||||
|
||||
if (1 == state->flags.trigger_exit)
|
||||
break;
|
||||
|
||||
|
||||
+18
-9
@@ -247,8 +247,8 @@ int pv_remote_set(pvstate_t state, pid_t remote)
|
||||
* If a message was received, update the current process's options with the
|
||||
* ones in the message.
|
||||
*
|
||||
* Note that this relies on pv_state_set_format() causing the output format
|
||||
* to be reparsed.
|
||||
* Note that this relies on pv_state_set_format_options() causing the output
|
||||
* format to be reparsed.
|
||||
*/
|
||||
static bool pv__rxsignal_usr2(pvstate_t state)
|
||||
{
|
||||
@@ -256,6 +256,7 @@ static bool pv__rxsignal_usr2(pvstate_t state)
|
||||
char control_filename[4096]; /* flawfinder: ignore */
|
||||
FILE *control_fptr;
|
||||
struct remote_msg msgbuf;
|
||||
pvformatoptions_s format_options;
|
||||
|
||||
/* flawfinder rationale: as above. */
|
||||
|
||||
@@ -306,11 +307,19 @@ static bool pv__rxsignal_usr2(pvstate_t state)
|
||||
msgbuf.format[sizeof(msgbuf.format) - 1] = '\0';
|
||||
msgbuf.extra_display[sizeof(msgbuf.extra_display) - 1] = '\0';
|
||||
|
||||
pv_state_set_format(state, msgbuf.progress, msgbuf.timer,
|
||||
msgbuf.eta, msgbuf.fineta, msgbuf.rate,
|
||||
msgbuf.average_rate,
|
||||
msgbuf.bytes, msgbuf.bufpercent,
|
||||
msgbuf.lastwritten, '\0' == msgbuf.name[0] ? NULL : msgbuf.name);
|
||||
pv_state_name_set(state, '\0' == msgbuf.name[0] ? NULL : msgbuf.name);
|
||||
|
||||
format_options.progress = msgbuf.progress;
|
||||
format_options.timer = msgbuf.timer;
|
||||
format_options.eta = msgbuf.eta;
|
||||
format_options.fineta = msgbuf.fineta;
|
||||
format_options.rate = msgbuf.rate;
|
||||
format_options.average_rate = msgbuf.average_rate;
|
||||
format_options.bytes = msgbuf.bytes;
|
||||
format_options.bufpercent = msgbuf.bufpercent;
|
||||
format_options.lastwritten = msgbuf.lastwritten;
|
||||
|
||||
pv_state_set_format_options(state, format_options);
|
||||
|
||||
if (msgbuf.rate_limit > 0)
|
||||
pv_state_rate_limit_set(state, msgbuf.rate_limit);
|
||||
@@ -475,8 +484,8 @@ static bool pv__rxsignal_usr1(pvstate_t state, pid_t match_sender)
|
||||
* send our transfer state to the sending process, depending on the content
|
||||
* of the message.
|
||||
*
|
||||
* NB --remote relies on pv_state_set_format() causing the output format to
|
||||
* be reparsed.
|
||||
* NB --remote relies on pv_state_set_format_options() causing the output
|
||||
* format to be reparsed.
|
||||
*
|
||||
* Returns true if a --remote message was received, false otherwise.
|
||||
*/
|
||||
|
||||
+64
-38
@@ -191,6 +191,9 @@ pvstate_t pv_state_alloc(void)
|
||||
|
||||
state->watchfd.count = 0;
|
||||
state->control.output_fd = -1;
|
||||
state->control.othermonitor_pid = 0;
|
||||
state->control.othermonitor_read_fd = -1;
|
||||
state->control.othermonitor_write_fd = -1;
|
||||
#ifdef HAVE_IPC
|
||||
state->cursor.shmid = -1;
|
||||
state->cursor.pvcount = 1;
|
||||
@@ -408,10 +411,13 @@ void pv_state_free(pvstate_t state)
|
||||
|
||||
|
||||
/*
|
||||
* Set the formatting string, given a set of old-style formatting options.
|
||||
* Set the format options and use them to build a default formatting string.
|
||||
* The default string is used if no format string is explicitly set.
|
||||
*
|
||||
* Call this *after* setting a name, so it can determine whether there
|
||||
* should be a name in the default format.
|
||||
*/
|
||||
void pv_state_set_format(pvstate_t state, bool progress, bool timer, bool eta, bool fineta, bool rate, bool average_rate, bool bytes, bool bufpercent, size_t lastwritten, /*@null@ */
|
||||
const char *name)
|
||||
void pv_state_set_format_options(pvstate_t state, pvformatoptions_s format)
|
||||
{
|
||||
#define PV_ADDFORMAT(x,y) if (x) { \
|
||||
if (state->control.default_format[0] != '\0') \
|
||||
@@ -419,15 +425,15 @@ void pv_state_set_format(pvstate_t state, bool progress, bool timer, bool eta, b
|
||||
(void) pv_strlcat(state->control.default_format, y, sizeof(state->control.default_format)); \
|
||||
}
|
||||
|
||||
state->control.format_option.progress = progress;
|
||||
state->control.format_option.timer = timer;
|
||||
state->control.format_option.eta = eta;
|
||||
state->control.format_option.fineta = fineta;
|
||||
state->control.format_option.rate = rate;
|
||||
state->control.format_option.average_rate = average_rate;
|
||||
state->control.format_option.bytes = bytes;
|
||||
state->control.format_option.bufpercent = bufpercent;
|
||||
state->control.format_option.lastwritten = lastwritten;
|
||||
state->control.format_option.progress = format.progress;
|
||||
state->control.format_option.timer = format.timer;
|
||||
state->control.format_option.eta = format.eta;
|
||||
state->control.format_option.fineta = format.fineta;
|
||||
state->control.format_option.rate = format.rate;
|
||||
state->control.format_option.average_rate = format.average_rate;
|
||||
state->control.format_option.bytes = format.bytes;
|
||||
state->control.format_option.bufpercent = format.bufpercent;
|
||||
state->control.format_option.lastwritten = format.lastwritten;
|
||||
|
||||
state->control.default_format[0] = '\0';
|
||||
|
||||
@@ -438,21 +444,21 @@ void pv_state_set_format(pvstate_t state, bool progress, bool timer, bool eta, b
|
||||
* Add the format strings for the enabled options in a
|
||||
* standard order.
|
||||
*/
|
||||
PV_ADDFORMAT(name, "%N");
|
||||
PV_ADDFORMAT(bytes, "%b");
|
||||
PV_ADDFORMAT(bufpercent, "%T");
|
||||
PV_ADDFORMAT(timer, "%t");
|
||||
PV_ADDFORMAT(rate, "%r");
|
||||
PV_ADDFORMAT(average_rate, "%a");
|
||||
PV_ADDFORMAT(progress, "%p");
|
||||
PV_ADDFORMAT(eta, "%e");
|
||||
PV_ADDFORMAT(fineta, "%I");
|
||||
PV_ADDFORMAT(NULL != state->control.name, "%N");
|
||||
PV_ADDFORMAT(format.bytes, "%b");
|
||||
PV_ADDFORMAT(format.bufpercent, "%T");
|
||||
PV_ADDFORMAT(format.timer, "%t");
|
||||
PV_ADDFORMAT(format.rate, "%r");
|
||||
PV_ADDFORMAT(format.average_rate, "%a");
|
||||
PV_ADDFORMAT(format.progress, "%p");
|
||||
PV_ADDFORMAT(format.eta, "%e");
|
||||
PV_ADDFORMAT(format.fineta, "%I");
|
||||
|
||||
if (lastwritten > 0) {
|
||||
if (format.lastwritten > 0) {
|
||||
char buf[16]; /* flawfinder: ignore */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
(void) pv_snprintf(buf, sizeof(buf), "%%%uA", (unsigned int) lastwritten);
|
||||
PV_ADDFORMAT(lastwritten > 0, buf);
|
||||
(void) pv_snprintf(buf, sizeof(buf), "%%%uA", (unsigned int) format.lastwritten);
|
||||
PV_ADDFORMAT(format.lastwritten > 0, buf);
|
||||
/*
|
||||
* flawfinder rationale: large enough for string,
|
||||
* zeroed before use, only written to by
|
||||
@@ -463,30 +469,31 @@ void pv_state_set_format(pvstate_t state, bool progress, bool timer, bool eta, b
|
||||
} else {
|
||||
/* Numeric mode has different behaviour. */
|
||||
|
||||
PV_ADDFORMAT(timer, "%t");
|
||||
PV_ADDFORMAT(bytes, "%b");
|
||||
PV_ADDFORMAT(rate, "%r");
|
||||
PV_ADDFORMAT(!(bytes || rate), "%{progress-amount-only}");
|
||||
PV_ADDFORMAT(format.timer, "%t");
|
||||
PV_ADDFORMAT(format.bytes, "%b");
|
||||
PV_ADDFORMAT(format.rate, "%r");
|
||||
PV_ADDFORMAT(!(format.bytes || format.rate), "%{progress-amount-only}");
|
||||
|
||||
}
|
||||
|
||||
debug("%s: [%s]", "default format set", state->control.default_format);
|
||||
|
||||
/* Free any previously set name. */
|
||||
if (NULL != state->control.name) {
|
||||
free(state->control.name);
|
||||
state->control.name = NULL;
|
||||
}
|
||||
|
||||
/* Set a new name if one was given. */
|
||||
if (NULL != name)
|
||||
state->control.name = pv_strdup(name);
|
||||
|
||||
/* Tell pv_format() that the format has changed. */
|
||||
state->flags.reparse_display = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Append the given string to the default format, and trigger a format
|
||||
* reparse.
|
||||
*/
|
||||
void pv_state_append_to_default_format(pvstate_t state, /*@null@ */ const char *val)
|
||||
{
|
||||
PV_ADDFORMAT(NULL != val, val);
|
||||
state->flags.reparse_display = 1;
|
||||
}
|
||||
|
||||
|
||||
void pv_state_force_set(pvstate_t state, bool val)
|
||||
{
|
||||
state->control.force = val;
|
||||
@@ -769,6 +776,25 @@ void pv_state_set_terminal_supports_utf8(pvstate_t state, bool val)
|
||||
state->status.terminal_supports_utf8 = val;
|
||||
}
|
||||
|
||||
void pv_state_othermonitor_set(pvstate_t state, pid_t pid, int read_fd, int write_fd)
|
||||
{
|
||||
state->control.othermonitor_pid = pid;
|
||||
state->control.othermonitor_read_fd = read_fd;
|
||||
state->control.othermonitor_write_fd = write_fd;
|
||||
}
|
||||
|
||||
/* If the format string is set to an empty string, stop all display output. */
|
||||
void pv_state_cancel_output_if_empty_format_string(pvstate_t state)
|
||||
{
|
||||
if (NULL == state->control.format_string)
|
||||
return;
|
||||
if ('\0' != state->control.format_string[0])
|
||||
return;
|
||||
debug("%s", "empty format string - setting no_display and turning off cursor positioning");
|
||||
state->control.no_display = true;
|
||||
state->control.cursor = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the array of input files.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user