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/systemd.h"
|
||||||
#include "qemu-version.h"
|
#include "qemu-version.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#include <dbt.h>
|
||||||
#include "qga/service-win32.h"
|
#include "qga/service-win32.h"
|
||||||
#include "qga/vss-win32.h"
|
#include "qga/vss-win32.h"
|
||||||
#endif
|
#endif
|
||||||
@ -83,6 +84,7 @@ struct GAState {
|
|||||||
bool logging_enabled;
|
bool logging_enabled;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
GAService service;
|
GAService service;
|
||||||
|
HANDLE wakeup_event;
|
||||||
#endif
|
#endif
|
||||||
bool delimit_response;
|
bool delimit_response;
|
||||||
bool frozen;
|
bool frozen;
|
||||||
@ -119,6 +121,7 @@ static const char *ga_freeze_whitelist[] = {
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
|
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
|
||||||
LPVOID ctx);
|
LPVOID ctx);
|
||||||
|
DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data);
|
||||||
VOID WINAPI service_main(DWORD argc, TCHAR *argv[]);
|
VOID WINAPI service_main(DWORD argc, TCHAR *argv[]);
|
||||||
#endif
|
#endif
|
||||||
static int run_agent(GAState *s);
|
static int run_agent(GAState *s);
|
||||||
@ -677,6 +680,36 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#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,
|
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
|
||||||
LPVOID ctx)
|
LPVOID ctx)
|
||||||
{
|
{
|
||||||
@ -688,9 +721,13 @@ DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
|
|||||||
case SERVICE_CONTROL_STOP:
|
case SERVICE_CONTROL_STOP:
|
||||||
case SERVICE_CONTROL_SHUTDOWN:
|
case SERVICE_CONTROL_SHUTDOWN:
|
||||||
quit_handler(SIGTERM);
|
quit_handler(SIGTERM);
|
||||||
|
SetEvent(ga_state->wakeup_event);
|
||||||
service->status.dwCurrentState = SERVICE_STOP_PENDING;
|
service->status.dwCurrentState = SERVICE_STOP_PENDING;
|
||||||
SetServiceStatus(service->status_handle, &service->status);
|
SetServiceStatus(service->status_handle, &service->status);
|
||||||
break;
|
break;
|
||||||
|
case SERVICE_CONTROL_DEVICEEVENT:
|
||||||
|
handle_serial_device_events(type, data);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ret = ERROR_CALL_NOT_IMPLEMENTED;
|
ret = ERROR_CALL_NOT_IMPLEMENTED;
|
||||||
@ -717,10 +754,24 @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
|
|||||||
service->status.dwServiceSpecificExitCode = NO_ERROR;
|
service->status.dwServiceSpecificExitCode = NO_ERROR;
|
||||||
service->status.dwCheckPoint = 0;
|
service->status.dwCheckPoint = 0;
|
||||||
service->status.dwWaitHint = 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);
|
SetServiceStatus(service->status_handle, &service->status);
|
||||||
|
|
||||||
run_agent(ga_state);
|
run_agent(ga_state);
|
||||||
|
|
||||||
|
UnregisterDeviceNotification(service->device_notification_handle);
|
||||||
service->status.dwCurrentState = SERVICE_STOPPED;
|
service->status.dwCurrentState = SERVICE_STOPPED;
|
||||||
SetServiceStatus(service->status_handle, &service->status);
|
SetServiceStatus(service->status_handle, &service->status);
|
||||||
}
|
}
|
||||||
@ -1328,12 +1379,24 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
|
|||||||
|
|
||||||
s->config = config;
|
s->config = config;
|
||||||
s->socket_activation = socket_activation;
|
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;
|
ga_state = s;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_agent(GAState *s)
|
static void cleanup_agent(GAState *s)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
CloseHandle(s->wakeup_event);
|
||||||
|
#endif
|
||||||
if (s->command_state) {
|
if (s->command_state) {
|
||||||
ga_command_state_cleanup_all(s->command_state);
|
ga_command_state_cleanup_all(s->command_state);
|
||||||
ga_command_state_free(s->command_state);
|
ga_command_state_free(s->command_state);
|
||||||
@ -1365,6 +1428,27 @@ static int run_agent_once(GAState *s)
|
|||||||
return EXIT_SUCCESS;
|
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)
|
static int run_agent(GAState *s)
|
||||||
{
|
{
|
||||||
int ret = EXIT_SUCCESS;
|
int ret = EXIT_SUCCESS;
|
||||||
@ -1375,7 +1459,7 @@ static int run_agent(GAState *s)
|
|||||||
ret = run_agent_once(s);
|
ret = run_agent_once(s);
|
||||||
if (s->config->retry_path && !s->force_exit) {
|
if (s->config->retry_path && !s->force_exit) {
|
||||||
g_warning("agent stopped unexpectedly, restarting...");
|
g_warning("agent stopped unexpectedly, restarting...");
|
||||||
sleep(QGA_RETRY_INTERVAL);
|
wait_for_channel_availability(s);
|
||||||
}
|
}
|
||||||
} while (s->config->retry_path && !s->force_exit);
|
} while (s->config->retry_path && !s->force_exit);
|
||||||
|
|
||||||
|
@ -20,9 +20,13 @@
|
|||||||
#define QGA_SERVICE_NAME "qemu-ga"
|
#define QGA_SERVICE_NAME "qemu-ga"
|
||||||
#define QGA_SERVICE_DESCRIPTION "Enables integration with QEMU machine emulator and virtualizer."
|
#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 {
|
typedef struct GAService {
|
||||||
SERVICE_STATUS status;
|
SERVICE_STATUS status;
|
||||||
SERVICE_STATUS_HANDLE status_handle;
|
SERVICE_STATUS_HANDLE status_handle;
|
||||||
|
HDEVNOTIFY device_notification_handle;
|
||||||
} GAService;
|
} GAService;
|
||||||
|
|
||||||
int ga_install_service(const char *path, const char *logfile,
|
int ga_install_service(const char *path, const char *logfile,
|
||||||
|
Loading…
Reference in New Issue
Block a user