qemu-ga: add Windows service integration
This allows qemu-ga to function as a Windows service:
 - to install the service (will auto-start on boot):
     qemu-ga --service install
 - to start the service:
     net start qemu-ga
 - to stop the service:
     net stop qemu-ga
 - to uninstall service:
     qemu-ga --service uninstall
Original patch by Gal Hammer <ghammer@redhat.com>
			
			
This commit is contained in:
		@@ -426,7 +426,7 @@ common-obj-y += qmp.o hmp.o
 | 
			
		||||
 | 
			
		||||
qga-nested-y = commands.o guest-agent-command-state.o
 | 
			
		||||
qga-nested-$(CONFIG_POSIX) += commands-posix.o channel-posix.o
 | 
			
		||||
qga-nested-$(CONFIG_WIN32) += commands-win32.o channel-win32.o
 | 
			
		||||
qga-nested-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
 | 
			
		||||
qga-obj-y = $(addprefix qga/, $(qga-nested-y))
 | 
			
		||||
qga-obj-y += qemu-ga.o module.o
 | 
			
		||||
qga-obj-$(CONFIG_WIN32) += oslib-win32.o
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										113
									
								
								qemu-ga.c
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								qemu-ga.c
									
									
									
									
									
								
							@@ -29,6 +29,10 @@
 | 
			
		||||
#include "error_int.h"
 | 
			
		||||
#include "qapi/qmp-core.h"
 | 
			
		||||
#include "qga/channel.h"
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include "qga/service-win32.h"
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
 | 
			
		||||
@@ -46,11 +50,19 @@ struct GAState {
 | 
			
		||||
    GLogLevelFlags log_level;
 | 
			
		||||
    FILE *log_file;
 | 
			
		||||
    bool logging_enabled;
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    GAService service;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct GAState *ga_state;
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
 | 
			
		||||
                                  LPVOID ctx);
 | 
			
		||||
VOID WINAPI service_main(DWORD argc, TCHAR *argv[]);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void quit_handler(int sig)
 | 
			
		||||
{
 | 
			
		||||
    g_debug("received signal num %d, quitting", sig);
 | 
			
		||||
@@ -60,6 +72,7 @@ static void quit_handler(int sig)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
static gboolean register_signal_handlers(void)
 | 
			
		||||
{
 | 
			
		||||
    struct sigaction sigact;
 | 
			
		||||
@@ -95,8 +108,9 @@ static void usage(const char *cmd)
 | 
			
		||||
"  -f, --pidfile     specify pidfile (default is %s)\n"
 | 
			
		||||
"  -v, --verbose     log extra debugging information\n"
 | 
			
		||||
"  -V, --version     print version information and exit\n"
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
"  -d, --daemonize   become a daemon\n"
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
"  -s, --service     service commands: install, uninstall\n"
 | 
			
		||||
#endif
 | 
			
		||||
"  -b, --blacklist   comma-separated list of RPCs to disable (no spaces, \"?\""
 | 
			
		||||
"                    to list available RPCs)\n"
 | 
			
		||||
@@ -394,20 +408,77 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path)
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
 | 
			
		||||
                                  LPVOID ctx)
 | 
			
		||||
{
 | 
			
		||||
    DWORD ret = NO_ERROR;
 | 
			
		||||
    GAService *service = &ga_state->service;
 | 
			
		||||
 | 
			
		||||
    switch (ctrl)
 | 
			
		||||
    {
 | 
			
		||||
        case SERVICE_CONTROL_STOP:
 | 
			
		||||
        case SERVICE_CONTROL_SHUTDOWN:
 | 
			
		||||
            quit_handler(SIGTERM);
 | 
			
		||||
            service->status.dwCurrentState = SERVICE_STOP_PENDING;
 | 
			
		||||
            SetServiceStatus(service->status_handle, &service->status);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            ret = ERROR_CALL_NOT_IMPLEMENTED;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
 | 
			
		||||
{
 | 
			
		||||
    GAService *service = &ga_state->service;
 | 
			
		||||
 | 
			
		||||
    service->status_handle = RegisterServiceCtrlHandlerEx(QGA_SERVICE_NAME,
 | 
			
		||||
        service_ctrl_handler, NULL);
 | 
			
		||||
 | 
			
		||||
    if (service->status_handle == 0) {
 | 
			
		||||
        g_critical("Failed to register extended requests function!\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    service->status.dwServiceType = SERVICE_WIN32;
 | 
			
		||||
    service->status.dwCurrentState = SERVICE_RUNNING;
 | 
			
		||||
    service->status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
 | 
			
		||||
    service->status.dwWin32ExitCode = NO_ERROR;
 | 
			
		||||
    service->status.dwServiceSpecificExitCode = NO_ERROR;
 | 
			
		||||
    service->status.dwCheckPoint = 0;
 | 
			
		||||
    service->status.dwWaitHint = 0;
 | 
			
		||||
    SetServiceStatus(service->status_handle, &service->status);
 | 
			
		||||
 | 
			
		||||
    g_main_loop_run(ga_state->main_loop);
 | 
			
		||||
 | 
			
		||||
    service->status.dwCurrentState = SERVICE_STOPPED;
 | 
			
		||||
    SetServiceStatus(service->status_handle, &service->status);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
    const char *sopt = "hVvdm:p:l:f:b:";
 | 
			
		||||
    const char *sopt = "hVvdm:p:l:f:b:s:";
 | 
			
		||||
    const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT;
 | 
			
		||||
    const char *log_file_name = NULL;
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    const char *service = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
    const struct option lopt[] = {
 | 
			
		||||
        { "help", 0, NULL, 'h' },
 | 
			
		||||
        { "version", 0, NULL, 'V' },
 | 
			
		||||
        { "logfile", 0, NULL, 'l' },
 | 
			
		||||
        { "pidfile", 0, NULL, 'f' },
 | 
			
		||||
        { "logfile", 1, NULL, 'l' },
 | 
			
		||||
        { "pidfile", 1, NULL, 'f' },
 | 
			
		||||
        { "verbose", 0, NULL, 'v' },
 | 
			
		||||
        { "method", 0, NULL, 'm' },
 | 
			
		||||
        { "path", 0, NULL, 'p' },
 | 
			
		||||
        { "method", 1, NULL, 'm' },
 | 
			
		||||
        { "path", 1, NULL, 'p' },
 | 
			
		||||
        { "daemonize", 0, NULL, 'd' },
 | 
			
		||||
        { "blacklist", 0, NULL, 'b' },
 | 
			
		||||
        { "blacklist", 1, NULL, 'b' },
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
        { "service", 1, NULL, 's' },
 | 
			
		||||
#endif        
 | 
			
		||||
        { NULL, 0, NULL, 0 }
 | 
			
		||||
    };
 | 
			
		||||
    int opt_ind = 0, ch, daemonize = 0, i, j, len;
 | 
			
		||||
@@ -426,7 +497,8 @@ int main(int argc, char **argv)
 | 
			
		||||
            path = optarg;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'l':
 | 
			
		||||
            log_file = fopen(optarg, "a");
 | 
			
		||||
            log_file_name = optarg;
 | 
			
		||||
            log_file = fopen(log_file_name, "a");
 | 
			
		||||
            if (!log_file) {
 | 
			
		||||
                g_critical("unable to open specified log file: %s",
 | 
			
		||||
                           strerror(errno));
 | 
			
		||||
@@ -472,6 +544,19 @@ int main(int argc, char **argv)
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
        case 's':
 | 
			
		||||
            service = optarg;
 | 
			
		||||
            if (strcmp(service, "install") == 0) {
 | 
			
		||||
                return ga_install_service(path, log_file_name);
 | 
			
		||||
            } else if (strcmp(service, "uninstall") == 0) {
 | 
			
		||||
                return ga_uninstall_service();
 | 
			
		||||
            } else {
 | 
			
		||||
                printf("Unknown service command.\n");
 | 
			
		||||
                return EXIT_FAILURE;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
#endif
 | 
			
		||||
        case 'h':
 | 
			
		||||
            usage(argv[0]);
 | 
			
		||||
            return 0;
 | 
			
		||||
@@ -512,7 +597,17 @@ int main(int argc, char **argv)
 | 
			
		||||
        g_critical("failed to initialize guest agent channel");
 | 
			
		||||
        goto out_bad;
 | 
			
		||||
    }
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
    g_main_loop_run(ga_state->main_loop);
 | 
			
		||||
#else
 | 
			
		||||
    if (daemonize) {
 | 
			
		||||
        SERVICE_TABLE_ENTRY service_table[] = {
 | 
			
		||||
            { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } };
 | 
			
		||||
        StartServiceCtrlDispatcher(service_table);
 | 
			
		||||
    } else {
 | 
			
		||||
        g_main_loop_run(ga_state->main_loop);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ga_command_state_cleanup_all(ga_state->command_state);
 | 
			
		||||
    ga_channel_free(ga_state->channel);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								qga/service-win32.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								qga/service-win32.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU Guest Agent helpers for win32 service management
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright IBM Corp. 2012
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Gal Hammer        <ghammer@redhat.com>
 | 
			
		||||
 *  Michael Roth      <mdroth@linux.vnet.ibm.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
 * See the COPYING file in the top-level directory.
 | 
			
		||||
 */
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include "qga/service-win32.h"
 | 
			
		||||
 | 
			
		||||
static int printf_win_error(const char *text)
 | 
			
		||||
{
 | 
			
		||||
    DWORD err = GetLastError();
 | 
			
		||||
    char *message;
 | 
			
		||||
    int n;
 | 
			
		||||
 | 
			
		||||
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
 | 
			
		||||
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 | 
			
		||||
        NULL,
 | 
			
		||||
        err,
 | 
			
		||||
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | 
			
		||||
        (char *)&message, 0,
 | 
			
		||||
        NULL);
 | 
			
		||||
    n = printf("%s. (Error: %d) %s", text, err, message);
 | 
			
		||||
    LocalFree(message);
 | 
			
		||||
 | 
			
		||||
    return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ga_install_service(const char *path, const char *logfile)
 | 
			
		||||
{
 | 
			
		||||
    SC_HANDLE manager;
 | 
			
		||||
    SC_HANDLE service;
 | 
			
		||||
    TCHAR cmdline[MAX_PATH];
 | 
			
		||||
 | 
			
		||||
    if (GetModuleFileName(NULL, cmdline, MAX_PATH) == 0) {
 | 
			
		||||
        printf_win_error("No full path to service's executable");
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _snprintf(cmdline, MAX_PATH - strlen(cmdline), "%s -d", cmdline);
 | 
			
		||||
 | 
			
		||||
    if (path) {
 | 
			
		||||
        _snprintf(cmdline, MAX_PATH - strlen(cmdline), "%s -p %s", cmdline, path);
 | 
			
		||||
    }
 | 
			
		||||
    if (logfile) {
 | 
			
		||||
        _snprintf(cmdline, MAX_PATH - strlen(cmdline), "%s -l %s -v",
 | 
			
		||||
            cmdline, logfile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_debug("service's cmdline: %s", cmdline);
 | 
			
		||||
 | 
			
		||||
    manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 | 
			
		||||
    if (manager == NULL) {
 | 
			
		||||
        printf_win_error("No handle to service control manager");
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    service = CreateService(manager, QGA_SERVICE_NAME, QGA_SERVICE_DISPLAY_NAME,
 | 
			
		||||
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
 | 
			
		||||
        SERVICE_ERROR_NORMAL, cmdline, NULL, NULL, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    if (service) {
 | 
			
		||||
        SERVICE_DESCRIPTION desc = { (char *)QGA_SERVICE_DESCRIPTION };
 | 
			
		||||
        ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &desc);
 | 
			
		||||
 | 
			
		||||
        printf("Service was installed successfully.\n");
 | 
			
		||||
    } else {
 | 
			
		||||
        printf_win_error("Failed to install service");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CloseServiceHandle(service);
 | 
			
		||||
    CloseServiceHandle(manager);
 | 
			
		||||
 | 
			
		||||
    return (service == NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ga_uninstall_service(void)
 | 
			
		||||
{
 | 
			
		||||
    SC_HANDLE manager;
 | 
			
		||||
    SC_HANDLE service;
 | 
			
		||||
 | 
			
		||||
    manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 | 
			
		||||
    if (manager == NULL) {
 | 
			
		||||
        printf_win_error("No handle to service control manager");
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    service = OpenService(manager, QGA_SERVICE_NAME, DELETE);
 | 
			
		||||
    if (service == NULL) {
 | 
			
		||||
        printf_win_error("No handle to service");
 | 
			
		||||
        CloseServiceHandle(manager);
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (DeleteService(service) == FALSE) {
 | 
			
		||||
        printf_win_error("Failed to delete service");
 | 
			
		||||
    } else {
 | 
			
		||||
        printf("Service was deleted successfully.\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CloseServiceHandle(service);
 | 
			
		||||
    CloseServiceHandle(manager);
 | 
			
		||||
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								qga/service-win32.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								qga/service-win32.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU Guest Agent helpers for win32 service management
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright IBM Corp. 2012
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Gal Hammer        <ghammer@redhat.com>
 | 
			
		||||
 *  Michael Roth      <mdroth@linux.vnet.ibm.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
 * See the COPYING file in the top-level directory.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef QGA_SERVICE_H
 | 
			
		||||
#define QGA_SERVICE_H
 | 
			
		||||
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
 | 
			
		||||
#define QGA_SERVICE_DISPLAY_NAME "QEMU Guest Agent"
 | 
			
		||||
#define QGA_SERVICE_NAME         "qemu-ga"
 | 
			
		||||
#define QGA_SERVICE_DESCRIPTION  "Enables integration with QEMU machine emulator and virtualizer."
 | 
			
		||||
 | 
			
		||||
typedef struct GAService {
 | 
			
		||||
    SERVICE_STATUS status;
 | 
			
		||||
    SERVICE_STATUS_HANDLE status_handle;
 | 
			
		||||
} GAService;
 | 
			
		||||
 | 
			
		||||
int ga_install_service(const char *path, const char *logfile);
 | 
			
		||||
int ga_uninstall_service(void);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Reference in New Issue
	
	Block a user