summaryrefslogtreecommitdiffstats
path: root/qga/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'qga/main.c')
-rw-r--r--qga/main.c93
1 files changed, 79 insertions, 14 deletions
diff --git a/qga/main.c b/qga/main.c
index 538e4ee299..92658bc0e2 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -28,6 +28,7 @@
#include "qga/channel.h"
#include "qemu/bswap.h"
#include "qemu/help_option.h"
+#include "qemu/sockets.h"
#ifdef _WIN32
#include "qga/service-win32.h"
#include "qga/vss-win32.h"
@@ -92,6 +93,7 @@ struct GAState {
};
struct GAState *ga_state;
+QmpCommandList ga_commands;
/* commands that are safe to issue while filesystems are frozen */
static const char *ga_freeze_whitelist[] = {
@@ -184,6 +186,37 @@ void reopen_fd_to_null(int fd)
}
#endif
+/**
+ * get_listen_fd:
+ * @consume: true to prevent future calls from succeeding
+ *
+ * Fetch a listen file descriptor that was passed via systemd socket
+ * activation. Use @consume to prevent child processes from thinking a file
+ * descriptor was passed.
+ *
+ * Returns: file descriptor or -1 if no fd was passed
+ */
+static int get_listen_fd(bool consume)
+{
+#ifdef _WIN32
+ return -1; /* no fd passing expected, unsetenv(3) not available */
+#else
+ const char *listen_fds = getenv("LISTEN_FDS");
+ int fd = STDERR_FILENO + 1;
+
+ if (!listen_fds || strcmp(listen_fds, "1") != 0) {
+ return -1;
+ }
+
+ if (consume) {
+ unsetenv("LISTEN_FDS");
+ }
+
+ qemu_set_cloexec(fd);
+ return fd;
+#endif /* !_WIN32 */
+}
+
static void usage(const char *cmd)
{
printf(
@@ -370,7 +403,7 @@ static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque)
}
if (!whitelisted) {
g_debug("disabling command: %s", name);
- qmp_disable_command(name);
+ qmp_disable_command(&ga_commands, name);
}
}
@@ -383,7 +416,7 @@ static void ga_enable_non_blacklisted(QmpCommand *cmd, void *opaque)
if (g_list_find_custom(blacklist, name, ga_strcmp) == NULL &&
!qmp_command_is_enabled(cmd)) {
g_debug("enabling command: %s", name);
- qmp_enable_command(name);
+ qmp_enable_command(&ga_commands, name);
}
}
@@ -420,7 +453,7 @@ void ga_set_frozen(GAState *s)
return;
}
/* disable all non-whitelisted (for frozen state) commands */
- qmp_for_each_command(ga_disable_non_whitelisted, NULL);
+ 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;
@@ -456,7 +489,7 @@ void ga_unset_frozen(GAState *s)
}
/* enable all disabled, non-blacklisted commands */
- qmp_for_each_command(ga_enable_non_blacklisted, s->blacklist);
+ 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",
@@ -555,7 +588,7 @@ static void process_command(GAState *s, QDict *req)
g_assert(req);
g_debug("processing command");
- rsp = qmp_dispatch(QOBJECT(req));
+ rsp = qmp_dispatch(&ga_commands, QOBJECT(req));
if (rsp) {
ret = send_response(s, rsp);
if (ret < 0) {
@@ -648,7 +681,8 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data)
return true;
}
-static gboolean channel_init(GAState *s, const gchar *method, const gchar *path)
+static gboolean channel_init(GAState *s, const gchar *method, const gchar *path,
+ int listen_fd)
{
GAChannelMethod channel_method;
@@ -666,7 +700,8 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path)
return false;
}
- s->channel = ga_channel_new(channel_method, path, channel_event_cb, s);
+ s->channel = ga_channel_new(channel_method, path, listen_fd,
+ channel_event_cb, s);
if (!s->channel) {
g_critical("failed to create guest agent channel");
return false;
@@ -1025,7 +1060,9 @@ static void config_dump(GAConfig *config)
g_key_file_set_boolean(keyfile, "general", "daemon", config->daemonize);
g_key_file_set_string(keyfile, "general", "method", config->method);
- g_key_file_set_string(keyfile, "general", "path", config->channel_path);
+ if (config->channel_path) {
+ g_key_file_set_string(keyfile, "general", "path", config->channel_path);
+ }
if (config->log_filepath) {
g_key_file_set_string(keyfile, "general", "logfile",
config->log_filepath);
@@ -1119,7 +1156,7 @@ static void config_parse(GAConfig *config, int argc, char **argv)
break;
case 'b': {
if (is_help_option(optarg)) {
- qmp_for_each_command(ga_print_cmd, NULL);
+ qmp_for_each_command(&ga_commands, ga_print_cmd, NULL);
exit(EXIT_SUCCESS);
}
config->blacklist = g_list_concat(config->blacklist,
@@ -1247,7 +1284,7 @@ static int run_agent(GAState *s, GAConfig *config)
s->deferred_options.log_filepath = config->log_filepath;
}
ga_disable_logging(s);
- qmp_for_each_command(ga_disable_non_whitelisted, NULL);
+ qmp_for_each_command(&ga_commands, ga_disable_non_whitelisted, NULL);
} else {
if (config->daemonize) {
become_daemon(config->pid_filepath);
@@ -1277,7 +1314,7 @@ static int run_agent(GAState *s, GAConfig *config)
s->blacklist = config->blacklist;
do {
g_debug("disabling command: %s", (char *)l->data);
- qmp_disable_command(l->data);
+ qmp_disable_command(&ga_commands, l->data);
l = g_list_next(l);
} while (l);
}
@@ -1294,7 +1331,9 @@ static int run_agent(GAState *s, GAConfig *config)
#endif
s->main_loop = g_main_loop_new(NULL, false);
- if (!channel_init(ga_state, config->method, config->channel_path)) {
+
+ if (!channel_init(ga_state, config->method, config->channel_path,
+ get_listen_fd(true))) {
g_critical("failed to initialize guest agent channel");
return EXIT_FAILURE;
}
@@ -1318,10 +1357,11 @@ int main(int argc, char **argv)
int ret = EXIT_SUCCESS;
GAState *s = g_new0(GAState, 1);
GAConfig *config = g_new0(GAConfig, 1);
+ int listen_fd;
config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
- module_call_init(MODULE_INIT_QAPI);
+ qga_qmp_init_marshal(&ga_commands);
init_dfl_pathnames();
config_load(config);
@@ -1339,7 +1379,32 @@ int main(int argc, char **argv)
config->method = g_strdup("virtio-serial");
}
- if (config->channel_path == NULL) {
+ listen_fd = get_listen_fd(false);
+ if (listen_fd >= 0) {
+ SocketAddress *addr;
+
+ g_free(config->method);
+ g_free(config->channel_path);
+ config->method = NULL;
+ config->channel_path = NULL;
+
+ addr = socket_local_address(listen_fd, NULL);
+ if (addr) {
+ if (addr->type == SOCKET_ADDRESS_KIND_UNIX) {
+ config->method = g_strdup("unix-listen");
+ } else if (addr->type == SOCKET_ADDRESS_KIND_VSOCK) {
+ config->method = g_strdup("vsock-listen");
+ }
+
+ qapi_free_SocketAddress(addr);
+ }
+
+ if (!config->method) {
+ g_critical("unsupported listen fd type");
+ ret = EXIT_FAILURE;
+ goto end;
+ }
+ } else if (config->channel_path == NULL) {
if (strcmp(config->method, "virtio-serial") == 0) {
/* try the default path for the virtio-serial port */
config->channel_path = g_strdup(QGA_VIRTIO_PATH_DEFAULT);