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:
Andrew Wood
2025-09-27 17:55:18 +01:00
parent c49fc4aa57
commit 3ea19829d7
4 changed files with 351 additions and 324 deletions
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.