void ga_unset_frozen(GAState *s) { if (!ga_is_frozen(s)) { return; } /* if we delayed creation/opening of pid/log files due to being * in a frozen state at start up, do it now */ if (s->deferred_options.log_filepath) { s->log_file = ga_open_logfile(s->deferred_options.log_filepath); if (!s->log_file) { s->log_file = stderr; } s->deferred_options.log_filepath = NULL; } ga_enable_logging(s); g_warning("logging re-enabled due to filesystem unfreeze"); if (s->deferred_options.pid_filepath) { if (!ga_open_pidfile(s->deferred_options.pid_filepath)) { g_warning("failed to create/open pid file"); } s->deferred_options.pid_filepath = NULL; } /* enable all disabled, non-blacklisted commands */ qmp_for_each_command(&ga_commands, ga_enable_non_blacklisted, s->blacklist); s->frozen = false; if (!ga_delete_file(s->state_filepath_isfrozen)) { g_warning("unable to delete %s, fsfreeze may not function properly", s->state_filepath_isfrozen); } }
int64_t ga_get_fd_handle(GAState *s, Error **errp) { int64_t handle; g_assert(s->pstate_filepath); /* we blacklist commands and avoid operations that potentially require * writing to disk when we're in a frozen state. this includes opening * new files, so we should never get here in that situation */ g_assert(!ga_is_frozen(s)); handle = s->pstate.fd_counter++; /* This should never happen on a reasonable timeframe, as guest-file-open * would have to be issued 2^63 times */ if (s->pstate.fd_counter == INT64_MAX) { abort(); } if (!write_persistent_state(&s->pstate, s->pstate_filepath)) { error_setg(errp, "failed to commit persistent state to disk"); return -1; } return handle; }
void ga_set_frozen(GAState *s) { if (ga_is_frozen(s)) { return; } /* disable all non-whitelisted (for frozen state) commands */ qmp_for_each_command(&ga_commands, ga_disable_non_whitelisted, NULL); g_warning("disabling logging due to filesystem freeze"); ga_disable_logging(s); s->frozen = true; if (!ga_create_file(s->state_filepath_isfrozen)) { g_warning("unable to create %s, fsfreeze may not function properly", s->state_filepath_isfrozen); } }
static void quit_handler(int sig) { /* if we're frozen, don't exit unless we're absolutely forced to, * because it's basically impossible for graceful exit to complete * unless all log/pid files are on unfreezable filesystems. there's * also a very likely chance killing the agent before unfreezing * the filesystems is a mistake (or will be viewed as one later). */ if (ga_is_frozen(ga_state)) { return; } g_debug("received signal num %d, quitting", sig); if (g_main_loop_is_running(ga_state->main_loop)) { g_main_loop_quit(ga_state->main_loop); } }
int64_t ga_get_fd_handle(GAState *s, Error **errp) { int64_t handle; g_assert(s->pstate_filepath); /* we blacklist commands and avoid operations that potentially require * writing to disk when we're in a frozen state. this includes opening * new files, so we should never get here in that situation */ g_assert(!ga_is_frozen(s)); handle = s->pstate.fd_counter++; if (s->pstate.fd_counter < 0) { s->pstate.fd_counter = 0; } if (!write_persistent_state(&s->pstate, s->pstate_filepath)) { error_setg(errp, "failed to commit persistent state to disk"); } return handle; }
static void quit_handler(int sig) { /* if we're frozen, don't exit unless we're absolutely forced to, * because it's basically impossible for graceful exit to complete * unless all log/pid files are on unfreezable filesystems. there's * also a very likely chance killing the agent before unfreezing * the filesystems is a mistake (or will be viewed as one later). * On Windows the freeze interval is limited to 10 seconds, so * we should quit, but first we should wait for the timeout, thaw * the filesystem and quit. */ if (ga_is_frozen(ga_state)) { #ifdef _WIN32 int i = 0; Error *err = NULL; HANDLE hEventTimeout; g_debug("Thawing filesystems before exiting"); hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); if (hEventTimeout) { WaitForSingleObject(hEventTimeout, 0); CloseHandle(hEventTimeout); } qga_vss_fsfreeze(&i, false, &err); if (err) { g_debug("Error unfreezing filesystems prior to exiting: %s", error_get_pretty(err)); error_free(err); } #else return; #endif } g_debug("received signal num %d, quitting", sig); if (g_main_loop_is_running(ga_state->main_loop)) { g_main_loop_quit(ga_state->main_loop); } }
int main(int argc, char **argv) { const char *sopt = "hVvdm:p:l:f:F::b:s:t:"; const char *method = NULL, *path = NULL; const char *log_filepath = NULL; const char *pid_filepath; #ifdef CONFIG_FSFREEZE const char *fsfreeze_hook = NULL; #endif const char *state_dir; #ifdef _WIN32 const char *service = NULL; #endif const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "logfile", 1, NULL, 'l' }, { "pidfile", 1, NULL, 'f' }, #ifdef CONFIG_FSFREEZE { "fsfreeze-hook", 2, NULL, 'F' }, #endif { "verbose", 0, NULL, 'v' }, { "method", 1, NULL, 'm' }, { "path", 1, NULL, 'p' }, { "daemonize", 0, NULL, 'd' }, { "blacklist", 1, NULL, 'b' }, #ifdef _WIN32 { "service", 1, NULL, 's' }, #endif { "statedir", 1, NULL, 't' }, { NULL, 0, NULL, 0 } }; int opt_ind = 0, ch, daemonize = 0, i, j, len; GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; GList *blacklist = NULL; GAState *s; module_call_init(MODULE_INIT_QAPI); init_dfl_pathnames(); pid_filepath = dfl_pathnames.pidfile; state_dir = dfl_pathnames.state_dir; while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 'm': method = optarg; break; case 'p': path = optarg; break; case 'l': log_filepath = optarg; break; case 'f': pid_filepath = optarg; break; #ifdef CONFIG_FSFREEZE case 'F': fsfreeze_hook = optarg ? optarg : QGA_FSFREEZE_HOOK_DEFAULT; break; #endif case 't': state_dir = optarg; break; case 'v': /* enable all log levels */ log_level = G_LOG_LEVEL_MASK; break; case 'V': printf("QEMU Guest Agent %s\n", QEMU_VERSION); return 0; case 'd': daemonize = 1; break; case 'b': { if (is_help_option(optarg)) { qmp_for_each_command(ga_print_cmd, NULL); return 0; } for (j = 0, i = 0, len = strlen(optarg); i < len; i++) { if (optarg[i] == ',') { optarg[i] = 0; blacklist = g_list_append(blacklist, &optarg[j]); j = i + 1; } } if (j < i) { blacklist = g_list_append(blacklist, &optarg[j]); } break; } #ifdef _WIN32 case 's': service = optarg; if (strcmp(service, "install") == 0) { const char *fixed_state_dir; /* If the user passed the "-t" option, we save that state dir * in the service. Otherwise we let the service fetch the state * dir from the environment when it starts. */ fixed_state_dir = (state_dir == dfl_pathnames.state_dir) ? NULL : state_dir; if (ga_install_vss_provider()) { return EXIT_FAILURE; } if (ga_install_service(path, log_filepath, fixed_state_dir)) { return EXIT_FAILURE; } return 0; } else if (strcmp(service, "uninstall") == 0) { ga_uninstall_vss_provider(); return ga_uninstall_service(); } else { printf("Unknown service command.\n"); return EXIT_FAILURE; } break; #endif case 'h': usage(argv[0]); return 0; case '?': g_print("Unknown option, try '%s --help' for more information.\n", argv[0]); return EXIT_FAILURE; } } #ifdef _WIN32 /* On win32 the state directory is application specific (be it the default * or a user override). We got past the command line parsing; let's create * the directory (with any intermediate directories). If we run into an * error later on, we won't try to clean up the directory, it is considered * persistent. */ if (g_mkdir_with_parents(state_dir, S_IRWXU) == -1) { g_critical("unable to create (an ancestor of) the state directory" " '%s': %s", state_dir, strerror(errno)); return EXIT_FAILURE; } #endif s = g_malloc0(sizeof(GAState)); s->log_level = log_level; s->log_file = stderr; #ifdef CONFIG_FSFREEZE s->fsfreeze_hook = fsfreeze_hook; #endif g_log_set_default_handler(ga_log, s); g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); ga_enable_logging(s); s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen", state_dir); s->pstate_filepath = g_strdup_printf("%s/qga.state", state_dir); s->frozen = false; #ifndef _WIN32 /* check if a previous instance of qemu-ga exited with filesystems' state * marked as frozen. this could be a stale value (a non-qemu-ga process * or reboot may have since unfrozen them), but better to require an * uneeded unfreeze than to risk hanging on start-up */ struct stat st; if (stat(s->state_filepath_isfrozen, &st) == -1) { /* it's okay if the file doesn't exist, but if we can't access for * some other reason, such as permissions, there's a configuration * that needs to be addressed. so just bail now before we get into * more trouble later */ if (errno != ENOENT) { g_critical("unable to access state file at path %s: %s", s->state_filepath_isfrozen, strerror(errno)); return EXIT_FAILURE; } } else { g_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); s->frozen = true; } #endif if (ga_is_frozen(s)) { if (daemonize) { /* delay opening/locking of pidfile till filesystems are unfrozen */ s->deferred_options.pid_filepath = pid_filepath; become_daemon(NULL); } if (log_filepath) { /* delay opening the log file till filesystems are unfrozen */ s->deferred_options.log_filepath = log_filepath; } ga_disable_logging(s); qmp_for_each_command(ga_disable_non_whitelisted, NULL); } else { if (daemonize) { become_daemon(pid_filepath); } if (log_filepath) { FILE *log_file = ga_open_logfile(log_filepath); if (!log_file) { g_critical("unable to open specified log file: %s", strerror(errno)); goto out_bad; } s->log_file = log_file; } } /* load persistent state from disk */ if (!read_persistent_state(&s->pstate, s->pstate_filepath, ga_is_frozen(s))) { g_critical("failed to load persistent state"); goto out_bad; } blacklist = ga_command_blacklist_init(blacklist); if (blacklist) { s->blacklist = blacklist; do { g_debug("disabling command: %s", (char *)blacklist->data); qmp_disable_command(blacklist->data); blacklist = g_list_next(blacklist); } while (blacklist); } s->command_state = ga_command_state_new(); ga_command_state_init(s, s->command_state); ga_command_state_init_all(s->command_state); json_message_parser_init(&s->parser, process_event); ga_state = s; #ifndef _WIN32 if (!register_signal_handlers()) { g_critical("failed to register signal handlers"); goto out_bad; } #endif s->main_loop = g_main_loop_new(NULL, false); if (!channel_init(ga_state, method, path)) { 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); if (daemonize) { unlink(pid_filepath); } return 0; out_bad: if (daemonize) { unlink(pid_filepath); } return EXIT_FAILURE; }
static gboolean read_persistent_state(GAPersistentState *pstate, const gchar *path, gboolean frozen) { GKeyFile *keyfile = NULL; GError *gerr = NULL; struct stat st; gboolean ret = true; g_assert(pstate); #ifdef USE_ANDROID_EMU if (android_stat(path, &st) == -1) { #else // !USE_ANDROID_EMU if (stat(path, &st) == -1) { #endif // USE_ANDROID_EMU /* it's okay if state file doesn't exist, but any other error * indicates a permissions issue or some other misconfiguration * that we likely won't be able to recover from. */ if (errno != ENOENT) { g_critical("unable to access state file at path %s: %s", path, strerror(errno)); ret = false; goto out; } /* file doesn't exist. initialize state to default values and * attempt to save now. (we could wait till later when we have * modified state we need to commit, but if there's a problem, * such as a missing parent directory, we want to catch it now) * * there is a potential scenario where someone either managed to * update the agent from a version that didn't use a key store * while qemu-ga thought the filesystem was frozen, or * deleted the key store prior to issuing a fsfreeze, prior * to restarting the agent. in this case we go ahead and defer * initial creation till we actually have modified state to * write, otherwise fail to recover from freeze. */ set_persistent_state_defaults(pstate); if (!frozen) { ret = write_persistent_state(pstate, path); if (!ret) { g_critical("unable to create state file at path %s", path); ret = false; goto out; } } ret = true; goto out; } keyfile = g_key_file_new(); g_key_file_load_from_file(keyfile, path, 0, &gerr); if (gerr) { g_critical("error loading persistent state from path: %s, %s", path, gerr->message); ret = false; goto out; } persistent_state_from_keyfile(pstate, keyfile); out: if (keyfile) { g_key_file_free(keyfile); } if (gerr) { g_error_free(gerr); } return ret; } int64_t ga_get_fd_handle(GAState *s, Error **errp) { int64_t handle; g_assert(s->pstate_filepath); /* we blacklist commands and avoid operations that potentially require * writing to disk when we're in a frozen state. this includes opening * new files, so we should never get here in that situation */ g_assert(!ga_is_frozen(s)); handle = s->pstate.fd_counter++; /* This should never happen on a reasonable timeframe, as guest-file-open * would have to be issued 2^63 times */ if (s->pstate.fd_counter == INT64_MAX) { abort(); } if (!write_persistent_state(&s->pstate, s->pstate_filepath)) { error_setg(errp, "failed to commit persistent state to disk"); return -1; } return handle; }
int main(int argc, char **argv) { const char *sopt = "hVvdm:p:l:f:b:s:t:"; const char *method = NULL, *path = NULL; const char *log_filepath = NULL; const char *pid_filepath = QGA_PIDFILE_DEFAULT; const char *state_dir = QGA_STATEDIR_DEFAULT; #ifdef _WIN32 const char *service = NULL; #endif const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "logfile", 1, NULL, 'l' }, { "pidfile", 1, NULL, 'f' }, { "verbose", 0, NULL, 'v' }, { "method", 1, NULL, 'm' }, { "path", 1, NULL, 'p' }, { "daemonize", 0, NULL, 'd' }, { "blacklist", 1, NULL, 'b' }, #ifdef _WIN32 { "service", 1, NULL, 's' }, #endif { "statedir", 1, NULL, 't' }, { NULL, 0, NULL, 0 } }; int opt_ind = 0, ch, daemonize = 0, i, j, len; GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; GList *blacklist = NULL; GAState *s; module_call_init(MODULE_INIT_QAPI); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 'm': method = optarg; break; case 'p': path = optarg; break; case 'l': log_filepath = optarg; break; case 'f': pid_filepath = optarg; break; case 't': state_dir = optarg; break; case 'v': /* enable all log levels */ log_level = G_LOG_LEVEL_MASK; break; case 'V': printf("QEMU Guest Agent %s\n", QEMU_VERSION); return 0; case 'd': daemonize = 1; break; case 'b': { char **list_head, **list; if (is_help_option(optarg)) { list_head = list = qmp_get_command_list(); while (*list != NULL) { printf("%s\n", *list); g_free(*list); list++; } g_free(list_head); return 0; } for (j = 0, i = 0, len = strlen(optarg); i < len; i++) { if (optarg[i] == ',') { optarg[i] = 0; blacklist = g_list_append(blacklist, &optarg[j]); j = i + 1; } } if (j < i) { blacklist = g_list_append(blacklist, &optarg[j]); } break; } #ifdef _WIN32 case 's': service = optarg; if (strcmp(service, "install") == 0) { return ga_install_service(path, log_filepath); } 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; case '?': g_print("Unknown option, try '%s --help' for more information.\n", argv[0]); return EXIT_FAILURE; } } s = g_malloc0(sizeof(GAState)); s->log_level = log_level; s->log_file = stderr; g_log_set_default_handler(ga_log, s); g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); ga_enable_logging(s); s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen", state_dir); s->frozen = false; #ifndef _WIN32 /* check if a previous instance of qemu-ga exited with filesystems' state * marked as frozen. this could be a stale value (a non-qemu-ga process * or reboot may have since unfrozen them), but better to require an * uneeded unfreeze than to risk hanging on start-up */ struct stat st; if (stat(s->state_filepath_isfrozen, &st) == -1) { /* it's okay if the file doesn't exist, but if we can't access for * some other reason, such as permissions, there's a configuration * that needs to be addressed. so just bail now before we get into * more trouble later */ if (errno != ENOENT) { g_critical("unable to access state file at path %s: %s", s->state_filepath_isfrozen, strerror(errno)); return EXIT_FAILURE; } } else { g_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); s->frozen = true; } #endif if (ga_is_frozen(s)) { if (daemonize) { /* delay opening/locking of pidfile till filesystem are unfrozen */ s->deferred_options.pid_filepath = pid_filepath; become_daemon(NULL); } if (log_filepath) { /* delay opening the log file till filesystems are unfrozen */ s->deferred_options.log_filepath = log_filepath; } ga_disable_logging(s); ga_disable_non_whitelisted(); } else { if (daemonize) { become_daemon(pid_filepath); } if (log_filepath) { FILE *log_file = fopen(log_filepath, "a"); if (!log_file) { g_critical("unable to open specified log file: %s", strerror(errno)); goto out_bad; } s->log_file = log_file; } } if (blacklist) { s->blacklist = blacklist; do { g_debug("disabling command: %s", (char *)blacklist->data); qmp_disable_command(blacklist->data); blacklist = g_list_next(blacklist); } while (blacklist); } s->command_state = ga_command_state_new(); ga_command_state_init(s, s->command_state); ga_command_state_init_all(s->command_state); json_message_parser_init(&s->parser, process_event); ga_state = s; #ifndef _WIN32 if (!register_signal_handlers()) { g_critical("failed to register signal handlers"); goto out_bad; } #endif s->main_loop = g_main_loop_new(NULL, false); if (!channel_init(ga_state, method, path)) { 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); if (daemonize) { unlink(pid_filepath); } return 0; out_bad: if (daemonize) { unlink(pid_filepath); } return EXIT_FAILURE; }