[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