From 4c1b8f1e8357d85c613d779596e4079cc581d74f Mon Sep 17 00:00:00 2001 From: Tomoki Sekiyama Date: Mon, 13 Jan 2014 12:25:23 -0500 Subject: [PATCH 1/6] qga: vss-win32: Use NULL as an invalid pointer for OpenEvent and CreateEvent OpenEvent and CreateEvent WinAPI return NULL when failed to open/create events handles, instead of INVALID_HANDLE_VALUE (although their return types are HANDLE). This replaces INVALID_HANDLE_VALUE related to event handles with NULL. Signed-off-by: Tomoki Sekiyama Reviewed-by: Gal Hammer Reviewed-by: Yan Vugenfirer Signed-off-by: Michael Roth --- qga/vss-win32/provider.cpp | 6 +++--- qga/vss-win32/requester.cpp | 24 ++++++++++-------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index bf42b5e95f2f..c3030d8e55b6 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -342,18 +342,18 @@ STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId) HANDLE hEventFrozen, hEventThaw, hEventTimeout; hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); - if (hEventFrozen == INVALID_HANDLE_VALUE) { + if (!hEventFrozen) { return E_FAIL; } hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW); - if (hEventThaw == INVALID_HANDLE_VALUE) { + if (!hEventThaw) { CloseHandle(hEventFrozen); return E_FAIL; } hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); - if (hEventTimeout == INVALID_HANDLE_VALUE) { + if (!hEventTimeout) { CloseHandle(hEventFrozen); CloseHandle(hEventThaw); return E_FAIL; diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 1e8dd3dfa8fa..0a55447e8101 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -50,10 +50,6 @@ static struct QGAVSSContext { STDAPI requester_init(void) { - vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; - vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; - vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; - COMInitializer initializer; /* to call CoInitializeSecurity */ HRESULT hr = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, @@ -94,17 +90,17 @@ STDAPI requester_init(void) static void requester_cleanup(void) { - if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventFrozen) { CloseHandle(vss_ctx.hEventFrozen); - vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; + vss_ctx.hEventFrozen = NULL; } - if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventThaw) { CloseHandle(vss_ctx.hEventThaw); - vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; + vss_ctx.hEventThaw = NULL; } - if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventTimeout) { CloseHandle(vss_ctx.hEventTimeout); - vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; + vss_ctx.hEventTimeout = NULL; } if (vss_ctx.pAsyncSnapshot) { vss_ctx.pAsyncSnapshot->Release(); @@ -374,19 +370,19 @@ void requester_freeze(int *num_vols, ErrorSet *errset) sa.bInheritHandle = FALSE; vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); - if (vss_ctx.hEventFrozen == INVALID_HANDLE_VALUE) { + if (!vss_ctx.hEventFrozen) { err_set(errset, GetLastError(), "failed to create event %s", EVENT_NAME_FROZEN); goto out; } vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); - if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { + if (!vss_ctx.hEventThaw) { err_set(errset, GetLastError(), "failed to create event %s", EVENT_NAME_THAW); goto out; } vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); - if (vss_ctx.hEventTimeout == INVALID_HANDLE_VALUE) { + if (!vss_ctx.hEventTimeout) { err_set(errset, GetLastError(), "failed to create event %s", EVENT_NAME_TIMEOUT); goto out; @@ -443,7 +439,7 @@ void requester_thaw(int *num_vols, ErrorSet *errset) { COMPointer pAsync; - if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { + if (!vss_ctx.hEventThaw) { /* * In this case, DoSnapshotSet is aborted or not started, * and no volumes must be frozen. We return without an error. From ff8adbcfdbbd9c0f2b01ff8a32bc75082fdd9844 Mon Sep 17 00:00:00 2001 From: Tomoki Sekiyama Date: Mon, 13 Jan 2014 12:25:29 -0500 Subject: [PATCH 2/6] qga: vss-win32: Fix interference with snapshot creation by other VSS requesters When a VSS requester such as vshadow.exe or diskshadow.exe requests to create disk snapshots, Windows may choose qemu-ga VSS provider if it is only provider registered on the system. However, because it provides only a function to freeze the filesystem, the snapshotting fails. This patch adds a check into CQGAVssProvider::IsVolumeSupported() to reject the request from other VSS requesters, so that the other provider is chosen. The check of requester is done by confirming event channels between qemu-ga's requester and provider established. To ensure that the events are initialized when CQGAVssProvider::IsVolumeSupported() is called, it moves the initialization earlier. Signed-off-by: Tomoki Sekiyama Reviewed-by: Gal Hammer Reviewed-by: Yan Vugenfirer Signed-off-by: Michael Roth --- qga/vss-win32/provider.cpp | 11 +++++++- qga/vss-win32/requester.cpp | 52 ++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index c3030d8e55b6..b2336465ac0b 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -291,8 +291,17 @@ STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( STDMETHODIMP CQGAVssProvider::IsVolumeSupported( VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider) { - *pbSupportedByThisProvider = TRUE; + HANDLE hEventFrozen; + + /* Check if a requester is qemu-ga by whether an event is created */ + hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); + if (!hEventFrozen) { + *pbSupportedByThisProvider = FALSE; + return S_OK; + } + CloseHandle(hEventFrozen); + *pbSupportedByThisProvider = TRUE; return S_OK; } diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 0a55447e8101..922e74ddfcb9 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -252,6 +252,32 @@ void requester_freeze(int *num_vols, ErrorSet *errset) CoInitialize(NULL); + /* Allow unrestricted access to events */ + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); + if (!vss_ctx.hEventFrozen) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_FROZEN); + goto out; + } + vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); + if (!vss_ctx.hEventThaw) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_THAW); + goto out; + } + vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); + if (!vss_ctx.hEventTimeout) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_TIMEOUT); + goto out; + } + assert(pCreateVssBackupComponents != NULL); hr = pCreateVssBackupComponents(&vss_ctx.pVssbc); if (FAILED(hr)) { @@ -362,32 +388,6 @@ void requester_freeze(int *num_vols, ErrorSet *errset) goto out; } - /* Allow unrestricted access to events */ - InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = &sd; - sa.bInheritHandle = FALSE; - - vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); - if (!vss_ctx.hEventFrozen) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_FROZEN); - goto out; - } - vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); - if (!vss_ctx.hEventThaw) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_THAW); - goto out; - } - vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); - if (!vss_ctx.hEventTimeout) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_TIMEOUT); - goto out; - } - /* * Start VSS quiescing operations. * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen From d9e1f574cb6eac0a3a2f97b67d2e7a3ad9c1dc95 Mon Sep 17 00:00:00 2001 From: Tomoki Sekiyama Date: Mon, 13 Jan 2014 12:25:39 -0500 Subject: [PATCH 3/6] qga: vss-win32: Fix interference with snapshot deletion by other VSS request When a VSS requester such as vshadow.exe or diskshadow.exe requests to delete snapshots, qemu-ga VSS provider's DeleteSnapshots() is also called and returns E_NOTIMPL, that makes the deletion fail. To avoid this issue, return S_OK and set values that represent no snapshots are deleted by qemu-ga VSS provider. Signed-off-by: Tomoki Sekiyama Reviewed-by: Gal Hammer Reviewed-by: Yan Vugenfirer Signed-off-by: Michael Roth --- qga/vss-win32/provider.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index b2336465ac0b..d5129f8f6588 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -278,7 +278,9 @@ STDMETHODIMP CQGAVssProvider::DeleteSnapshots( VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID) { - return E_NOTIMPL; + *plDeletedSnapshots = 0; + *pNondeletedSnapshotID = SourceObjectId; + return S_OK; } STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( From 2c958923bc09b1faf2505a988b4b1c458580e9ac Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Fri, 31 Jan 2014 11:29:51 +0100 Subject: [PATCH 4/6] qga: Don't require 'time' argument in guest-set-time command As the description to the guest-set-time states, the command is there to ease time synchronization after resume. If guest was suspended for longer period of time, its system time can go off so badly, that even NTP refuses to set it. That's why the command was invented: to give users chance to set the time (not necessarily 100% correct). However, there's is no real need for us to require users to pass an arbitrary time. Especially if we can read the correct value from RTC (boiling down to reading host's time). Hence this commit enables logic: guest-set-time() == guest-set-time($now_from_rtc) Signed-off-by: Michal Privoznik Reviewed-by: Eric Blake Signed-off-by: Michael Roth --- qga/commands-posix.c | 37 +++++++++++++++++++++++-------------- qga/commands-win32.c | 34 +++++++++++++++++++++++----------- qga/qapi-schema.json | 9 +++++---- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index cae41716cae4..91885604c471 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -142,7 +142,7 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } -void qmp_guest_set_time(int64_t time_ns, Error **errp) +void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { int ret; int status; @@ -150,22 +150,28 @@ void qmp_guest_set_time(int64_t time_ns, Error **errp) Error *local_err = NULL; struct timeval tv; - /* year-2038 will overflow in case time_t is 32bit */ - if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { - error_setg(errp, "Time %" PRId64 " is too large", time_ns); - return; - } + /* If user has passed a time, validate and set it. */ + if (has_time) { + /* year-2038 will overflow in case time_t is 32bit */ + if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { + error_setg(errp, "Time %" PRId64 " is too large", time_ns); + return; + } - tv.tv_sec = time_ns / 1000000000; - tv.tv_usec = (time_ns % 1000000000) / 1000; + tv.tv_sec = time_ns / 1000000000; + tv.tv_usec = (time_ns % 1000000000) / 1000; - ret = settimeofday(&tv, NULL); - if (ret < 0) { - error_setg_errno(errp, errno, "Failed to set time to guest"); - return; + ret = settimeofday(&tv, NULL); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to set time to guest"); + return; + } } - /* Set the Hardware Clock to the current System Time. */ + /* Now, if user has passed a time to set and the system time is set, we + * just need to synchronize the hardware clock. However, if no time was + * passed, user is requesting the opposite: set the system time from the + * hardware clock. */ pid = fork(); if (pid == 0) { setsid(); @@ -173,7 +179,10 @@ void qmp_guest_set_time(int64_t time_ns, Error **errp) reopen_fd_to_null(1); reopen_fd_to_null(2); - execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); + /* Use '/sbin/hwclock -w' to set RTC from the system time, + * or '/sbin/hwclock -s' to set the system time from RTC. */ + execle("/sbin/hwclock", "hwclock", has_time ? "-w" : "-s", + NULL, environ); _exit(EXIT_FAILURE); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 50094ddfd335..0ee07b6e2309 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -370,25 +370,37 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } -void qmp_guest_set_time(int64_t time_ns, Error **errp) +void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { SYSTEMTIME ts; FILETIME tf; LONGLONG time; - if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { - error_setg(errp, "Time %" PRId64 "is invalid", time_ns); - return; - } + if (has_time) { + /* Okay, user passed a time to set. Validate it. */ + if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { + error_setg(errp, "Time %" PRId64 "is invalid", time_ns); + return; + } - time = time_ns / 100 + W32_FT_OFFSET; + time = time_ns / 100 + W32_FT_OFFSET; - tf.dwLowDateTime = (DWORD) time; - tf.dwHighDateTime = (DWORD) (time >> 32); + tf.dwLowDateTime = (DWORD) time; + tf.dwHighDateTime = (DWORD) (time >> 32); - if (!FileTimeToSystemTime(&tf, &ts)) { - error_setg(errp, "Failed to convert system time %d", (int)GetLastError()); - return; + if (!FileTimeToSystemTime(&tf, &ts)) { + error_setg(errp, "Failed to convert system time %d", + (int)GetLastError()); + return; + } + } else { + /* Otherwise read the time from RTC which contains the correct value. + * Hopefully. */ + GetSystemTime(&ts); + if (ts.wYear < 1601 || ts.wYear > 30827) { + error_setg(errp, "Failed to get time"); + return; + } } acquire_privilege(SE_SYSTEMTIME_NAME, errp); diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 245f968bc285..80edca143aef 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -120,17 +120,18 @@ # This command tries to set guest time to the given value, # then sets the Hardware Clock to the current System Time. # This will make it easier for a guest to resynchronize -# without waiting for NTP. +# without waiting for NTP. If no @time is specified, then +# the time to set is read from RTC. # -# @time: time of nanoseconds, relative to the Epoch of -# 1970-01-01 in UTC. +# @time: #optional time of nanoseconds, relative to the Epoch +# of 1970-01-01 in UTC. # # Returns: Nothing on success. # # Since: 1.5 ## { 'command': 'guest-set-time', - 'data': { 'time': 'int' } } + 'data': { '*time': 'int' } } ## # @GuestAgentCommandInfo: From 10b7c5dd0da1a92182e87f5fc1887d779ad1a9e8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 21 Feb 2014 13:36:49 +0100 Subject: [PATCH 5/6] qga: Fix memory allocation pasto qmp_guest_file_seek() allocates memory for a GuestFileRead object instead of the GuestFileSeek object it actually uses. Harmless, because the GuestFileRead is slightly larger. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Michael Roth --- qga/commands-posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 91885604c471..6b5f11f83f9b 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -534,7 +534,7 @@ struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, if (ret == -1) { error_setg_errno(err, errno, "failed to seek file"); } else { - seek_data = g_malloc0(sizeof(GuestFileRead)); + seek_data = g_new0(GuestFileSeek, 1); seek_data->position = ftell(fh); seek_data->eof = feof(fh); } From a749f42da5129bbfadea6926964d9a213ed4bc5f Mon Sep 17 00:00:00 2001 From: Miki Mishael Date: Wed, 15 Jan 2014 04:33:44 -0500 Subject: [PATCH 6/6] qemu-ga: isa-serial support on Windows Add support for isa-serial method for qemu-ga on Windows, Added -p command line parameter for serial port name specification, e.g. "-p COM15". Signed-off-by: Miki Mishael Signed-off-by: Dmitry Fleytman *added default isa-serial path to help output Signed-off-by: Michael Roth --- qga/channel-win32.c | 20 ++++++++++++++++++-- qga/main.c | 17 +++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 8a303f35ecab..0d5e5f511f02 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -287,12 +287,22 @@ GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size) static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, const gchar *path) { - if (method != GA_CHANNEL_VIRTIO_SERIAL) { + COMMTIMEOUTS comTimeOut = {0}; + gchar newpath[MAXPATHLEN] = {0}; + comTimeOut.ReadIntervalTimeout = 1; + + if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) { g_critical("unsupported communication method"); return false; } - c->handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, + if (method == GA_CHANNEL_ISA_SERIAL){ + snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path); + }else { + g_strlcpy(newpath, path, sizeof(newpath)); + } + + c->handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); if (c->handle == INVALID_HANDLE_VALUE) { @@ -300,6 +310,12 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, return false; } + if (method == GA_CHANNEL_ISA_SERIAL && !SetCommTimeouts(c->handle,&comTimeOut)) { + g_critical("error setting timeout for com port: %lu",GetLastError()); + CloseHandle(c->handle); + return false; + } + return true; } diff --git a/qga/main.c b/qga/main.c index c58b26a9a0f0..cfca291b43ef 100644 --- a/qga/main.c +++ b/qga/main.c @@ -47,9 +47,11 @@ #ifndef _WIN32 #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" #define QGA_STATE_RELATIVE_DIR "run" +#define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0" #else #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0" #define QGA_STATE_RELATIVE_DIR "qemu-ga" +#define QGA_SERIAL_PATH_DEFAULT "COM1" #endif #ifdef CONFIG_FSFREEZE #define QGA_FSFREEZE_HOOK_DEFAULT CONFIG_QEMU_CONFDIR "/fsfreeze-hook" @@ -189,6 +191,8 @@ static void usage(const char *cmd) " -m, --method transport method: one of unix-listen, virtio-serial, or\n" " isa-serial (virtio-serial is the default)\n" " -p, --path device/socket path (the default for virtio-serial is:\n" +" %s,\n" +" the default for isa-serial is:\n" " %s)\n" " -l, --logfile set logfile path, logs to stderr by default\n" " -f, --pidfile specify pidfile (default is %s)\n" @@ -215,7 +219,8 @@ static void usage(const char *cmd) " -h, --help display this help and exit\n" "\n" "Report bugs to \n" - , cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, dfl_pathnames.pidfile, + , cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, + dfl_pathnames.pidfile, #ifdef CONFIG_FSFREEZE QGA_FSFREEZE_HOOK_DEFAULT, #endif @@ -659,12 +664,16 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path) } if (path == NULL) { - if (strcmp(method, "virtio-serial") != 0) { + if (strcmp(method, "virtio-serial") == 0 ) { + /* try the default path for the virtio-serial port */ + path = QGA_VIRTIO_PATH_DEFAULT; + } else if (strcmp(method, "isa-serial") == 0){ + /* try the default path for the serial port - COM1 */ + path = QGA_SERIAL_PATH_DEFAULT; + } else { g_critical("must specify a path for this channel"); return false; } - /* try the default path for the virtio-serial port */ - path = QGA_VIRTIO_PATH_DEFAULT; } if (strcmp(method, "virtio-serial") == 0) {