Merge pv_watchpid_loop into pv_watchfd_loop, and allow multiple PID / PID:FD sets to be watched at once (#12).
This commit is contained in:
@@ -619,7 +619,7 @@ int pv_remote_set(pvstate_t);
|
|||||||
int pv_watchfd_info(pvstate_t, pvwatchfd_t, bool);
|
int pv_watchfd_info(pvstate_t, pvwatchfd_t, bool);
|
||||||
bool pv_watchfd_changed(pvwatchfd_t);
|
bool pv_watchfd_changed(pvwatchfd_t);
|
||||||
off_t pv_watchfd_position(pvwatchfd_t);
|
off_t pv_watchfd_position(pvwatchfd_t);
|
||||||
int pv_watchpid_scanfds(pvstate_t, pid_t, int *, pvwatchfd_t *);
|
int pv_watchpid_scanfds(pvstate_t, pid_t, int, int *, pvwatchfd_t *);
|
||||||
void pv_watchpid_setname(pvstate_t, pvwatchfd_t);
|
void pv_watchpid_setname(pvstate_t, pvwatchfd_t);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
+1
-6
@@ -273,15 +273,10 @@ extern bool pv_sigusr2_received(pvstate_t, pid_t *);
|
|||||||
extern int pv_main_loop(pvstate_t);
|
extern int pv_main_loop(pvstate_t);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Watch the selected file descriptor of the selected process.
|
* Watch the selected file descriptors of the selected processes.
|
||||||
*/
|
*/
|
||||||
extern int pv_watchfd_loop(pvstate_t);
|
extern int pv_watchfd_loop(pvstate_t);
|
||||||
|
|
||||||
/*
|
|
||||||
* Watch the selected process.
|
|
||||||
*/
|
|
||||||
extern int pv_watchpid_loop(pvstate_t);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shut down signal handlers after running the main loop.
|
* Shut down signal handlers after running the main loop.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+340
-314
@@ -584,25 +584,40 @@ int pv_main_loop(pvstate_t state)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Watch the progress of file descriptor state->watchfd.fd[0] in process
|
* Watch the progress of the PID:FD pairs, or of all FDs under PIDs, as
|
||||||
* state->watch_fd.pid[0] and show details about the transfer on standard
|
* specified in state->watchfd, showing details on standard error according
|
||||||
* error according to the given options.
|
* to the given options.
|
||||||
|
*
|
||||||
|
* Replaces format_string in "state" so that starts with "%N " if it doesn't
|
||||||
|
* already do so.
|
||||||
*
|
*
|
||||||
* Returns nonzero on error.
|
* Returns nonzero on error.
|
||||||
*
|
*
|
||||||
* TODO: unify this with pv_watchpid_loop.
|
* TODO: if more than one PID is being watched, display the PID as well.
|
||||||
* TODO: watch more than one fd, if watchfd.count > 1.
|
|
||||||
*/
|
*/
|
||||||
int pv_watchfd_loop(pvstate_t state)
|
int pv_watchfd_loop(pvstate_t state)
|
||||||
{
|
{
|
||||||
struct pvwatchfd_s info;
|
struct { /* details for each PID:FD specified by operator */
|
||||||
off_t position_now;
|
pid_t pid; /* watched PID */
|
||||||
struct timespec next_update, cur_time;
|
int fd; /* watched fd, or -1 for all */
|
||||||
struct timespec init_time, next_remotecheck, transfer_elapsed;
|
pvwatchfd_t info_array; /* watch information for each fd */
|
||||||
bool ended, first_check;
|
int array_length; /* length of watch info array */
|
||||||
int rc;
|
bool finished; /* "PID:FD": fd closed; or PID gone */
|
||||||
|
} *watching;
|
||||||
|
unsigned int watch_idx;
|
||||||
|
bool all_watching_finished;
|
||||||
|
const char *original_format_string;
|
||||||
|
char new_format_string[512]; /* flawfinder: ignore */
|
||||||
|
struct timespec next_update, next_remotecheck, cur_time;
|
||||||
|
int prev_displayed_lines, blank_lines;
|
||||||
|
|
||||||
/* If there's nothing to watch - do nothing. */
|
/*
|
||||||
|
* flawfinder rationale (new_format_string): zeroed with memset(),
|
||||||
|
* only written to with pv_snprintf() which checks boundaries, and
|
||||||
|
* explicitly terminated with \0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* If there's nothing to watch, do nothing at all. */
|
||||||
if (state->watchfd.count < 1)
|
if (state->watchfd.count < 1)
|
||||||
return 0;
|
return 0;
|
||||||
if (NULL == state->watchfd.pid)
|
if (NULL == state->watchfd.pid)
|
||||||
@@ -610,27 +625,138 @@ int pv_watchfd_loop(pvstate_t state)
|
|||||||
if (NULL == state->watchfd.fd)
|
if (NULL == state->watchfd.fd)
|
||||||
return PV_ERROREXIT_MEMORY;
|
return PV_ERROREXIT_MEMORY;
|
||||||
|
|
||||||
/* Call pv_watchpid_loop() instead if no specific fd was given. */
|
/*
|
||||||
if (-1 == state->watchfd.fd[0]) {
|
* Make sure there's no name set.
|
||||||
return pv_watchpid_loop(state);
|
*/
|
||||||
|
if (NULL != state->control.name) {
|
||||||
|
free(state->control.name);
|
||||||
|
state->control.name = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
/*
|
||||||
info.watch_pid = state->watchfd.pid[0];
|
* Make sure there's a format string, and then insert %N into it if
|
||||||
info.watch_fd = state->watchfd.fd[0];
|
* it's not present AND we're either watching more than one item, or
|
||||||
info.displayable = true;
|
* the item we're watching is a whole PID, not a single FD.
|
||||||
info.unused = false;
|
*/
|
||||||
pv_reset_watchfd(&info);
|
original_format_string =
|
||||||
rc = pv_watchfd_info(state, &info, false);
|
NULL != state->control.format_string ? state->control.format_string : state->control.default_format;
|
||||||
if (0 != rc) {
|
memset(new_format_string, 0, sizeof(new_format_string));
|
||||||
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
|
||||||
pv_freecontents_watchfd(&info);
|
if (state->watchfd.count > 1 || -1 == state->watchfd.fd[0]) {
|
||||||
/*@-compdestroy@ */
|
/* Watching more than one single FD; need %N. */
|
||||||
return state->status.exit_status;
|
if (NULL == original_format_string) {
|
||||||
/*@+compdestroy@ */
|
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%%N");
|
||||||
/* splint: no leak of info.state as it is unused so far. */
|
} else if (NULL == strstr(original_format_string, "%N")) {
|
||||||
|
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%%N %s",
|
||||||
|
original_format_string);
|
||||||
|
} else {
|
||||||
|
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%s", original_format_string);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Watching only one FD; don't prepend %N. */
|
||||||
|
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%s",
|
||||||
|
NULL == original_format_string ? "" : original_format_string);
|
||||||
|
}
|
||||||
|
new_format_string[sizeof(new_format_string) - 1] = '\0';
|
||||||
|
if (NULL != state->control.format_string)
|
||||||
|
free(state->control.format_string);
|
||||||
|
state->control.format_string = pv_strdup(new_format_string);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate the watching array.
|
||||||
|
*/
|
||||||
|
watching = malloc(state->watchfd.count * sizeof(*watching));
|
||||||
|
if (NULL == watching) {
|
||||||
|
pv_error("%s: %s", _("buffer allocation failed"), strerror(errno));
|
||||||
|
return PV_ERROREXIT_MEMORY;
|
||||||
|
}
|
||||||
|
memset(watching, 0, state->watchfd.count * sizeof(*watching));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Populate the watching array. In the process of doing so, raise
|
||||||
|
* errors if any of the initially specified PIDs or PID:FD pairs
|
||||||
|
* don't exist or aren't readable.
|
||||||
|
*/
|
||||||
|
for (watch_idx = 0; watch_idx < state->watchfd.count; watch_idx++) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
watching[watch_idx].pid = state->watchfd.pid[watch_idx];
|
||||||
|
watching[watch_idx].fd = state->watchfd.fd[watch_idx];
|
||||||
|
watching[watch_idx].info_array = NULL;
|
||||||
|
watching[watch_idx].array_length = 0;
|
||||||
|
watching[watch_idx].finished = false;
|
||||||
|
|
||||||
|
if (kill(watching[watch_idx].pid, 0) != 0) {
|
||||||
|
/* Inaccessible PID - error, mark as finished. */
|
||||||
|
pv_error("%s %u: %s", _("pid"), watching[watch_idx].pid, strerror(errno));
|
||||||
|
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
||||||
|
watching[watch_idx].finished = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-1 == watching[watch_idx].fd) {
|
||||||
|
/*
|
||||||
|
* If this is a whole-PID watch, do the initial FD
|
||||||
|
* scan to list all the FDs under that PID.
|
||||||
|
*/
|
||||||
|
rc = pv_watchpid_scanfds(state, watching[watch_idx].pid, -1,
|
||||||
|
&(watching[watch_idx].array_length),
|
||||||
|
&(watching[watch_idx].info_array));
|
||||||
|
if (rc != 0) {
|
||||||
|
/* Scan failed - error, mark as finished. */
|
||||||
|
pv_error("%s %u: %s", _("pid"), watching[watch_idx].pid, strerror(errno));
|
||||||
|
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
||||||
|
watching[watch_idx].finished = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Scan the PID for the one specific FD given.
|
||||||
|
*/
|
||||||
|
rc = pv_watchpid_scanfds(state, watching[watch_idx].pid, watching[watch_idx].fd,
|
||||||
|
&(watching[watch_idx].array_length),
|
||||||
|
&(watching[watch_idx].info_array));
|
||||||
|
if (rc != 0) {
|
||||||
|
/* Scan failed - mark as finished. */
|
||||||
|
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
||||||
|
watching[watch_idx].finished = true;
|
||||||
|
} else if (0 == watching[watch_idx].array_length) {
|
||||||
|
/* FD not found - error, mark as finished. */
|
||||||
|
pv_error("%s %u: %s %d: %s",
|
||||||
|
_("pid"), watching[watch_idx].pid, _("fd"), watching[watch_idx].fd,
|
||||||
|
strerror(ENOENT));
|
||||||
|
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
||||||
|
watching[watch_idx].finished = true;
|
||||||
|
} else if (!watching[watch_idx].info_array[0].displayable) {
|
||||||
|
/* FD not displayable - mark as finished. */
|
||||||
|
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
||||||
|
watching[watch_idx].finished = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip to the end if none of the items to watch can be watched.
|
||||||
|
*/
|
||||||
|
all_watching_finished = true;
|
||||||
|
for (watch_idx = 0; watch_idx < state->watchfd.count; watch_idx++) {
|
||||||
|
if (watching[watch_idx].finished)
|
||||||
|
continue;
|
||||||
|
all_watching_finished = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (all_watching_finished)
|
||||||
|
goto end_pv_watchfd_loop;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: special-case, if we are watching one single PID:FD,
|
||||||
|
* state->control.size < 1, then set state->control.size to the size
|
||||||
|
* that FD points to, and if there's still no size, remove the ETA
|
||||||
|
* from the format string.
|
||||||
|
* Need to check whether retaining this old feature is still
|
||||||
|
* relevant.
|
||||||
|
*/
|
||||||
|
/* Old code to refactor into here: */
|
||||||
|
#if 0
|
||||||
/*
|
/*
|
||||||
* Use a size if one was passed, otherwise use the total size
|
* Use a size if one was passed, otherwise use the total size
|
||||||
* calculated.
|
* calculated.
|
||||||
@@ -648,54 +774,47 @@ int pv_watchfd_loop(pvstate_t state)
|
|||||||
state->flags.reparse_display = 1;
|
state->flags.reparse_display = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare timing structures for the main loop.
|
||||||
|
*/
|
||||||
|
|
||||||
memset(&cur_time, 0, sizeof(cur_time));
|
memset(&cur_time, 0, sizeof(cur_time));
|
||||||
memset(&next_remotecheck, 0, sizeof(next_remotecheck));
|
memset(&next_remotecheck, 0, sizeof(next_remotecheck));
|
||||||
memset(&next_update, 0, sizeof(next_update));
|
memset(&next_update, 0, sizeof(next_update));
|
||||||
memset(&init_time, 0, sizeof(init_time));
|
|
||||||
memset(&transfer_elapsed, 0, sizeof(transfer_elapsed));
|
|
||||||
|
|
||||||
pv_elapsedtime_read(&cur_time);
|
pv_elapsedtime_read(&cur_time);
|
||||||
pv_elapsedtime_copy(&(info.start_time), &cur_time);
|
|
||||||
pv_elapsedtime_copy(&next_remotecheck, &cur_time);
|
pv_elapsedtime_copy(&next_remotecheck, &cur_time);
|
||||||
pv_elapsedtime_copy(&next_update, &cur_time);
|
pv_elapsedtime_copy(&next_update, &cur_time);
|
||||||
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
|
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
|
||||||
|
|
||||||
ended = false;
|
/*
|
||||||
first_check = true;
|
* Main loop - continually check each watching[] item and display
|
||||||
|
* progress until all watched items have finished or an exit signal
|
||||||
|
* is received.
|
||||||
|
*/
|
||||||
|
|
||||||
while (!ended) {
|
prev_displayed_lines = 0;
|
||||||
/*
|
all_watching_finished = false;
|
||||||
* Check for remote messages from -R every short while.
|
|
||||||
*/
|
while (!all_watching_finished) {
|
||||||
|
int displayed_lines;
|
||||||
|
bool terminal_resized;
|
||||||
|
|
||||||
|
/* Check for remote messages from -R every short while. */
|
||||||
if (pv_elapsedtime_compare(&cur_time, &next_remotecheck) > 0) {
|
if (pv_elapsedtime_compare(&cur_time, &next_remotecheck) > 0) {
|
||||||
pv_remote_check(state);
|
pv_remote_check(state);
|
||||||
pv_elapsedtime_add_nsec(&next_remotecheck, REMOTE_INTERVAL);
|
pv_elapsedtime_add_nsec(&next_remotecheck, REMOTE_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* End the loop if a signal handler has set the exit flag. */
|
||||||
if (1 == state->flags.trigger_exit)
|
if (1 == state->flags.trigger_exit)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
position_now = pv_watchfd_position(&info);
|
/* Get the current time. */
|
||||||
|
|
||||||
if (position_now < 0) {
|
|
||||||
ended = true;
|
|
||||||
} else {
|
|
||||||
if (first_check) {
|
|
||||||
state->display.initial_offset = position_now;
|
|
||||||
first_check = false;
|
|
||||||
}
|
|
||||||
state->transfer.transferred = position_now;
|
|
||||||
state->transfer.total_written = position_now;
|
|
||||||
}
|
|
||||||
|
|
||||||
pv_elapsedtime_read(&cur_time);
|
pv_elapsedtime_read(&cur_time);
|
||||||
|
|
||||||
/* Ended - force a display update. */
|
|
||||||
if (ended) {
|
|
||||||
pv_elapsedtime_copy(&next_update, &cur_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restart the loop after a brief delay, if it's not time to
|
* Restart the loop after a brief delay, if it's not time to
|
||||||
* update the display.
|
* update the display.
|
||||||
@@ -705,6 +824,7 @@ int pv_watchfd_loop(pvstate_t state)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set the time of the next display update. */
|
||||||
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
|
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
|
||||||
|
|
||||||
/* Set the "next update" time to now, if it's in the past. */
|
/* Set the "next update" time to now, if it's in the past. */
|
||||||
@@ -712,175 +832,13 @@ int pv_watchfd_loop(pvstate_t state)
|
|||||||
pv_elapsedtime_copy(&next_update, &cur_time);
|
pv_elapsedtime_copy(&next_update, &cur_time);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the effective start time: the time we actually
|
* Resize the display, if a resize signal was received.
|
||||||
* started, plus the total time we spent stopped.
|
*
|
||||||
|
* We also set a local flag to tell the display loop below
|
||||||
|
* to trigger a name reset and display reparse for every FD
|
||||||
|
* output line, to inherit the size change.
|
||||||
*/
|
*/
|
||||||
pv_elapsedtime_add(&init_time, &(info.start_time), &(state->signal.toffset));
|
terminal_resized = false;
|
||||||
|
|
||||||
/*
|
|
||||||
* Now get the effective elapsed transfer time - current
|
|
||||||
* time minus effective start time.
|
|
||||||
*/
|
|
||||||
pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &init_time);
|
|
||||||
|
|
||||||
state->transfer.elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
|
|
||||||
|
|
||||||
/* Resize the display, if a resize signal was received. */
|
|
||||||
if (1 == state->flags.terminal_resized) {
|
|
||||||
unsigned int new_width, new_height;
|
|
||||||
|
|
||||||
state->flags.terminal_resized = 0;
|
|
||||||
|
|
||||||
new_width = (unsigned int) (state->control.width);
|
|
||||||
new_height = state->control.height;
|
|
||||||
pv_screensize(&new_width, &new_height);
|
|
||||||
|
|
||||||
if (new_width > PVDISPLAY_WIDTH_MAX)
|
|
||||||
new_width = PVDISPLAY_WIDTH_MAX;
|
|
||||||
|
|
||||||
if (!state->control.width_set_manually)
|
|
||||||
state->control.width = (pvdisplay_width_t) new_width;
|
|
||||||
if (!state->control.height_set_manually)
|
|
||||||
state->control.height = new_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
pv_display(&(state->status), &(state->control), &(state->flags), &(state->transfer),
|
|
||||||
&(state->calc), &(state->cursor), &(state->display), &(state->extra_display), ended);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state->control.numeric)
|
|
||||||
pv_tty_write(&(state->flags), "\n", 1);
|
|
||||||
|
|
||||||
if (1 == state->flags.trigger_exit)
|
|
||||||
state->status.exit_status |= PV_ERROREXIT_SIGNAL;
|
|
||||||
|
|
||||||
pv_freecontents_watchfd(&info);
|
|
||||||
|
|
||||||
/*@-compdestroy@ */
|
|
||||||
return state->status.exit_status;
|
|
||||||
/*@+compdestroy@ */
|
|
||||||
/* splint: no leak of info.state as we just freed it above. */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Watch the progress of all file descriptors in process
|
|
||||||
* state->watchfd.pid[0] and show details about the transfers on standard
|
|
||||||
* error according to the given options.
|
|
||||||
*
|
|
||||||
* Replaces format_string in "state" so that starts with "%N " if it doesn't
|
|
||||||
* already do so.
|
|
||||||
*
|
|
||||||
* Returns nonzero on error.
|
|
||||||
*/
|
|
||||||
int pv_watchpid_loop(pvstate_t state)
|
|
||||||
{
|
|
||||||
const char *original_format_string;
|
|
||||||
char new_format_string[512]; /* flawfinder: ignore */
|
|
||||||
struct pvwatchfd_s *info_array = NULL;
|
|
||||||
int array_length = 0;
|
|
||||||
struct timespec next_update, cur_time;
|
|
||||||
int idx;
|
|
||||||
int prev_displayed_lines, blank_lines;
|
|
||||||
bool first_pass = true;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* flawfinder rationale (new_format_string): zeroed with memset(),
|
|
||||||
* only written to with pv_snprintf() which checks boundaries, and
|
|
||||||
* explicitly terminated with \0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* If there's nothing to watch - do nothing. */
|
|
||||||
if (state->watchfd.count < 1)
|
|
||||||
return 0;
|
|
||||||
if (NULL == state->watchfd.pid)
|
|
||||||
return PV_ERROREXIT_MEMORY;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure the process exists first, so we can give an error if
|
|
||||||
* it's not there at the start.
|
|
||||||
*/
|
|
||||||
if (kill(state->watchfd.pid[0], 0) != 0) {
|
|
||||||
pv_error("%s %u: %s", _("pid"), state->watchfd.pid[0], strerror(errno));
|
|
||||||
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
|
||||||
return PV_ERROREXIT_ACCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure there's no name set.
|
|
||||||
*/
|
|
||||||
if (NULL != state->control.name) {
|
|
||||||
free(state->control.name);
|
|
||||||
state->control.name = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure there's a format string, and then insert %N into it if
|
|
||||||
* it's not present.
|
|
||||||
*/
|
|
||||||
original_format_string =
|
|
||||||
NULL != state->control.format_string ? state->control.format_string : state->control.default_format;
|
|
||||||
memset(new_format_string, 0, sizeof(new_format_string));
|
|
||||||
if (NULL == original_format_string) {
|
|
||||||
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%%N");
|
|
||||||
} else if (NULL == strstr(original_format_string, "%N")) {
|
|
||||||
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%%N %s", original_format_string);
|
|
||||||
} else {
|
|
||||||
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%s", original_format_string);
|
|
||||||
}
|
|
||||||
new_format_string[sizeof(new_format_string) - 1] = '\0';
|
|
||||||
if (NULL != state->control.format_string)
|
|
||||||
free(state->control.format_string);
|
|
||||||
state->control.format_string = pv_strdup(new_format_string);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get things ready for the main loop.
|
|
||||||
*/
|
|
||||||
|
|
||||||
memset(&cur_time, 0, sizeof(cur_time));
|
|
||||||
memset(&next_update, 0, sizeof(next_update));
|
|
||||||
|
|
||||||
pv_elapsedtime_read(&cur_time);
|
|
||||||
pv_elapsedtime_copy(&next_update, &cur_time);
|
|
||||||
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
|
|
||||||
|
|
||||||
prev_displayed_lines = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
int rc, displayed_lines;
|
|
||||||
|
|
||||||
if (1 == state->flags.trigger_exit)
|
|
||||||
break;
|
|
||||||
|
|
||||||
pv_elapsedtime_read(&cur_time);
|
|
||||||
|
|
||||||
if (kill(state->watchfd.pid[0], 0) != 0) {
|
|
||||||
if (first_pass) {
|
|
||||||
pv_error("%s %u: %s", _("pid"), state->watchfd.pid[0], strerror(errno));
|
|
||||||
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
|
||||||
if (NULL != info_array)
|
|
||||||
free(info_array);
|
|
||||||
return PV_ERROREXIT_ACCESS;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Restart the loop after a brief delay, if it's not time to
|
|
||||||
* update the display.
|
|
||||||
*/
|
|
||||||
if (pv_elapsedtime_compare(&cur_time, &next_update) < 0) {
|
|
||||||
pv_nanosleep(50000000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
|
|
||||||
|
|
||||||
/* Set the "next update" time to now, if it's in the past. */
|
|
||||||
if (pv_elapsedtime_compare(&next_update, &cur_time) < 0)
|
|
||||||
pv_elapsedtime_copy(&next_update, &cur_time);
|
|
||||||
|
|
||||||
/* Resize the display, if a resize signal was received. */
|
|
||||||
if (1 == state->flags.terminal_resized) {
|
if (1 == state->flags.terminal_resized) {
|
||||||
unsigned int new_width, new_height;
|
unsigned int new_width, new_height;
|
||||||
|
|
||||||
@@ -896,110 +854,146 @@ int pv_watchpid_loop(pvstate_t state)
|
|||||||
state->control.width = (pvdisplay_width_t) new_width;
|
state->control.width = (pvdisplay_width_t) new_width;
|
||||||
state->control.height = new_height;
|
state->control.height = new_height;
|
||||||
|
|
||||||
for (idx = 0; NULL != info_array && idx < array_length; idx++) {
|
terminal_resized = true;
|
||||||
if (!info_array[idx].displayable)
|
|
||||||
continue;
|
|
||||||
pv_watchpid_setname(state, &(info_array[idx]));
|
|
||||||
info_array[idx].flags.reparse_display = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = pv_watchpid_scanfds(state, state->watchfd.pid[0], &array_length, &info_array);
|
/*
|
||||||
if (rc != 0) {
|
* Run through each watched item.
|
||||||
if (first_pass) {
|
*/
|
||||||
pv_error("%s %u: %s", _("pid"), state->watchfd.pid[0], strerror(errno));
|
|
||||||
state->status.exit_status |= PV_ERROREXIT_ACCESS;
|
|
||||||
if (NULL != info_array)
|
|
||||||
free(info_array);
|
|
||||||
return PV_ERROREXIT_ACCESS;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
first_pass = false;
|
|
||||||
displayed_lines = 0;
|
displayed_lines = 0;
|
||||||
|
for (watch_idx = 0; NULL != watching && watch_idx < state->watchfd.count; watch_idx++) {
|
||||||
|
int info_idx;
|
||||||
|
|
||||||
for (idx = 0; NULL != info_array && idx < array_length; idx++) {
|
/* Skip watched items that have finished. */
|
||||||
off_t position_now;
|
if (watching[watch_idx].finished)
|
||||||
struct timespec init_time, transfer_elapsed;
|
|
||||||
|
|
||||||
/* Skip unused array entries. */
|
|
||||||
if (info_array[idx].unused)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* No more lines if we've filled the display. */
|
if (-1 == watching[watch_idx].fd) {
|
||||||
if (displayed_lines >= (int) (state->control.height))
|
int rc;
|
||||||
break;
|
|
||||||
|
|
||||||
if (!info_array[idx].displayable) {
|
|
||||||
/*
|
/*
|
||||||
* Non-displayable fd - just remove if
|
* If this watched item is a whole PID,
|
||||||
* changed
|
* rescan that PID's FDs.
|
||||||
*/
|
*/
|
||||||
if (pv_watchfd_changed(&(info_array[idx]))) {
|
rc = pv_watchpid_scanfds(state, watching[watch_idx].pid, -1,
|
||||||
debug("%s %d: %s", "fd", info_array[idx].watch_fd, "removing");
|
&(watching[watch_idx].array_length),
|
||||||
info_array[idx].unused = true;
|
&(watching[watch_idx].info_array));
|
||||||
info_array[idx].displayable = false;
|
if (rc != 0) {
|
||||||
pv_freecontents_watchfd(&(info_array[idx]));
|
/*
|
||||||
|
* PID now inaccessible - mark it as
|
||||||
|
* finished and skip over it.
|
||||||
|
*/
|
||||||
|
watching[watch_idx].finished = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* It this watched item is a single FD, and
|
||||||
|
* that FD is no longer usable, mark the
|
||||||
|
* item as finished and move on.
|
||||||
|
*/
|
||||||
|
if ((NULL == watching[watch_idx].info_array) || (0 == watching[watch_idx].array_length)
|
||||||
|
|| (watching[watch_idx].info_array[0].unused)
|
||||||
|
|| (!watching[watch_idx].info_array[0].displayable)) {
|
||||||
|
watching[watch_idx].finished = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info_array[idx].watch_fd < 0) {
|
|
||||||
debug("%s %d: %s", "fd", info_array[idx].watch_fd, "negative fd - skipping");
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Displayable fd - display, or remove if changed
|
* Run through the info array of this item.
|
||||||
*/
|
*/
|
||||||
|
for (info_idx = 0;
|
||||||
|
NULL != watching[watch_idx].info_array && info_idx < watching[watch_idx].array_length;
|
||||||
|
info_idx++) {
|
||||||
|
off_t position_now;
|
||||||
|
struct timespec init_time, transfer_elapsed;
|
||||||
|
pvwatchfd_t info_item = &(watching[watch_idx].info_array[info_idx]);
|
||||||
|
|
||||||
position_now = pv_watchfd_position(&(info_array[idx]));
|
/* Skip unused array entries. */
|
||||||
|
if (info_item->unused)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (position_now < 0) {
|
/* No more lines if we've filled the display. */
|
||||||
debug("%s %d: %s", "fd", info_array[idx].watch_fd, "removing");
|
if (displayed_lines >= (int) (state->control.height))
|
||||||
info_array[idx].unused = true;
|
break;
|
||||||
info_array[idx].displayable = false;
|
|
||||||
pv_freecontents_watchfd(&(info_array[idx]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
info_array[idx].position = position_now;
|
if (!info_item->displayable) {
|
||||||
|
/*
|
||||||
|
* Non-displayable fd - just remove if
|
||||||
|
* changed.
|
||||||
|
*/
|
||||||
|
if (pv_watchfd_changed(info_item)) {
|
||||||
|
debug("%s %d: %s", "fd", info_item->watch_fd, "removing");
|
||||||
|
info_item->unused = true;
|
||||||
|
info_item->displayable = false;
|
||||||
|
pv_freecontents_watchfd(info_item);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
memset(&init_time, 0, sizeof(init_time));
|
if (info_item->watch_fd < 0) {
|
||||||
memset(&transfer_elapsed, 0, sizeof(transfer_elapsed));
|
debug("%s %d: %s", "fd", info_item->watch_fd, "negative fd - skipping");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the effective start time: the time we actually
|
* Displayable fd - display, or remove if
|
||||||
* started, plus the total time we spent stopped.
|
* changed.
|
||||||
*/
|
*/
|
||||||
pv_elapsedtime_add(&init_time, &(info_array[idx].start_time), &(state->signal.toffset));
|
|
||||||
|
|
||||||
/*
|
position_now = pv_watchfd_position(info_item);
|
||||||
* Now get the effective elapsed transfer time - current
|
|
||||||
* time minus effective start time.
|
|
||||||
*/
|
|
||||||
pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &init_time);
|
|
||||||
|
|
||||||
info_array[idx].transfer.elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
|
if (position_now < 0) {
|
||||||
|
debug("%s %d: %s", "fd", info_item->watch_fd, "removing");
|
||||||
|
info_item->unused = true;
|
||||||
|
info_item->displayable = false;
|
||||||
|
pv_freecontents_watchfd(info_item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (displayed_lines > 0) {
|
info_item->position = position_now;
|
||||||
debug("%s", "adding newline");
|
|
||||||
pv_tty_write(&(state->flags), "\n", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("%s %d [%d]: %Lf / %Ld", "fd", info_array[idx].watch_fd, idx,
|
memset(&init_time, 0, sizeof(init_time));
|
||||||
info_array[idx].transfer.elapsed_seconds, position_now);
|
memset(&transfer_elapsed, 0, sizeof(transfer_elapsed));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the effective start time: the time we actually
|
||||||
|
* started, plus the total time we spent stopped.
|
||||||
|
*/
|
||||||
|
pv_elapsedtime_add(&init_time, &(info_item->start_time), &(state->signal.toffset));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now get the effective elapsed transfer time - current
|
||||||
|
* time minus effective start time.
|
||||||
|
*/
|
||||||
|
pv_elapsedtime_subtract(&transfer_elapsed, &cur_time, &init_time);
|
||||||
|
|
||||||
|
info_item->transfer.elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
|
||||||
|
|
||||||
|
if (displayed_lines > 0) {
|
||||||
|
debug("%s", "adding newline");
|
||||||
|
pv_tty_write(&(state->flags), "\n", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("%s %d, %s %d [%d/%d]: %Lf / %Ld", "pid", (int) (info_item->watch_pid), "fd",
|
||||||
|
info_item->watch_fd, watch_idx, info_idx, info_item->transfer.elapsed_seconds,
|
||||||
|
position_now);
|
||||||
|
|
||||||
|
if (terminal_resized) {
|
||||||
|
pv_watchpid_setname(state, info_item);
|
||||||
|
info_item->flags.reparse_display = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
info_item->transfer.transferred = position_now;
|
||||||
|
info_item->transfer.total_written = position_now;
|
||||||
|
state->control.name = info_item->display_name;
|
||||||
|
state->control.size = info_item->size;
|
||||||
|
|
||||||
if (info_array[idx].watch_fd >= 0) {
|
|
||||||
info_array[idx].transfer.transferred = position_now;
|
|
||||||
info_array[idx].transfer.total_written = position_now;
|
|
||||||
state->control.name = info_array[idx].display_name;
|
|
||||||
state->control.size = info_array[idx].size;
|
|
||||||
pv_display(&(state->status),
|
pv_display(&(state->status),
|
||||||
&(state->control), &(info_array[idx].flags),
|
&(state->control), &(info_item->flags),
|
||||||
&(info_array[idx].transfer), &(info_array[idx].calc),
|
&(info_item->transfer), &(info_item->calc),
|
||||||
&(state->cursor), &(info_array[idx].display), NULL, false);
|
&(state->cursor), &(info_item->display), NULL, false);
|
||||||
|
|
||||||
/*@-mustfreeonly@ */
|
/*@-mustfreeonly@ */
|
||||||
state->control.name = NULL;
|
state->control.name = NULL;
|
||||||
/*
|
/*
|
||||||
@@ -1008,8 +1002,10 @@ int pv_watchpid_loop(pvstate_t state)
|
|||||||
* so nothing is lost here.
|
* so nothing is lost here.
|
||||||
*/
|
*/
|
||||||
/*@+mustfreeonly@ */
|
/*@+mustfreeonly@ */
|
||||||
|
|
||||||
displayed_lines++;
|
displayed_lines++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1039,10 +1035,22 @@ int pv_watchpid_loop(pvstate_t state)
|
|||||||
pv_tty_write(&(state->flags), "\033[A", 3);
|
pv_tty_write(&(state->flags), "\033[A", 3);
|
||||||
displayed_lines--;
|
displayed_lines--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check whether all watched items have finished. */
|
||||||
|
all_watching_finished = true;
|
||||||
|
for (watch_idx = 0; watch_idx < state->watchfd.count; watch_idx++) {
|
||||||
|
if (watching[watch_idx].finished)
|
||||||
|
continue;
|
||||||
|
all_watching_finished = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!state->control.numeric)
|
||||||
|
pv_tty_write(&(state->flags), "\n", 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clean up our displayed lines on exit.
|
* Clean up our displayed lines after the display loop.
|
||||||
*/
|
*/
|
||||||
blank_lines = prev_displayed_lines;
|
blank_lines = prev_displayed_lines;
|
||||||
while (blank_lines > 0) {
|
while (blank_lines > 0) {
|
||||||
@@ -1060,16 +1068,34 @@ int pv_watchpid_loop(pvstate_t state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free the per-fd state.
|
* If a signal caused the end of the loop, reflect that in the
|
||||||
|
* return code.
|
||||||
*/
|
*/
|
||||||
for (idx = 0; NULL != info_array && idx < array_length; idx++) {
|
if (1 == state->flags.trigger_exit)
|
||||||
pv_freecontents_watchfd(&(info_array[idx]));
|
state->status.exit_status |= PV_ERROREXIT_SIGNAL;
|
||||||
info_array[idx].unused = true;
|
|
||||||
info_array[idx].displayable = false;
|
end_pv_watchfd_loop:
|
||||||
|
/* Free all allocated memory. */
|
||||||
|
/*@-compdestroy@ */
|
||||||
|
for (watch_idx = 0; NULL != watching && watch_idx < state->watchfd.count; watch_idx++) {
|
||||||
|
int info_idx;
|
||||||
|
if (NULL == watching[watch_idx].info_array)
|
||||||
|
continue;
|
||||||
|
for (info_idx = 0; info_idx < watching[watch_idx].array_length; info_idx++) {
|
||||||
|
pv_freecontents_watchfd(&(watching[watch_idx].info_array[info_idx]));
|
||||||
|
}
|
||||||
|
free(watching[watch_idx].info_array);
|
||||||
|
watching[watch_idx].info_array = NULL;
|
||||||
|
watching[watch_idx].array_length = 0;
|
||||||
}
|
}
|
||||||
|
free(watching);
|
||||||
|
watching = NULL;
|
||||||
|
|
||||||
if (NULL != info_array)
|
return state->status.exit_status;
|
||||||
free(info_array);
|
/*@+compdestroy @ */
|
||||||
|
/*
|
||||||
return 0;
|
* splint warns that watching[].info_array->transfer.transfer_buffer
|
||||||
|
* and other deep structures may not be deallocated and so leak
|
||||||
|
* memory, but that's what pv_freecontents_watchfd() takes care of.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-3
@@ -418,12 +418,14 @@ static int pv_compare_watchfd(const void *a, const void *b)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan the given process and update the arrays with any new file
|
* Scan the given process and update the arrays with any new file
|
||||||
* descriptors.
|
* descriptors. If "watch_fd" is not -1, then all other file descriptors
|
||||||
|
* numbers will be ignored,
|
||||||
*
|
*
|
||||||
* Returns 0 on success, 1 if the process no longer exists or could not be
|
* Returns 0 on success, 1 if the process no longer exists or could not be
|
||||||
* read, or 2 for a memory allocation error.
|
* read, or 2 for a memory allocation error.
|
||||||
*/
|
*/
|
||||||
int pv_watchpid_scanfds(pvstate_t state, pid_t watch_pid, int *array_length_ptr, pvwatchfd_t * info_array_ptr)
|
int pv_watchpid_scanfds(pvstate_t state, pid_t watch_pid, int watch_fd, int *array_length_ptr,
|
||||||
|
pvwatchfd_t * info_array_ptr)
|
||||||
{
|
{
|
||||||
int array_length = 0;
|
int array_length = 0;
|
||||||
struct pvwatchfd_s *info_array = NULL;
|
struct pvwatchfd_s *info_array = NULL;
|
||||||
@@ -479,6 +481,10 @@ int pv_watchpid_scanfds(pvstate_t state, pid_t watch_pid, int *array_length_ptr,
|
|||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* If a watch_fd was specified, skip if this isn't it. */
|
||||||
|
if (watch_fd >= 0 && watch_fd != fd)
|
||||||
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Skip if this fd is already known to us.
|
* Skip if this fd is already known to us.
|
||||||
*/
|
*/
|
||||||
@@ -544,7 +550,7 @@ int pv_watchpid_scanfds(pvstate_t state, pid_t watch_pid, int *array_length_ptr,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Retrieve the details of this file descriptor. */
|
/* Retrieve the details of this file descriptor. */
|
||||||
rc = pv_watchfd_info(state, &(info_array[use_idx]), true);
|
rc = pv_watchfd_info(state, &(info_array[use_idx]), -1 == watch_fd ? true : false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lookup failed - mark this slot as being free for re-use.
|
* Lookup failed - mark this slot as being free for re-use.
|
||||||
|
|||||||
Reference in New Issue
Block a user