X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qga%2Fmain.c;h=c0d77c79c45991c3c47c56fe9fd5a863d6f64e11;hb=HEAD;hp=15fd3a4149f4a5565c6bd5c9ed5d2e79ec452b34;hpb=7286d62d4e259be8cecf3dc2deea80ecc14489a5;p=mirror_qemu.git diff --git a/qga/main.c b/qga/main.c index 15fd3a4149..bdf5344584 100644 --- a/qga/main.c +++ b/qga/main.c @@ -18,16 +18,14 @@ #include #include #endif -#include "qemu-common.h" +#include "qemu/help-texts.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "guest-agent-core.h" #include "qga-qapi-init-commands.h" -#include "qapi/qmp/qerror.h" #include "qapi/error.h" #include "channel.h" -#include "qemu/bswap.h" #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/sockets.h" @@ -38,17 +36,16 @@ #include "qga/service-win32.h" #include "qga/vss-win32.h" #endif -#ifdef __linux__ -#include -#ifdef FIFREEZE -#define CONFIG_FSFREEZE -#endif -#endif +#include "commands-common.h" #ifndef _WIN32 +#ifdef CONFIG_BSD +#define QGA_VIRTIO_PATH_DEFAULT "/dev/vtcon/org.qemu.guest_agent.0" +#else /* CONFIG_BSD */ #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" -#define QGA_STATE_RELATIVE_DIR "run" +#endif /* CONFIG_BSD */ #define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0" +#define QGA_STATE_RELATIVE_DIR "run" #else #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0" #define QGA_STATE_RELATIVE_DIR "qemu-ga" @@ -85,10 +82,12 @@ struct GAState { #ifdef _WIN32 GAService service; HANDLE wakeup_event; + HANDLE event_log; #endif bool delimit_response; bool frozen; - GList *blacklist; + GList *blockedrpcs; + GList *allowedrpcs; char *state_filepath_isfrozen; struct { const char *log_filepath; @@ -108,7 +107,7 @@ struct GAState *ga_state; QmpCommandList ga_commands; /* commands that are safe to issue while filesystems are frozen */ -static const char *ga_freeze_whitelist[] = { +static const char *ga_freeze_allowlist[] = { "guest-ping", "guest-info", "guest-sync", @@ -130,12 +129,12 @@ static void stop_agent(GAState *s, bool requested); static void init_dfl_pathnames(void) { + g_autofree char *state = qemu_get_local_state_dir(); + g_assert(dfl_pathnames.state_dir == NULL); g_assert(dfl_pathnames.pidfile == NULL); - dfl_pathnames.state_dir = qemu_get_local_state_pathname( - QGA_STATE_RELATIVE_DIR); - dfl_pathnames.pidfile = qemu_get_local_state_pathname( - QGA_STATE_RELATIVE_DIR G_DIR_SEPARATOR_S "qemu-ga.pid"); + dfl_pathnames.state_dir = g_build_filename(state, QGA_STATE_RELATIVE_DIR, NULL); + dfl_pathnames.pidfile = g_build_filename(state, QGA_STATE_RELATIVE_DIR, "qemu-ga.pid", NULL); } static void quit_handler(int sig) @@ -224,6 +223,10 @@ void reopen_fd_to_null(int fd) static void usage(const char *cmd) { +#ifdef CONFIG_FSFREEZE + g_autofree char *fsfreeze_hook = get_relocated_path(QGA_FSFREEZE_HOOK_DEFAULT); +#endif + printf( "Usage: %s [-m -p ] []\n" "QEMU Guest Agent " QEMU_FULL_VERSION "\n" @@ -257,8 +260,10 @@ QEMU_COPYRIGHT "\n" #ifdef _WIN32 " -s, --service service commands: install, uninstall, vss-install, vss-uninstall\n" #endif -" -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"\n" -" to list available RPCs)\n" +" -b, --block-rpcs comma-separated list of RPCs to disable (no spaces,\n" +" use \"--block-rpcs=help\" to list available RPCs)\n" +" -a, --allow-rpcs comma-separated list of RPCs to enable (no spaces,\n" +" use \"--allow-rpcs=help\" to list available RPCs)\n" " -D, --dump-conf dump a qemu-ga config file based on current config\n" " options / command-line parameters to stdout\n" " -r, --retry-path attempt re-opening path if it's unavailable or closed\n" @@ -271,7 +276,7 @@ QEMU_HELP_BOTTOM "\n" , cmd, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, dfl_pathnames.pidfile, #ifdef CONFIG_FSFREEZE - QGA_FSFREEZE_HOOK_DEFAULT, + fsfreeze_hook, #endif dfl_pathnames.state_dir); } @@ -311,11 +316,42 @@ void ga_enable_logging(GAState *s) s->logging_enabled = true; } +static int glib_log_level_to_system(int level) +{ + switch (level) { +#ifndef _WIN32 + case G_LOG_LEVEL_ERROR: + return LOG_ERR; + case G_LOG_LEVEL_CRITICAL: + return LOG_CRIT; + case G_LOG_LEVEL_WARNING: + return LOG_WARNING; + case G_LOG_LEVEL_MESSAGE: + return LOG_NOTICE; + case G_LOG_LEVEL_DEBUG: + return LOG_DEBUG; + case G_LOG_LEVEL_INFO: + default: + return LOG_INFO; +#else + case G_LOG_LEVEL_ERROR: + case G_LOG_LEVEL_CRITICAL: + return EVENTLOG_ERROR_TYPE; + case G_LOG_LEVEL_WARNING: + return EVENTLOG_WARNING_TYPE; + case G_LOG_LEVEL_MESSAGE: + case G_LOG_LEVEL_INFO: + case G_LOG_LEVEL_DEBUG: + default: + return EVENTLOG_INFORMATION_TYPE; +#endif + } +} + static void ga_log(const gchar *domain, GLogLevelFlags level, const gchar *msg, gpointer opaque) { GAState *s = opaque; - GTimeVal time; const char *level_str = ga_log_level_str(level); if (!ga_logging_enabled(s)) { @@ -323,16 +359,17 @@ static void ga_log(const gchar *domain, GLogLevelFlags level, } level &= G_LOG_LEVEL_MASK; -#ifndef _WIN32 if (g_strcmp0(domain, "syslog") == 0) { - syslog(LOG_INFO, "%s: %s", level_str, msg); - } else if (level & s->log_level) { +#ifndef _WIN32 + syslog(glib_log_level_to_system(level), "%s: %s", level_str, msg); #else - if (level & s->log_level) { + ReportEvent(s->event_log, glib_log_level_to_system(level), + 0, 1, NULL, 1, 0, &msg, NULL); #endif - g_get_current_time(&time); - fprintf(s->log_file, - "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg); + } else if (level & s->log_level) { + g_autoptr(GDateTime) now = g_date_time_new_now_utc(); + g_autofree char *nowstr = g_date_time_format(now, "%s.%f"); + fprintf(s->log_file, "%s: %s: %s\n", nowstr, level_str, msg); fflush(s->log_file); } } @@ -361,37 +398,59 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2) } /* disable commands that aren't safe for fsfreeze */ -static void ga_disable_non_whitelisted(const QmpCommand *cmd, void *opaque) +static void ga_disable_not_allowed_freeze(const QmpCommand *cmd, void *opaque) { - bool whitelisted = false; + bool allowed = false; int i = 0; const char *name = qmp_command_name(cmd); - while (ga_freeze_whitelist[i] != NULL) { - if (strcmp(name, ga_freeze_whitelist[i]) == 0) { - whitelisted = true; + while (ga_freeze_allowlist[i] != NULL) { + if (strcmp(name, ga_freeze_allowlist[i]) == 0) { + allowed = true; } i++; } - if (!whitelisted) { + if (!allowed) { g_debug("disabling command: %s", name); qmp_disable_command(&ga_commands, name, "the agent is in frozen state"); } } -/* [re-]enable all commands, except those explicitly blacklisted by user */ -static void ga_enable_non_blacklisted(const QmpCommand *cmd, void *opaque) +/* [re-]enable all commands, except those explicitly blocked by user */ +static void ga_enable_non_blocked(const QmpCommand *cmd, void *opaque) { - GList *blacklist = opaque; + GAState *s = opaque; + GList *blockedrpcs = s->blockedrpcs; + GList *allowedrpcs = s->allowedrpcs; const char *name = qmp_command_name(cmd); - if (g_list_find_custom(blacklist, name, ga_strcmp) == NULL && - !qmp_command_is_enabled(cmd)) { + if (g_list_find_custom(blockedrpcs, name, ga_strcmp) == NULL) { + if (qmp_command_is_enabled(cmd)) { + return; + } + + if (allowedrpcs && + g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) { + return; + } + g_debug("enabling command: %s", name); qmp_enable_command(&ga_commands, name); } } +/* disable commands that aren't allowed */ +static void ga_disable_not_allowed(const QmpCommand *cmd, void *opaque) +{ + GList *allowedrpcs = opaque; + const char *name = qmp_command_name(cmd); + + if (g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) { + g_debug("disabling command: %s", name); + qmp_disable_command(&ga_commands, name, "the command is not allowed"); + } +} + static bool ga_create_file(const char *path) { int fd = open(path, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR); @@ -424,8 +483,8 @@ 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); + /* disable all forbidden (for frozen state) commands */ + qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL); g_warning("disabling logging due to filesystem freeze"); ga_disable_logging(s); s->frozen = true; @@ -463,8 +522,8 @@ void ga_unset_frozen(GAState *s) s->deferred_options.pid_filepath = NULL; } - /* enable all disabled, non-blacklisted commands */ - qmp_for_each_command(&ga_commands, ga_enable_non_blacklisted, s->blacklist); + /* enable all disabled, non-blocked and allowed commands */ + qmp_for_each_command(&ga_commands, ga_enable_non_blocked, s); s->frozen = false; if (!ga_delete_file(s->state_filepath_isfrozen)) { g_warning("unable to delete %s, fsfreeze may not function properly", @@ -610,7 +669,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) * host-side chardev. sleep a bit to mitigate this */ if (s->virtio) { - usleep(100 * 1000); + g_usleep(G_USEC_PER_SEC / 10); } return true; default: @@ -894,7 +953,8 @@ 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 + /* + * We block 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 */ @@ -948,8 +1008,10 @@ struct GAConfig { #ifdef _WIN32 const char *service; #endif - gchar *bliststr; /* blacklist may point to this string */ - GList *blacklist; + gchar *bliststr; /* blockedrpcs may point to this string */ + gchar *aliststr; /* allowedrpcs may point to this string */ + GList *blockedrpcs; + GList *allowedrpcs; int daemonize; GLogLevelFlags log_level; int dumpconf; @@ -961,6 +1023,7 @@ static void config_load(GAConfig *config) GError *gerr = NULL; GKeyFile *keyfile; g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?: get_relocated_path(QGA_CONF_DEFAULT); + const gchar *blockrpcs_key = "block-rpcs"; /* read system config */ keyfile = g_key_file_new(); @@ -1007,12 +1070,31 @@ static void config_load(GAConfig *config) config->retry_path = g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr); } + if (g_key_file_has_key(keyfile, "general", "blacklist", NULL)) { + g_warning("config using deprecated 'blacklist' key, should be replaced" + " with the 'block-rpcs' key."); + blockrpcs_key = "blacklist"; + } + if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL)) { config->bliststr = - g_key_file_get_string(keyfile, "general", "blacklist", &gerr); - config->blacklist = g_list_concat(config->blacklist, + g_key_file_get_string(keyfile, "general", blockrpcs_key, &gerr); + config->blockedrpcs = g_list_concat(config->blockedrpcs, split_list(config->bliststr, ",")); } + if (g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) { + config->aliststr = + g_key_file_get_string(keyfile, "general", "allow-rpcs", &gerr); + config->allowedrpcs = g_list_concat(config->allowedrpcs, + split_list(config->aliststr, ",")); + } + + if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL) && + g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) { + g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs' keys at" + " the same time is not allowed"); + exit(EXIT_FAILURE); + } end: g_key_file_free(keyfile); @@ -1070,8 +1152,11 @@ static void config_dump(GAConfig *config) config->log_level == G_LOG_LEVEL_MASK); g_key_file_set_boolean(keyfile, "general", "retry-path", config->retry_path); - tmp = list_join(config->blacklist, ','); - g_key_file_set_string(keyfile, "general", "blacklist", tmp); + tmp = list_join(config->blockedrpcs, ','); + g_key_file_set_string(keyfile, "general", "block-rpcs", tmp); + g_free(tmp); + tmp = list_join(config->allowedrpcs, ','); + g_key_file_set_string(keyfile, "general", "allow-rpcs", tmp); g_free(tmp); tmp = g_key_file_to_data(keyfile, NULL, &error); @@ -1088,8 +1173,9 @@ static void config_dump(GAConfig *config) static void config_parse(GAConfig *config, int argc, char **argv) { - const char *sopt = "hVvdm:p:l:f:F::b:s:t:Dr"; + const char *sopt = "hVvdm:p:l:f:F::b:a:s:t:Dr"; int opt_ind = 0, ch; + bool block_rpcs = false, allow_rpcs = false; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -1103,7 +1189,9 @@ static void config_parse(GAConfig *config, int argc, char **argv) { "method", 1, NULL, 'm' }, { "path", 1, NULL, 'p' }, { "daemonize", 0, NULL, 'd' }, - { "blacklist", 1, NULL, 'b' }, + { "block-rpcs", 1, NULL, 'b' }, + { "blacklist", 1, NULL, 'b' }, /* deprecated alias for 'block-rpcs' */ + { "allow-rpcs", 1, NULL, 'a' }, #ifdef _WIN32 { "service", 1, NULL, 's' }, #endif @@ -1161,8 +1249,19 @@ static void config_parse(GAConfig *config, int argc, char **argv) qmp_for_each_command(&ga_commands, ga_print_cmd, NULL); exit(EXIT_SUCCESS); } - config->blacklist = g_list_concat(config->blacklist, - split_list(optarg, ",")); + config->blockedrpcs = g_list_concat(config->blockedrpcs, + split_list(optarg, ",")); + block_rpcs = true; + break; + } + case 'a': { + if (is_help_option(optarg)) { + qmp_for_each_command(&ga_commands, ga_print_cmd, NULL); + exit(EXIT_SUCCESS); + } + config->allowedrpcs = g_list_concat(config->allowedrpcs, + split_list(optarg, ",")); + allow_rpcs = true; break; } #ifdef _WIN32 @@ -1203,6 +1302,12 @@ static void config_parse(GAConfig *config, int argc, char **argv) exit(EXIT_FAILURE); } } + + if (block_rpcs && allow_rpcs) { + g_critical("wrong commandline, using --block-rpcs and --allow-rpcs at the" + " same time is not allowed"); + exit(EXIT_FAILURE); + } } static void config_free(GAConfig *config) @@ -1213,10 +1318,12 @@ static void config_free(GAConfig *config) g_free(config->state_dir); g_free(config->channel_path); g_free(config->bliststr); + g_free(config->aliststr); #ifdef CONFIG_FSFREEZE g_free(config->fsfreeze_hook); #endif - g_list_free_full(config->blacklist, g_free); + g_list_free_full(config->blockedrpcs, g_free); + g_list_free_full(config->allowedrpcs, g_free); g_free(config); } @@ -1226,7 +1333,7 @@ static bool check_is_frozen(GAState *s) /* 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 + * unneeded unfreeze than to risk hanging on start-up */ struct stat st; if (stat(s->state_filepath_isfrozen, &st) == -1) { @@ -1273,7 +1380,16 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); ga_enable_logging(s); + g_debug("Guest agent version %s started", QEMU_FULL_VERSION); + #ifdef _WIN32 + s->event_log = RegisterEventSource(NULL, "qemu-ga"); + if (!s->event_log) { + g_autofree gchar *errmsg = g_win32_error_message(GetLastError()); + g_critical("unable to register event source: %s", errmsg); + return NULL; + } + /* 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 @@ -1298,7 +1414,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) s->deferred_options.log_filepath = config->log_filepath; } ga_disable_logging(s); - qmp_for_each_command(&ga_commands, ga_disable_non_whitelisted, NULL); + qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL); } else { if (config->daemonize) { become_daemon(config->pid_filepath); @@ -1322,10 +1438,19 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) return NULL; } - config->blacklist = ga_command_blacklist_init(config->blacklist); - if (config->blacklist) { - GList *l = config->blacklist; - s->blacklist = config->blacklist; + if (config->allowedrpcs) { + qmp_for_each_command(&ga_commands, ga_disable_not_allowed, config->allowedrpcs); + s->allowedrpcs = config->allowedrpcs; + } + + /* + * Some commands can be blocked due to system limitation. + * Initialize blockedrpcs list even if allowedrpcs specified. + */ + config->blockedrpcs = ga_command_init_blockedrpcs(config->blockedrpcs); + if (config->blockedrpcs) { + GList *l = config->blockedrpcs; + s->blockedrpcs = config->blockedrpcs; do { g_debug("disabling command: %s", (char *)l->data); qmp_disable_command(&ga_commands, l->data, NULL); @@ -1365,6 +1490,7 @@ static void cleanup_agent(GAState *s) { #ifdef _WIN32 CloseHandle(s->wakeup_event); + CloseHandle(s->event_log); #endif if (s->command_state) { ga_command_state_cleanup_all(s->command_state);