Implement --query message send/receive (#101).

This commit is contained in:
Andrew Wood
2025-10-23 23:27:34 +01:00
parent 4df6fbf6f7
commit 15ed99eee7
3 changed files with 221 additions and 18 deletions
-1
View File
@@ -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);
+2 -2
View File
@@ -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);
+219 -15
View File
@@ -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 <stdio.h>
#include <stdlib.h>
@@ -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"));