Peter Maydell | 4459bf3 | 2016-01-29 17:49:58 +0000 | [diff] [blame] | 1 | #include "qemu/osdep.h" |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 2 | #include <windows.h> |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 3 | #include <io.h> |
Michael S. Tsirkin | dc03272 | 2018-05-03 22:50:57 +0300 | [diff] [blame] | 4 | #include "guest-agent-core.h" |
| 5 | #include "channel.h" |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 6 | |
| 7 | typedef struct GAChannelReadState { |
| 8 | guint thread_id; |
| 9 | uint8_t *buf; |
| 10 | size_t buf_size; |
| 11 | size_t cur; /* current buffer start */ |
| 12 | size_t pending; /* pending buffered bytes to read */ |
| 13 | OVERLAPPED ov; |
| 14 | bool ov_pending; /* whether on async read is outstanding */ |
| 15 | } GAChannelReadState; |
| 16 | |
| 17 | struct GAChannel { |
| 18 | HANDLE handle; |
| 19 | GAChannelCallback cb; |
| 20 | gpointer user_data; |
| 21 | GAChannelReadState rstate; |
| 22 | GIOCondition pending_events; /* TODO: use GAWatch.pollfd.revents */ |
| 23 | GSource *source; |
| 24 | }; |
| 25 | |
| 26 | typedef struct GAWatch { |
| 27 | GSource source; |
| 28 | GPollFD pollfd; |
| 29 | GAChannel *channel; |
| 30 | GIOCondition events_mask; |
| 31 | } GAWatch; |
| 32 | |
| 33 | /* |
| 34 | * Called by glib prior to polling to set up poll events if polling is needed. |
| 35 | * |
| 36 | */ |
| 37 | static gboolean ga_channel_prepare(GSource *source, gint *timeout_ms) |
| 38 | { |
| 39 | GAWatch *watch = (GAWatch *)source; |
| 40 | GAChannel *c = (GAChannel *)watch->channel; |
| 41 | GAChannelReadState *rs = &c->rstate; |
| 42 | DWORD count_read, count_to_read = 0; |
| 43 | bool success; |
| 44 | GIOCondition new_events = 0; |
| 45 | |
| 46 | g_debug("prepare"); |
| 47 | /* go ahead and submit another read if there's room in the buffer |
| 48 | * and no previous reads are outstanding |
| 49 | */ |
| 50 | if (!rs->ov_pending) { |
| 51 | if (rs->cur + rs->pending >= rs->buf_size) { |
| 52 | if (rs->cur) { |
| 53 | memmove(rs->buf, rs->buf + rs->cur, rs->pending); |
| 54 | rs->cur = 0; |
| 55 | } |
| 56 | } |
| 57 | count_to_read = rs->buf_size - rs->cur - rs->pending; |
| 58 | } |
| 59 | |
| 60 | if (rs->ov_pending || count_to_read <= 0) { |
| 61 | goto out; |
| 62 | } |
| 63 | |
| 64 | /* submit the read */ |
| 65 | success = ReadFile(c->handle, rs->buf + rs->cur + rs->pending, |
| 66 | count_to_read, &count_read, &rs->ov); |
| 67 | if (success) { |
| 68 | rs->pending += count_read; |
| 69 | rs->ov_pending = false; |
| 70 | } else { |
| 71 | if (GetLastError() == ERROR_IO_PENDING) { |
| 72 | rs->ov_pending = true; |
| 73 | } else { |
| 74 | new_events |= G_IO_ERR; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | out: |
Stefan Weil | cb8d4c8 | 2016-03-23 15:59:57 +0100 | [diff] [blame] | 79 | /* don't block forever, iterate the main loop every once in a while */ |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 80 | *timeout_ms = 500; |
| 81 | /* if there's data in the read buffer, or another event is pending, |
| 82 | * skip polling and issue user cb. |
| 83 | */ |
| 84 | if (rs->pending) { |
| 85 | new_events |= G_IO_IN; |
| 86 | } |
| 87 | c->pending_events |= new_events; |
| 88 | return !!c->pending_events; |
| 89 | } |
| 90 | |
| 91 | /* |
| 92 | * Called by glib after an outstanding read request is completed. |
| 93 | */ |
| 94 | static gboolean ga_channel_check(GSource *source) |
| 95 | { |
| 96 | GAWatch *watch = (GAWatch *)source; |
| 97 | GAChannel *c = (GAChannel *)watch->channel; |
| 98 | GAChannelReadState *rs = &c->rstate; |
| 99 | DWORD count_read, error; |
| 100 | BOOL success; |
| 101 | |
| 102 | GIOCondition new_events = 0; |
| 103 | |
| 104 | g_debug("check"); |
| 105 | |
| 106 | /* failing this implies we issued a read that completed immediately, |
| 107 | * yet no data was placed into the buffer (and thus we did not skip |
| 108 | * polling). but since EOF is not obtainable until we retrieve an |
| 109 | * overlapped result, it must be the case that there was data placed |
| 110 | * into the buffer, or an error was generated by Readfile(). in either |
| 111 | * case, we should've skipped the polling for this round. |
| 112 | */ |
| 113 | g_assert(rs->ov_pending); |
| 114 | |
| 115 | success = GetOverlappedResult(c->handle, &rs->ov, &count_read, FALSE); |
| 116 | if (success) { |
| 117 | g_debug("thread: overlapped result, count_read: %d", (int)count_read); |
| 118 | rs->pending += count_read; |
| 119 | new_events |= G_IO_IN; |
| 120 | } else { |
| 121 | error = GetLastError(); |
| 122 | if (error == 0 || error == ERROR_HANDLE_EOF || |
| 123 | error == ERROR_NO_SYSTEM_RESOURCES || |
| 124 | error == ERROR_OPERATION_ABORTED) { |
| 125 | /* note: On WinXP SP3 with rhel6ga virtio-win-1.1.16 vioser drivers, |
| 126 | * ENSR seems to be synonymous with when we'd normally expect |
| 127 | * ERROR_HANDLE_EOF. So treat it as such. Microsoft's |
| 128 | * recommendation for ERROR_NO_SYSTEM_RESOURCES is to |
| 129 | * retry the read, so this happens to work out anyway. On newer |
| 130 | * virtio-win driver, this seems to be replaced with EOA, so |
| 131 | * handle that in the same fashion. |
| 132 | */ |
| 133 | new_events |= G_IO_HUP; |
| 134 | } else if (error != ERROR_IO_INCOMPLETE) { |
| 135 | g_critical("error retrieving overlapped result: %d", (int)error); |
| 136 | new_events |= G_IO_ERR; |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | if (new_events) { |
| 141 | rs->ov_pending = 0; |
| 142 | } |
| 143 | c->pending_events |= new_events; |
| 144 | |
| 145 | return !!c->pending_events; |
| 146 | } |
| 147 | |
| 148 | /* |
| 149 | * Called by glib after either prepare or check routines signal readiness |
| 150 | */ |
| 151 | static gboolean ga_channel_dispatch(GSource *source, GSourceFunc unused, |
| 152 | gpointer user_data) |
| 153 | { |
| 154 | GAWatch *watch = (GAWatch *)source; |
| 155 | GAChannel *c = (GAChannel *)watch->channel; |
| 156 | GAChannelReadState *rs = &c->rstate; |
| 157 | gboolean success; |
| 158 | |
| 159 | g_debug("dispatch"); |
| 160 | success = c->cb(watch->pollfd.revents, c->user_data); |
| 161 | |
| 162 | if (c->pending_events & G_IO_ERR) { |
| 163 | g_critical("channel error, removing source"); |
| 164 | return false; |
| 165 | } |
| 166 | |
| 167 | /* TODO: replace rs->pending with watch->revents */ |
| 168 | c->pending_events &= ~G_IO_HUP; |
| 169 | if (!rs->pending) { |
| 170 | c->pending_events &= ~G_IO_IN; |
| 171 | } else { |
| 172 | c->pending_events = 0; |
| 173 | } |
| 174 | return success; |
| 175 | } |
| 176 | |
| 177 | static void ga_channel_finalize(GSource *source) |
| 178 | { |
| 179 | g_debug("finalize"); |
| 180 | } |
| 181 | |
| 182 | GSourceFuncs ga_channel_watch_funcs = { |
| 183 | ga_channel_prepare, |
| 184 | ga_channel_check, |
| 185 | ga_channel_dispatch, |
| 186 | ga_channel_finalize |
| 187 | }; |
| 188 | |
| 189 | static GSource *ga_channel_create_watch(GAChannel *c) |
| 190 | { |
| 191 | GSource *source = g_source_new(&ga_channel_watch_funcs, sizeof(GAWatch)); |
| 192 | GAWatch *watch = (GAWatch *)source; |
| 193 | |
| 194 | watch->channel = c; |
| 195 | watch->pollfd.fd = (gintptr) c->rstate.ov.hEvent; |
| 196 | g_source_add_poll(source, &watch->pollfd); |
| 197 | |
| 198 | return source; |
| 199 | } |
| 200 | |
| 201 | GIOStatus ga_channel_read(GAChannel *c, char *buf, size_t size, gsize *count) |
| 202 | { |
| 203 | GAChannelReadState *rs = &c->rstate; |
| 204 | GIOStatus status; |
| 205 | size_t to_read = 0; |
| 206 | |
| 207 | if (c->pending_events & G_IO_ERR) { |
| 208 | return G_IO_STATUS_ERROR; |
| 209 | } |
| 210 | |
| 211 | *count = to_read = MIN(size, rs->pending); |
| 212 | if (to_read) { |
| 213 | memcpy(buf, rs->buf + rs->cur, to_read); |
| 214 | rs->cur += to_read; |
| 215 | rs->pending -= to_read; |
| 216 | status = G_IO_STATUS_NORMAL; |
| 217 | } else { |
| 218 | status = G_IO_STATUS_AGAIN; |
| 219 | } |
| 220 | |
| 221 | return status; |
| 222 | } |
| 223 | |
| 224 | static GIOStatus ga_channel_write(GAChannel *c, const char *buf, size_t size, |
| 225 | size_t *count) |
| 226 | { |
| 227 | GIOStatus status; |
| 228 | OVERLAPPED ov = {0}; |
| 229 | BOOL ret; |
| 230 | DWORD written; |
| 231 | |
| 232 | ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 233 | ret = WriteFile(c->handle, buf, size, &written, &ov); |
| 234 | if (!ret) { |
| 235 | if (GetLastError() == ERROR_IO_PENDING) { |
| 236 | /* write is pending */ |
| 237 | ret = GetOverlappedResult(c->handle, &ov, &written, TRUE); |
| 238 | if (!ret) { |
| 239 | if (!GetLastError()) { |
| 240 | status = G_IO_STATUS_AGAIN; |
| 241 | } else { |
| 242 | status = G_IO_STATUS_ERROR; |
| 243 | } |
| 244 | } else { |
| 245 | /* write is complete */ |
| 246 | status = G_IO_STATUS_NORMAL; |
| 247 | *count = written; |
| 248 | } |
| 249 | } else { |
| 250 | status = G_IO_STATUS_ERROR; |
| 251 | } |
| 252 | } else { |
| 253 | /* write returned immediately */ |
| 254 | status = G_IO_STATUS_NORMAL; |
| 255 | *count = written; |
| 256 | } |
| 257 | |
Jeff Cody | b71706d | 2012-03-15 14:26:18 -0400 | [diff] [blame] | 258 | if (ov.hEvent) { |
| 259 | CloseHandle(ov.hEvent); |
| 260 | ov.hEvent = NULL; |
| 261 | } |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 262 | return status; |
| 263 | } |
| 264 | |
| 265 | GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size) |
| 266 | { |
Dong Xu Wang | c7e775e | 2013-05-09 15:53:50 +0800 | [diff] [blame] | 267 | GIOStatus status = G_IO_STATUS_NORMAL; |
Michael Roth | e853ea1 | 2015-10-17 10:31:16 -0500 | [diff] [blame] | 268 | size_t count = 0; |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 269 | |
| 270 | while (size) { |
| 271 | status = ga_channel_write(c, buf, size, &count); |
| 272 | if (status == G_IO_STATUS_NORMAL) { |
| 273 | size -= count; |
| 274 | buf += count; |
| 275 | } else if (status != G_IO_STATUS_AGAIN) { |
| 276 | break; |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | return status; |
| 281 | } |
| 282 | |
| 283 | static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, |
| 284 | const gchar *path) |
| 285 | { |
Miki Mishael | a749f42 | 2014-01-15 04:33:44 -0500 | [diff] [blame] | 286 | COMMTIMEOUTS comTimeOut = {0}; |
| 287 | gchar newpath[MAXPATHLEN] = {0}; |
| 288 | comTimeOut.ReadIntervalTimeout = 1; |
| 289 | |
| 290 | if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) { |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 291 | g_critical("unsupported communication method"); |
| 292 | return false; |
| 293 | } |
| 294 | |
AlexChen | 0697e9e | 2020-10-26 17:05:38 +0800 | [diff] [blame] | 295 | if (method == GA_CHANNEL_ISA_SERIAL) { |
Miki Mishael | a749f42 | 2014-01-15 04:33:44 -0500 | [diff] [blame] | 296 | snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path); |
AlexChen | 0697e9e | 2020-10-26 17:05:38 +0800 | [diff] [blame] | 297 | } else { |
Miki Mishael | a749f42 | 2014-01-15 04:33:44 -0500 | [diff] [blame] | 298 | g_strlcpy(newpath, path, sizeof(newpath)); |
| 299 | } |
| 300 | |
| 301 | c->handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 302 | OPEN_EXISTING, |
| 303 | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); |
| 304 | if (c->handle == INVALID_HANDLE_VALUE) { |
Philippe Mathieu-Daudé | d1eddab | 2020-02-28 11:07:26 +0100 | [diff] [blame] | 305 | g_autofree gchar *emsg = g_win32_error_message(GetLastError()); |
| 306 | g_critical("error opening path %s: %s", newpath, emsg); |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 307 | return false; |
| 308 | } |
| 309 | |
AlexChen | 0697e9e | 2020-10-26 17:05:38 +0800 | [diff] [blame] | 310 | if (method == GA_CHANNEL_ISA_SERIAL |
| 311 | && !SetCommTimeouts(c->handle, &comTimeOut)) { |
Philippe Mathieu-Daudé | 672db77 | 2020-02-28 11:07:25 +0100 | [diff] [blame] | 312 | g_autofree gchar *emsg = g_win32_error_message(GetLastError()); |
| 313 | g_critical("error setting timeout for com port: %s", emsg); |
Miki Mishael | a749f42 | 2014-01-15 04:33:44 -0500 | [diff] [blame] | 314 | CloseHandle(c->handle); |
| 315 | return false; |
| 316 | } |
| 317 | |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 318 | return true; |
| 319 | } |
| 320 | |
| 321 | GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path, |
Stefan Hajnoczi | 26de229 | 2017-01-06 15:29:30 +0000 | [diff] [blame] | 322 | int listen_fd, GAChannelCallback cb, gpointer opaque) |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 323 | { |
Markus Armbruster | f3a0640 | 2015-09-14 13:50:44 +0200 | [diff] [blame] | 324 | GAChannel *c = g_new0(GAChannel, 1); |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 325 | SECURITY_ATTRIBUTES sec_attrs; |
| 326 | |
| 327 | if (!ga_channel_open(c, method, path)) { |
| 328 | g_critical("error opening channel"); |
| 329 | g_free(c); |
| 330 | return NULL; |
| 331 | } |
| 332 | |
| 333 | c->cb = cb; |
| 334 | c->user_data = opaque; |
| 335 | |
| 336 | sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 337 | sec_attrs.lpSecurityDescriptor = NULL; |
| 338 | sec_attrs.bInheritHandle = false; |
| 339 | |
| 340 | c->rstate.buf_size = QGA_READ_COUNT_DEFAULT; |
| 341 | c->rstate.buf = g_malloc(QGA_READ_COUNT_DEFAULT); |
| 342 | c->rstate.ov.hEvent = CreateEvent(&sec_attrs, FALSE, FALSE, NULL); |
| 343 | |
| 344 | c->source = ga_channel_create_watch(c); |
| 345 | g_source_attach(c->source, NULL); |
| 346 | return c; |
| 347 | } |
| 348 | |
| 349 | void ga_channel_free(GAChannel *c) |
| 350 | { |
| 351 | if (c->source) { |
| 352 | g_source_destroy(c->source); |
| 353 | } |
| 354 | if (c->rstate.ov.hEvent) { |
| 355 | CloseHandle(c->rstate.ov.hEvent); |
| 356 | } |
| 357 | g_free(c->rstate.buf); |
| 358 | g_free(c); |
| 359 | } |