Skip to content

Commit

Permalink
qga-win: changing --retry-path option behavior
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Sameeh Jubran <[email protected]>
Signed-off-by: Michael Roth <[email protected]>
  • Loading branch information
bish22ah authored and mdroth committed Oct 30, 2018
1 parent f5701e2 commit 9634c55
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 1 deletion.
86 changes: 85 additions & 1 deletion qga/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -83,6 +84,7 @@ struct GAState {
bool logging_enabled;
#ifdef _WIN32
GAService service;
HANDLE wakeup_event;
#endif
bool delimit_response;
bool frozen;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand All @@ -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(&notification_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,
&notification_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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);

Expand Down
4 changes: 4 additions & 0 deletions qga/service-win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 9634c55

Please sign in to comment.