On Windows it used to work only once and leave the batch script behind.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
Now that it works right it could be used to at least partially automate
some VM updates.
testbot/src/testagentd/platform.h | 9 ++-
testbot/src/testagentd/platform_unix.c | 62 +++++++++++++++------
testbot/src/testagentd/platform_windows.c | 92 ++++++++++++++++++++++++-------
testbot/src/testagentd/testagentd.c | 31 ++++-------
4 files changed, 130 insertions(+), 64 deletions(-)
diff --git a/testbot/src/testagentd/platform.h b/testbot/src/testagentd/platform.h
index 98dfe394c..06882dedc 100644
--- a/testbot/src/testagentd/platform.h
+++ b/testbot/src/testagentd/platform.h
@@ -96,12 +96,11 @@ int platform_rmchildproc(SOCKET client, uint64_t pid);
*/
int platform_settime(uint64_t epoch, uint32_t leeway);
-/* Creates a script to be invoked to upgrade the current server.
- * The current server is responsible for starting the script and quickly exit.
- * The script will wait a bit, replace the server file and restart the server
- * with the same arguments as the original server.
+/* Replaces server file and restarts the server.
+ * Returns non-zero if successful, which will let the caller know to complete
+ * the current RPC and cleanly exit. Returns 0 otherwise.
*/
-int platform_upgrade_script(const char* script, const char* tmpserver, char** argv);
+int platform_upgrade(const char* tmpserver, char** argv);
/* Called when the message has been dismissed by the user.
*/
diff --git a/testbot/src/testagentd/platform_unix.c b/testbot/src/testagentd/platform_unix.c
index fbaa995b0..7a3a8f8e0 100644
--- a/testbot/src/testagentd/platform_unix.c
+++ b/testbot/src/testagentd/platform_unix.c
@@ -239,34 +239,60 @@ int platform_settime(uint64_t epoch, uint32_t leeway)
return 1;
}
-int platform_upgrade_script(const char* script, const char* tmpserver, char** argv)
+
+int platform_upgrade(const char* tmpserver, char** argv)
{
- char** arg;
- FILE* fh;
+ static const char* oldserver = "testagentd.old";
+ int pipefds[2];
+ pid_t pid;
- fh = fopen(script, "w");
- if (!fh)
+ if (rename(argv[0], oldserver))
{
- set_status(ST_ERROR, "unable to open '%s' for writing: %s", script, strerror(errno));
+ set_status(ST_ERROR, "unable to move the current server file out of the way: %s", strerror(errno));
return 0;
}
- /* Allow time for the server to exit */
- fprintf(fh, "#!/bin/sh\n");
- fprintf(fh, "sleep 1\n");
- fprintf(fh, "mv %s %s\n", tmpserver, argv[0]);
- arg = argv;
- while (*arg)
+
+ if (rename(tmpserver, argv[0]))
{
- fprintf(fh, "'%s' ", *arg);
- arg++;
+ set_status(ST_ERROR, "unable to move the currentnew server file into place: %s", strerror(errno));
+ rename(oldserver, argv[0]);
+ return 0;
}
- fprintf(fh, "\n");
- fclose(fh);
- if (chmod(script, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
+ if (pipe(pipefds))
{
- set_status(ST_ERROR, "could not make '%s' executable: %s", script, strerror(errno));
+ set_status(ST_ERROR, "could not synchronize with the new process: %s", strerror(errno));
+ rename(oldserver, argv[0]);
return 0;
}
+
+ pid = fork();
+ if (pid < 0)
+ {
+ set_status(ST_ERROR, "unable to start the new server: %s", strerror(errno));
+ close(pipefds[0]);
+ close(pipefds[1]);
+ rename(oldserver, argv[0]);
+ return 0;
+ }
+
+ unlink(oldserver);
+ if (!pid)
+ {
+ /* The child process is responsible for cleanly closing the connection
+ * to the client.
+ */
+ close(pipefds[0]);
+ return 1;
+ }
+ close(pipefds[1]);
+
+ /* Wait for the read to fail which means the child exited and released the
+ * TestAgentd port.
+ */
+ read(pipefds[0], &pid, 1);
+ close(pipefds[0]);
+
+ execvp(argv[0], argv);
return 1;
}
diff --git a/testbot/src/testagentd/platform_windows.c b/testbot/src/testagentd/platform_windows.c
index 5c55b2004..bb52a0241 100644
--- a/testbot/src/testagentd/platform_windows.c
+++ b/testbot/src/testagentd/platform_windows.c
@@ -280,36 +280,68 @@ int platform_settime(uint64_t epoch, uint32_t leeway)
}
-int platform_upgrade_script(const char* script, const char* tmpserver, char** argv)
+char* get_server_filename(int old)
{
- char testagentd[MAX_PATH];
+ char filename[MAX_PATH];
DWORD rc;
- FILE* fh;
- rc = GetModuleFileName(NULL, testagentd, sizeof(testagentd));
- if (!rc || rc == sizeof(testagentd))
+ rc = GetModuleFileName(NULL, filename, sizeof(filename));
+ if (!rc || rc == sizeof(filename))
+ return NULL;
+
+ if (old)
{
- set_status(ST_ERROR, "unable to get the current process filename (%lu, le=%lu)", rc, GetLastError());
- return 0;
+ if (rc >= sizeof(filename) - 5)
+ return NULL;
+ strcat(filename, ".old");
}
+ return strdup(filename);
+}
+
+int platform_upgrade(const char* tmpserver, char** argv)
+{
+ char *testagentd = NULL, *oldtestagentd = NULL;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ int success = 0;
- fh = fopen(script, "w");
- if (!fh)
+ testagentd = get_server_filename(0);
+ oldtestagentd = get_server_filename(1);
+ if (!testagentd || !oldtestagentd)
{
- set_status(ST_ERROR, "unable to open '%s' for writing: %s", script, strerror(errno));
- return 0;
+ set_status(ST_ERROR, "unable to get the process filenames (le=%lu)", GetLastError());
+ goto done;
}
- /* Allow time for the server to exit */
- fprintf(fh, "ping -n 1 -w 1000 1.1.1.1 >/nul\r\n");
- /* Note that preserving the server filename is necessary and sufficient
- * in order to get through the Windows firewall.
- */
- fprintf(fh, "copy /y \"%s\" \"%s\"\r\n", tmpserver, testagentd);
- fprintf(fh, "del \"%s\"\r\n", tmpserver);
- fprintf(fh, "%s\r\n", GetCommandLine());
- fclose(fh);
- return 1;
+ if (!MoveFile(testagentd, oldtestagentd))
+ {
+ set_status(ST_ERROR, "unable to move the current server file out of the way (le=%lu)", GetLastError());
+ goto done;
+ }
+ if (!MoveFile(tmpserver, testagentd))
+ {
+ set_status(ST_ERROR, "unable to move the new server file in place (le=%lu)", GetLastError());
+ MoveFile(oldtestagentd, testagentd);
+ goto done;
+ }
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ if (!CreateProcessA(testagentd, GetCommandLineA(), NULL, NULL, TRUE,
+ CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
+ {
+ set_status(ST_ERROR, "could not run '%s': %lu", GetCommandLineA(), GetLastError());
+ MoveFile(oldtestagentd, testagentd);
+ goto done;
+ }
+
+ /* The new server will delete the old server file on startup */
+ success = 1;
+
+ done:
+ free(testagentd);
+ free(oldtestagentd);
+ return success;
}
struct msg_thread_t
@@ -488,11 +520,29 @@ void platform_detach_console(void)
int platform_init(void)
{
+ char *oldtestagentd;
HMODULE hdll;
WORD wVersionRequested;
WSADATA wsaData;
int rc;
+ /* Delete the old server file if any */
+ oldtestagentd = get_server_filename(1);
+ if (oldtestagentd)
+ {
+ /* This also serves to ensure the old server has released the port
+ * before we attempt to open our own.
+ */
+ do
+ {
+ if (!DeleteFileA(oldtestagentd))
+ Sleep(500);
+ }
+ while (GetLastError() == ERROR_ACCESS_DENIED);
+ free(oldtestagentd);
+ }
+
+
wVersionRequested = MAKEWORD(2, 2);
rc = WSAStartup(wVersionRequested, &wsaData);
if (rc)
diff --git a/testbot/src/testagentd/testagentd.c b/testbot/src/testagentd/testagentd.c
index 79016c067..b4a26fc58 100644
--- a/testbot/src/testagentd/testagentd.c
+++ b/testbot/src/testagentd/testagentd.c
@@ -1078,7 +1078,6 @@ static void do_setproperty(SOCKET client)
static void do_upgrade(SOCKET client)
{
static const char *filename = "testagentd.tmp";
- static const char* upgrade_script = "./replace.bat";
int fd, success;
if (!expect_list_size(client, 1)
@@ -1103,34 +1102,26 @@ static void do_upgrade(SOCKET client)
close(fd);
}
- if (!success)
- unlink(filename);
- else
- success = platform_upgrade_script(upgrade_script, filename, server_argv);
+ if (success)
+ success = platform_upgrade(filename, server_argv);
if (success)
{
- char* args[2];
- char* redirects[3] = {"", "", ""};
-
send_list_size(client, 0);
- args[0] = strdup(upgrade_script);
- args[1] = NULL;
- success = platform_run(args, RUN_DNT, redirects);
- free(args[0]);
- if (success)
- {
- /* Decrement the start count since this one is intentional */
- start_count--;
- save_start_count();
+ /* Decrement the start count since this one is intentional */
+ start_count--;
+ save_start_count();
- broken = 1;
- quit = 1;
- }
+ /* Tell the main loop to quit */
+ broken = 1;
+ quit = 1;
}
else
+ {
send_error(client);
+ unlink(filename);
+ }
}
static void do_getcwd(SOCKET client)
--
2.16.2