Exemple #1
0
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);
    }
}
Exemple #2
0
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;
}
Exemple #3
0
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);
    }
}
Exemple #4
0
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);
    }
}
Exemple #5
0
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;
}
Exemple #6
0
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);
    }
}
Exemple #7
0
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;
}
Exemple #9
0
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;
}