diff --git a/src/include/pv-internal.h b/src/include/pv-internal.h index 1c48fee..9fa2d4b 100644 --- a/src/include/pv-internal.h +++ b/src/include/pv-internal.h @@ -624,7 +624,6 @@ void pv_sig_checkbg(void); void pv_sig_nopause(void); bool pv_remote_check(pvstate_t); -int pv_remote_set(pvstate_t); int pv_watchfd_info(pvstate_t, pvwatchfd_t, bool); bool pv_watchfd_changed(pvwatchfd_t); diff --git a/src/main/main.c b/src/main/main.c index 0f64d29..38fc1d9 100644 --- a/src/main/main.c +++ b/src/main/main.c @@ -24,7 +24,7 @@ #endif int pv_remote_set(opts_t, pvstate_t); -int pv_remote_transferstate_fetch(pvstate_t, pid_t, /*@null@ */ off_t *); +int pv_remote_transferstate_fetch(pvstate_t, pid_t); /* * Write a PID file, returning nonzero on error. Write it atomically, such @@ -500,7 +500,7 @@ int main(int argc, char **argv) */ if (PV_ACTION_QUERY == opts->action) { opts->size = 0; - retcode = pv_remote_transferstate_fetch(state, opts->query, &(opts->size)); + retcode = pv_remote_transferstate_fetch(state, opts->query); if (0 != retcode) { pv_sig_fini(state); pv_state_free(state); diff --git a/src/main/remote.c b/src/main/remote.c index 867a68a..4918654 100644 --- a/src/main/remote.c +++ b/src/main/remote.c @@ -6,9 +6,12 @@ * License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'. */ +/* TODO: move this to srv/pv/ instead of src/main/ since it uses internal pv structures. */ + #include "config.h" #include "options.h" #include "pv.h" +#include "pv-internal.h" #include #include @@ -25,6 +28,7 @@ void pv_error(char *, ...); #ifdef PV_REMOTE_CONTROL +/* Structure for transferring settings with --remote. */ struct remote_msg { bool progress; /* progress bar flag */ bool timer; /* timer flag */ @@ -48,6 +52,14 @@ struct remote_msg { char extra_display[256]; /* flawfinder: ignore */ }; +/* Structure for transferring transfer state with --query. */ +struct query_msg { + long double elapsed_seconds; /* from state.transfer */ + off_t transferred; /* from state.transfer */ + off_t size; /* from state.control */ + bool response; /* true if this is the response signal. */ +}; + /* * flawfinder rationale: name, format, and extra_display are always * explicitly zeroed and bounded to one less than their size so they are @@ -210,6 +222,7 @@ int pv_remote_set(opts_t opts, pvstate_t state) /* * Remove the remote control file. */ + debug("%s: %s", "removing", control_filename); if (0 != remove(control_filename)) { pv_error("%s", strerror(errno)); } @@ -333,13 +346,20 @@ static bool pv__rxsignal_usr2(pvstate_t state) * message containing the total size and current transfer state to the * control file and send a SIGUSR1 to the sending process. If it's type 1 * (response), update the transfer state - and control.size - from the - * message in the control file. + * message in the control file. In both cases, the receiver of the message + * deletes the associated control file. + * + * Returns true if a signal was received and dealt with, false otherwise. + * + * If match_sender is not zero, then ignores the signal and returns false if + * the sending PID is not match_sender. */ -static void pv__rxsignal_usr1(pvstate_t state) +static bool pv__rxsignal_usr1(pvstate_t state, pid_t match_sender) { pid_t signal_sender; char control_filename[4096]; /* flawfinder: ignore */ FILE *control_fptr; + struct query_msg msgbuf; /* flawfinder rationale: as above. */ @@ -348,23 +368,97 @@ static void pv__rxsignal_usr1(pvstate_t state) */ signal_sender = 0; if (!pv_sigusr1_received(state, &signal_sender)) - return; + return false; + + /* + * If match_sender was specified, ignore the signal if the sender + * doesn't match. + */ + if ((0 != match_sender) && (signal_sender != match_sender)) { + debug("%s=%d, %s=%d - %s", "match_sender", match_sender, "signal_sender", signal_sender, + "ignoring USR1"); + return false; + } memset(control_filename, 0, sizeof(control_filename)); control_fptr = pv_open_controlfile(control_filename, sizeof(control_filename), signal_sender, SIGUSR1, false); if (NULL == control_fptr) { pv_error("%s: %s", control_filename, strerror(errno)); - return; + return false; } -/* TODO: read the message */ + /* + * Read the message buffer from the remote control file, close it, + * and delete it. + */ + if (1 != fread(&msgbuf, sizeof(msgbuf), 1, control_fptr)) { + pv_error("%s", strerror(errno)); + (void) fclose(control_fptr); + return false; + } if (0 != fclose(control_fptr)) { pv_error("%s", strerror(errno)); - return; + return false; } -/* TODO: handle message */ + debug("%s: %s", "removing", control_filename); + if (0 != remove(control_filename)) { + pv_error("%s", strerror(errno)); + return false; + } + + if (msgbuf.response) { + /* Response message - update local state. */ + debug("%s: %d", "query response received", signal_sender); + state->transfer.elapsed_seconds = msgbuf.elapsed_seconds; + state->transfer.transferred = msgbuf.transferred; + state->control.size = msgbuf.size; + return true; + } + + /* Query message - transmit state to sender. */ + + debug("%s: %d", "query received", signal_sender); + + msgbuf.elapsed_seconds = state->transfer.elapsed_seconds; + msgbuf.transferred = state->transfer.transferred; + msgbuf.size = state->control.size; + msgbuf.response = true; + + memset(control_filename, 0, sizeof(control_filename)); + control_fptr = pv_open_controlfile(control_filename, sizeof(control_filename), (pid_t) getpid(), SIGUSR1, true); + if (NULL == control_fptr) { + pv_error("%s", strerror(errno)); + return true; /* true since the signal was received. */ + } + + if (1 != fwrite(&msgbuf, sizeof(msgbuf), 1, control_fptr)) { + pv_error("%s", strerror(errno)); + (void) fclose(control_fptr); + (void) remove(control_filename); + return true; /* as above. */ + } + + if (0 != fclose(control_fptr)) { + pv_error("%s", strerror(errno)); + (void) remove(control_filename); + return true; + } + + /* + * Send a SIGUSR1 signal to the remote process, to tell it this + * query response message is ready to read. + */ + if (kill((pid_t) (signal_sender), SIGUSR1) != 0) { + pv_error("%u: %s", signal_sender, strerror(errno)); + (void) remove(control_filename); + return true; + } + + debug("%s: %d", "query response sent", signal_sender); + + return true; } @@ -385,23 +479,134 @@ bool pv_remote_check(pvstate_t state) bool received_remote; received_remote = pv__rxsignal_usr2(state); - pv__rxsignal_usr1(state); + (void) pv__rxsignal_usr1(state, 0); return received_remote; } /* - * Replace the transfer state with that of the given process, populating - * *sizeptr with that process's idea of the total transfer size if sizeptr - * isn't NULL. + * Replace the transfer state with that of the given process, including the + * total transfer size. * * Returns nonzero on error, after reporting the error. */ -int pv_remote_transferstate_fetch(pvstate_t state, pid_t query, /*@null@ */ off_t * sizeptr) +int pv_remote_transferstate_fetch(pvstate_t state, pid_t query) { - /* TODO: write this */ + char control_filename[4096]; /* flawfinder: ignore */ + FILE *control_fptr; + struct query_msg msgbuf; + pid_t signal_sender; + long timeout; + bool received; + + /* + * flawfinder rationale: buffer is large enough, explicitly zeroed, + * and always bounded properly as we are only writing to it with + * pv_snprintf(). + */ + + /* + * Check that the remote process exists. + */ + if (kill((pid_t) (query), 0) != 0) { + pv_error("%u: %s", query, strerror(errno)); + return PV_ERROREXIT_REMOTE_OR_PID; + } + + /* Set up the query message. */ + memset(&msgbuf, 0, sizeof(msgbuf)); + msgbuf.response = false; + + /* + * Get the filename and file stream to use. + */ + memset(control_filename, 0, sizeof(control_filename)); + control_fptr = pv_open_controlfile(control_filename, sizeof(control_filename), (pid_t) getpid(), SIGUSR1, true); + if (NULL == control_fptr) { + pv_error("%s", strerror(errno)); + return PV_ERROREXIT_REMOTE_OR_PID; + } + + /* + * Write the message to the control file, and close it. + */ + if (1 != fwrite(&msgbuf, sizeof(msgbuf), 1, control_fptr)) { + pv_error("%s", strerror(errno)); + (void) fclose(control_fptr); + (void) remove(control_filename); + return PV_ERROREXIT_REMOTE_OR_PID; + } + + if (0 != fclose(control_fptr)) { + pv_error("%s", strerror(errno)); + (void) remove(control_filename); + return PV_ERROREXIT_REMOTE_OR_PID; + } + + /* + * Send a SIGUSR1 signal to the remote process, to tell it a query + * message is ready, after first clearing our own SIGUSR1 received + * flag. + */ + signal_sender = 0; + (void) pv_sigusr1_received(state, &signal_sender); + if (kill((pid_t) (query), SIGUSR1) != 0) { + pv_error("%u: %s", query, strerror(errno)); + (void) remove(control_filename); + return PV_ERROREXIT_REMOTE_OR_PID; + } + + debug("%s: %d", "query sent", query); + + /* + * Wait for a SIGUSR1 signal to be sent back with a query response + * message. + */ + + timeout = 1100000; + received = false; + + while (timeout > 10000 && !received && 0 == state->flags.trigger_exit) { + struct timeval tv; + + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = 0; + tv.tv_usec = 10000; + /*@-nullpass@ *//* splint: NULL is OK with select() */ + (void) select(0, NULL, NULL, NULL, &tv); + /*@+nullpass@ */ + timeout -= 10000; + + if (pv__rxsignal_usr1(state, query)) { + debug("%s", "response received"); + received = true; + } + } + + /* + * Remove the control file in case it still exists - the other + * process should have removed it. + */ + debug("%s: %s", "cleaning up", control_filename); + (void) remove(control_filename); + + /* + * Return 0 if the message was received. + */ + if (received) + return 0; + + /*@-mustfreefresh@ */ + /* + * splint note: the gettext calls made by _() cause memory leak + * warnings, but in this case it's unavoidable, and mitigated by the + * fact we only translate each string once. + */ + pv_error("%u: %s", query, _("message not received")); return PV_ERROREXIT_REMOTE_OR_PID; + /*@+mustfreefresh @ */ + } @@ -428,8 +633,7 @@ int pv_remote_set( /*@unused@ */ int pv_remote_transferstate_fetch( /*@unused@ */ __attribute__((unused)) pvstate_t state, /*@unused@ */ - __attribute__((unused)) pid_t query, /*@null@ *//*@unused@ */ - __attribute__((unused)) off_t * sizeptr) + __attribute__((unused)) pid_t query) { /*@-mustfreefresh@ *//* splint - see above */ fprintf(stderr, "%s\n", _("SA_SIGINFO not supported on this system"));