File: | qemu-ga.c |
Location: | line 439, column 5 |
Description: | Null pointer passed as an argument to a 'nonnull' parameter |
1 | /* | ||
2 | * QEMU Guest Agent | ||
3 | * | ||
4 | * Copyright IBM Corp. 2011 | ||
5 | * | ||
6 | * Authors: | ||
7 | * Adam Litke <aglitke@linux.vnet.ibm.com> | ||
8 | * Michael Roth <mdroth@linux.vnet.ibm.com> | ||
9 | * | ||
10 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
11 | * See the COPYING file in the top-level directory. | ||
12 | */ | ||
13 | #include <stdlib.h> | ||
14 | #include <stdio.h> | ||
15 | #include <stdbool.h> | ||
16 | #include <glib.h> | ||
17 | #include <getopt.h> | ||
18 | #ifndef _WIN32 | ||
19 | #include <syslog.h> | ||
20 | #include <sys/wait.h> | ||
21 | #include <sys/stat.h> | ||
22 | #endif | ||
23 | #include "json-streamer.h" | ||
24 | #include "json-parser.h" | ||
25 | #include "qint.h" | ||
26 | #include "qjson.h" | ||
27 | #include "qga/guest-agent-core.h" | ||
28 | #include "module.h" | ||
29 | #include "signal.h" | ||
30 | #include "qerror.h" | ||
31 | #include "error_int.h" | ||
32 | #include "qapi/qmp-core.h" | ||
33 | #include "qga/channel.h" | ||
34 | #ifdef _WIN32 | ||
35 | #include "qga/service-win32.h" | ||
36 | #include <windows.h> | ||
37 | #endif | ||
38 | |||
39 | #ifndef _WIN32 | ||
40 | #define QGA_VIRTIO_PATH_DEFAULT"/dev/virtio-ports/org.qemu.guest_agent.0" "/dev/virtio-ports/org.qemu.guest_agent.0" | ||
41 | #else | ||
42 | #define QGA_VIRTIO_PATH_DEFAULT"/dev/virtio-ports/org.qemu.guest_agent.0" "\\\\.\\Global\\org.qemu.guest_agent.0" | ||
43 | #endif | ||
44 | #define QGA_PIDFILE_DEFAULT"/var/run/qemu-ga.pid" "/var/run/qemu-ga.pid" | ||
45 | #define QGA_STATEDIR_DEFAULT"/tmp" "/tmp" | ||
46 | #define QGA_SENTINEL_BYTE0xFF 0xFF | ||
47 | |||
48 | struct GAState { | ||
49 | JSONMessageParser parser; | ||
50 | GMainLoop *main_loop; | ||
51 | GAChannel *channel; | ||
52 | bool_Bool virtio; /* fastpath to check for virtio to deal with poll() quirks */ | ||
53 | GACommandState *command_state; | ||
54 | GLogLevelFlags log_level; | ||
55 | FILE *log_file; | ||
56 | bool_Bool logging_enabled; | ||
57 | #ifdef _WIN32 | ||
58 | GAService service; | ||
59 | #endif | ||
60 | bool_Bool delimit_response; | ||
61 | bool_Bool frozen; | ||
62 | GList *blacklist; | ||
63 | const char *state_filepath_isfrozen; | ||
64 | struct { | ||
65 | const char *log_filepath; | ||
66 | const char *pid_filepath; | ||
67 | } deferred_options; | ||
68 | }; | ||
69 | |||
70 | struct GAState *ga_state; | ||
71 | |||
72 | /* commands that are safe to issue while filesystems are frozen */ | ||
73 | static const char *ga_freeze_whitelist[] = { | ||
74 | "guest-ping", | ||
75 | "guest-info", | ||
76 | "guest-sync", | ||
77 | "guest-fsfreeze-status", | ||
78 | "guest-fsfreeze-thaw", | ||
79 | NULL((void*)0) | ||
80 | }; | ||
81 | |||
82 | #ifdef _WIN32 | ||
83 | DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, | ||
84 | LPVOID ctx); | ||
85 | VOID WINAPI service_main(DWORD argc, TCHAR *argv[]); | ||
86 | #endif | ||
87 | |||
88 | static void quit_handler(int sig) | ||
89 | { | ||
90 | /* if we're frozen, don't exit unless we're absolutely forced to, | ||
91 | * because it's basically impossible for graceful exit to complete | ||
92 | * unless all log/pid files are on unfreezable filesystems. there's | ||
93 | * also a very likely chance killing the agent before unfreezing | ||
94 | * the filesystems is a mistake (or will be viewed as one later). | ||
95 | */ | ||
96 | if (ga_is_frozen(ga_state)) { | ||
97 | return; | ||
98 | } | ||
99 | g_debug("received signal num %d, quitting", sig)g_log (((gchar*) 0), G_LOG_LEVEL_DEBUG, "received signal num %d, quitting" , sig); | ||
100 | |||
101 | if (g_main_loop_is_running(ga_state->main_loop)) { | ||
102 | g_main_loop_quit(ga_state->main_loop); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | #ifndef _WIN32 | ||
107 | static gboolean register_signal_handlers(void) | ||
108 | { | ||
109 | struct sigaction sigact; | ||
110 | int ret; | ||
111 | |||
112 | memset(&sigact, 0, sizeof(struct sigaction)); | ||
113 | sigact.sa_handler__sigaction_handler.sa_handler = quit_handler; | ||
114 | |||
115 | ret = sigaction(SIGINT2, &sigact, NULL((void*)0)); | ||
116 | if (ret == -1) { | ||
117 | g_error("error configuring signal handler: %s", strerror(errno))do { g_log (((gchar*) 0), G_LOG_LEVEL_ERROR, "error configuring signal handler: %s" , strerror((*__errno_location ()))); for (;;) ; } while (0); | ||
118 | return false0; | ||
119 | } | ||
120 | ret = sigaction(SIGTERM15, &sigact, NULL((void*)0)); | ||
121 | if (ret == -1) { | ||
122 | g_error("error configuring signal handler: %s", strerror(errno))do { g_log (((gchar*) 0), G_LOG_LEVEL_ERROR, "error configuring signal handler: %s" , strerror((*__errno_location ()))); for (;;) ; } while (0); | ||
123 | return false0; | ||
124 | } | ||
125 | |||
126 | return true1; | ||
127 | } | ||
128 | |||
129 | /* TODO: use this in place of all post-fork() fclose(std*) callers */ | ||
130 | void reopen_fd_to_null(int fd) | ||
131 | { | ||
132 | int nullfd; | ||
133 | |||
134 | nullfd = open("/dev/null", O_RDWR02); | ||
135 | if (nullfd < 0) { | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | dup2(nullfd, fd); | ||
140 | |||
141 | if (nullfd != fd) { | ||
142 | close(nullfd); | ||
143 | } | ||
144 | } | ||
145 | #endif | ||
146 | |||
147 | static void usage(const char *cmd) | ||
148 | { | ||
149 | printf( | ||
150 | "Usage: %s [-m <method> -p <path>] [<options>]\n" | ||
151 | "QEMU Guest Agent %s\n" | ||
152 | "\n" | ||
153 | " -m, --method transport method: one of unix-listen, virtio-serial, or\n" | ||
154 | " isa-serial (virtio-serial is the default)\n" | ||
155 | " -p, --path device/socket path (the default for virtio-serial is:\n" | ||
156 | " %s)\n" | ||
157 | " -l, --logfile set logfile path, logs to stderr by default\n" | ||
158 | " -f, --pidfile specify pidfile (default is %s)\n" | ||
159 | " -t, --statedir specify dir to store state information (absolute paths\n" | ||
160 | " only, default is %s)\n" | ||
161 | " -v, --verbose log extra debugging information\n" | ||
162 | " -V, --version print version information and exit\n" | ||
163 | " -d, --daemonize become a daemon\n" | ||
164 | #ifdef _WIN32 | ||
165 | " -s, --service service commands: install, uninstall\n" | ||
166 | #endif | ||
167 | " -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"\n" | ||
168 | " to list available RPCs)\n" | ||
169 | " -h, --help display this help and exit\n" | ||
170 | "\n" | ||
171 | "Report bugs to <mdroth@linux.vnet.ibm.com>\n" | ||
172 | , cmd, QEMU_VERSION"1.1.50", QGA_VIRTIO_PATH_DEFAULT"/dev/virtio-ports/org.qemu.guest_agent.0", QGA_PIDFILE_DEFAULT"/var/run/qemu-ga.pid", | ||
173 | QGA_STATEDIR_DEFAULT"/tmp"); | ||
174 | } | ||
175 | |||
176 | static const char *ga_log_level_str(GLogLevelFlags level) | ||
177 | { | ||
178 | switch (level & G_LOG_LEVEL_MASK) { | ||
179 | case G_LOG_LEVEL_ERROR: | ||
180 | return "error"; | ||
181 | case G_LOG_LEVEL_CRITICAL: | ||
182 | return "critical"; | ||
183 | case G_LOG_LEVEL_WARNING: | ||
184 | return "warning"; | ||
185 | case G_LOG_LEVEL_MESSAGE: | ||
186 | return "message"; | ||
187 | case G_LOG_LEVEL_INFO: | ||
188 | return "info"; | ||
189 | case G_LOG_LEVEL_DEBUG: | ||
190 | return "debug"; | ||
191 | default: | ||
192 | return "user"; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | bool_Bool ga_logging_enabled(GAState *s) | ||
197 | { | ||
198 | return s->logging_enabled; | ||
199 | } | ||
200 | |||
201 | void ga_disable_logging(GAState *s) | ||
202 | { | ||
203 | s->logging_enabled = false0; | ||
204 | } | ||
205 | |||
206 | void ga_enable_logging(GAState *s) | ||
207 | { | ||
208 | s->logging_enabled = true1; | ||
209 | } | ||
210 | |||
211 | static void ga_log(const gchar *domain, GLogLevelFlags level, | ||
212 | const gchar *msg, gpointer opaque) | ||
213 | { | ||
214 | GAState *s = opaque; | ||
215 | GTimeVal time; | ||
216 | const char *level_str = ga_log_level_str(level); | ||
217 | |||
218 | if (!ga_logging_enabled(s)) { | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | level &= G_LOG_LEVEL_MASK; | ||
223 | #ifndef _WIN32 | ||
224 | if (domain && strcmp(domain, "syslog") == 0) { | ||
225 | syslog(LOG_INFO6, "%s: %s", level_str, msg); | ||
226 | } else if (level & s->log_level) { | ||
227 | #else | ||
228 | if (level & s->log_level) { | ||
229 | #endif | ||
230 | g_get_current_time(&time); | ||
231 | fprintf(s->log_file, | ||
232 | "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg); | ||
233 | fflush(s->log_file); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | void ga_set_response_delimited(GAState *s) | ||
238 | { | ||
239 | s->delimit_response = true1; | ||
240 | } | ||
241 | |||
242 | #ifndef _WIN32 | ||
243 | static bool_Bool ga_open_pidfile(const char *pidfile) | ||
244 | { | ||
245 | int pidfd; | ||
246 | char pidstr[32]; | ||
247 | |||
248 | pidfd = open(pidfile, O_CREAT0100|O_WRONLY01, S_IRUSR0400|S_IWUSR0200); | ||
249 | if (pidfd == -1 || lockf(pidfd, F_TLOCK2, 0)) { | ||
250 | g_critical("Cannot lock pid file, %s", strerror(errno))g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "Cannot lock pid file, %s" , strerror((*__errno_location ()))); | ||
251 | return false0; | ||
252 | } | ||
253 | |||
254 | if (ftruncate(pidfd, 0) || lseek(pidfd, 0, SEEK_SET0)) { | ||
255 | g_critical("Failed to truncate pid file")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "Failed to truncate pid file" ); | ||
256 | goto fail; | ||
257 | } | ||
258 | sprintf(pidstr, "%d", getpid()); | ||
259 | if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) { | ||
260 | g_critical("Failed to write pid file")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "Failed to write pid file" ); | ||
261 | goto fail; | ||
262 | } | ||
263 | |||
264 | return true1; | ||
265 | |||
266 | fail: | ||
267 | unlink(pidfile); | ||
268 | return false0; | ||
269 | } | ||
270 | #else /* _WIN32 */ | ||
271 | static bool_Bool ga_open_pidfile(const char *pidfile) | ||
272 | { | ||
273 | return true1; | ||
274 | } | ||
275 | #endif | ||
276 | |||
277 | static gint ga_strcmp(gconstpointer str1, gconstpointer str2) | ||
278 | { | ||
279 | return strcmp(str1, str2); | ||
280 | } | ||
281 | |||
282 | /* disable commands that aren't safe for fsfreeze */ | ||
283 | static void ga_disable_non_whitelisted(void) | ||
284 | { | ||
285 | char **list_head, **list; | ||
286 | bool_Bool whitelisted; | ||
287 | int i; | ||
288 | |||
289 | list_head = list = qmp_get_command_list(); | ||
290 | while (*list != NULL((void*)0)) { | ||
291 | whitelisted = false0; | ||
292 | i = 0; | ||
293 | while (ga_freeze_whitelist[i] != NULL((void*)0)) { | ||
294 | if (strcmp(*list, ga_freeze_whitelist[i]) == 0) { | ||
295 | whitelisted = true1; | ||
296 | } | ||
297 | i++; | ||
298 | } | ||
299 | if (!whitelisted) { | ||
300 | g_debug("disabling command: %s", *list)g_log (((gchar*) 0), G_LOG_LEVEL_DEBUG, "disabling command: %s" , *list); | ||
301 | qmp_disable_command(*list); | ||
302 | } | ||
303 | g_free(*list); | ||
304 | list++; | ||
305 | } | ||
306 | g_free(list_head); | ||
307 | } | ||
308 | |||
309 | /* [re-]enable all commands, except those explicitly blacklisted by user */ | ||
310 | static void ga_enable_non_blacklisted(GList *blacklist) | ||
311 | { | ||
312 | char **list_head, **list; | ||
313 | |||
314 | list_head = list = qmp_get_command_list(); | ||
315 | while (*list != NULL((void*)0)) { | ||
316 | if (g_list_find_custom(blacklist, *list, ga_strcmp) == NULL((void*)0) && | ||
317 | !qmp_command_is_enabled(*list)) { | ||
318 | g_debug("enabling command: %s", *list)g_log (((gchar*) 0), G_LOG_LEVEL_DEBUG, "enabling command: %s" , *list); | ||
319 | qmp_enable_command(*list); | ||
320 | } | ||
321 | g_free(*list); | ||
322 | list++; | ||
323 | } | ||
324 | g_free(list_head); | ||
325 | } | ||
326 | |||
327 | static bool_Bool ga_create_file(const char *path) | ||
328 | { | ||
329 | int fd = open(path, O_CREAT0100 | O_WRONLY01, S_IWUSR0200 | S_IRUSR0400); | ||
330 | if (fd == -1) { | ||
331 | g_warning("unable to open/create file %s: %s", path, strerror(errno))g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "unable to open/create file %s: %s" , path, strerror((*__errno_location ()))); | ||
332 | return false0; | ||
333 | } | ||
334 | close(fd); | ||
335 | return true1; | ||
336 | } | ||
337 | |||
338 | static bool_Bool ga_delete_file(const char *path) | ||
339 | { | ||
340 | int ret = unlink(path); | ||
341 | if (ret == -1) { | ||
342 | g_warning("unable to delete file: %s: %s", path, strerror(errno))g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "unable to delete file: %s: %s" , path, strerror((*__errno_location ()))); | ||
343 | return false0; | ||
344 | } | ||
345 | |||
346 | return true1; | ||
347 | } | ||
348 | |||
349 | bool_Bool ga_is_frozen(GAState *s) | ||
350 | { | ||
351 | return s->frozen; | ||
352 | } | ||
353 | |||
354 | void ga_set_frozen(GAState *s) | ||
355 | { | ||
356 | if (ga_is_frozen(s)) { | ||
357 | return; | ||
358 | } | ||
359 | /* disable all non-whitelisted (for frozen state) commands */ | ||
360 | ga_disable_non_whitelisted(); | ||
361 | g_warning("disabling logging due to filesystem freeze")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "disabling logging due to filesystem freeze" ); | ||
362 | ga_disable_logging(s); | ||
363 | s->frozen = true1; | ||
364 | if (!ga_create_file(s->state_filepath_isfrozen)) { | ||
365 | g_warning("unable to create %s, fsfreeze may not function properly",g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "unable to create %s, fsfreeze may not function properly" , s->state_filepath_isfrozen) | ||
366 | s->state_filepath_isfrozen)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "unable to create %s, fsfreeze may not function properly" , s->state_filepath_isfrozen); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | void ga_unset_frozen(GAState *s) | ||
371 | { | ||
372 | if (!ga_is_frozen(s)) { | ||
373 | return; | ||
374 | } | ||
375 | |||
376 | /* if we delayed creation/opening of pid/log files due to being | ||
377 | * in a frozen state at start up, do it now | ||
378 | */ | ||
379 | if (s->deferred_options.log_filepath) { | ||
380 | s->log_file = fopen(s->deferred_options.log_filepath, "a"); | ||
381 | if (!s->log_file) { | ||
382 | s->log_file = stderrstderr; | ||
383 | } | ||
384 | s->deferred_options.log_filepath = NULL((void*)0); | ||
385 | } | ||
386 | ga_enable_logging(s); | ||
387 | g_warning("logging re-enabled due to filesystem unfreeze")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "logging re-enabled due to filesystem unfreeze" ); | ||
388 | if (s->deferred_options.pid_filepath) { | ||
389 | if (!ga_open_pidfile(s->deferred_options.pid_filepath)) { | ||
390 | g_warning("failed to create/open pid file")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "failed to create/open pid file" ); | ||
391 | } | ||
392 | s->deferred_options.pid_filepath = NULL((void*)0); | ||
393 | } | ||
394 | |||
395 | /* enable all disabled, non-blacklisted commands */ | ||
396 | ga_enable_non_blacklisted(s->blacklist); | ||
397 | s->frozen = false0; | ||
398 | if (!ga_delete_file(s->state_filepath_isfrozen)) { | ||
399 | g_warning("unable to delete %s, fsfreeze may not function properly",g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "unable to delete %s, fsfreeze may not function properly" , s->state_filepath_isfrozen) | ||
400 | s->state_filepath_isfrozen)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "unable to delete %s, fsfreeze may not function properly" , s->state_filepath_isfrozen); | ||
401 | } | ||
402 | } | ||
403 | |||
404 | static void become_daemon(const char *pidfile) | ||
405 | { | ||
406 | #ifndef _WIN32 | ||
407 | pid_t pid, sid; | ||
408 | |||
409 | pid = fork(); | ||
410 | if (pid < 0) { | ||
| |||
411 | exit(EXIT_FAILURE1); | ||
412 | } | ||
413 | if (pid > 0) { | ||
| |||
414 | exit(EXIT_SUCCESS0); | ||
415 | } | ||
416 | |||
417 | if (pidfile) { | ||
| |||
| |||
418 | if (!ga_open_pidfile(pidfile)) { | ||
419 | g_critical("failed to create pidfile")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "failed to create pidfile" ); | ||
420 | exit(EXIT_FAILURE1); | ||
421 | } | ||
422 | } | ||
423 | |||
424 | umask(0); | ||
425 | sid = setsid(); | ||
426 | if (sid < 0) { | ||
| |||
427 | goto fail; | ||
| |||
428 | } | ||
429 | if ((chdir("/")) < 0) { | ||
430 | goto fail; | ||
431 | } | ||
432 | |||
433 | reopen_fd_to_null(STDIN_FILENO0); | ||
434 | reopen_fd_to_null(STDOUT_FILENO1); | ||
435 | reopen_fd_to_null(STDERR_FILENO2); | ||
436 | return; | ||
437 | |||
438 | fail: | ||
439 | unlink(pidfile); | ||
| |||
440 | g_critical("failed to daemonize")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "failed to daemonize" ); | ||
441 | exit(EXIT_FAILURE1); | ||
442 | #endif | ||
443 | } | ||
444 | |||
445 | static int send_response(GAState *s, QObject *payload) | ||
446 | { | ||
447 | const char *buf; | ||
448 | QString *payload_qstr, *response_qstr; | ||
449 | GIOStatus status; | ||
450 | |||
451 | g_assert(payload && s->channel)do { if (payload && s->channel) ; else g_assertion_message_expr (((gchar*) 0), "/home/stefan/src/qemu/qemu.org/qemu/qemu-ga.c" , 451, ((const char*) (__PRETTY_FUNCTION__)), "payload && s->channel" ); } while (0); | ||
452 | |||
453 | payload_qstr = qobject_to_json(payload); | ||
454 | if (!payload_qstr) { | ||
455 | return -EINVAL22; | ||
456 | } | ||
457 | |||
458 | if (s->delimit_response) { | ||
459 | s->delimit_response = false0; | ||
460 | response_qstr = qstring_new(); | ||
461 | qstring_append_chr(response_qstr, QGA_SENTINEL_BYTE0xFF); | ||
462 | qstring_append(response_qstr, qstring_get_str(payload_qstr)); | ||
463 | QDECREF(payload_qstr)qobject_decref((&(payload_qstr)->base)); | ||
464 | } else { | ||
465 | response_qstr = payload_qstr; | ||
466 | } | ||
467 | |||
468 | qstring_append_chr(response_qstr, '\n'); | ||
469 | buf = qstring_get_str(response_qstr); | ||
470 | status = ga_channel_write_all(s->channel, buf, strlen(buf)); | ||
471 | QDECREF(response_qstr)qobject_decref((&(response_qstr)->base)); | ||
472 | if (status != G_IO_STATUS_NORMAL) { | ||
473 | return -EIO5; | ||
474 | } | ||
475 | |||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static void process_command(GAState *s, QDict *req) | ||
480 | { | ||
481 | QObject *rsp = NULL((void*)0); | ||
482 | int ret; | ||
483 | |||
484 | g_assert(req)do { if (req) ; else g_assertion_message_expr (((gchar*) 0), "/home/stefan/src/qemu/qemu.org/qemu/qemu-ga.c" , 484, ((const char*) (__PRETTY_FUNCTION__)), "req"); } while (0); | ||
485 | g_debug("processing command")g_log (((gchar*) 0), G_LOG_LEVEL_DEBUG, "processing command"); | ||
486 | rsp = qmp_dispatch(QOBJECT(req)(&(req)->base)); | ||
487 | if (rsp) { | ||
488 | ret = send_response(s, rsp); | ||
489 | if (ret) { | ||
490 | g_warning("error sending response: %s", strerror(ret))g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "error sending response: %s" , strerror(ret)); | ||
491 | } | ||
492 | qobject_decref(rsp); | ||
493 | } | ||
494 | } | ||
495 | |||
496 | /* handle requests/control events coming in over the channel */ | ||
497 | static void process_event(JSONMessageParser *parser, QList *tokens) | ||
498 | { | ||
499 | GAState *s = container_of(parser, GAState, parser)({ const typeof(((GAState *) 0)->parser) *__mptr = (parser ); (GAState *) ((char *) __mptr - __builtin_offsetof(GAState, parser));}); | ||
500 | QObject *obj; | ||
501 | QDict *qdict; | ||
502 | Error *err = NULL((void*)0); | ||
503 | int ret; | ||
504 | |||
505 | g_assert(s && parser)do { if (s && parser) ; else g_assertion_message_expr (((gchar*) 0), "/home/stefan/src/qemu/qemu.org/qemu/qemu-ga.c" , 505, ((const char*) (__PRETTY_FUNCTION__)), "s && parser" ); } while (0); | ||
506 | |||
507 | g_debug("process_event: called")g_log (((gchar*) 0), G_LOG_LEVEL_DEBUG, "process_event: called" ); | ||
508 | obj = json_parser_parse_err(tokens, NULL((void*)0), &err); | ||
509 | if (err || !obj || qobject_type(obj) != QTYPE_QDICT) { | ||
510 | qobject_decref(obj); | ||
511 | qdict = qdict_new(); | ||
512 | if (!err) { | ||
513 | g_warning("failed to parse event: unknown error")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "failed to parse event: unknown error" ); | ||
514 | error_set(&err, QERR_JSON_PARSING"{ 'class': 'JSONParsing', 'data': {} }"); | ||
515 | } else { | ||
516 | g_warning("failed to parse event: %s", error_get_pretty(err))g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "failed to parse event: %s" , error_get_pretty(err)); | ||
517 | } | ||
518 | qdict_put_obj(qdict, "error", error_get_qobject(err)); | ||
519 | error_free(err); | ||
520 | } else { | ||
521 | qdict = qobject_to_qdict(obj); | ||
522 | } | ||
523 | |||
524 | g_assert(qdict)do { if (qdict) ; else g_assertion_message_expr (((gchar*) 0) , "/home/stefan/src/qemu/qemu.org/qemu/qemu-ga.c", 524, ((const char*) (__PRETTY_FUNCTION__)), "qdict"); } while (0); | ||
525 | |||
526 | /* handle host->guest commands */ | ||
527 | if (qdict_haskey(qdict, "execute")) { | ||
528 | process_command(s, qdict); | ||
529 | } else { | ||
530 | if (!qdict_haskey(qdict, "error")) { | ||
531 | QDECREF(qdict)qobject_decref((&(qdict)->base)); | ||
532 | qdict = qdict_new(); | ||
533 | g_warning("unrecognized payload format")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "unrecognized payload format" ); | ||
534 | error_set(&err, QERR_UNSUPPORTED"{ 'class': 'Unsupported', 'data': {} }"); | ||
535 | qdict_put_obj(qdict, "error", error_get_qobject(err)); | ||
536 | error_free(err); | ||
537 | } | ||
538 | ret = send_response(s, QOBJECT(qdict)(&(qdict)->base)); | ||
539 | if (ret) { | ||
540 | g_warning("error sending error response: %s", strerror(ret))g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "error sending error response: %s" , strerror(ret)); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | QDECREF(qdict)qobject_decref((&(qdict)->base)); | ||
545 | } | ||
546 | |||
547 | /* false return signals GAChannel to close the current client connection */ | ||
548 | static gboolean channel_event_cb(GIOCondition condition, gpointer data) | ||
549 | { | ||
550 | GAState *s = data; | ||
551 | gchar buf[QGA_READ_COUNT_DEFAULT4096+1]; | ||
552 | gsize count; | ||
553 | GError *err = NULL((void*)0); | ||
554 | GIOStatus status = ga_channel_read(s->channel, buf, QGA_READ_COUNT_DEFAULT4096, &count); | ||
555 | if (err != NULL((void*)0)) { | ||
556 | g_warning("error reading channel: %s", err->message)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "error reading channel: %s" , err->message); | ||
557 | g_error_free(err); | ||
558 | return false0; | ||
559 | } | ||
560 | switch (status) { | ||
561 | case G_IO_STATUS_ERROR: | ||
562 | g_warning("error reading channel")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "error reading channel" ); | ||
563 | return false0; | ||
564 | case G_IO_STATUS_NORMAL: | ||
565 | buf[count] = 0; | ||
566 | g_debug("read data, count: %d, data: %s", (int)count, buf)g_log (((gchar*) 0), G_LOG_LEVEL_DEBUG, "read data, count: %d, data: %s" , (int)count, buf); | ||
567 | json_message_parser_feed(&s->parser, (char *)buf, (int)count); | ||
568 | break; | ||
569 | case G_IO_STATUS_EOF: | ||
570 | g_debug("received EOF")g_log (((gchar*) 0), G_LOG_LEVEL_DEBUG, "received EOF"); | ||
571 | if (!s->virtio) { | ||
572 | return false0; | ||
573 | } | ||
574 | case G_IO_STATUS_AGAIN: | ||
575 | /* virtio causes us to spin here when no process is attached to | ||
576 | * host-side chardev. sleep a bit to mitigate this | ||
577 | */ | ||
578 | if (s->virtio) { | ||
579 | usleep(100*1000); | ||
580 | } | ||
581 | return true1; | ||
582 | default: | ||
583 | g_warning("unknown channel read status, closing")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "unknown channel read status, closing" ); | ||
584 | return false0; | ||
585 | } | ||
586 | return true1; | ||
587 | } | ||
588 | |||
589 | static gboolean channel_init(GAState *s, const gchar *method, const gchar *path) | ||
590 | { | ||
591 | GAChannelMethod channel_method; | ||
592 | |||
593 | if (method == NULL((void*)0)) { | ||
594 | method = "virtio-serial"; | ||
595 | } | ||
596 | |||
597 | if (path == NULL((void*)0)) { | ||
598 | if (strcmp(method, "virtio-serial") != 0) { | ||
599 | g_critical("must specify a path for this channel")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "must specify a path for this channel" ); | ||
600 | return false0; | ||
601 | } | ||
602 | /* try the default path for the virtio-serial port */ | ||
603 | path = QGA_VIRTIO_PATH_DEFAULT"/dev/virtio-ports/org.qemu.guest_agent.0"; | ||
604 | } | ||
605 | |||
606 | if (strcmp(method, "virtio-serial") == 0) { | ||
607 | s->virtio = true1; /* virtio requires special handling in some cases */ | ||
608 | channel_method = GA_CHANNEL_VIRTIO_SERIAL; | ||
609 | } else if (strcmp(method, "isa-serial") == 0) { | ||
610 | channel_method = GA_CHANNEL_ISA_SERIAL; | ||
611 | } else if (strcmp(method, "unix-listen") == 0) { | ||
612 | channel_method = GA_CHANNEL_UNIX_LISTEN; | ||
613 | } else { | ||
614 | g_critical("unsupported channel method/type: %s", method)g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "unsupported channel method/type: %s" , method); | ||
615 | return false0; | ||
616 | } | ||
617 | |||
618 | s->channel = ga_channel_new(channel_method, path, channel_event_cb, s); | ||
619 | if (!s->channel) { | ||
620 | g_critical("failed to create guest agent channel")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "failed to create guest agent channel" ); | ||
621 | return false0; | ||
622 | } | ||
623 | |||
624 | return true1; | ||
625 | } | ||
626 | |||
627 | #ifdef _WIN32 | ||
628 | DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, | ||
629 | LPVOID ctx) | ||
630 | { | ||
631 | DWORD ret = NO_ERROR; | ||
632 | GAService *service = &ga_state->service; | ||
633 | |||
634 | switch (ctrl) | ||
635 | { | ||
636 | case SERVICE_CONTROL_STOP: | ||
637 | case SERVICE_CONTROL_SHUTDOWN: | ||
638 | quit_handler(SIGTERM15); | ||
639 | service->status.dwCurrentState = SERVICE_STOP_PENDING; | ||
640 | SetServiceStatus(service->status_handle, &service->status); | ||
641 | break; | ||
642 | |||
643 | default: | ||
644 | ret = ERROR_CALL_NOT_IMPLEMENTED; | ||
645 | } | ||
646 | return ret; | ||
647 | } | ||
648 | |||
649 | VOID WINAPI service_main(DWORD argc, TCHAR *argv[]) | ||
650 | { | ||
651 | GAService *service = &ga_state->service; | ||
652 | |||
653 | service->status_handle = RegisterServiceCtrlHandlerEx(QGA_SERVICE_NAME, | ||
654 | service_ctrl_handler, NULL((void*)0)); | ||
655 | |||
656 | if (service->status_handle == 0) { | ||
657 | g_critical("Failed to register extended requests function!\n")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "Failed to register extended requests function!\n" ); | ||
658 | return; | ||
659 | } | ||
660 | |||
661 | service->status.dwServiceType = SERVICE_WIN32; | ||
662 | service->status.dwCurrentState = SERVICE_RUNNING; | ||
663 | service->status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; | ||
664 | service->status.dwWin32ExitCode = NO_ERROR; | ||
665 | service->status.dwServiceSpecificExitCode = NO_ERROR; | ||
666 | service->status.dwCheckPoint = 0; | ||
667 | service->status.dwWaitHint = 0; | ||
668 | SetServiceStatus(service->status_handle, &service->status); | ||
669 | |||
670 | g_main_loop_run(ga_state->main_loop); | ||
671 | |||
672 | service->status.dwCurrentState = SERVICE_STOPPED; | ||
673 | SetServiceStatus(service->status_handle, &service->status); | ||
674 | } | ||
675 | #endif | ||
676 | |||
677 | int main(int argc, char **argv) | ||
678 | { | ||
679 | const char *sopt = "hVvdm:p:l:f:b:s:t:"; | ||
680 | const char *method = NULL((void*)0), *path = NULL((void*)0); | ||
681 | const char *log_filepath = NULL((void*)0); | ||
682 | const char *pid_filepath = QGA_PIDFILE_DEFAULT"/var/run/qemu-ga.pid"; | ||
683 | const char *state_dir = QGA_STATEDIR_DEFAULT"/tmp"; | ||
684 | #ifdef _WIN32 | ||
685 | const char *service = NULL((void*)0); | ||
686 | #endif | ||
687 | const struct option lopt[] = { | ||
688 | { "help", 0, NULL((void*)0), 'h' }, | ||
689 | { "version", 0, NULL((void*)0), 'V' }, | ||
690 | { "logfile", 1, NULL((void*)0), 'l' }, | ||
691 | { "pidfile", 1, NULL((void*)0), 'f' }, | ||
692 | { "verbose", 0, NULL((void*)0), 'v' }, | ||
693 | { "method", 1, NULL((void*)0), 'm' }, | ||
694 | { "path", 1, NULL((void*)0), 'p' }, | ||
695 | { "daemonize", 0, NULL((void*)0), 'd' }, | ||
696 | { "blacklist", 1, NULL((void*)0), 'b' }, | ||
697 | #ifdef _WIN32 | ||
698 | { "service", 1, NULL((void*)0), 's' }, | ||
699 | #endif | ||
700 | { "statedir", 1, NULL((void*)0), 't' }, | ||
701 | { NULL((void*)0), 0, NULL((void*)0), 0 } | ||
702 | }; | ||
703 | int opt_ind = 0, ch, daemonize = 0, i, j, len; | ||
704 | GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; | ||
705 | GList *blacklist = NULL((void*)0); | ||
706 | GAState *s; | ||
707 | |||
708 | module_call_init(MODULE_INIT_QAPI); | ||
709 | |||
710 | while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { | ||
711 | switch (ch) { | ||
712 | case 'm': | ||
713 | method = optarg; | ||
714 | break; | ||
715 | case 'p': | ||
716 | path = optarg; | ||
717 | break; | ||
718 | case 'l': | ||
719 | log_filepath = optarg; | ||
720 | break; | ||
721 | case 'f': | ||
722 | pid_filepath = optarg; | ||
723 | break; | ||
724 | case 't': | ||
725 | state_dir = optarg; | ||
726 | break; | ||
727 | case 'v': | ||
728 | /* enable all log levels */ | ||
729 | log_level = G_LOG_LEVEL_MASK; | ||
730 | break; | ||
731 | case 'V': | ||
732 | printf("QEMU Guest Agent %s\n", QEMU_VERSION"1.1.50"); | ||
733 | return 0; | ||
734 | case 'd': | ||
735 | daemonize = 1; | ||
736 | break; | ||
737 | case 'b': { | ||
738 | char **list_head, **list; | ||
739 | if (*optarg == '?') { | ||
740 | list_head = list = qmp_get_command_list(); | ||
741 | while (*list != NULL((void*)0)) { | ||
742 | printf("%s\n", *list); | ||
743 | g_free(*list); | ||
744 | list++; | ||
745 | } | ||
746 | g_free(list_head); | ||
747 | return 0; | ||
748 | } | ||
749 | for (j = 0, i = 0, len = strlen(optarg); i < len; i++) { | ||
750 | if (optarg[i] == ',') { | ||
751 | optarg[i] = 0; | ||
752 | blacklist = g_list_append(blacklist, &optarg[j]); | ||
753 | j = i + 1; | ||
754 | } | ||
755 | } | ||
756 | if (j < i) { | ||
757 | blacklist = g_list_append(blacklist, &optarg[j]); | ||
758 | } | ||
759 | break; | ||
760 | } | ||
761 | #ifdef _WIN32 | ||
762 | case 's': | ||
763 | service = optarg; | ||
764 | if (strcmp(service, "install") == 0) { | ||
765 | return ga_install_service(path, log_filepath); | ||
766 | } else if (strcmp(service, "uninstall") == 0) { | ||
767 | return ga_uninstall_service(); | ||
768 | } else { | ||
769 | printf("Unknown service command.\n"); | ||
770 | return EXIT_FAILURE1; | ||
771 | } | ||
772 | break; | ||
773 | #endif | ||
774 | case 'h': | ||
775 | usage(argv[0]); | ||
776 | return 0; | ||
777 | case '?': | ||
778 | g_print("Unknown option, try '%s --help' for more information.\n", | ||
779 | argv[0]); | ||
780 | return EXIT_FAILURE1; | ||
781 | } | ||
782 | } | ||
783 | |||
784 | s = g_malloc0(sizeof(GAState)); | ||
785 | s->log_level = log_level; | ||
786 | s->log_file = stderrstderr; | ||
787 | g_log_set_default_handler(ga_log, s); | ||
788 | g_log_set_fatal_mask(NULL((void*)0), G_LOG_LEVEL_ERROR); | ||
789 | ga_enable_logging(s); | ||
790 | s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen", | ||
791 | state_dir); | ||
792 | s->frozen = false0; | ||
793 | #ifndef _WIN32 | ||
794 | /* check if a previous instance of qemu-ga exited with filesystems' state | ||
795 | * marked as frozen. this could be a stale value (a non-qemu-ga process | ||
796 | * or reboot may have since unfrozen them), but better to require an | ||
797 | * uneeded unfreeze than to risk hanging on start-up | ||
798 | */ | ||
799 | struct stat st; | ||
800 | if (stat(s->state_filepath_isfrozen, &st) == -1) { | ||
801 | /* it's okay if the file doesn't exist, but if we can't access for | ||
802 | * some other reason, such as permissions, there's a configuration | ||
803 | * that needs to be addressed. so just bail now before we get into | ||
804 | * more trouble later | ||
805 | */ | ||
806 | if (errno(*__errno_location ()) != ENOENT2) { | ||
807 | g_critical("unable to access state file at path %s: %s",g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "unable to access state file at path %s: %s" , s->state_filepath_isfrozen, strerror((*__errno_location ( )))) | ||
808 | s->state_filepath_isfrozen, strerror(errno))g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "unable to access state file at path %s: %s" , s->state_filepath_isfrozen, strerror((*__errno_location ( )))); | ||
809 | return EXIT_FAILURE1; | ||
810 | } | ||
811 | } else { | ||
812 | g_warning("previous instance appears to have exited with frozen"g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "previous instance appears to have exited with frozen" " filesystems. deferring logging/pidfile creation and" " disabling non-fsfreeze-safe commands until" " guest-fsfreeze-thaw is issued, or filesystems are" " manually unfrozen and the file %s is removed" , s->state_filepath_isfrozen) | ||
813 | " filesystems. deferring logging/pidfile creation and"g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "previous instance appears to have exited with frozen" " filesystems. deferring logging/pidfile creation and" " disabling non-fsfreeze-safe commands until" " guest-fsfreeze-thaw is issued, or filesystems are" " manually unfrozen and the file %s is removed" , s->state_filepath_isfrozen) | ||
814 | " disabling non-fsfreeze-safe commands until"g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "previous instance appears to have exited with frozen" " filesystems. deferring logging/pidfile creation and" " disabling non-fsfreeze-safe commands until" " guest-fsfreeze-thaw is issued, or filesystems are" " manually unfrozen and the file %s is removed" , s->state_filepath_isfrozen) | ||
815 | " guest-fsfreeze-thaw is issued, or filesystems are"g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "previous instance appears to have exited with frozen" " filesystems. deferring logging/pidfile creation and" " disabling non-fsfreeze-safe commands until" " guest-fsfreeze-thaw is issued, or filesystems are" " manually unfrozen and the file %s is removed" , s->state_filepath_isfrozen) | ||
816 | " manually unfrozen and the file %s is removed",g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "previous instance appears to have exited with frozen" " filesystems. deferring logging/pidfile creation and" " disabling non-fsfreeze-safe commands until" " guest-fsfreeze-thaw is issued, or filesystems are" " manually unfrozen and the file %s is removed" , s->state_filepath_isfrozen) | ||
817 | s->state_filepath_isfrozen)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "previous instance appears to have exited with frozen" " filesystems. deferring logging/pidfile creation and" " disabling non-fsfreeze-safe commands until" " guest-fsfreeze-thaw is issued, or filesystems are" " manually unfrozen and the file %s is removed" , s->state_filepath_isfrozen); | ||
818 | s->frozen = true1; | ||
819 | } | ||
820 | #endif | ||
821 | |||
822 | if (ga_is_frozen(s)) { | ||
823 | if (daemonize) { | ||
824 | /* delay opening/locking of pidfile till filesystem are unfrozen */ | ||
825 | s->deferred_options.pid_filepath = pid_filepath; | ||
826 | become_daemon(NULL((void*)0)); | ||
827 | } | ||
828 | if (log_filepath) { | ||
829 | /* delay opening the log file till filesystems are unfrozen */ | ||
830 | s->deferred_options.log_filepath = log_filepath; | ||
831 | } | ||
832 | ga_disable_logging(s); | ||
833 | ga_disable_non_whitelisted(); | ||
834 | } else { | ||
835 | if (daemonize) { | ||
836 | become_daemon(pid_filepath); | ||
837 | } | ||
838 | if (log_filepath) { | ||
839 | FILE *log_file = fopen(log_filepath, "a"); | ||
840 | if (!log_file) { | ||
841 | g_critical("unable to open specified log file: %s",g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "unable to open specified log file: %s" , strerror((*__errno_location ()))) | ||
842 | strerror(errno))g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "unable to open specified log file: %s" , strerror((*__errno_location ()))); | ||
843 | goto out_bad; | ||
844 | } | ||
845 | s->log_file = log_file; | ||
846 | } | ||
847 | } | ||
848 | |||
849 | if (blacklist) { | ||
850 | s->blacklist = blacklist; | ||
851 | do { | ||
852 | g_debug("disabling command: %s", (char *)blacklist->data)g_log (((gchar*) 0), G_LOG_LEVEL_DEBUG, "disabling command: %s" , (char *)blacklist->data); | ||
853 | qmp_disable_command(blacklist->data); | ||
854 | blacklist = g_list_next(blacklist)((blacklist) ? (((GList *)(blacklist))->next) : ((void*)0) ); | ||
855 | } while (blacklist); | ||
856 | } | ||
857 | s->command_state = ga_command_state_new(); | ||
858 | ga_command_state_init(s, s->command_state); | ||
859 | ga_command_state_init_all(s->command_state); | ||
860 | json_message_parser_init(&s->parser, process_event); | ||
861 | ga_state = s; | ||
862 | #ifndef _WIN32 | ||
863 | if (!register_signal_handlers()) { | ||
864 | g_critical("failed to register signal handlers")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "failed to register signal handlers" ); | ||
865 | goto out_bad; | ||
866 | } | ||
867 | #endif | ||
868 | |||
869 | s->main_loop = g_main_loop_new(NULL((void*)0), false0); | ||
870 | if (!channel_init(ga_state, method, path)) { | ||
871 | g_critical("failed to initialize guest agent channel")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "failed to initialize guest agent channel" ); | ||
872 | goto out_bad; | ||
873 | } | ||
874 | #ifndef _WIN32 | ||
875 | g_main_loop_run(ga_state->main_loop); | ||
876 | #else | ||
877 | if (daemonize) { | ||
878 | SERVICE_TABLE_ENTRY service_table[] = { | ||
879 | { (char *)QGA_SERVICE_NAME, service_main }, { NULL((void*)0), NULL((void*)0) } }; | ||
880 | StartServiceCtrlDispatcher(service_table); | ||
881 | } else { | ||
882 | g_main_loop_run(ga_state->main_loop); | ||
883 | } | ||
884 | #endif | ||
885 | |||
886 | ga_command_state_cleanup_all(ga_state->command_state); | ||
887 | ga_channel_free(ga_state->channel); | ||
888 | |||
889 | if (daemonize) { | ||
890 | unlink(pid_filepath); | ||
891 | } | ||
892 | return 0; | ||
893 | |||
894 | out_bad: | ||
895 | if (daemonize) { | ||
896 | unlink(pid_filepath); | ||
897 | } | ||
898 | return EXIT_FAILURE1; | ||
899 | } |