qga-win: changing --retry-path option behavior
Currently whenever the qemu-ga's service doesn't find the virtio-serial the run_agent() loops in a QGA_RETRY_INTERVAL (default 5 seconds) intervals and try to restart the qemu-ga which causes a synchronous loop. Changed to wait and listen for the serial events by registering for notifications a proper serial event handler that deals with events: DBT_DEVICEARRIVAL indicates that the device has been inserted and is available DBT_DEVICEREMOVECOMPLETE indicates that the devive has been removed Which allow us to determine when the channel path is available for the qemu-ga to restart. Signed-off-by: Bishara AbuHattoum <bishara@daynix.com> Signed-off-by: Sameeh Jubran <sameeh@daynix.com> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
This commit is contained in:
parent
a2c1ac4e22
commit
b70d6afe4d
86
qga/main.c
86
qga/main.c
@ -34,6 +34,7 @@
|
||||
#include "qemu/systemd.h"
|
||||
#include "qemu-version.h"
|
||||
#ifdef _WIN32
|
||||
#include <dbt.h>
|
||||
#include "qga/service-win32.h"
|
||||
#include "qga/vss-win32.h"
|
||||
#endif
|
||||
@ -83,6 +84,7 @@ struct GAState {
|
||||
bool logging_enabled;
|
||||
#ifdef _WIN32
|
||||
GAService service;
|
||||
HANDLE wakeup_event;
|
||||
#endif
|
||||
bool delimit_response;
|
||||
bool frozen;
|
||||
@ -119,6 +121,7 @@ static const char *ga_freeze_whitelist[] = {
|
||||
#ifdef _WIN32
|
||||
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
|
||||
LPVOID ctx);
|
||||
DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data);
|
||||
VOID WINAPI service_main(DWORD argc, TCHAR *argv[]);
|
||||
#endif
|
||||
static int run_agent(GAState *s);
|
||||
@ -677,6 +680,36 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path,
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data)
|
||||
{
|
||||
DWORD ret = NO_ERROR;
|
||||
PDEV_BROADCAST_HDR broadcast_header = (PDEV_BROADCAST_HDR)data;
|
||||
|
||||
if (broadcast_header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
switch (type) {
|
||||
/* Device inserted */
|
||||
case DBT_DEVICEARRIVAL:
|
||||
/* Start QEMU-ga's service */
|
||||
if (!SetEvent(ga_state->wakeup_event)) {
|
||||
ret = GetLastError();
|
||||
}
|
||||
break;
|
||||
/* Device removed */
|
||||
case DBT_DEVICEQUERYREMOVE:
|
||||
case DBT_DEVICEREMOVEPENDING:
|
||||
case DBT_DEVICEREMOVECOMPLETE:
|
||||
/* Stop QEMU-ga's service */
|
||||
if (!ResetEvent(ga_state->wakeup_event)) {
|
||||
ret = GetLastError();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = ERROR_CALL_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
|
||||
LPVOID ctx)
|
||||
{
|
||||
@ -688,9 +721,13 @@ DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
|
||||
case SERVICE_CONTROL_STOP:
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
quit_handler(SIGTERM);
|
||||
SetEvent(ga_state->wakeup_event);
|
||||
service->status.dwCurrentState = SERVICE_STOP_PENDING;
|
||||
SetServiceStatus(service->status_handle, &service->status);
|
||||
break;
|
||||
case SERVICE_CONTROL_DEVICEEVENT:
|
||||
handle_serial_device_events(type, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = ERROR_CALL_NOT_IMPLEMENTED;
|
||||
@ -717,10 +754,24 @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
|
||||
service->status.dwServiceSpecificExitCode = NO_ERROR;
|
||||
service->status.dwCheckPoint = 0;
|
||||
service->status.dwWaitHint = 0;
|
||||
DEV_BROADCAST_DEVICEINTERFACE notification_filter;
|
||||
ZeroMemory(¬ification_filter, sizeof(notification_filter));
|
||||
notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
||||
notification_filter.dbcc_classguid = GUID_VIOSERIAL_PORT;
|
||||
|
||||
service->device_notification_handle =
|
||||
RegisterDeviceNotification(service->status_handle,
|
||||
¬ification_filter, DEVICE_NOTIFY_SERVICE_HANDLE);
|
||||
if (!service->device_notification_handle) {
|
||||
g_critical("Failed to register device notification handle!\n");
|
||||
return;
|
||||
}
|
||||
SetServiceStatus(service->status_handle, &service->status);
|
||||
|
||||
run_agent(ga_state);
|
||||
|
||||
UnregisterDeviceNotification(service->device_notification_handle);
|
||||
service->status.dwCurrentState = SERVICE_STOPPED;
|
||||
SetServiceStatus(service->status_handle, &service->status);
|
||||
}
|
||||
@ -1328,12 +1379,24 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
|
||||
|
||||
s->config = config;
|
||||
s->socket_activation = socket_activation;
|
||||
|
||||
#ifdef _WIN32
|
||||
s->wakeup_event = CreateEvent(NULL, TRUE, FALSE, TEXT("WakeUp"));
|
||||
if (s->wakeup_event == NULL) {
|
||||
g_critical("CreateEvent failed");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
ga_state = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void cleanup_agent(GAState *s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CloseHandle(s->wakeup_event);
|
||||
#endif
|
||||
if (s->command_state) {
|
||||
ga_command_state_cleanup_all(s->command_state);
|
||||
ga_command_state_free(s->command_state);
|
||||
@ -1365,6 +1428,27 @@ static int run_agent_once(GAState *s)
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void wait_for_channel_availability(GAState *s)
|
||||
{
|
||||
g_warning("waiting for channel path...");
|
||||
#ifndef _WIN32
|
||||
sleep(QGA_RETRY_INTERVAL);
|
||||
#else
|
||||
DWORD dwWaitResult;
|
||||
|
||||
dwWaitResult = WaitForSingleObject(s->wakeup_event, INFINITE);
|
||||
|
||||
switch (dwWaitResult) {
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
break;
|
||||
default:
|
||||
g_critical("WaitForSingleObject failed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int run_agent(GAState *s)
|
||||
{
|
||||
int ret = EXIT_SUCCESS;
|
||||
@ -1375,7 +1459,7 @@ static int run_agent(GAState *s)
|
||||
ret = run_agent_once(s);
|
||||
if (s->config->retry_path && !s->force_exit) {
|
||||
g_warning("agent stopped unexpectedly, restarting...");
|
||||
sleep(QGA_RETRY_INTERVAL);
|
||||
wait_for_channel_availability(s);
|
||||
}
|
||||
} while (s->config->retry_path && !s->force_exit);
|
||||
|
||||
|
@ -20,9 +20,13 @@
|
||||
#define QGA_SERVICE_NAME "qemu-ga"
|
||||
#define QGA_SERVICE_DESCRIPTION "Enables integration with QEMU machine emulator and virtualizer."
|
||||
|
||||
static const GUID GUID_VIOSERIAL_PORT = { 0x6fde7521, 0x1b65, 0x48ae,
|
||||
{ 0xb6, 0x28, 0x80, 0xbe, 0x62, 0x1, 0x60, 0x26 } };
|
||||
|
||||
typedef struct GAService {
|
||||
SERVICE_STATUS status;
|
||||
SERVICE_STATUS_HANDLE status_handle;
|
||||
HDEVNOTIFY device_notification_handle;
|
||||
} GAService;
|
||||
|
||||
int ga_install_service(const char *path, const char *logfile,
|
||||
|
Loading…
Reference in New Issue
Block a user