[PATCH] testbot/testagentd: Show a message when testagentd is restarted.
Francois Gouget
fgouget at codeweavers.com
Sun Mar 25 22:20:10 CDT 2018
It can be useful to set up a VM to auto-login on Windows and have
TestAgentd start automatically on boot. However in such a configuration
it can be hard to detect when a test causes Windows to reboot: the
client reconnects automatically just like after a network glitch and the
VM desktop looks normal.
So with this patch TestAgentd can keep track of its restarts, lets the
client query that value, and brings up a dialog if an unexpected restart
occurs.
The start count can also be overriden to deal with cases where some
automated process causes a Windows reboot as part of the VM set up
process.
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
testbot/lib/WineTestBot/TestAgent.pm | 20 +++++
testbot/scripts/TestAgent | 15 +++-
testbot/src/testagentd/platform.h | 8 ++
testbot/src/testagentd/platform_unix.c | 9 ++
testbot/src/testagentd/platform_windows.c | 31 +++++++
testbot/src/testagentd/testagentd.c | 131 +++++++++++++++++++++++++++++-
6 files changed, 210 insertions(+), 4 deletions(-)
diff --git a/testbot/lib/WineTestBot/TestAgent.pm b/testbot/lib/WineTestBot/TestAgent.pm
index 255c1a19b..2f7ec4def 100644
--- a/testbot/lib/WineTestBot/TestAgent.pm
+++ b/testbot/lib/WineTestBot/TestAgent.pm
@@ -42,6 +42,7 @@ my $RPC_GETPROPERTIES = 8;
my $RPC_UPGRADE = 9;
my $RPC_RMCHILDPROC = 10;
my $RPC_GETCWD = 11;
+my $RPC_SETPROPERTY = 12;
my %RpcNames=(
$RPC_PING => 'ping',
@@ -56,6 +57,7 @@ my %RpcNames=(
$RPC_UPGRADE => 'upgrade',
$RPC_RMCHILDPROC => 'rmchildproc',
$RPC_GETCWD => 'getcwd',
+ $RPC_SETPROPERTY => 'setproperty',
);
my $Debug = 0;
@@ -1386,6 +1388,24 @@ sub GetProperties($;$)
return $Properties;
}
+sub SetProperty($$$)
+{
+ my ($self, $PropName, $PropValue) = @_;
+ debug("SetProperty\n");
+
+ # Send the command
+ if (!$self->_StartRPC($RPC_SETPROPERTY) or
+ !$self->_SendListSize('ArgC', 2) or
+ !$self->_SendString('PropName', $PropName) or
+ !$self->_SendString('PropValue', $PropValue))
+ {
+ return undef;
+ }
+
+ # Get the reply
+ return $self->_RecvList('');
+}
+
sub Upgrade($$)
{
my ($self, $Filename) = @_;
diff --git a/testbot/scripts/TestAgent b/testbot/scripts/TestAgent
index c41055616..25fc0d426 100755
--- a/testbot/scripts/TestAgent
+++ b/testbot/scripts/TestAgent
@@ -52,7 +52,7 @@ sub error(@)
print STDERR "$name0:error: ", @_;
}
-my ($Cmd, $Hostname, $LocalFilename, $ServerFilename, $PropName, @Rm);
+my ($Cmd, $Hostname, $LocalFilename, $ServerFilename, $PropName, $PropValue, @Rm);
my (@Run, $RunIn, $RunOut, $RunErr, $WaitPid);
my $SendFlags = 0;
my $RunFlags = 0;
@@ -229,6 +229,12 @@ while (@ARGV)
set_cmd($arg);
$PropName = @ARGV ? check_opt_val($arg, $PropName) : '*';
}
+ elsif ($arg eq "setproperty")
+ {
+ set_cmd($arg);
+ $PropName = check_opt_val($arg, $PropName);
+ $PropValue = check_opt_val($arg, $PropValue);
+ }
elsif ($arg eq "upgrade")
{
set_cmd($arg);
@@ -319,6 +325,8 @@ if (defined $Usage)
print "or $name0 [options] <hostname> wait <pid>\n";
print "or $name0 [options] <hostname> settime\n";
print "or $name0 [options] <hostname> rm <serverfiles>\n";
+ print "or $name0 [options] <hostname> getproperty <name>\n";
+ print "or $name0 [options] <hostname> setproperty <name> <value>\n";
print "or $name0 [options] <hostname> [getcwd|ping|version]\n";
print "\n";
print "This is a testagentd client. It can be used to send/receive files and to run commands on the server.\n";
@@ -346,6 +354,7 @@ if (defined $Usage)
print " getproperty <name> Retrieves and prints the specified server property, for\n";
print " instance its architecture, 'server.arch'. One can print all the\n";
print " properties at once by omitting the name or setting it to '*'.\n";
+ print " setproperty <name> <value> Sets the specified property.\n";
print " getcwd Returns the server's current working directory.\n";
print " ping Makes sure the server is still alive.\n";
print " upgrade Replaces the server executable with the specified file and\n";
@@ -480,6 +489,10 @@ elsif ($Cmd eq "getversion")
$Result = $TA->GetVersion();
print "Version=$Result\n" if (defined $Result);
}
+elsif ($Cmd eq "setproperty")
+{
+ $Result = $TA->SetProperty($PropName, $PropValue);
+}
elsif ($Cmd eq "ping")
{
$Result = $TA->Ping();
diff --git a/testbot/src/testagentd/platform.h b/testbot/src/testagentd/platform.h
index 84ab4917c..98dfe394c 100644
--- a/testbot/src/testagentd/platform.h
+++ b/testbot/src/testagentd/platform.h
@@ -103,6 +103,14 @@ int platform_settime(uint64_t epoch, uint32_t leeway);
*/
int platform_upgrade_script(const char* script, const char* tmpserver, char** argv);
+/* Called when the message has been dismissed by the user.
+ */
+typedef void (*message_dismissed_func)(void);
+
+/* Shows the specified message.
+ */
+void platform_show_message(const char* message, message_dismissed_func func);
+
/* Returns a string describing the last socket-related error */
int sockeintr(void);
const char* sockerror(void);
diff --git a/testbot/src/testagentd/platform_unix.c b/testbot/src/testagentd/platform_unix.c
index dcd4d69b6..fbaa995b0 100644
--- a/testbot/src/testagentd/platform_unix.c
+++ b/testbot/src/testagentd/platform_unix.c
@@ -270,6 +270,15 @@ int platform_upgrade_script(const char* script, const char* tmpserver, char** ar
return 1;
}
+void platform_show_message(const char* message, message_dismissed_func dismissed)
+{
+ /* Don't bother trying to pop up a GUI. There may not be one anyway.
+ * Since the user has no way to dismiss the dialog the dismissed function
+ * is not called.
+ */
+ fprintf(stderr, "%s", message);
+}
+
int sockeintr(void)
{
return errno == EINTR;
diff --git a/testbot/src/testagentd/platform_windows.c b/testbot/src/testagentd/platform_windows.c
index 1efac1c77..5c55b2004 100644
--- a/testbot/src/testagentd/platform_windows.c
+++ b/testbot/src/testagentd/platform_windows.c
@@ -312,6 +312,37 @@ int platform_upgrade_script(const char* script, const char* tmpserver, char** ar
return 1;
}
+struct msg_thread_t
+{
+ char* message;
+ message_dismissed_func dismissed;
+};
+
+DWORD WINAPI msg_thread(LPVOID parameter)
+{
+ struct msg_thread_t* data = parameter;
+
+ MessageBoxA(NULL, data->message, "Message", MB_OK);
+ free(data->message);
+ if (data->dismissed)
+ (*data->dismissed)();
+ free(data);
+ return 0;
+}
+
+void platform_show_message(const char* message, message_dismissed_func dismissed)
+{
+ HANDLE thread;
+ struct msg_thread_t* data = malloc(sizeof(struct msg_thread_t));
+
+ fprintf(stderr, message);
+
+ data->message = strdup(message);
+ data->dismissed = dismissed;
+ thread = CreateThread(NULL, 0, &msg_thread, data, 0, NULL);
+ CloseHandle(thread);
+}
+
int sockretry(void)
{
return (WSAGetLastError() == WSAEINTR);
diff --git a/testbot/src/testagentd/testagentd.c b/testbot/src/testagentd/testagentd.c
index 5f0f54ca2..79016c067 100644
--- a/testbot/src/testagentd/testagentd.c
+++ b/testbot/src/testagentd/testagentd.c
@@ -37,9 +37,10 @@
* 1.3: Fix the zero / infinite timeouts in the wait2 RPC.
* 1.4: Add the settime RPC.
* 1.5: Add support for upgrading the server.
- * 1.6: Add support for the rmchildproc and getcwd RPC.
+ * 1.6: Add the rmchildproc and getcwd RPCs.
+ * 1.7: Add --show-restarts and the setproperty RPC.
*/
-#define PROTOCOL_VERSION "testagentd 1.6"
+#define PROTOCOL_VERSION "testagentd 1.7"
#define BLOCK_SIZE 65536
@@ -91,6 +92,7 @@ enum rpc_ids_t
RPCID_UPGRADE,
RPCID_RMCHILDPROC,
RPCID_GETCWD,
+ RPCID_SETPROPERTY,
};
/* This is the RPC currently being processed */
@@ -113,6 +115,7 @@ static const char* rpc_name(uint32_t id)
"upgrade",
"rmchildproc",
"getcwd",
+ "setproperty",
};
if (id < sizeof(names) / sizeof(*names))
@@ -619,6 +622,47 @@ static int send_file(SOCKET client, int fd, const char* filename)
}
+/*
+ * TestAgentd restart / Windows reboot tracking.
+ */
+
+static unsigned int start_count = 0;
+
+static char* start_count_filename(void)
+{
+ char* filename = malloc(strlen(name0) + 5 + 1);
+ sprintf(filename, "%s.data", name0);
+ return filename;
+}
+
+static void load_start_count(void)
+{
+ FILE *fh;
+ char* filename = start_count_filename();
+ fh = fopen(filename, "r");
+ if (fh)
+ {
+ if (!fscanf(fh, "%d", &start_count))
+ start_count = 0;
+ fclose(fh);
+ }
+ free(filename);
+}
+
+static void save_start_count(void)
+{
+ FILE *fh;
+ char* filename = start_count_filename();
+ fh = fopen(filename, "w");
+ if (fh)
+ {
+ fprintf(fh, "%d\n", start_count);
+ fclose(fh);
+ }
+ free(filename);
+}
+
+
/*
* High-level operations.
*/
@@ -969,7 +1013,7 @@ static void do_getproperties(SOCKET client)
send_error(client);
return;
}
- send_list_size(client, 2);
+ send_list_size(client, 3);
format_msg(&buf, &size, "protocol.version=%s", PROTOCOL_VERSION);
send_string(client, buf);
@@ -984,9 +1028,53 @@ static void do_getproperties(SOCKET client)
#endif
format_msg(&buf, &size, "server.arch=%s", arch);
send_string(client, buf);
+
+ format_msg(&buf, &size, "start.count=%u", start_count);
+ send_string(client, buf);
+
free(buf);
}
+static void do_setproperty(SOCKET client)
+{
+ char *name = NULL, *value = NULL;
+
+ if (!expect_list_size(client, 2) ||
+ !recv_string(client, &name) ||
+ !recv_string(client, &value))
+ {
+ send_error(client);
+ return;
+ }
+
+ if (strcmp(name, "start.count") == 0)
+ {
+ unsigned int val;
+ if (sscanf(value, "%u", &val) == 1)
+ {
+ start_count = val;
+ save_start_count();
+ send_list_size(client, 0);
+ }
+ else
+ {
+ set_status(ST_ERROR, "'%s' is not a valid %s value", value, name);
+ send_error(client);
+ }
+ }
+ else if (strcmp(name, "protocol.version") == 0 ||
+ strcmp(name, "server.arch") == 0)
+ {
+ set_status(ST_ERROR, "%s is read-only", name);
+ send_error(client);
+ }
+ else
+ {
+ set_status(ST_ERROR, "unknown property %s", name);
+ send_error(client);
+ }
+}
+
static void do_upgrade(SOCKET client)
{
static const char *filename = "testagentd.tmp";
@@ -1033,6 +1121,10 @@ static void do_upgrade(SOCKET client)
free(args[0]);
if (success)
{
+ /* Decrement the start count since this one is intentional */
+ start_count--;
+ save_start_count();
+
broken = 1;
quit = 1;
}
@@ -1129,6 +1221,9 @@ static void process_rpc(SOCKET client)
case RPCID_RMCHILDPROC:
do_rmchildproc(client);
break;
+ case RPCID_SETPROPERTY:
+ do_setproperty(client);
+ break;
default:
do_unknown(client, rpcid);
}
@@ -1214,11 +1309,33 @@ static int is_host_allowed(SOCKET client, const char* srchost, int addrlen)
return 0;
}
+
+static void reset_start_count(void)
+{
+ start_count = 1;
+ save_start_count();
+}
+
+static void check_start_count(void)
+{
+ load_start_count();
+ start_count++;
+ save_start_count();
+
+ if (start_count > 1)
+ {
+ char msg[255];
+ sprintf(msg, "%s was restarted (%d). Did Windows reboot?\n", name0, start_count);
+ platform_show_message(msg, &reset_start_count);
+ }
+}
+
int main(int argc, char** argv)
{
const char* p;
char** arg;
int opt_detach = 0;
+ int opt_show_restarts = 0;
char* opt_port = NULL;
char* opt_srchost = NULL;
struct addrinfo *addresses, *addrp;
@@ -1247,6 +1364,10 @@ int main(int argc, char** argv)
{
opt_detach = 1;
}
+ else if (strcmp(*arg, "--show-restarts") == 0)
+ {
+ opt_show_restarts = 1;
+ }
else if (strcmp(*arg, "--help") == 0)
{
opt_usage = 1;
@@ -1328,11 +1449,15 @@ int main(int argc, char** argv)
printf(" --debug Prints detailed information about what happens.\n");
printf(" --detach Detach from the console / terminal. Note that on Windows you should\n");
printf(" combine this with start: start %s --detach ...\n", name0);
+ printf(" --show-restarts Shows a message if %s has been restarted (for instance\n", name0);
+ printf(" because of a Windows reboot).\n");
printf(" --help Shows this usage message.\n");
exit(0);
}
if (opt_detach)
platform_detach_console();
+ if (opt_show_restarts)
+ check_start_count();
/* Bind to the host in a protocol neutral way */
#ifdef SOCK_CLOEXEC
--
2.16.2
More information about the wine-devel
mailing list