xref: /openbmc/qemu/qga/main.c (revision 2eefd4fcec4b8fe41ceee2a8f00cdec1fe81b75c)
12870dc34SPaolo Bonzini /*
22870dc34SPaolo Bonzini  * QEMU Guest Agent
32870dc34SPaolo Bonzini  *
42870dc34SPaolo Bonzini  * Copyright IBM Corp. 2011
52870dc34SPaolo Bonzini  *
62870dc34SPaolo Bonzini  * Authors:
72870dc34SPaolo Bonzini  *  Adam Litke        <aglitke@linux.vnet.ibm.com>
82870dc34SPaolo Bonzini  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
92870dc34SPaolo Bonzini  *
102870dc34SPaolo Bonzini  * This work is licensed under the terms of the GNU GPL, version 2 or later.
112870dc34SPaolo Bonzini  * See the COPYING file in the top-level directory.
122870dc34SPaolo Bonzini  */
13e688df6bSMarkus Armbruster 
144459bf38SPeter Maydell #include "qemu/osdep.h"
152870dc34SPaolo Bonzini #include <getopt.h>
1639097dafSMichael Roth #include <glib/gstdio.h>
172870dc34SPaolo Bonzini #ifndef _WIN32
182870dc34SPaolo Bonzini #include <syslog.h>
192870dc34SPaolo Bonzini #include <sys/wait.h>
202870dc34SPaolo Bonzini #endif
2149f95221SMarc-André Lureau #include "qemu/help-texts.h"
2286cdf9ecSMarkus Armbruster #include "qapi/qmp/json-parser.h"
23452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h"
247b1b5d19SPaolo Bonzini #include "qapi/qmp/qjson.h"
25dc03272dSMichael S. Tsirkin #include "guest-agent-core.h"
2600ca24ffSMarkus Armbruster #include "qga-qapi-init-commands.h"
27e688df6bSMarkus Armbruster #include "qapi/error.h"
28dc03272dSMichael S. Tsirkin #include "channel.h"
29a9eacf8bSPaolo Bonzini #include "qemu/cutils.h"
30f348b6d1SVeronia Bahaa #include "qemu/help_option.h"
3126de2296SStefan Hajnoczi #include "qemu/sockets.h"
3253fabd4bSPaolo Bonzini #include "qemu/systemd.h"
338f1c29afSEric Blake #include "qemu-version.h"
342870dc34SPaolo Bonzini #ifdef _WIN32
35b70d6afeSBishara AbuHattoum #include <dbt.h>
362870dc34SPaolo Bonzini #include "qga/service-win32.h"
37f311f2c2STomoki Sekiyama #include "qga/vss-win32.h"
382870dc34SPaolo Bonzini #endif
39bad0001eSAlexander Ivanov #include "commands-common.h"
402870dc34SPaolo Bonzini 
412870dc34SPaolo Bonzini #ifndef _WIN32
4228236ad8SBrad Smith #ifdef CONFIG_BSD
43c6cd588bSAlexander Ivanov #define QGA_VIRTIO_PATH_DEFAULT "/dev/vtcon/org.qemu.guest_agent.0"
4428236ad8SBrad Smith #else /* CONFIG_BSD */
452870dc34SPaolo Bonzini #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
4628236ad8SBrad Smith #endif /* CONFIG_BSD */
47a749f42dSMiki Mishael #define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0"
48c6cd588bSAlexander Ivanov #define QGA_STATE_RELATIVE_DIR  "run"
492870dc34SPaolo Bonzini #else
502870dc34SPaolo Bonzini #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0"
51c394ecb7SLaszlo Ersek #define QGA_STATE_RELATIVE_DIR  "qemu-ga"
52a749f42dSMiki Mishael #define QGA_SERIAL_PATH_DEFAULT "COM1"
532870dc34SPaolo Bonzini #endif
54ec0f694cSTomoki Sekiyama #ifdef CONFIG_FSFREEZE
55ec0f694cSTomoki Sekiyama #define QGA_FSFREEZE_HOOK_DEFAULT CONFIG_QEMU_CONFDIR "/fsfreeze-hook"
56ec0f694cSTomoki Sekiyama #endif
572870dc34SPaolo Bonzini #define QGA_SENTINEL_BYTE 0xFF
58e236d060SMarc-André Lureau #define QGA_CONF_DEFAULT CONFIG_QEMU_CONFDIR G_DIR_SEPARATOR_S "qemu-ga.conf"
59d951fadaSMichael Roth #define QGA_RETRY_INTERVAL 5
602870dc34SPaolo Bonzini 
61c394ecb7SLaszlo Ersek static struct {
62c394ecb7SLaszlo Ersek     const char *state_dir;
63c394ecb7SLaszlo Ersek     const char *pidfile;
64c394ecb7SLaszlo Ersek } dfl_pathnames;
65c394ecb7SLaszlo Ersek 
6639097dafSMichael Roth typedef struct GAPersistentState {
6739097dafSMichael Roth #define QGA_PSTATE_DEFAULT_FD_COUNTER 1000
6839097dafSMichael Roth     int64_t fd_counter;
6939097dafSMichael Roth } GAPersistentState;
7039097dafSMichael Roth 
710f4d3a49SMichael Roth typedef struct GAConfig GAConfig;
720f4d3a49SMichael Roth 
733390a0deSDaniel P. Berrangé struct GAConfig {
743390a0deSDaniel P. Berrangé     char *channel_path;
753390a0deSDaniel P. Berrangé     char *method;
763390a0deSDaniel P. Berrangé     char *log_filepath;
773390a0deSDaniel P. Berrangé     char *pid_filepath;
783390a0deSDaniel P. Berrangé #ifdef CONFIG_FSFREEZE
793390a0deSDaniel P. Berrangé     char *fsfreeze_hook;
803390a0deSDaniel P. Berrangé #endif
813390a0deSDaniel P. Berrangé     char *state_dir;
823390a0deSDaniel P. Berrangé #ifdef _WIN32
833390a0deSDaniel P. Berrangé     const char *service;
843390a0deSDaniel P. Berrangé #endif
853390a0deSDaniel P. Berrangé     gchar *bliststr; /* blockedrpcs may point to this string */
863390a0deSDaniel P. Berrangé     gchar *aliststr; /* allowedrpcs may point to this string */
873390a0deSDaniel P. Berrangé     GList *blockedrpcs;
883390a0deSDaniel P. Berrangé     GList *allowedrpcs;
893390a0deSDaniel P. Berrangé     int daemonize;
903390a0deSDaniel P. Berrangé     GLogLevelFlags log_level;
913390a0deSDaniel P. Berrangé     int dumpconf;
923390a0deSDaniel P. Berrangé     bool retry_path;
933390a0deSDaniel P. Berrangé };
943390a0deSDaniel P. Berrangé 
952870dc34SPaolo Bonzini struct GAState {
962870dc34SPaolo Bonzini     JSONMessageParser parser;
972870dc34SPaolo Bonzini     GMainLoop *main_loop;
982870dc34SPaolo Bonzini     GAChannel *channel;
992870dc34SPaolo Bonzini     bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
1002870dc34SPaolo Bonzini     GACommandState *command_state;
1012870dc34SPaolo Bonzini     GLogLevelFlags log_level;
1022870dc34SPaolo Bonzini     FILE *log_file;
1032870dc34SPaolo Bonzini     bool logging_enabled;
1042870dc34SPaolo Bonzini #ifdef _WIN32
1052870dc34SPaolo Bonzini     GAService service;
106b70d6afeSBishara AbuHattoum     HANDLE wakeup_event;
107f9f0e617SAndrey Drobyshev     HANDLE event_log;
1082870dc34SPaolo Bonzini #endif
1092870dc34SPaolo Bonzini     bool delimit_response;
1102870dc34SPaolo Bonzini     bool frozen;
1110e4ef702SThomas Huth     GList *blockedrpcs;
112133789e1SKonstantin Kostiuk     GList *allowedrpcs;
113d4c8a5d4SMarc-André Lureau     char *state_filepath_isfrozen;
1142870dc34SPaolo Bonzini     struct {
1152870dc34SPaolo Bonzini         const char *log_filepath;
1162870dc34SPaolo Bonzini         const char *pid_filepath;
1172870dc34SPaolo Bonzini     } deferred_options;
118ec0f694cSTomoki Sekiyama #ifdef CONFIG_FSFREEZE
119ec0f694cSTomoki Sekiyama     const char *fsfreeze_hook;
120ec0f694cSTomoki Sekiyama #endif
121d4c8a5d4SMarc-André Lureau     gchar *pstate_filepath;
12239097dafSMichael Roth     GAPersistentState pstate;
1230f4d3a49SMichael Roth     GAConfig *config;
1240f4d3a49SMichael Roth     int socket_activation;
125d951fadaSMichael Roth     bool force_exit;
1262870dc34SPaolo Bonzini };
1272870dc34SPaolo Bonzini 
1282870dc34SPaolo Bonzini struct GAState *ga_state;
1291527badbSMarkus Armbruster QmpCommandList ga_commands;
1302870dc34SPaolo Bonzini 
1312870dc34SPaolo Bonzini /* commands that are safe to issue while filesystems are frozen */
1320e4ef702SThomas Huth static const char *ga_freeze_allowlist[] = {
1332870dc34SPaolo Bonzini     "guest-ping",
1342870dc34SPaolo Bonzini     "guest-info",
1352870dc34SPaolo Bonzini     "guest-sync",
136c5dcb6aeSMichael Roth     "guest-sync-delimited",
1372870dc34SPaolo Bonzini     "guest-fsfreeze-status",
1382870dc34SPaolo Bonzini     "guest-fsfreeze-thaw",
1392870dc34SPaolo Bonzini     NULL
1402870dc34SPaolo Bonzini };
1412870dc34SPaolo Bonzini 
1422870dc34SPaolo Bonzini #ifdef _WIN32
1432870dc34SPaolo Bonzini DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
1442870dc34SPaolo Bonzini                                   LPVOID ctx);
145b70d6afeSBishara AbuHattoum DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data);
1462870dc34SPaolo Bonzini VOID WINAPI service_main(DWORD argc, TCHAR *argv[]);
1472870dc34SPaolo Bonzini #endif
148d88495a8SMichael Roth static int run_agent(GAState *s);
149d951fadaSMichael Roth static void stop_agent(GAState *s, bool requested);
1502870dc34SPaolo Bonzini 
151c394ecb7SLaszlo Ersek static void
init_dfl_pathnames(void)152c394ecb7SLaszlo Ersek init_dfl_pathnames(void)
153c394ecb7SLaszlo Ersek {
1541fbf2665SMarc-André Lureau     g_autofree char *state = qemu_get_local_state_dir();
1551fbf2665SMarc-André Lureau 
156c394ecb7SLaszlo Ersek     g_assert(dfl_pathnames.state_dir == NULL);
157c394ecb7SLaszlo Ersek     g_assert(dfl_pathnames.pidfile == NULL);
1581fbf2665SMarc-André Lureau     dfl_pathnames.state_dir = g_build_filename(state, QGA_STATE_RELATIVE_DIR, NULL);
1591fbf2665SMarc-André Lureau     dfl_pathnames.pidfile = g_build_filename(state, QGA_STATE_RELATIVE_DIR, "qemu-ga.pid", NULL);
160c394ecb7SLaszlo Ersek }
161c394ecb7SLaszlo Ersek 
quit_handler(int sig)1622870dc34SPaolo Bonzini static void quit_handler(int sig)
1632870dc34SPaolo Bonzini {
1642870dc34SPaolo Bonzini     /* if we're frozen, don't exit unless we're absolutely forced to,
1652870dc34SPaolo Bonzini      * because it's basically impossible for graceful exit to complete
1662870dc34SPaolo Bonzini      * unless all log/pid files are on unfreezable filesystems. there's
1672870dc34SPaolo Bonzini      * also a very likely chance killing the agent before unfreezing
1682870dc34SPaolo Bonzini      * the filesystems is a mistake (or will be viewed as one later).
16994d81ae8SSameeh Jubran      * On Windows the freeze interval is limited to 10 seconds, so
17094d81ae8SSameeh Jubran      * we should quit, but first we should wait for the timeout, thaw
17194d81ae8SSameeh Jubran      * the filesystem and quit.
1722870dc34SPaolo Bonzini      */
1732870dc34SPaolo Bonzini     if (ga_is_frozen(ga_state)) {
17494d81ae8SSameeh Jubran #ifdef _WIN32
17594d81ae8SSameeh Jubran         int i = 0;
17694d81ae8SSameeh Jubran         Error *err = NULL;
17794d81ae8SSameeh Jubran         HANDLE hEventTimeout;
17894d81ae8SSameeh Jubran 
17994d81ae8SSameeh Jubran         g_debug("Thawing filesystems before exiting");
18094d81ae8SSameeh Jubran 
18194d81ae8SSameeh Jubran         hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
18294d81ae8SSameeh Jubran         if (hEventTimeout) {
18394d81ae8SSameeh Jubran             WaitForSingleObject(hEventTimeout, 0);
18494d81ae8SSameeh Jubran             CloseHandle(hEventTimeout);
18594d81ae8SSameeh Jubran         }
1860692b03eSChen Hanxiao         qga_vss_fsfreeze(&i, false, NULL, &err);
18794d81ae8SSameeh Jubran         if (err) {
18894d81ae8SSameeh Jubran             g_debug("Error unfreezing filesystems prior to exiting: %s",
18994d81ae8SSameeh Jubran                 error_get_pretty(err));
19094d81ae8SSameeh Jubran             error_free(err);
19194d81ae8SSameeh Jubran         }
19294d81ae8SSameeh Jubran #else
1932870dc34SPaolo Bonzini         return;
19494d81ae8SSameeh Jubran #endif
1952870dc34SPaolo Bonzini     }
1962870dc34SPaolo Bonzini     g_debug("received signal num %d, quitting", sig);
1972870dc34SPaolo Bonzini 
198d951fadaSMichael Roth     stop_agent(ga_state, true);
1992870dc34SPaolo Bonzini }
2002870dc34SPaolo Bonzini 
2012870dc34SPaolo Bonzini #ifndef _WIN32
register_signal_handlers(void)2022870dc34SPaolo Bonzini static gboolean register_signal_handlers(void)
2032870dc34SPaolo Bonzini {
2042870dc34SPaolo Bonzini     struct sigaction sigact;
2052870dc34SPaolo Bonzini     int ret;
2062870dc34SPaolo Bonzini 
2072870dc34SPaolo Bonzini     memset(&sigact, 0, sizeof(struct sigaction));
2082870dc34SPaolo Bonzini     sigact.sa_handler = quit_handler;
2092870dc34SPaolo Bonzini 
2102870dc34SPaolo Bonzini     ret = sigaction(SIGINT, &sigact, NULL);
2112870dc34SPaolo Bonzini     if (ret == -1) {
2122870dc34SPaolo Bonzini         g_error("error configuring signal handler: %s", strerror(errno));
2132870dc34SPaolo Bonzini     }
2142870dc34SPaolo Bonzini     ret = sigaction(SIGTERM, &sigact, NULL);
2152870dc34SPaolo Bonzini     if (ret == -1) {
2162870dc34SPaolo Bonzini         g_error("error configuring signal handler: %s", strerror(errno));
2172870dc34SPaolo Bonzini     }
2182870dc34SPaolo Bonzini 
2194005b473SDenis V. Lunev     sigact.sa_handler = SIG_IGN;
2204005b473SDenis V. Lunev     if (sigaction(SIGPIPE, &sigact, NULL) != 0) {
2214005b473SDenis V. Lunev         g_error("error configuring SIGPIPE signal handler: %s",
2224005b473SDenis V. Lunev                 strerror(errno));
2234005b473SDenis V. Lunev     }
2244005b473SDenis V. Lunev 
2252870dc34SPaolo Bonzini     return true;
2262870dc34SPaolo Bonzini }
2272870dc34SPaolo Bonzini 
2282870dc34SPaolo Bonzini /* TODO: use this in place of all post-fork() fclose(std*) callers */
reopen_fd_to_null(int fd)2292870dc34SPaolo Bonzini void reopen_fd_to_null(int fd)
2302870dc34SPaolo Bonzini {
2312870dc34SPaolo Bonzini     int nullfd;
2322870dc34SPaolo Bonzini 
2332870dc34SPaolo Bonzini     nullfd = open("/dev/null", O_RDWR);
2342870dc34SPaolo Bonzini     if (nullfd < 0) {
2352870dc34SPaolo Bonzini         return;
2362870dc34SPaolo Bonzini     }
2372870dc34SPaolo Bonzini 
2382870dc34SPaolo Bonzini     dup2(nullfd, fd);
2392870dc34SPaolo Bonzini 
2402870dc34SPaolo Bonzini     if (nullfd != fd) {
2412870dc34SPaolo Bonzini         close(nullfd);
2422870dc34SPaolo Bonzini     }
2432870dc34SPaolo Bonzini }
2442870dc34SPaolo Bonzini #endif
2452870dc34SPaolo Bonzini 
usage(const char * cmd)2462870dc34SPaolo Bonzini static void usage(const char *cmd)
2472870dc34SPaolo Bonzini {
248a2482794SAkihiko Odaki #ifdef CONFIG_FSFREEZE
249a2482794SAkihiko Odaki     g_autofree char *fsfreeze_hook = get_relocated_path(QGA_FSFREEZE_HOOK_DEFAULT);
250a2482794SAkihiko Odaki #endif
251f8bf2347SDaniel P. Berrangé     g_autofree char *conf_path = get_relocated_path(QGA_CONF_DEFAULT);
252a2482794SAkihiko Odaki 
2532870dc34SPaolo Bonzini     printf(
2542870dc34SPaolo Bonzini "Usage: %s [-m <method> -p <path>] [<options>]\n"
2557e563bfbSThomas Huth "QEMU Guest Agent " QEMU_FULL_VERSION "\n"
2568f1c29afSEric Blake QEMU_COPYRIGHT "\n"
2572870dc34SPaolo Bonzini "\n"
258f8bf2347SDaniel P. Berrangé "  -c, --config=PATH configuration file path (default is\n"
259f8bf2347SDaniel P. Berrangé "                    %s/qemu-ga.conf\n"
260*24c32ed3SStefan Weil "                    unless overridden by the QGA_CONF environment variable)\n"
261586ef5deSStefan Hajnoczi "  -m, --method      transport method: one of unix-listen, virtio-serial,\n"
262586ef5deSStefan Hajnoczi "                    isa-serial, or vsock-listen (virtio-serial is the default)\n"
2632870dc34SPaolo Bonzini "  -p, --path        device/socket path (the default for virtio-serial is:\n"
264a749f42dSMiki Mishael "                    %s,\n"
265a749f42dSMiki Mishael "                    the default for isa-serial is:\n"
2667b46aadbSStefan Hajnoczi "                    %s).\n"
2677b46aadbSStefan Hajnoczi "                    Socket addresses for vsock-listen are written as\n"
2687b46aadbSStefan Hajnoczi "                    <cid>:<port>.\n"
2692870dc34SPaolo Bonzini "  -l, --logfile     set logfile path, logs to stderr by default\n"
2702870dc34SPaolo Bonzini "  -f, --pidfile     specify pidfile (default is %s)\n"
271ec0f694cSTomoki Sekiyama #ifdef CONFIG_FSFREEZE
272ec0f694cSTomoki Sekiyama "  -F, --fsfreeze-hook\n"
273ec0f694cSTomoki Sekiyama "                    enable fsfreeze hook. Accepts an optional argument that\n"
274ec0f694cSTomoki Sekiyama "                    specifies script to run on freeze/thaw. Script will be\n"
275ec0f694cSTomoki Sekiyama "                    called with 'freeze'/'thaw' arguments accordingly.\n"
276ec0f694cSTomoki Sekiyama "                    (default is %s)\n"
277ec0f694cSTomoki Sekiyama "                    If using -F with an argument, do not follow -F with a\n"
278ec0f694cSTomoki Sekiyama "                    space.\n"
279ec0f694cSTomoki Sekiyama "                    (for example: -F/var/run/fsfreezehook.sh)\n"
280ec0f694cSTomoki Sekiyama #endif
2812870dc34SPaolo Bonzini "  -t, --statedir    specify dir to store state information (absolute paths\n"
2822870dc34SPaolo Bonzini "                    only, default is %s)\n"
2832870dc34SPaolo Bonzini "  -v, --verbose     log extra debugging information\n"
2842870dc34SPaolo Bonzini "  -V, --version     print version information and exit\n"
2852870dc34SPaolo Bonzini "  -d, --daemonize   become a daemon\n"
2862870dc34SPaolo Bonzini #ifdef _WIN32
2875e031072SYossi Hindin "  -s, --service     service commands: install, uninstall, vss-install, vss-uninstall\n"
2882870dc34SPaolo Bonzini #endif
289582a098eSThomas Huth "  -b, --block-rpcs  comma-separated list of RPCs to disable (no spaces,\n"
290b68d8b67SAngel M. Villegas "                    use \"--block-rpcs=help\" to list available RPCs)\n"
291133789e1SKonstantin Kostiuk "  -a, --allow-rpcs  comma-separated list of RPCs to enable (no spaces,\n"
292b68d8b67SAngel M. Villegas "                    use \"--allow-rpcs=help\" to list available RPCs)\n"
293aeadcbb6SMarc-André Lureau "  -D, --dump-conf   dump a qemu-ga config file based on current config\n"
294aeadcbb6SMarc-André Lureau "                    options / command-line parameters to stdout\n"
295d951fadaSMichael Roth "  -r, --retry-path  attempt re-opening path if it's unavailable or closed\n"
296d951fadaSMichael Roth "                    due to an error which may be recoverable in the future\n"
297d951fadaSMichael Roth "                    (virtio-serial driver re-install, serial device hot\n"
298d951fadaSMichael Roth "                    plug/unplug, etc.)\n"
2992870dc34SPaolo Bonzini "  -h, --help        display this help and exit\n"
3002870dc34SPaolo Bonzini "\n"
301f8bf2347SDaniel P. Berrangé QEMU_HELP_BOTTOM "\n",
302f8bf2347SDaniel P. Berrangé     cmd, conf_path, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT,
303a749f42dSMiki Mishael     dfl_pathnames.pidfile,
304ec0f694cSTomoki Sekiyama #ifdef CONFIG_FSFREEZE
305a2482794SAkihiko Odaki     fsfreeze_hook,
306ec0f694cSTomoki Sekiyama #endif
307c394ecb7SLaszlo Ersek     dfl_pathnames.state_dir);
3082870dc34SPaolo Bonzini }
3092870dc34SPaolo Bonzini 
ga_log_level_str(GLogLevelFlags level)3102870dc34SPaolo Bonzini static const char *ga_log_level_str(GLogLevelFlags level)
3112870dc34SPaolo Bonzini {
3122870dc34SPaolo Bonzini     switch (level & G_LOG_LEVEL_MASK) {
3132870dc34SPaolo Bonzini     case G_LOG_LEVEL_ERROR:
3142870dc34SPaolo Bonzini         return "error";
3152870dc34SPaolo Bonzini     case G_LOG_LEVEL_CRITICAL:
3162870dc34SPaolo Bonzini         return "critical";
3172870dc34SPaolo Bonzini     case G_LOG_LEVEL_WARNING:
3182870dc34SPaolo Bonzini         return "warning";
3192870dc34SPaolo Bonzini     case G_LOG_LEVEL_MESSAGE:
3202870dc34SPaolo Bonzini         return "message";
3212870dc34SPaolo Bonzini     case G_LOG_LEVEL_INFO:
3222870dc34SPaolo Bonzini         return "info";
3232870dc34SPaolo Bonzini     case G_LOG_LEVEL_DEBUG:
3242870dc34SPaolo Bonzini         return "debug";
3252870dc34SPaolo Bonzini     default:
3262870dc34SPaolo Bonzini         return "user";
3272870dc34SPaolo Bonzini     }
3282870dc34SPaolo Bonzini }
3292870dc34SPaolo Bonzini 
ga_logging_enabled(GAState * s)3302870dc34SPaolo Bonzini bool ga_logging_enabled(GAState *s)
3312870dc34SPaolo Bonzini {
3322870dc34SPaolo Bonzini     return s->logging_enabled;
3332870dc34SPaolo Bonzini }
3342870dc34SPaolo Bonzini 
ga_disable_logging(GAState * s)3352870dc34SPaolo Bonzini void ga_disable_logging(GAState *s)
3362870dc34SPaolo Bonzini {
3372870dc34SPaolo Bonzini     s->logging_enabled = false;
3382870dc34SPaolo Bonzini }
3392870dc34SPaolo Bonzini 
ga_enable_logging(GAState * s)3402870dc34SPaolo Bonzini void ga_enable_logging(GAState *s)
3412870dc34SPaolo Bonzini {
3422870dc34SPaolo Bonzini     s->logging_enabled = true;
3432870dc34SPaolo Bonzini }
3442870dc34SPaolo Bonzini 
glib_log_level_to_system(int level)3458e86851bSAndrey Drobyshev static int glib_log_level_to_system(int level)
3468e86851bSAndrey Drobyshev {
3478e86851bSAndrey Drobyshev     switch (level) {
3488e86851bSAndrey Drobyshev #ifndef _WIN32
3498e86851bSAndrey Drobyshev     case G_LOG_LEVEL_ERROR:
3508e86851bSAndrey Drobyshev         return LOG_ERR;
3518e86851bSAndrey Drobyshev     case G_LOG_LEVEL_CRITICAL:
3528e86851bSAndrey Drobyshev         return LOG_CRIT;
3538e86851bSAndrey Drobyshev     case G_LOG_LEVEL_WARNING:
3548e86851bSAndrey Drobyshev         return LOG_WARNING;
3558e86851bSAndrey Drobyshev     case G_LOG_LEVEL_MESSAGE:
3568e86851bSAndrey Drobyshev         return LOG_NOTICE;
3578e86851bSAndrey Drobyshev     case G_LOG_LEVEL_DEBUG:
3588e86851bSAndrey Drobyshev         return LOG_DEBUG;
3598e86851bSAndrey Drobyshev     case G_LOG_LEVEL_INFO:
3608e86851bSAndrey Drobyshev     default:
3618e86851bSAndrey Drobyshev         return LOG_INFO;
3628e86851bSAndrey Drobyshev #else
3638e86851bSAndrey Drobyshev     case G_LOG_LEVEL_ERROR:
3648e86851bSAndrey Drobyshev     case G_LOG_LEVEL_CRITICAL:
3658e86851bSAndrey Drobyshev         return EVENTLOG_ERROR_TYPE;
3668e86851bSAndrey Drobyshev     case G_LOG_LEVEL_WARNING:
3678e86851bSAndrey Drobyshev         return EVENTLOG_WARNING_TYPE;
3688e86851bSAndrey Drobyshev     case G_LOG_LEVEL_MESSAGE:
3698e86851bSAndrey Drobyshev     case G_LOG_LEVEL_INFO:
3708e86851bSAndrey Drobyshev     case G_LOG_LEVEL_DEBUG:
3718e86851bSAndrey Drobyshev     default:
3728e86851bSAndrey Drobyshev         return EVENTLOG_INFORMATION_TYPE;
3738e86851bSAndrey Drobyshev #endif
3748e86851bSAndrey Drobyshev     }
3758e86851bSAndrey Drobyshev }
3768e86851bSAndrey Drobyshev 
ga_log(const gchar * domain,GLogLevelFlags level,const gchar * msg,gpointer opaque)3772870dc34SPaolo Bonzini static void ga_log(const gchar *domain, GLogLevelFlags level,
3782870dc34SPaolo Bonzini                    const gchar *msg, gpointer opaque)
3792870dc34SPaolo Bonzini {
3802870dc34SPaolo Bonzini     GAState *s = opaque;
3812870dc34SPaolo Bonzini     const char *level_str = ga_log_level_str(level);
3822870dc34SPaolo Bonzini 
3832870dc34SPaolo Bonzini     if (!ga_logging_enabled(s)) {
3842870dc34SPaolo Bonzini         return;
3852870dc34SPaolo Bonzini     }
3862870dc34SPaolo Bonzini 
3872870dc34SPaolo Bonzini     level &= G_LOG_LEVEL_MASK;
388f300414cSMarkus Armbruster     if (g_strcmp0(domain, "syslog") == 0) {
389f9f0e617SAndrey Drobyshev #ifndef _WIN32
3908e86851bSAndrey Drobyshev         syslog(glib_log_level_to_system(level), "%s: %s", level_str, msg);
3912870dc34SPaolo Bonzini #else
3928e86851bSAndrey Drobyshev         ReportEvent(s->event_log, glib_log_level_to_system(level),
393f9f0e617SAndrey Drobyshev                     0, 1, NULL, 1, 0, &msg, NULL);
3942870dc34SPaolo Bonzini #endif
395f9f0e617SAndrey Drobyshev     } else if (level & s->log_level) {
39655fa0170SMarc-André Lureau         g_autoptr(GDateTime) now = g_date_time_new_now_utc();
39755fa0170SMarc-André Lureau         g_autofree char *nowstr = g_date_time_format(now, "%s.%f");
39855fa0170SMarc-André Lureau         fprintf(s->log_file, "%s: %s: %s\n", nowstr, level_str, msg);
3992870dc34SPaolo Bonzini         fflush(s->log_file);
4002870dc34SPaolo Bonzini     }
4012870dc34SPaolo Bonzini }
4022870dc34SPaolo Bonzini 
ga_set_response_delimited(GAState * s)4032870dc34SPaolo Bonzini void ga_set_response_delimited(GAState *s)
4042870dc34SPaolo Bonzini {
4052870dc34SPaolo Bonzini     s->delimit_response = true;
4062870dc34SPaolo Bonzini }
4072870dc34SPaolo Bonzini 
ga_open_logfile(const char * logfile)4089e92f6d4SLuiz Capitulino static FILE *ga_open_logfile(const char *logfile)
4099e92f6d4SLuiz Capitulino {
4109e92f6d4SLuiz Capitulino     FILE *f;
4119e92f6d4SLuiz Capitulino 
4129e92f6d4SLuiz Capitulino     f = fopen(logfile, "a");
4139e92f6d4SLuiz Capitulino     if (!f) {
4149e92f6d4SLuiz Capitulino         return NULL;
4159e92f6d4SLuiz Capitulino     }
4169e92f6d4SLuiz Capitulino 
4179e92f6d4SLuiz Capitulino     qemu_set_cloexec(fileno(f));
4189e92f6d4SLuiz Capitulino     return f;
4199e92f6d4SLuiz Capitulino }
4209e92f6d4SLuiz Capitulino 
ga_strcmp(gconstpointer str1,gconstpointer str2)4212870dc34SPaolo Bonzini static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
4222870dc34SPaolo Bonzini {
4232870dc34SPaolo Bonzini     return strcmp(str1, str2);
4242870dc34SPaolo Bonzini }
4252870dc34SPaolo Bonzini 
ga_command_is_allowed(const QmpCommand * cmd,GAState * state)4262e3b166cSDaniel P. Berrangé static bool ga_command_is_allowed(const QmpCommand *cmd, GAState *state)
4272870dc34SPaolo Bonzini {
4288dc4d915SMark Wu     int i = 0;
4292e3b166cSDaniel P. Berrangé     GAConfig *config = state->config;
4308dc4d915SMark Wu     const char *name = qmp_command_name(cmd);
4312e3b166cSDaniel P. Berrangé     /* Fallback policy is allow everything */
4322e3b166cSDaniel P. Berrangé     bool allowed = true;
4332e3b166cSDaniel P. Berrangé 
4342e3b166cSDaniel P. Berrangé     if (config->allowedrpcs) {
4352e3b166cSDaniel P. Berrangé         /*
4362e3b166cSDaniel P. Berrangé          * If an allow-list is given, this changes the fallback
4372e3b166cSDaniel P. Berrangé          * policy to deny everything
4382e3b166cSDaniel P. Berrangé          */
4392e3b166cSDaniel P. Berrangé         allowed = false;
4402e3b166cSDaniel P. Berrangé 
4412e3b166cSDaniel P. Berrangé         if (g_list_find_custom(config->allowedrpcs, name, ga_strcmp) != NULL) {
4422e3b166cSDaniel P. Berrangé             allowed = true;
4432e3b166cSDaniel P. Berrangé         }
4442e3b166cSDaniel P. Berrangé     }
4452e3b166cSDaniel P. Berrangé 
4462e3b166cSDaniel P. Berrangé     /*
4472e3b166cSDaniel P. Berrangé      * If both allowedrpcs and blockedrpcs are set, the blocked
4482e3b166cSDaniel P. Berrangé      * list will take priority
4492e3b166cSDaniel P. Berrangé      */
4502e3b166cSDaniel P. Berrangé     if (config->blockedrpcs) {
4512e3b166cSDaniel P. Berrangé         if (g_list_find_custom(config->blockedrpcs, name, ga_strcmp) != NULL) {
4522e3b166cSDaniel P. Berrangé             allowed = false;
4532e3b166cSDaniel P. Berrangé         }
4542e3b166cSDaniel P. Berrangé     }
4552e3b166cSDaniel P. Berrangé 
4562e3b166cSDaniel P. Berrangé     /*
4572e3b166cSDaniel P. Berrangé      * If frozen, this filtering must take priority over
4582e3b166cSDaniel P. Berrangé      * absolutely everything
4592e3b166cSDaniel P. Berrangé      */
4602e3b166cSDaniel P. Berrangé     if (state->frozen) {
4612e3b166cSDaniel P. Berrangé         allowed = false;
4622870dc34SPaolo Bonzini 
4630e4ef702SThomas Huth         while (ga_freeze_allowlist[i] != NULL) {
4640e4ef702SThomas Huth             if (strcmp(name, ga_freeze_allowlist[i]) == 0) {
4650e4ef702SThomas Huth                 allowed = true;
4662870dc34SPaolo Bonzini             }
4672870dc34SPaolo Bonzini             i++;
4682870dc34SPaolo Bonzini         }
4692870dc34SPaolo Bonzini     }
4702870dc34SPaolo Bonzini 
4712e3b166cSDaniel P. Berrangé     return allowed;
4722e3b166cSDaniel P. Berrangé }
4732e3b166cSDaniel P. Berrangé 
ga_apply_command_filters_iter(const QmpCommand * cmd,void * opaque)4742e3b166cSDaniel P. Berrangé static void ga_apply_command_filters_iter(const QmpCommand *cmd, void *opaque)
4752870dc34SPaolo Bonzini {
4762e3b166cSDaniel P. Berrangé     GAState *state = opaque;
4772e3b166cSDaniel P. Berrangé     bool want = ga_command_is_allowed(cmd, state);
4782e3b166cSDaniel P. Berrangé     bool have = qmp_command_is_enabled(cmd);
4798dc4d915SMark Wu     const char *name = qmp_command_name(cmd);
4802870dc34SPaolo Bonzini 
4812e3b166cSDaniel P. Berrangé     if (want == have) {
482133789e1SKonstantin Kostiuk         return;
483133789e1SKonstantin Kostiuk     }
484133789e1SKonstantin Kostiuk 
4852e3b166cSDaniel P. Berrangé     if (have) {
4862e3b166cSDaniel P. Berrangé         g_debug("disabling command: %s", name);
4872e3b166cSDaniel P. Berrangé         qmp_disable_command(&ga_commands, name, "the command is not allowed");
4882e3b166cSDaniel P. Berrangé     } else {
4898dc4d915SMark Wu         g_debug("enabling command: %s", name);
4901527badbSMarkus Armbruster         qmp_enable_command(&ga_commands, name);
4912870dc34SPaolo Bonzini     }
4922870dc34SPaolo Bonzini }
4932870dc34SPaolo Bonzini 
ga_apply_command_filters(GAState * state)4942e3b166cSDaniel P. Berrangé static void ga_apply_command_filters(GAState *state)
495133789e1SKonstantin Kostiuk {
4962e3b166cSDaniel P. Berrangé     qmp_for_each_command(&ga_commands, ga_apply_command_filters_iter, state);
497133789e1SKonstantin Kostiuk }
498133789e1SKonstantin Kostiuk 
ga_create_file(const char * path)4992870dc34SPaolo Bonzini static bool ga_create_file(const char *path)
5002870dc34SPaolo Bonzini {
5012870dc34SPaolo Bonzini     int fd = open(path, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR);
5022870dc34SPaolo Bonzini     if (fd == -1) {
5032870dc34SPaolo Bonzini         g_warning("unable to open/create file %s: %s", path, strerror(errno));
5042870dc34SPaolo Bonzini         return false;
5052870dc34SPaolo Bonzini     }
5062870dc34SPaolo Bonzini     close(fd);
5072870dc34SPaolo Bonzini     return true;
5082870dc34SPaolo Bonzini }
5092870dc34SPaolo Bonzini 
ga_delete_file(const char * path)5102870dc34SPaolo Bonzini static bool ga_delete_file(const char *path)
5112870dc34SPaolo Bonzini {
5122870dc34SPaolo Bonzini     int ret = unlink(path);
5132870dc34SPaolo Bonzini     if (ret == -1) {
5142870dc34SPaolo Bonzini         g_warning("unable to delete file: %s: %s", path, strerror(errno));
5152870dc34SPaolo Bonzini         return false;
5162870dc34SPaolo Bonzini     }
5172870dc34SPaolo Bonzini 
5182870dc34SPaolo Bonzini     return true;
5192870dc34SPaolo Bonzini }
5202870dc34SPaolo Bonzini 
ga_is_frozen(GAState * s)5212870dc34SPaolo Bonzini bool ga_is_frozen(GAState *s)
5222870dc34SPaolo Bonzini {
5232870dc34SPaolo Bonzini     return s->frozen;
5242870dc34SPaolo Bonzini }
5252870dc34SPaolo Bonzini 
ga_set_frozen(GAState * s)5262870dc34SPaolo Bonzini void ga_set_frozen(GAState *s)
5272870dc34SPaolo Bonzini {
5282870dc34SPaolo Bonzini     if (ga_is_frozen(s)) {
5292870dc34SPaolo Bonzini         return;
5302870dc34SPaolo Bonzini     }
5312870dc34SPaolo Bonzini     g_warning("disabling logging due to filesystem freeze");
5322870dc34SPaolo Bonzini     s->frozen = true;
5332870dc34SPaolo Bonzini     if (!ga_create_file(s->state_filepath_isfrozen)) {
5342870dc34SPaolo Bonzini         g_warning("unable to create %s, fsfreeze may not function properly",
5352870dc34SPaolo Bonzini                   s->state_filepath_isfrozen);
5362870dc34SPaolo Bonzini     }
5372e3b166cSDaniel P. Berrangé     ga_apply_command_filters(s);
5382e3b166cSDaniel P. Berrangé     ga_disable_logging(s);
5392870dc34SPaolo Bonzini }
5402870dc34SPaolo Bonzini 
ga_unset_frozen(GAState * s)5412870dc34SPaolo Bonzini void ga_unset_frozen(GAState *s)
5422870dc34SPaolo Bonzini {
5432870dc34SPaolo Bonzini     if (!ga_is_frozen(s)) {
5442870dc34SPaolo Bonzini         return;
5452870dc34SPaolo Bonzini     }
5462870dc34SPaolo Bonzini 
5472870dc34SPaolo Bonzini     /* if we delayed creation/opening of pid/log files due to being
5482870dc34SPaolo Bonzini      * in a frozen state at start up, do it now
5492870dc34SPaolo Bonzini      */
5502870dc34SPaolo Bonzini     if (s->deferred_options.log_filepath) {
5519e92f6d4SLuiz Capitulino         s->log_file = ga_open_logfile(s->deferred_options.log_filepath);
5522870dc34SPaolo Bonzini         if (!s->log_file) {
5532870dc34SPaolo Bonzini             s->log_file = stderr;
5542870dc34SPaolo Bonzini         }
5552870dc34SPaolo Bonzini         s->deferred_options.log_filepath = NULL;
5562870dc34SPaolo Bonzini     }
5572870dc34SPaolo Bonzini     ga_enable_logging(s);
5582870dc34SPaolo Bonzini     g_warning("logging re-enabled due to filesystem unfreeze");
5592870dc34SPaolo Bonzini     if (s->deferred_options.pid_filepath) {
5609e6bdef2SMarc-André Lureau         Error *err = NULL;
5619e6bdef2SMarc-André Lureau 
5629e6bdef2SMarc-André Lureau         if (!qemu_write_pidfile(s->deferred_options.pid_filepath, &err)) {
5639e6bdef2SMarc-André Lureau             g_warning("%s", error_get_pretty(err));
5649e6bdef2SMarc-André Lureau             error_free(err);
5652870dc34SPaolo Bonzini         }
5662870dc34SPaolo Bonzini         s->deferred_options.pid_filepath = NULL;
5672870dc34SPaolo Bonzini     }
5682870dc34SPaolo Bonzini 
569133789e1SKonstantin Kostiuk     /* enable all disabled, non-blocked and allowed commands */
5702870dc34SPaolo Bonzini     s->frozen = false;
5712870dc34SPaolo Bonzini     if (!ga_delete_file(s->state_filepath_isfrozen)) {
5722870dc34SPaolo Bonzini         g_warning("unable to delete %s, fsfreeze may not function properly",
5732870dc34SPaolo Bonzini                   s->state_filepath_isfrozen);
5742870dc34SPaolo Bonzini     }
5752e3b166cSDaniel P. Berrangé     ga_apply_command_filters(s);
5762870dc34SPaolo Bonzini }
5772870dc34SPaolo Bonzini 
578ec0f694cSTomoki Sekiyama #ifdef CONFIG_FSFREEZE
ga_fsfreeze_hook(GAState * s)579ec0f694cSTomoki Sekiyama const char *ga_fsfreeze_hook(GAState *s)
580ec0f694cSTomoki Sekiyama {
581ec0f694cSTomoki Sekiyama     return s->fsfreeze_hook;
582ec0f694cSTomoki Sekiyama }
583ec0f694cSTomoki Sekiyama #endif
584ec0f694cSTomoki Sekiyama 
become_daemon(const char * pidfile)5852870dc34SPaolo Bonzini static void become_daemon(const char *pidfile)
5862870dc34SPaolo Bonzini {
5872870dc34SPaolo Bonzini #ifndef _WIN32
5882870dc34SPaolo Bonzini     pid_t pid, sid;
5892870dc34SPaolo Bonzini 
5902870dc34SPaolo Bonzini     pid = fork();
5912870dc34SPaolo Bonzini     if (pid < 0) {
5922870dc34SPaolo Bonzini         exit(EXIT_FAILURE);
5932870dc34SPaolo Bonzini     }
5942870dc34SPaolo Bonzini     if (pid > 0) {
5952870dc34SPaolo Bonzini         exit(EXIT_SUCCESS);
5962870dc34SPaolo Bonzini     }
5972870dc34SPaolo Bonzini 
5982870dc34SPaolo Bonzini     if (pidfile) {
5999e6bdef2SMarc-André Lureau         Error *err = NULL;
6009e6bdef2SMarc-André Lureau 
6019e6bdef2SMarc-André Lureau         if (!qemu_write_pidfile(pidfile, &err)) {
6029e6bdef2SMarc-André Lureau             g_critical("%s", error_get_pretty(err));
6039e6bdef2SMarc-André Lureau             error_free(err);
6042870dc34SPaolo Bonzini             exit(EXIT_FAILURE);
6052870dc34SPaolo Bonzini         }
6062870dc34SPaolo Bonzini     }
6072870dc34SPaolo Bonzini 
608c689b4f1SLaszlo Ersek     umask(S_IRWXG | S_IRWXO);
6092870dc34SPaolo Bonzini     sid = setsid();
6102870dc34SPaolo Bonzini     if (sid < 0) {
6112870dc34SPaolo Bonzini         goto fail;
6122870dc34SPaolo Bonzini     }
6132870dc34SPaolo Bonzini     if ((chdir("/")) < 0) {
6142870dc34SPaolo Bonzini         goto fail;
6152870dc34SPaolo Bonzini     }
6162870dc34SPaolo Bonzini 
6172870dc34SPaolo Bonzini     reopen_fd_to_null(STDIN_FILENO);
6182870dc34SPaolo Bonzini     reopen_fd_to_null(STDOUT_FILENO);
6192870dc34SPaolo Bonzini     reopen_fd_to_null(STDERR_FILENO);
6202870dc34SPaolo Bonzini     return;
6212870dc34SPaolo Bonzini 
6222870dc34SPaolo Bonzini fail:
6232870dc34SPaolo Bonzini     if (pidfile) {
6242870dc34SPaolo Bonzini         unlink(pidfile);
6252870dc34SPaolo Bonzini     }
6262870dc34SPaolo Bonzini     g_critical("failed to daemonize");
6272870dc34SPaolo Bonzini     exit(EXIT_FAILURE);
6282870dc34SPaolo Bonzini #endif
6292870dc34SPaolo Bonzini }
6302870dc34SPaolo Bonzini 
send_response(GAState * s,const QDict * rsp)631781f2b3dSMarc-André Lureau static int send_response(GAState *s, const QDict *rsp)
6322870dc34SPaolo Bonzini {
633eab3a467SMarkus Armbruster     GString *response;
6342870dc34SPaolo Bonzini     GIOStatus status;
6352870dc34SPaolo Bonzini 
636844bd70bSMarc-André Lureau     g_assert(s->channel);
637844bd70bSMarc-André Lureau 
638844bd70bSMarc-André Lureau     if (!rsp) {
639844bd70bSMarc-André Lureau         return 0;
640844bd70bSMarc-André Lureau     }
6412870dc34SPaolo Bonzini 
642eab3a467SMarkus Armbruster     response = qobject_to_json(QOBJECT(rsp));
643eab3a467SMarkus Armbruster     if (!response) {
6442870dc34SPaolo Bonzini         return -EINVAL;
6452870dc34SPaolo Bonzini     }
6462870dc34SPaolo Bonzini 
6472870dc34SPaolo Bonzini     if (s->delimit_response) {
6482870dc34SPaolo Bonzini         s->delimit_response = false;
649eab3a467SMarkus Armbruster         g_string_prepend_c(response, QGA_SENTINEL_BYTE);
6502870dc34SPaolo Bonzini     }
6512870dc34SPaolo Bonzini 
652eab3a467SMarkus Armbruster     g_string_append_c(response, '\n');
653eab3a467SMarkus Armbruster     status = ga_channel_write_all(s->channel, response->str, response->len);
654eab3a467SMarkus Armbruster     g_string_free(response, true);
6552870dc34SPaolo Bonzini     if (status != G_IO_STATUS_NORMAL) {
6562870dc34SPaolo Bonzini         return -EIO;
6572870dc34SPaolo Bonzini     }
6582870dc34SPaolo Bonzini 
6592870dc34SPaolo Bonzini     return 0;
6602870dc34SPaolo Bonzini }
6612870dc34SPaolo Bonzini 
6622870dc34SPaolo Bonzini /* handle requests/control events coming in over the channel */
process_event(void * opaque,QObject * obj,Error * err)66362815d85SMarkus Armbruster static void process_event(void *opaque, QObject *obj, Error *err)
6642870dc34SPaolo Bonzini {
66562815d85SMarkus Armbruster     GAState *s = opaque;
666781f2b3dSMarc-André Lureau     QDict *rsp;
6672870dc34SPaolo Bonzini     int ret;
6682870dc34SPaolo Bonzini 
6692870dc34SPaolo Bonzini     g_debug("process_event: called");
67084a56f38SMarkus Armbruster     assert(!obj != !err);
671ae7da1e5SMarc-André Lureau     if (err) {
672ae7da1e5SMarc-André Lureau         rsp = qmp_error_response(err);
673781f2b3dSMarc-André Lureau         goto end;
674781f2b3dSMarc-André Lureau     }
675781f2b3dSMarc-André Lureau 
676781f2b3dSMarc-André Lureau     g_debug("processing command");
67741725fa7SKevin Wolf     rsp = qmp_dispatch(&ga_commands, obj, false, NULL);
678781f2b3dSMarc-André Lureau 
679781f2b3dSMarc-André Lureau end:
680ae7da1e5SMarc-André Lureau     ret = send_response(s, rsp);
6811def7454SGonglei     if (ret < 0) {
6821def7454SGonglei         g_warning("error sending error response: %s", strerror(-ret));
6832870dc34SPaolo Bonzini     }
684ae7da1e5SMarc-André Lureau     qobject_unref(rsp);
685ae7da1e5SMarc-André Lureau     qobject_unref(obj);
6862870dc34SPaolo Bonzini }
6872870dc34SPaolo Bonzini 
6882870dc34SPaolo Bonzini /* false return signals GAChannel to close the current client connection */
channel_event_cb(GIOCondition condition,gpointer data)6892870dc34SPaolo Bonzini static gboolean channel_event_cb(GIOCondition condition, gpointer data)
6902870dc34SPaolo Bonzini {
6912870dc34SPaolo Bonzini     GAState *s = data;
6922870dc34SPaolo Bonzini     gchar buf[QGA_READ_COUNT_DEFAULT + 1];
6932870dc34SPaolo Bonzini     gsize count;
6942870dc34SPaolo Bonzini     GIOStatus status = ga_channel_read(s->channel, buf, QGA_READ_COUNT_DEFAULT, &count);
6952870dc34SPaolo Bonzini     switch (status) {
6962870dc34SPaolo Bonzini     case G_IO_STATUS_ERROR:
6972870dc34SPaolo Bonzini         g_warning("error reading channel");
698d951fadaSMichael Roth         stop_agent(s, false);
6992870dc34SPaolo Bonzini         return false;
7002870dc34SPaolo Bonzini     case G_IO_STATUS_NORMAL:
7012870dc34SPaolo Bonzini         buf[count] = 0;
7022870dc34SPaolo Bonzini         g_debug("read data, count: %d, data: %s", (int)count, buf);
7032870dc34SPaolo Bonzini         json_message_parser_feed(&s->parser, (char *)buf, (int)count);
7042870dc34SPaolo Bonzini         break;
7052870dc34SPaolo Bonzini     case G_IO_STATUS_EOF:
7062870dc34SPaolo Bonzini         g_debug("received EOF");
7072870dc34SPaolo Bonzini         if (!s->virtio) {
7082870dc34SPaolo Bonzini             return false;
7092870dc34SPaolo Bonzini         }
710f5b79578SMarkus Armbruster         /* fall through */
7112870dc34SPaolo Bonzini     case G_IO_STATUS_AGAIN:
7122870dc34SPaolo Bonzini         /* virtio causes us to spin here when no process is attached to
7132870dc34SPaolo Bonzini          * host-side chardev. sleep a bit to mitigate this
7142870dc34SPaolo Bonzini          */
7152870dc34SPaolo Bonzini         if (s->virtio) {
7164e8c4194SMarc-André Lureau             g_usleep(G_USEC_PER_SEC / 10);
7172870dc34SPaolo Bonzini         }
7182870dc34SPaolo Bonzini         return true;
7192870dc34SPaolo Bonzini     default:
7202870dc34SPaolo Bonzini         g_warning("unknown channel read status, closing");
7212870dc34SPaolo Bonzini         return false;
7222870dc34SPaolo Bonzini     }
7232870dc34SPaolo Bonzini     return true;
7242870dc34SPaolo Bonzini }
7252870dc34SPaolo Bonzini 
channel_init(GAState * s,const gchar * method,const gchar * path,int listen_fd)72626de2296SStefan Hajnoczi static gboolean channel_init(GAState *s, const gchar *method, const gchar *path,
72726de2296SStefan Hajnoczi                              int listen_fd)
7282870dc34SPaolo Bonzini {
7292870dc34SPaolo Bonzini     GAChannelMethod channel_method;
7302870dc34SPaolo Bonzini 
7312870dc34SPaolo Bonzini     if (strcmp(method, "virtio-serial") == 0) {
7322870dc34SPaolo Bonzini         s->virtio = true; /* virtio requires special handling in some cases */
7332870dc34SPaolo Bonzini         channel_method = GA_CHANNEL_VIRTIO_SERIAL;
7342870dc34SPaolo Bonzini     } else if (strcmp(method, "isa-serial") == 0) {
7352870dc34SPaolo Bonzini         channel_method = GA_CHANNEL_ISA_SERIAL;
7362870dc34SPaolo Bonzini     } else if (strcmp(method, "unix-listen") == 0) {
7372870dc34SPaolo Bonzini         channel_method = GA_CHANNEL_UNIX_LISTEN;
738586ef5deSStefan Hajnoczi     } else if (strcmp(method, "vsock-listen") == 0) {
739586ef5deSStefan Hajnoczi         channel_method = GA_CHANNEL_VSOCK_LISTEN;
7402870dc34SPaolo Bonzini     } else {
7412870dc34SPaolo Bonzini         g_critical("unsupported channel method/type: %s", method);
7422870dc34SPaolo Bonzini         return false;
7432870dc34SPaolo Bonzini     }
7442870dc34SPaolo Bonzini 
74526de2296SStefan Hajnoczi     s->channel = ga_channel_new(channel_method, path, listen_fd,
74626de2296SStefan Hajnoczi                                 channel_event_cb, s);
7472870dc34SPaolo Bonzini     if (!s->channel) {
7482870dc34SPaolo Bonzini         g_critical("failed to create guest agent channel");
7492870dc34SPaolo Bonzini         return false;
7502870dc34SPaolo Bonzini     }
7512870dc34SPaolo Bonzini 
7522870dc34SPaolo Bonzini     return true;
7532870dc34SPaolo Bonzini }
7542870dc34SPaolo Bonzini 
7552870dc34SPaolo Bonzini #ifdef _WIN32
handle_serial_device_events(DWORD type,LPVOID data)756b70d6afeSBishara AbuHattoum DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data)
757b70d6afeSBishara AbuHattoum {
758b70d6afeSBishara AbuHattoum     DWORD ret = NO_ERROR;
759b70d6afeSBishara AbuHattoum     PDEV_BROADCAST_HDR broadcast_header = (PDEV_BROADCAST_HDR)data;
760b70d6afeSBishara AbuHattoum 
761b70d6afeSBishara AbuHattoum     if (broadcast_header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
762b70d6afeSBishara AbuHattoum         switch (type) {
763b70d6afeSBishara AbuHattoum             /* Device inserted */
764b70d6afeSBishara AbuHattoum         case DBT_DEVICEARRIVAL:
765b70d6afeSBishara AbuHattoum             /* Start QEMU-ga's service */
766b70d6afeSBishara AbuHattoum             if (!SetEvent(ga_state->wakeup_event)) {
767b70d6afeSBishara AbuHattoum                 ret = GetLastError();
768b70d6afeSBishara AbuHattoum             }
769b70d6afeSBishara AbuHattoum             break;
770b70d6afeSBishara AbuHattoum             /* Device removed */
771b70d6afeSBishara AbuHattoum         case DBT_DEVICEQUERYREMOVE:
772b70d6afeSBishara AbuHattoum         case DBT_DEVICEREMOVEPENDING:
773b70d6afeSBishara AbuHattoum         case DBT_DEVICEREMOVECOMPLETE:
774b70d6afeSBishara AbuHattoum             /* Stop QEMU-ga's service */
775b70d6afeSBishara AbuHattoum             if (!ResetEvent(ga_state->wakeup_event)) {
776b70d6afeSBishara AbuHattoum                 ret = GetLastError();
777b70d6afeSBishara AbuHattoum             }
778b70d6afeSBishara AbuHattoum             break;
779b70d6afeSBishara AbuHattoum         default:
780b70d6afeSBishara AbuHattoum             ret = ERROR_CALL_NOT_IMPLEMENTED;
781b70d6afeSBishara AbuHattoum         }
782b70d6afeSBishara AbuHattoum     }
783b70d6afeSBishara AbuHattoum     return ret;
784b70d6afeSBishara AbuHattoum }
785b70d6afeSBishara AbuHattoum 
service_ctrl_handler(DWORD ctrl,DWORD type,LPVOID data,LPVOID ctx)7862870dc34SPaolo Bonzini DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
7872870dc34SPaolo Bonzini                                   LPVOID ctx)
7882870dc34SPaolo Bonzini {
7892870dc34SPaolo Bonzini     DWORD ret = NO_ERROR;
7902870dc34SPaolo Bonzini     GAService *service = &ga_state->service;
7912870dc34SPaolo Bonzini 
792aaaed199SAlexChen     switch (ctrl) {
7932870dc34SPaolo Bonzini     case SERVICE_CONTROL_STOP:
7942870dc34SPaolo Bonzini     case SERVICE_CONTROL_SHUTDOWN:
7952870dc34SPaolo Bonzini         quit_handler(SIGTERM);
796b70d6afeSBishara AbuHattoum         SetEvent(ga_state->wakeup_event);
7972870dc34SPaolo Bonzini         service->status.dwCurrentState = SERVICE_STOP_PENDING;
7982870dc34SPaolo Bonzini         SetServiceStatus(service->status_handle, &service->status);
7992870dc34SPaolo Bonzini         break;
800b70d6afeSBishara AbuHattoum     case SERVICE_CONTROL_DEVICEEVENT:
801b70d6afeSBishara AbuHattoum         handle_serial_device_events(type, data);
802b70d6afeSBishara AbuHattoum         break;
8032870dc34SPaolo Bonzini 
8042870dc34SPaolo Bonzini     default:
8052870dc34SPaolo Bonzini         ret = ERROR_CALL_NOT_IMPLEMENTED;
8062870dc34SPaolo Bonzini     }
8072870dc34SPaolo Bonzini     return ret;
8082870dc34SPaolo Bonzini }
8092870dc34SPaolo Bonzini 
service_main(DWORD argc,TCHAR * argv[])8102870dc34SPaolo Bonzini VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
8112870dc34SPaolo Bonzini {
8122870dc34SPaolo Bonzini     GAService *service = &ga_state->service;
8132870dc34SPaolo Bonzini 
8142870dc34SPaolo Bonzini     service->status_handle = RegisterServiceCtrlHandlerEx(QGA_SERVICE_NAME,
8152870dc34SPaolo Bonzini         service_ctrl_handler, NULL);
8162870dc34SPaolo Bonzini 
8172870dc34SPaolo Bonzini     if (service->status_handle == 0) {
8182870dc34SPaolo Bonzini         g_critical("Failed to register extended requests function!\n");
8192870dc34SPaolo Bonzini         return;
8202870dc34SPaolo Bonzini     }
8212870dc34SPaolo Bonzini 
8222870dc34SPaolo Bonzini     service->status.dwServiceType = SERVICE_WIN32;
8232870dc34SPaolo Bonzini     service->status.dwCurrentState = SERVICE_RUNNING;
8242870dc34SPaolo Bonzini     service->status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
8252870dc34SPaolo Bonzini     service->status.dwWin32ExitCode = NO_ERROR;
8262870dc34SPaolo Bonzini     service->status.dwServiceSpecificExitCode = NO_ERROR;
8272870dc34SPaolo Bonzini     service->status.dwCheckPoint = 0;
8282870dc34SPaolo Bonzini     service->status.dwWaitHint = 0;
829b70d6afeSBishara AbuHattoum     DEV_BROADCAST_DEVICEINTERFACE notification_filter;
830b70d6afeSBishara AbuHattoum     ZeroMemory(&notification_filter, sizeof(notification_filter));
831b70d6afeSBishara AbuHattoum     notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
832b70d6afeSBishara AbuHattoum     notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
833b70d6afeSBishara AbuHattoum     notification_filter.dbcc_classguid = GUID_VIOSERIAL_PORT;
834b70d6afeSBishara AbuHattoum 
835b70d6afeSBishara AbuHattoum     service->device_notification_handle =
836b70d6afeSBishara AbuHattoum         RegisterDeviceNotification(service->status_handle,
837b70d6afeSBishara AbuHattoum             &notification_filter, DEVICE_NOTIFY_SERVICE_HANDLE);
838b70d6afeSBishara AbuHattoum     if (!service->device_notification_handle) {
839b70d6afeSBishara AbuHattoum         g_critical("Failed to register device notification handle!\n");
840b70d6afeSBishara AbuHattoum         return;
841b70d6afeSBishara AbuHattoum     }
8422870dc34SPaolo Bonzini     SetServiceStatus(service->status_handle, &service->status);
8432870dc34SPaolo Bonzini 
844d88495a8SMichael Roth     run_agent(ga_state);
8452870dc34SPaolo Bonzini 
846b70d6afeSBishara AbuHattoum     UnregisterDeviceNotification(service->device_notification_handle);
8472870dc34SPaolo Bonzini     service->status.dwCurrentState = SERVICE_STOPPED;
8482870dc34SPaolo Bonzini     SetServiceStatus(service->status_handle, &service->status);
8492870dc34SPaolo Bonzini }
8502870dc34SPaolo Bonzini #endif
8512870dc34SPaolo Bonzini 
set_persistent_state_defaults(GAPersistentState * pstate)85239097dafSMichael Roth static void set_persistent_state_defaults(GAPersistentState *pstate)
85339097dafSMichael Roth {
85439097dafSMichael Roth     g_assert(pstate);
85539097dafSMichael Roth     pstate->fd_counter = QGA_PSTATE_DEFAULT_FD_COUNTER;
85639097dafSMichael Roth }
85739097dafSMichael Roth 
persistent_state_from_keyfile(GAPersistentState * pstate,GKeyFile * keyfile)85839097dafSMichael Roth static void persistent_state_from_keyfile(GAPersistentState *pstate,
85939097dafSMichael Roth                                           GKeyFile *keyfile)
86039097dafSMichael Roth {
86139097dafSMichael Roth     g_assert(pstate);
86239097dafSMichael Roth     g_assert(keyfile);
86339097dafSMichael Roth     /* if any fields are missing, either because the file was tampered with
86439097dafSMichael Roth      * by agents of chaos, or because the field wasn't present at the time the
86539097dafSMichael Roth      * file was created, the best we can ever do is start over with the default
86639097dafSMichael Roth      * values. so load them now, and ignore any errors in accessing key-value
86739097dafSMichael Roth      * pairs
86839097dafSMichael Roth      */
86939097dafSMichael Roth     set_persistent_state_defaults(pstate);
87039097dafSMichael Roth 
87139097dafSMichael Roth     if (g_key_file_has_key(keyfile, "global", "fd_counter", NULL)) {
87239097dafSMichael Roth         pstate->fd_counter =
8734f306496SPeter Crosthwaite             g_key_file_get_integer(keyfile, "global", "fd_counter", NULL);
87439097dafSMichael Roth     }
87539097dafSMichael Roth }
87639097dafSMichael Roth 
persistent_state_to_keyfile(const GAPersistentState * pstate,GKeyFile * keyfile)87739097dafSMichael Roth static void persistent_state_to_keyfile(const GAPersistentState *pstate,
87839097dafSMichael Roth                                         GKeyFile *keyfile)
87939097dafSMichael Roth {
88039097dafSMichael Roth     g_assert(pstate);
88139097dafSMichael Roth     g_assert(keyfile);
88239097dafSMichael Roth 
8834f306496SPeter Crosthwaite     g_key_file_set_integer(keyfile, "global", "fd_counter", pstate->fd_counter);
88439097dafSMichael Roth }
88539097dafSMichael Roth 
write_persistent_state(const GAPersistentState * pstate,const gchar * path)88639097dafSMichael Roth static gboolean write_persistent_state(const GAPersistentState *pstate,
88739097dafSMichael Roth                                        const gchar *path)
88839097dafSMichael Roth {
88939097dafSMichael Roth     GKeyFile *keyfile = g_key_file_new();
89039097dafSMichael Roth     GError *gerr = NULL;
89139097dafSMichael Roth     gboolean ret = true;
89239097dafSMichael Roth     gchar *data = NULL;
89339097dafSMichael Roth     gsize data_len;
89439097dafSMichael Roth 
89539097dafSMichael Roth     g_assert(pstate);
89639097dafSMichael Roth 
89739097dafSMichael Roth     persistent_state_to_keyfile(pstate, keyfile);
89839097dafSMichael Roth     data = g_key_file_to_data(keyfile, &data_len, &gerr);
89939097dafSMichael Roth     if (gerr) {
90039097dafSMichael Roth         g_critical("failed to convert persistent state to string: %s",
90139097dafSMichael Roth                    gerr->message);
90239097dafSMichael Roth         ret = false;
90339097dafSMichael Roth         goto out;
90439097dafSMichael Roth     }
90539097dafSMichael Roth 
90639097dafSMichael Roth     g_file_set_contents(path, data, data_len, &gerr);
90739097dafSMichael Roth     if (gerr) {
90839097dafSMichael Roth         g_critical("failed to write persistent state to %s: %s",
90939097dafSMichael Roth                     path, gerr->message);
91039097dafSMichael Roth         ret = false;
91139097dafSMichael Roth         goto out;
91239097dafSMichael Roth     }
91339097dafSMichael Roth 
91439097dafSMichael Roth out:
91539097dafSMichael Roth     if (gerr) {
91639097dafSMichael Roth         g_error_free(gerr);
91739097dafSMichael Roth     }
91839097dafSMichael Roth     if (keyfile) {
91939097dafSMichael Roth         g_key_file_free(keyfile);
92039097dafSMichael Roth     }
92139097dafSMichael Roth     g_free(data);
92239097dafSMichael Roth     return ret;
92339097dafSMichael Roth }
92439097dafSMichael Roth 
read_persistent_state(GAPersistentState * pstate,const gchar * path,gboolean frozen)92539097dafSMichael Roth static gboolean read_persistent_state(GAPersistentState *pstate,
92639097dafSMichael Roth                                       const gchar *path, gboolean frozen)
92739097dafSMichael Roth {
92839097dafSMichael Roth     GKeyFile *keyfile = NULL;
92939097dafSMichael Roth     GError *gerr = NULL;
93039097dafSMichael Roth     struct stat st;
93139097dafSMichael Roth     gboolean ret = true;
93239097dafSMichael Roth 
93339097dafSMichael Roth     g_assert(pstate);
93439097dafSMichael Roth 
93539097dafSMichael Roth     if (stat(path, &st) == -1) {
93639097dafSMichael Roth         /* it's okay if state file doesn't exist, but any other error
93739097dafSMichael Roth          * indicates a permissions issue or some other misconfiguration
93839097dafSMichael Roth          * that we likely won't be able to recover from.
93939097dafSMichael Roth          */
94039097dafSMichael Roth         if (errno != ENOENT) {
94139097dafSMichael Roth             g_critical("unable to access state file at path %s: %s",
94239097dafSMichael Roth                        path, strerror(errno));
94339097dafSMichael Roth             ret = false;
94439097dafSMichael Roth             goto out;
94539097dafSMichael Roth         }
94639097dafSMichael Roth 
94739097dafSMichael Roth         /* file doesn't exist. initialize state to default values and
94839097dafSMichael Roth          * attempt to save now. (we could wait till later when we have
94939097dafSMichael Roth          * modified state we need to commit, but if there's a problem,
95039097dafSMichael Roth          * such as a missing parent directory, we want to catch it now)
95139097dafSMichael Roth          *
95239097dafSMichael Roth          * there is a potential scenario where someone either managed to
95339097dafSMichael Roth          * update the agent from a version that didn't use a key store
95439097dafSMichael Roth          * while qemu-ga thought the filesystem was frozen, or
95539097dafSMichael Roth          * deleted the key store prior to issuing a fsfreeze, prior
95639097dafSMichael Roth          * to restarting the agent. in this case we go ahead and defer
95739097dafSMichael Roth          * initial creation till we actually have modified state to
95839097dafSMichael Roth          * write, otherwise fail to recover from freeze.
95939097dafSMichael Roth          */
96039097dafSMichael Roth         set_persistent_state_defaults(pstate);
96139097dafSMichael Roth         if (!frozen) {
96239097dafSMichael Roth             ret = write_persistent_state(pstate, path);
96339097dafSMichael Roth             if (!ret) {
96439097dafSMichael Roth                 g_critical("unable to create state file at path %s", path);
96539097dafSMichael Roth                 ret = false;
96639097dafSMichael Roth                 goto out;
96739097dafSMichael Roth             }
96839097dafSMichael Roth         }
96939097dafSMichael Roth         ret = true;
97039097dafSMichael Roth         goto out;
97139097dafSMichael Roth     }
97239097dafSMichael Roth 
97339097dafSMichael Roth     keyfile = g_key_file_new();
97439097dafSMichael Roth     g_key_file_load_from_file(keyfile, path, 0, &gerr);
97539097dafSMichael Roth     if (gerr) {
97639097dafSMichael Roth         g_critical("error loading persistent state from path: %s, %s",
97739097dafSMichael Roth                    path, gerr->message);
97839097dafSMichael Roth         ret = false;
97939097dafSMichael Roth         goto out;
98039097dafSMichael Roth     }
98139097dafSMichael Roth 
98239097dafSMichael Roth     persistent_state_from_keyfile(pstate, keyfile);
98339097dafSMichael Roth 
98439097dafSMichael Roth out:
98539097dafSMichael Roth     if (keyfile) {
98639097dafSMichael Roth         g_key_file_free(keyfile);
98739097dafSMichael Roth     }
98839097dafSMichael Roth     if (gerr) {
98939097dafSMichael Roth         g_error_free(gerr);
99039097dafSMichael Roth     }
99139097dafSMichael Roth 
99239097dafSMichael Roth     return ret;
99339097dafSMichael Roth }
99439097dafSMichael Roth 
ga_get_fd_handle(GAState * s,Error ** errp)99539097dafSMichael Roth int64_t ga_get_fd_handle(GAState *s, Error **errp)
99639097dafSMichael Roth {
99739097dafSMichael Roth     int64_t handle;
99839097dafSMichael Roth 
99939097dafSMichael Roth     g_assert(s->pstate_filepath);
10000e4ef702SThomas Huth     /*
10010e4ef702SThomas Huth      * We block commands and avoid operations that potentially require
100239097dafSMichael Roth      * writing to disk when we're in a frozen state. this includes opening
100339097dafSMichael Roth      * new files, so we should never get here in that situation
100439097dafSMichael Roth      */
100539097dafSMichael Roth     g_assert(!ga_is_frozen(s));
100639097dafSMichael Roth 
100739097dafSMichael Roth     handle = s->pstate.fd_counter++;
1008ce7f7cc2SLuiz Capitulino 
1009ce7f7cc2SLuiz Capitulino     /* This should never happen on a reasonable timeframe, as guest-file-open
1010ce7f7cc2SLuiz Capitulino      * would have to be issued 2^63 times */
1011ce7f7cc2SLuiz Capitulino     if (s->pstate.fd_counter == INT64_MAX) {
1012ce7f7cc2SLuiz Capitulino         abort();
101339097dafSMichael Roth     }
1014ce7f7cc2SLuiz Capitulino 
101539097dafSMichael Roth     if (!write_persistent_state(&s->pstate, s->pstate_filepath)) {
101639097dafSMichael Roth         error_setg(errp, "failed to commit persistent state to disk");
1017a903f40cSMarkus Armbruster         return -1;
101839097dafSMichael Roth     }
101939097dafSMichael Roth 
102039097dafSMichael Roth     return handle;
102139097dafSMichael Roth }
102239097dafSMichael Roth 
ga_print_cmd(const QmpCommand * cmd,void * opaque)1023f0ccc00bSMarc-André Lureau static void ga_print_cmd(const QmpCommand *cmd, void *opaque)
10248dc4d915SMark Wu {
10258dc4d915SMark Wu     printf("%s\n", qmp_command_name(cmd));
10268dc4d915SMark Wu }
10278dc4d915SMark Wu 
split_list(const gchar * str,const gchar * delim)10284bca81ceSMarc-André Lureau static GList *split_list(const gchar *str, const gchar *delim)
102923b42894SMarc-André Lureau {
103023b42894SMarc-André Lureau     GList *list = NULL;
10314bca81ceSMarc-André Lureau     int i;
10324bca81ceSMarc-André Lureau     gchar **strv;
103323b42894SMarc-André Lureau 
10344bca81ceSMarc-André Lureau     strv = g_strsplit(str, delim, -1);
10354bca81ceSMarc-André Lureau     for (i = 0; strv[i]; i++) {
10364bca81ceSMarc-André Lureau         list = g_list_prepend(list, strv[i]);
103723b42894SMarc-André Lureau     }
10384bca81ceSMarc-André Lureau     g_free(strv);
103923b42894SMarc-André Lureau 
104023b42894SMarc-André Lureau     return list;
104123b42894SMarc-André Lureau }
104223b42894SMarc-André Lureau 
config_load(GAConfig * config,const char * confpath,bool required)1043f8bf2347SDaniel P. Berrangé static void config_load(GAConfig *config, const char *confpath, bool required)
10447a406694SMarc-André Lureau {
1045e236d060SMarc-André Lureau     GError *gerr = NULL;
1046e236d060SMarc-André Lureau     GKeyFile *keyfile;
1047e236d060SMarc-André Lureau 
1048e236d060SMarc-André Lureau     /* read system config */
1049e236d060SMarc-André Lureau     keyfile = g_key_file_new();
1050f8bf2347SDaniel P. Berrangé     if (!g_key_file_load_from_file(keyfile, confpath, 0, &gerr)) {
1051e236d060SMarc-André Lureau         goto end;
1052e236d060SMarc-André Lureau     }
1053e236d060SMarc-André Lureau     if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) {
1054e236d060SMarc-André Lureau         config->daemonize =
1055e236d060SMarc-André Lureau             g_key_file_get_boolean(keyfile, "general", "daemon", &gerr);
1056e236d060SMarc-André Lureau     }
1057e236d060SMarc-André Lureau     if (g_key_file_has_key(keyfile, "general", "method", NULL)) {
1058e236d060SMarc-André Lureau         config->method =
1059e236d060SMarc-André Lureau             g_key_file_get_string(keyfile, "general", "method", &gerr);
1060e236d060SMarc-André Lureau     }
1061e236d060SMarc-André Lureau     if (g_key_file_has_key(keyfile, "general", "path", NULL)) {
1062e236d060SMarc-André Lureau         config->channel_path =
1063e236d060SMarc-André Lureau             g_key_file_get_string(keyfile, "general", "path", &gerr);
1064e236d060SMarc-André Lureau     }
1065e236d060SMarc-André Lureau     if (g_key_file_has_key(keyfile, "general", "logfile", NULL)) {
1066e236d060SMarc-André Lureau         config->log_filepath =
1067e236d060SMarc-André Lureau             g_key_file_get_string(keyfile, "general", "logfile", &gerr);
1068e236d060SMarc-André Lureau     }
1069e236d060SMarc-André Lureau     if (g_key_file_has_key(keyfile, "general", "pidfile", NULL)) {
1070e236d060SMarc-André Lureau         config->pid_filepath =
1071e236d060SMarc-André Lureau             g_key_file_get_string(keyfile, "general", "pidfile", &gerr);
1072e236d060SMarc-André Lureau     }
1073e236d060SMarc-André Lureau #ifdef CONFIG_FSFREEZE
1074e236d060SMarc-André Lureau     if (g_key_file_has_key(keyfile, "general", "fsfreeze-hook", NULL)) {
1075e236d060SMarc-André Lureau         config->fsfreeze_hook =
1076e236d060SMarc-André Lureau             g_key_file_get_string(keyfile,
1077e236d060SMarc-André Lureau                                   "general", "fsfreeze-hook", &gerr);
1078e236d060SMarc-André Lureau     }
1079e236d060SMarc-André Lureau #endif
1080e236d060SMarc-André Lureau     if (g_key_file_has_key(keyfile, "general", "statedir", NULL)) {
1081e236d060SMarc-André Lureau         config->state_dir =
1082e236d060SMarc-André Lureau             g_key_file_get_string(keyfile, "general", "statedir", &gerr);
1083e236d060SMarc-André Lureau     }
1084e236d060SMarc-André Lureau     if (g_key_file_has_key(keyfile, "general", "verbose", NULL) &&
1085e236d060SMarc-André Lureau         g_key_file_get_boolean(keyfile, "general", "verbose", &gerr)) {
1086e236d060SMarc-André Lureau         /* enable all log levels */
1087e236d060SMarc-André Lureau         config->log_level = G_LOG_LEVEL_MASK;
1088e236d060SMarc-André Lureau     }
1089d951fadaSMichael Roth     if (g_key_file_has_key(keyfile, "general", "retry-path", NULL)) {
1090d951fadaSMichael Roth         config->retry_path =
1091d951fadaSMichael Roth             g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr);
1092d951fadaSMichael Roth     }
1093582a098eSThomas Huth 
10948909fc17SDaniel P. Berrangé     if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL)) {
1095e236d060SMarc-André Lureau         config->bliststr =
10968909fc17SDaniel P. Berrangé             g_key_file_get_string(keyfile, "general", "block-rpcs", &gerr);
10970e4ef702SThomas Huth         config->blockedrpcs = g_list_concat(config->blockedrpcs,
1098e236d060SMarc-André Lureau                                           split_list(config->bliststr, ","));
1099e236d060SMarc-André Lureau     }
1100133789e1SKonstantin Kostiuk     if (g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) {
1101133789e1SKonstantin Kostiuk         config->aliststr =
1102133789e1SKonstantin Kostiuk             g_key_file_get_string(keyfile, "general", "allow-rpcs", &gerr);
1103133789e1SKonstantin Kostiuk         config->allowedrpcs = g_list_concat(config->allowedrpcs,
1104133789e1SKonstantin Kostiuk                                           split_list(config->aliststr, ","));
1105133789e1SKonstantin Kostiuk     }
1106133789e1SKonstantin Kostiuk 
1107e236d060SMarc-André Lureau end:
1108e236d060SMarc-André Lureau     g_key_file_free(keyfile);
1109f8bf2347SDaniel P. Berrangé     if (gerr && (required ||
1110f8bf2347SDaniel P. Berrangé                  !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT))) {
1111e236d060SMarc-André Lureau         g_critical("error loading configuration from path: %s, %s",
1112f8bf2347SDaniel P. Berrangé                    confpath, gerr->message);
1113e236d060SMarc-André Lureau         exit(EXIT_FAILURE);
1114e236d060SMarc-André Lureau     }
1115e236d060SMarc-André Lureau     g_clear_error(&gerr);
1116e236d060SMarc-André Lureau }
1117e236d060SMarc-André Lureau 
list_join(GList * list,const gchar separator)1118aeadcbb6SMarc-André Lureau static gchar *list_join(GList *list, const gchar separator)
1119aeadcbb6SMarc-André Lureau {
1120aeadcbb6SMarc-André Lureau     GString *str = g_string_new("");
1121aeadcbb6SMarc-André Lureau 
1122aeadcbb6SMarc-André Lureau     while (list) {
1123aeadcbb6SMarc-André Lureau         str = g_string_append(str, (gchar *)list->data);
1124aeadcbb6SMarc-André Lureau         list = g_list_next(list);
1125aeadcbb6SMarc-André Lureau         if (list) {
1126aeadcbb6SMarc-André Lureau             str = g_string_append_c(str, separator);
1127aeadcbb6SMarc-André Lureau         }
1128aeadcbb6SMarc-André Lureau     }
1129aeadcbb6SMarc-André Lureau 
1130aeadcbb6SMarc-André Lureau     return g_string_free(str, FALSE);
1131aeadcbb6SMarc-André Lureau }
1132aeadcbb6SMarc-André Lureau 
config_dump(GAConfig * config)1133aeadcbb6SMarc-André Lureau static void config_dump(GAConfig *config)
1134aeadcbb6SMarc-André Lureau {
1135aeadcbb6SMarc-André Lureau     GError *error = NULL;
1136aeadcbb6SMarc-André Lureau     GKeyFile *keyfile;
1137aeadcbb6SMarc-André Lureau     gchar *tmp;
1138aeadcbb6SMarc-André Lureau 
1139aeadcbb6SMarc-André Lureau     keyfile = g_key_file_new();
1140aeadcbb6SMarc-André Lureau     g_assert(keyfile);
1141aeadcbb6SMarc-André Lureau 
1142aeadcbb6SMarc-André Lureau     g_key_file_set_boolean(keyfile, "general", "daemon", config->daemonize);
1143aeadcbb6SMarc-André Lureau     g_key_file_set_string(keyfile, "general", "method", config->method);
114426de2296SStefan Hajnoczi     if (config->channel_path) {
1145aeadcbb6SMarc-André Lureau         g_key_file_set_string(keyfile, "general", "path", config->channel_path);
114626de2296SStefan Hajnoczi     }
1147aeadcbb6SMarc-André Lureau     if (config->log_filepath) {
1148aeadcbb6SMarc-André Lureau         g_key_file_set_string(keyfile, "general", "logfile",
1149aeadcbb6SMarc-André Lureau                               config->log_filepath);
1150aeadcbb6SMarc-André Lureau     }
1151aeadcbb6SMarc-André Lureau     g_key_file_set_string(keyfile, "general", "pidfile", config->pid_filepath);
1152aeadcbb6SMarc-André Lureau #ifdef CONFIG_FSFREEZE
1153aeadcbb6SMarc-André Lureau     if (config->fsfreeze_hook) {
1154aeadcbb6SMarc-André Lureau         g_key_file_set_string(keyfile, "general", "fsfreeze-hook",
1155aeadcbb6SMarc-André Lureau                               config->fsfreeze_hook);
1156aeadcbb6SMarc-André Lureau     }
1157aeadcbb6SMarc-André Lureau #endif
1158aeadcbb6SMarc-André Lureau     g_key_file_set_string(keyfile, "general", "statedir", config->state_dir);
1159aeadcbb6SMarc-André Lureau     g_key_file_set_boolean(keyfile, "general", "verbose",
1160aeadcbb6SMarc-André Lureau                            config->log_level == G_LOG_LEVEL_MASK);
1161d951fadaSMichael Roth     g_key_file_set_boolean(keyfile, "general", "retry-path",
1162d951fadaSMichael Roth                            config->retry_path);
11630e4ef702SThomas Huth     tmp = list_join(config->blockedrpcs, ',');
1164582a098eSThomas Huth     g_key_file_set_string(keyfile, "general", "block-rpcs", tmp);
1165aeadcbb6SMarc-André Lureau     g_free(tmp);
1166133789e1SKonstantin Kostiuk     tmp = list_join(config->allowedrpcs, ',');
1167133789e1SKonstantin Kostiuk     g_key_file_set_string(keyfile, "general", "allow-rpcs", tmp);
1168133789e1SKonstantin Kostiuk     g_free(tmp);
1169aeadcbb6SMarc-André Lureau 
1170aeadcbb6SMarc-André Lureau     tmp = g_key_file_to_data(keyfile, NULL, &error);
1171cbcd9ba1SMarc-André Lureau     if (error) {
1172cbcd9ba1SMarc-André Lureau         g_critical("Failed to dump keyfile: %s", error->message);
1173cbcd9ba1SMarc-André Lureau         g_clear_error(&error);
1174cbcd9ba1SMarc-André Lureau     } else {
1175aeadcbb6SMarc-André Lureau         printf("%s", tmp);
1176cbcd9ba1SMarc-André Lureau     }
1177aeadcbb6SMarc-André Lureau 
1178aeadcbb6SMarc-André Lureau     g_free(tmp);
1179aeadcbb6SMarc-André Lureau     g_key_file_free(keyfile);
1180aeadcbb6SMarc-André Lureau }
1181aeadcbb6SMarc-André Lureau 
config_parse(GAConfig * config,int argc,char ** argv)1182e236d060SMarc-André Lureau static void config_parse(GAConfig *config, int argc, char **argv)
1183e236d060SMarc-André Lureau {
1184f8bf2347SDaniel P. Berrangé     const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr";
11857a406694SMarc-André Lureau     int opt_ind = 0, ch;
11862870dc34SPaolo Bonzini     const struct option lopt[] = {
11872870dc34SPaolo Bonzini         { "help", 0, NULL, 'h' },
11882870dc34SPaolo Bonzini         { "version", 0, NULL, 'V' },
1189f8bf2347SDaniel P. Berrangé         { "config", 1, NULL, 'c' },
1190aeadcbb6SMarc-André Lureau         { "dump-conf", 0, NULL, 'D' },
11912870dc34SPaolo Bonzini         { "logfile", 1, NULL, 'l' },
11922870dc34SPaolo Bonzini         { "pidfile", 1, NULL, 'f' },
1193ec0f694cSTomoki Sekiyama #ifdef CONFIG_FSFREEZE
1194ec0f694cSTomoki Sekiyama         { "fsfreeze-hook", 2, NULL, 'F' },
1195ec0f694cSTomoki Sekiyama #endif
11962870dc34SPaolo Bonzini         { "verbose", 0, NULL, 'v' },
11972870dc34SPaolo Bonzini         { "method", 1, NULL, 'm' },
11982870dc34SPaolo Bonzini         { "path", 1, NULL, 'p' },
11992870dc34SPaolo Bonzini         { "daemonize", 0, NULL, 'd' },
1200582a098eSThomas Huth         { "block-rpcs", 1, NULL, 'b' },
1201133789e1SKonstantin Kostiuk         { "allow-rpcs", 1, NULL, 'a' },
12022870dc34SPaolo Bonzini #ifdef _WIN32
12032870dc34SPaolo Bonzini         { "service", 1, NULL, 's' },
12042870dc34SPaolo Bonzini #endif
12052870dc34SPaolo Bonzini         { "statedir", 1, NULL, 't' },
1206d951fadaSMichael Roth         { "retry-path", 0, NULL, 'r' },
12072870dc34SPaolo Bonzini         { NULL, 0, NULL, 0 }
12082870dc34SPaolo Bonzini     };
1209f8bf2347SDaniel P. Berrangé     g_autofree char *confpath = g_strdup(g_getenv("QGA_CONF")) ?:
1210f8bf2347SDaniel P. Berrangé         get_relocated_path(QGA_CONF_DEFAULT);
1211f8bf2347SDaniel P. Berrangé     bool confrequired = false;
1212f8bf2347SDaniel P. Berrangé 
1213f8bf2347SDaniel P. Berrangé     while ((ch = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
1214f8bf2347SDaniel P. Berrangé         switch (ch) {
1215f8bf2347SDaniel P. Berrangé         case 'c':
1216f8bf2347SDaniel P. Berrangé             g_free(confpath);
1217f8bf2347SDaniel P. Berrangé             confpath = g_strdup(optarg);
1218f8bf2347SDaniel P. Berrangé             confrequired = true;
1219f8bf2347SDaniel P. Berrangé             break;
1220f8bf2347SDaniel P. Berrangé         default:
1221f8bf2347SDaniel P. Berrangé             break;
1222f8bf2347SDaniel P. Berrangé         }
1223f8bf2347SDaniel P. Berrangé     }
1224f8bf2347SDaniel P. Berrangé 
1225f8bf2347SDaniel P. Berrangé     config_load(config, confpath, confrequired);
1226f8bf2347SDaniel P. Berrangé 
1227f8bf2347SDaniel P. Berrangé     /* Reset for second pass */
1228f8bf2347SDaniel P. Berrangé     optind = 1;
12292870dc34SPaolo Bonzini 
12302870dc34SPaolo Bonzini     while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
12312870dc34SPaolo Bonzini         switch (ch) {
12322870dc34SPaolo Bonzini         case 'm':
1233e236d060SMarc-André Lureau             g_free(config->method);
12347a406694SMarc-André Lureau             config->method = g_strdup(optarg);
12352870dc34SPaolo Bonzini             break;
12362870dc34SPaolo Bonzini         case 'p':
1237e236d060SMarc-André Lureau             g_free(config->channel_path);
12387a406694SMarc-André Lureau             config->channel_path = g_strdup(optarg);
12392870dc34SPaolo Bonzini             break;
12402870dc34SPaolo Bonzini         case 'l':
1241e236d060SMarc-André Lureau             g_free(config->log_filepath);
12427a406694SMarc-André Lureau             config->log_filepath = g_strdup(optarg);
12432870dc34SPaolo Bonzini             break;
12442870dc34SPaolo Bonzini         case 'f':
1245e236d060SMarc-André Lureau             g_free(config->pid_filepath);
12467a406694SMarc-André Lureau             config->pid_filepath = g_strdup(optarg);
12472870dc34SPaolo Bonzini             break;
1248ec0f694cSTomoki Sekiyama #ifdef CONFIG_FSFREEZE
1249ec0f694cSTomoki Sekiyama         case 'F':
1250e236d060SMarc-André Lureau             g_free(config->fsfreeze_hook);
1251a9eacf8bSPaolo Bonzini             config->fsfreeze_hook = optarg ? g_strdup(optarg) : get_relocated_path(QGA_FSFREEZE_HOOK_DEFAULT);
1252ec0f694cSTomoki Sekiyama             break;
1253ec0f694cSTomoki Sekiyama #endif
12542870dc34SPaolo Bonzini         case 't':
1255e236d060SMarc-André Lureau             g_free(config->state_dir);
12567a406694SMarc-André Lureau             config->state_dir = g_strdup(optarg);
12572870dc34SPaolo Bonzini             break;
12582870dc34SPaolo Bonzini         case 'v':
12592870dc34SPaolo Bonzini             /* enable all log levels */
12607a406694SMarc-André Lureau             config->log_level = G_LOG_LEVEL_MASK;
12612870dc34SPaolo Bonzini             break;
12622870dc34SPaolo Bonzini         case 'V':
12632870dc34SPaolo Bonzini             printf("QEMU Guest Agent %s\n", QEMU_VERSION);
1264c6c84523SMarc-André Lureau             exit(EXIT_SUCCESS);
12652870dc34SPaolo Bonzini         case 'd':
12667a406694SMarc-André Lureau             config->daemonize = 1;
12672870dc34SPaolo Bonzini             break;
1268aeadcbb6SMarc-André Lureau         case 'D':
1269aeadcbb6SMarc-André Lureau             config->dumpconf = 1;
1270aeadcbb6SMarc-André Lureau             break;
1271d951fadaSMichael Roth         case 'r':
1272d951fadaSMichael Roth             config->retry_path = true;
1273d951fadaSMichael Roth             break;
12742870dc34SPaolo Bonzini         case 'b': {
12752870dc34SPaolo Bonzini             if (is_help_option(optarg)) {
12761527badbSMarkus Armbruster                 qmp_for_each_command(&ga_commands, ga_print_cmd, NULL);
1277c6c84523SMarc-André Lureau                 exit(EXIT_SUCCESS);
12782870dc34SPaolo Bonzini             }
12790e4ef702SThomas Huth             config->blockedrpcs = g_list_concat(config->blockedrpcs,
12807a406694SMarc-André Lureau                                                 split_list(optarg, ","));
1281133789e1SKonstantin Kostiuk             break;
1282133789e1SKonstantin Kostiuk         }
1283133789e1SKonstantin Kostiuk         case 'a': {
1284133789e1SKonstantin Kostiuk             if (is_help_option(optarg)) {
1285133789e1SKonstantin Kostiuk                 qmp_for_each_command(&ga_commands, ga_print_cmd, NULL);
1286133789e1SKonstantin Kostiuk                 exit(EXIT_SUCCESS);
1287133789e1SKonstantin Kostiuk             }
1288133789e1SKonstantin Kostiuk             config->allowedrpcs = g_list_concat(config->allowedrpcs,
1289133789e1SKonstantin Kostiuk                                                 split_list(optarg, ","));
12902870dc34SPaolo Bonzini             break;
12912870dc34SPaolo Bonzini         }
12922870dc34SPaolo Bonzini #ifdef _WIN32
12932870dc34SPaolo Bonzini         case 's':
12947a406694SMarc-André Lureau             config->service = optarg;
12957a406694SMarc-André Lureau             if (strcmp(config->service, "install") == 0) {
1296f311f2c2STomoki Sekiyama                 if (ga_install_vss_provider()) {
1297c6c84523SMarc-André Lureau                     exit(EXIT_FAILURE);
1298f311f2c2STomoki Sekiyama                 }
12997a406694SMarc-André Lureau                 if (ga_install_service(config->channel_path,
13007a406694SMarc-André Lureau                                        config->log_filepath, config->state_dir)) {
1301c6c84523SMarc-André Lureau                     exit(EXIT_FAILURE);
1302f311f2c2STomoki Sekiyama                 }
1303c6c84523SMarc-André Lureau                 exit(EXIT_SUCCESS);
13047a406694SMarc-André Lureau             } else if (strcmp(config->service, "uninstall") == 0) {
1305f311f2c2STomoki Sekiyama                 ga_uninstall_vss_provider();
1306c6c84523SMarc-André Lureau                 exit(ga_uninstall_service());
13077a406694SMarc-André Lureau             } else if (strcmp(config->service, "vss-install") == 0) {
13085e031072SYossi Hindin                 if (ga_install_vss_provider()) {
1309c6c84523SMarc-André Lureau                     exit(EXIT_FAILURE);
13105e031072SYossi Hindin                 }
1311c6c84523SMarc-André Lureau                 exit(EXIT_SUCCESS);
13127a406694SMarc-André Lureau             } else if (strcmp(config->service, "vss-uninstall") == 0) {
13135e031072SYossi Hindin                 ga_uninstall_vss_provider();
1314c6c84523SMarc-André Lureau                 exit(EXIT_SUCCESS);
13152870dc34SPaolo Bonzini             } else {
13162870dc34SPaolo Bonzini                 printf("Unknown service command.\n");
1317c6c84523SMarc-André Lureau                 exit(EXIT_FAILURE);
13182870dc34SPaolo Bonzini             }
13192870dc34SPaolo Bonzini             break;
13202870dc34SPaolo Bonzini #endif
13212870dc34SPaolo Bonzini         case 'h':
13222870dc34SPaolo Bonzini             usage(argv[0]);
1323c6c84523SMarc-André Lureau             exit(EXIT_SUCCESS);
13242870dc34SPaolo Bonzini         case '?':
13252870dc34SPaolo Bonzini             g_print("Unknown option, try '%s --help' for more information.\n",
13262870dc34SPaolo Bonzini                     argv[0]);
1327c6c84523SMarc-André Lureau             exit(EXIT_FAILURE);
13282870dc34SPaolo Bonzini         }
13292870dc34SPaolo Bonzini     }
13302e38d990SMarc-André Lureau }
13312e38d990SMarc-André Lureau 
config_free(GAConfig * config)13327a406694SMarc-André Lureau static void config_free(GAConfig *config)
13337a406694SMarc-André Lureau {
13347a406694SMarc-André Lureau     g_free(config->method);
13357a406694SMarc-André Lureau     g_free(config->log_filepath);
13367a406694SMarc-André Lureau     g_free(config->pid_filepath);
13377a406694SMarc-André Lureau     g_free(config->state_dir);
13387a406694SMarc-André Lureau     g_free(config->channel_path);
1339e236d060SMarc-André Lureau     g_free(config->bliststr);
1340133789e1SKonstantin Kostiuk     g_free(config->aliststr);
13417a406694SMarc-André Lureau #ifdef CONFIG_FSFREEZE
13427a406694SMarc-André Lureau     g_free(config->fsfreeze_hook);
13437a406694SMarc-André Lureau #endif
13440e4ef702SThomas Huth     g_list_free_full(config->blockedrpcs, g_free);
1345133789e1SKonstantin Kostiuk     g_list_free_full(config->allowedrpcs, g_free);
13467a406694SMarc-André Lureau     g_free(config);
13477a406694SMarc-André Lureau }
13487a406694SMarc-André Lureau 
check_is_frozen(GAState * s)1349e3d31039SMarc-André Lureau static bool check_is_frozen(GAState *s)
1350e3d31039SMarc-André Lureau {
1351e3d31039SMarc-André Lureau #ifndef _WIN32
1352e3d31039SMarc-André Lureau     /* check if a previous instance of qemu-ga exited with filesystems' state
1353e3d31039SMarc-André Lureau      * marked as frozen. this could be a stale value (a non-qemu-ga process
1354e3d31039SMarc-André Lureau      * or reboot may have since unfrozen them), but better to require an
135501dc0651SMichael Tokarev      * unneeded unfreeze than to risk hanging on start-up
1356e3d31039SMarc-André Lureau      */
1357e3d31039SMarc-André Lureau     struct stat st;
1358e3d31039SMarc-André Lureau     if (stat(s->state_filepath_isfrozen, &st) == -1) {
1359e3d31039SMarc-André Lureau         /* it's okay if the file doesn't exist, but if we can't access for
1360e3d31039SMarc-André Lureau          * some other reason, such as permissions, there's a configuration
1361e3d31039SMarc-André Lureau          * that needs to be addressed. so just bail now before we get into
1362e3d31039SMarc-André Lureau          * more trouble later
1363e3d31039SMarc-André Lureau          */
1364e3d31039SMarc-André Lureau         if (errno != ENOENT) {
1365e3d31039SMarc-André Lureau             g_critical("unable to access state file at path %s: %s",
1366e3d31039SMarc-André Lureau                        s->state_filepath_isfrozen, strerror(errno));
1367e3d31039SMarc-André Lureau             return EXIT_FAILURE;
1368e3d31039SMarc-André Lureau         }
1369e3d31039SMarc-André Lureau     } else {
1370e3d31039SMarc-André Lureau         g_warning("previous instance appears to have exited with frozen"
1371e3d31039SMarc-André Lureau                   " filesystems. deferring logging/pidfile creation and"
1372e3d31039SMarc-André Lureau                   " disabling non-fsfreeze-safe commands until"
1373e3d31039SMarc-André Lureau                   " guest-fsfreeze-thaw is issued, or filesystems are"
1374e3d31039SMarc-André Lureau                   " manually unfrozen and the file %s is removed",
1375e3d31039SMarc-André Lureau                   s->state_filepath_isfrozen);
1376e3d31039SMarc-André Lureau         return true;
1377e3d31039SMarc-André Lureau     }
1378e3d31039SMarc-André Lureau #endif
1379e3d31039SMarc-André Lureau     return false;
1380e3d31039SMarc-André Lureau }
1381e3d31039SMarc-André Lureau 
initialize_agent(GAConfig * config,int socket_activation)13820f4d3a49SMichael Roth static GAState *initialize_agent(GAConfig *config, int socket_activation)
1383e3d31039SMarc-André Lureau {
138450d5b3c4SMichael Roth     GAState *s = g_new0(GAState, 1);
138550d5b3c4SMichael Roth 
138650d5b3c4SMichael Roth     g_assert(ga_state == NULL);
138750d5b3c4SMichael Roth 
138850d5b3c4SMichael Roth     s->log_level = config->log_level;
138950d5b3c4SMichael Roth     s->log_file = stderr;
139050d5b3c4SMichael Roth #ifdef CONFIG_FSFREEZE
139150d5b3c4SMichael Roth     s->fsfreeze_hook = config->fsfreeze_hook;
139250d5b3c4SMichael Roth #endif
139350d5b3c4SMichael Roth     s->pstate_filepath = g_strdup_printf("%s/qga.state", config->state_dir);
139450d5b3c4SMichael Roth     s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen",
139550d5b3c4SMichael Roth                                                  config->state_dir);
139650d5b3c4SMichael Roth     s->frozen = check_is_frozen(s);
1397e3d31039SMarc-André Lureau 
1398e3d31039SMarc-André Lureau     g_log_set_default_handler(ga_log, s);
1399e3d31039SMarc-André Lureau     g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
1400e3d31039SMarc-André Lureau     ga_enable_logging(s);
1401e3d31039SMarc-André Lureau 
1402323f3a8fSKonstantin Kostiuk     g_debug("Guest agent version %s started", QEMU_FULL_VERSION);
1403323f3a8fSKonstantin Kostiuk 
1404e3d31039SMarc-André Lureau #ifdef _WIN32
1405f9f0e617SAndrey Drobyshev     s->event_log = RegisterEventSource(NULL, "qemu-ga");
1406f9f0e617SAndrey Drobyshev     if (!s->event_log) {
1407f9f0e617SAndrey Drobyshev         g_autofree gchar *errmsg = g_win32_error_message(GetLastError());
1408f9f0e617SAndrey Drobyshev         g_critical("unable to register event source: %s", errmsg);
1409f9f0e617SAndrey Drobyshev         return NULL;
1410f9f0e617SAndrey Drobyshev     }
1411f9f0e617SAndrey Drobyshev 
1412e3d31039SMarc-André Lureau     /* On win32 the state directory is application specific (be it the default
1413e3d31039SMarc-André Lureau      * or a user override). We got past the command line parsing; let's create
1414e3d31039SMarc-André Lureau      * the directory (with any intermediate directories). If we run into an
1415e3d31039SMarc-André Lureau      * error later on, we won't try to clean up the directory, it is considered
1416e3d31039SMarc-André Lureau      * persistent.
1417e3d31039SMarc-André Lureau      */
1418e3d31039SMarc-André Lureau     if (g_mkdir_with_parents(config->state_dir, S_IRWXU) == -1) {
1419e3d31039SMarc-André Lureau         g_critical("unable to create (an ancestor of) the state directory"
1420e3d31039SMarc-André Lureau                    " '%s': %s", config->state_dir, strerror(errno));
142150d5b3c4SMichael Roth         return NULL;
1422e3d31039SMarc-André Lureau     }
1423b81837f0SDaniel P. Berrangé 
1424b81837f0SDaniel P. Berrangé     if (!vss_init(true)) {
1425b81837f0SDaniel P. Berrangé         g_debug("vss_init failed, vss commands will not function");
1426b81837f0SDaniel P. Berrangé     }
1427e3d31039SMarc-André Lureau #endif
1428e3d31039SMarc-André Lureau 
1429e3d31039SMarc-André Lureau     if (ga_is_frozen(s)) {
1430e3d31039SMarc-André Lureau         if (config->daemonize) {
1431e3d31039SMarc-André Lureau             /* delay opening/locking of pidfile till filesystems are unfrozen */
1432e3d31039SMarc-André Lureau             s->deferred_options.pid_filepath = config->pid_filepath;
1433e3d31039SMarc-André Lureau             become_daemon(NULL);
1434e3d31039SMarc-André Lureau         }
1435e3d31039SMarc-André Lureau         if (config->log_filepath) {
1436e3d31039SMarc-André Lureau             /* delay opening the log file till filesystems are unfrozen */
1437e3d31039SMarc-André Lureau             s->deferred_options.log_filepath = config->log_filepath;
1438e3d31039SMarc-André Lureau         }
1439e3d31039SMarc-André Lureau         ga_disable_logging(s);
1440e3d31039SMarc-André Lureau     } else {
1441e3d31039SMarc-André Lureau         if (config->daemonize) {
1442e3d31039SMarc-André Lureau             become_daemon(config->pid_filepath);
1443e3d31039SMarc-André Lureau         }
1444e3d31039SMarc-André Lureau         if (config->log_filepath) {
1445e3d31039SMarc-André Lureau             FILE *log_file = ga_open_logfile(config->log_filepath);
1446e3d31039SMarc-André Lureau             if (!log_file) {
1447e3d31039SMarc-André Lureau                 g_critical("unable to open specified log file: %s",
1448e3d31039SMarc-André Lureau                            strerror(errno));
144950d5b3c4SMichael Roth                 return NULL;
1450e3d31039SMarc-André Lureau             }
1451e3d31039SMarc-André Lureau             s->log_file = log_file;
1452e3d31039SMarc-André Lureau         }
1453e3d31039SMarc-André Lureau     }
1454e3d31039SMarc-André Lureau 
1455e3d31039SMarc-André Lureau     /* load persistent state from disk */
1456e3d31039SMarc-André Lureau     if (!read_persistent_state(&s->pstate,
1457e3d31039SMarc-André Lureau                                s->pstate_filepath,
1458e3d31039SMarc-André Lureau                                ga_is_frozen(s))) {
1459e3d31039SMarc-André Lureau         g_critical("failed to load persistent state");
146050d5b3c4SMichael Roth         return NULL;
1461e3d31039SMarc-André Lureau     }
1462e3d31039SMarc-André Lureau 
1463e3d31039SMarc-André Lureau     s->command_state = ga_command_state_new();
1464e3d31039SMarc-André Lureau     ga_command_state_init(s, s->command_state);
1465e3d31039SMarc-André Lureau     ga_command_state_init_all(s->command_state);
146662815d85SMarkus Armbruster     json_message_parser_init(&s->parser, process_event, s, NULL);
1467f8837b37SPeng Hao 
1468e3d31039SMarc-André Lureau #ifndef _WIN32
1469e3d31039SMarc-André Lureau     if (!register_signal_handlers()) {
1470e3d31039SMarc-André Lureau         g_critical("failed to register signal handlers");
147150d5b3c4SMichael Roth         return NULL;
1472e3d31039SMarc-André Lureau     }
1473e3d31039SMarc-André Lureau #endif
1474e3d31039SMarc-André Lureau 
1475e3d31039SMarc-André Lureau     s->main_loop = g_main_loop_new(NULL, false);
147626de2296SStefan Hajnoczi 
14770f4d3a49SMichael Roth     s->config = config;
14780f4d3a49SMichael Roth     s->socket_activation = socket_activation;
1479b70d6afeSBishara AbuHattoum 
1480b70d6afeSBishara AbuHattoum #ifdef _WIN32
1481b70d6afeSBishara AbuHattoum     s->wakeup_event = CreateEvent(NULL, TRUE, FALSE, TEXT("WakeUp"));
1482b70d6afeSBishara AbuHattoum     if (s->wakeup_event == NULL) {
1483b70d6afeSBishara AbuHattoum         g_critical("CreateEvent failed");
1484b70d6afeSBishara AbuHattoum         return NULL;
1485b70d6afeSBishara AbuHattoum     }
1486b70d6afeSBishara AbuHattoum #endif
1487b70d6afeSBishara AbuHattoum 
14882e3b166cSDaniel P. Berrangé     ga_apply_command_filters(s);
14892e3b166cSDaniel P. Berrangé 
149050d5b3c4SMichael Roth     ga_state = s;
149150d5b3c4SMichael Roth     return s;
149250d5b3c4SMichael Roth }
149350d5b3c4SMichael Roth 
cleanup_agent(GAState * s)149450d5b3c4SMichael Roth static void cleanup_agent(GAState *s)
149550d5b3c4SMichael Roth {
1496b70d6afeSBishara AbuHattoum #ifdef _WIN32
1497b70d6afeSBishara AbuHattoum     CloseHandle(s->wakeup_event);
1498f9f0e617SAndrey Drobyshev     CloseHandle(s->event_log);
1499b70d6afeSBishara AbuHattoum #endif
150050d5b3c4SMichael Roth     if (s->command_state) {
150150d5b3c4SMichael Roth         ga_command_state_cleanup_all(s->command_state);
150250d5b3c4SMichael Roth         ga_command_state_free(s->command_state);
150350d5b3c4SMichael Roth         json_message_parser_destroy(&s->parser);
150450d5b3c4SMichael Roth     }
150550d5b3c4SMichael Roth     g_free(s->pstate_filepath);
150650d5b3c4SMichael Roth     g_free(s->state_filepath_isfrozen);
150750d5b3c4SMichael Roth     if (s->main_loop) {
150850d5b3c4SMichael Roth         g_main_loop_unref(s->main_loop);
150950d5b3c4SMichael Roth     }
151050d5b3c4SMichael Roth     g_free(s);
151150d5b3c4SMichael Roth     ga_state = NULL;
151250d5b3c4SMichael Roth }
151350d5b3c4SMichael Roth 
run_agent_once(GAState * s)1514d951fadaSMichael Roth static int run_agent_once(GAState *s)
151550d5b3c4SMichael Roth {
15160f4d3a49SMichael Roth     if (!channel_init(s, s->config->method, s->config->channel_path,
15170f4d3a49SMichael Roth                       s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) {
1518e3d31039SMarc-André Lureau         g_critical("failed to initialize guest agent channel");
1519e3d31039SMarc-André Lureau         return EXIT_FAILURE;
1520e3d31039SMarc-André Lureau     }
1521d88495a8SMichael Roth 
1522e3d31039SMarc-André Lureau     g_main_loop_run(ga_state->main_loop);
1523e3d31039SMarc-André Lureau 
1524d951fadaSMichael Roth     if (s->channel) {
1525d951fadaSMichael Roth         ga_channel_free(s->channel);
1526d951fadaSMichael Roth     }
1527d951fadaSMichael Roth 
1528e3d31039SMarc-André Lureau     return EXIT_SUCCESS;
1529e3d31039SMarc-André Lureau }
1530e3d31039SMarc-André Lureau 
wait_for_channel_availability(GAState * s)1531b70d6afeSBishara AbuHattoum static void wait_for_channel_availability(GAState *s)
1532b70d6afeSBishara AbuHattoum {
1533b70d6afeSBishara AbuHattoum     g_warning("waiting for channel path...");
1534b70d6afeSBishara AbuHattoum #ifndef _WIN32
1535b70d6afeSBishara AbuHattoum     sleep(QGA_RETRY_INTERVAL);
1536b70d6afeSBishara AbuHattoum #else
1537b70d6afeSBishara AbuHattoum     DWORD dwWaitResult;
1538b70d6afeSBishara AbuHattoum 
1539b70d6afeSBishara AbuHattoum     dwWaitResult = WaitForSingleObject(s->wakeup_event, INFINITE);
1540b70d6afeSBishara AbuHattoum 
1541b70d6afeSBishara AbuHattoum     switch (dwWaitResult) {
1542b70d6afeSBishara AbuHattoum     case WAIT_OBJECT_0:
1543b70d6afeSBishara AbuHattoum         break;
1544b70d6afeSBishara AbuHattoum     case WAIT_TIMEOUT:
1545b70d6afeSBishara AbuHattoum         break;
1546b70d6afeSBishara AbuHattoum     default:
1547b70d6afeSBishara AbuHattoum         g_critical("WaitForSingleObject failed");
1548b70d6afeSBishara AbuHattoum     }
1549b70d6afeSBishara AbuHattoum #endif
1550b70d6afeSBishara AbuHattoum }
1551b70d6afeSBishara AbuHattoum 
run_agent(GAState * s)1552d951fadaSMichael Roth static int run_agent(GAState *s)
1553d951fadaSMichael Roth {
1554d951fadaSMichael Roth     int ret = EXIT_SUCCESS;
1555d951fadaSMichael Roth 
1556d951fadaSMichael Roth     s->force_exit = false;
1557d951fadaSMichael Roth 
1558d951fadaSMichael Roth     do {
1559d951fadaSMichael Roth         ret = run_agent_once(s);
1560d951fadaSMichael Roth         if (s->config->retry_path && !s->force_exit) {
1561d951fadaSMichael Roth             g_warning("agent stopped unexpectedly, restarting...");
1562b70d6afeSBishara AbuHattoum             wait_for_channel_availability(s);
1563d951fadaSMichael Roth         }
1564d951fadaSMichael Roth     } while (s->config->retry_path && !s->force_exit);
1565d951fadaSMichael Roth 
1566d951fadaSMichael Roth     return ret;
1567d951fadaSMichael Roth }
1568d951fadaSMichael Roth 
stop_agent(GAState * s,bool requested)1569d951fadaSMichael Roth static void stop_agent(GAState *s, bool requested)
1570d951fadaSMichael Roth {
1571d951fadaSMichael Roth     if (!s->force_exit) {
1572d951fadaSMichael Roth         s->force_exit = requested;
1573d951fadaSMichael Roth     }
1574d951fadaSMichael Roth 
1575d951fadaSMichael Roth     if (g_main_loop_is_running(s->main_loop)) {
1576d951fadaSMichael Roth         g_main_loop_quit(s->main_loop);
1577d951fadaSMichael Roth     }
1578d951fadaSMichael Roth }
1579d951fadaSMichael Roth 
main(int argc,char ** argv)15807a406694SMarc-André Lureau int main(int argc, char **argv)
15817a406694SMarc-André Lureau {
1582e3d31039SMarc-André Lureau     int ret = EXIT_SUCCESS;
158350d5b3c4SMichael Roth     GAState *s;
1584e236d060SMarc-André Lureau     GAConfig *config = g_new0(GAConfig, 1);
158553fabd4bSPaolo Bonzini     int socket_activation;
15867a406694SMarc-André Lureau 
15876eaeae37SMarc-André Lureau     config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
15886eaeae37SMarc-André Lureau 
1589a9eacf8bSPaolo Bonzini     qemu_init_exec_dir(argv[0]);
15901527badbSMarkus Armbruster     qga_qmp_init_marshal(&ga_commands);
15917a406694SMarc-André Lureau 
15927a406694SMarc-André Lureau     init_dfl_pathnames();
1593e236d060SMarc-André Lureau     config_parse(config, argc, argv);
15947a406694SMarc-André Lureau 
15957a406694SMarc-André Lureau     if (config->pid_filepath == NULL) {
15967a406694SMarc-André Lureau         config->pid_filepath = g_strdup(dfl_pathnames.pidfile);
15977a406694SMarc-André Lureau     }
15987a406694SMarc-André Lureau 
15997a406694SMarc-André Lureau     if (config->state_dir == NULL) {
16007a406694SMarc-André Lureau         config->state_dir = g_strdup(dfl_pathnames.state_dir);
16012e38d990SMarc-André Lureau     }
16022e38d990SMarc-André Lureau 
1603ef8be554SMarc-André Lureau     if (config->method == NULL) {
1604ef8be554SMarc-André Lureau         config->method = g_strdup("virtio-serial");
1605ef8be554SMarc-André Lureau     }
1606ef8be554SMarc-André Lureau 
160753fabd4bSPaolo Bonzini     socket_activation = check_socket_activation();
160853fabd4bSPaolo Bonzini     if (socket_activation > 1) {
160953fabd4bSPaolo Bonzini         g_critical("qemu-ga only supports listening on one socket");
161053fabd4bSPaolo Bonzini         ret = EXIT_FAILURE;
161153fabd4bSPaolo Bonzini         goto end;
161253fabd4bSPaolo Bonzini     }
161353fabd4bSPaolo Bonzini     if (socket_activation) {
1614bd269ebcSMarkus Armbruster         SocketAddress *addr;
161526de2296SStefan Hajnoczi 
161626de2296SStefan Hajnoczi         g_free(config->method);
161726de2296SStefan Hajnoczi         g_free(config->channel_path);
161826de2296SStefan Hajnoczi         config->method = NULL;
161926de2296SStefan Hajnoczi         config->channel_path = NULL;
162026de2296SStefan Hajnoczi 
162153fabd4bSPaolo Bonzini         addr = socket_local_address(FIRST_SOCKET_ACTIVATION_FD, NULL);
162226de2296SStefan Hajnoczi         if (addr) {
1623bd269ebcSMarkus Armbruster             if (addr->type == SOCKET_ADDRESS_TYPE_UNIX) {
162426de2296SStefan Hajnoczi                 config->method = g_strdup("unix-listen");
1625bd269ebcSMarkus Armbruster             } else if (addr->type == SOCKET_ADDRESS_TYPE_VSOCK) {
162626de2296SStefan Hajnoczi                 config->method = g_strdup("vsock-listen");
162726de2296SStefan Hajnoczi             }
162826de2296SStefan Hajnoczi 
1629bd269ebcSMarkus Armbruster             qapi_free_SocketAddress(addr);
163026de2296SStefan Hajnoczi         }
163126de2296SStefan Hajnoczi 
163226de2296SStefan Hajnoczi         if (!config->method) {
163326de2296SStefan Hajnoczi             g_critical("unsupported listen fd type");
163426de2296SStefan Hajnoczi             ret = EXIT_FAILURE;
163526de2296SStefan Hajnoczi             goto end;
163626de2296SStefan Hajnoczi         }
163726de2296SStefan Hajnoczi     } else if (config->channel_path == NULL) {
1638ef8be554SMarc-André Lureau         if (strcmp(config->method, "virtio-serial") == 0) {
1639ef8be554SMarc-André Lureau             /* try the default path for the virtio-serial port */
1640ef8be554SMarc-André Lureau             config->channel_path = g_strdup(QGA_VIRTIO_PATH_DEFAULT);
1641ef8be554SMarc-André Lureau         } else if (strcmp(config->method, "isa-serial") == 0) {
1642ef8be554SMarc-André Lureau             /* try the default path for the serial port - COM1 */
1643ef8be554SMarc-André Lureau             config->channel_path = g_strdup(QGA_SERIAL_PATH_DEFAULT);
1644ef8be554SMarc-André Lureau         } else {
1645ef8be554SMarc-André Lureau             g_critical("must specify a path for this channel");
1646e3d31039SMarc-André Lureau             ret = EXIT_FAILURE;
1647e3d31039SMarc-André Lureau             goto end;
1648ef8be554SMarc-André Lureau         }
1649ef8be554SMarc-André Lureau     }
1650ef8be554SMarc-André Lureau 
1651aeadcbb6SMarc-André Lureau     if (config->dumpconf) {
1652aeadcbb6SMarc-André Lureau         config_dump(config);
1653aeadcbb6SMarc-André Lureau         goto end;
1654aeadcbb6SMarc-André Lureau     }
1655aeadcbb6SMarc-André Lureau 
16560f4d3a49SMichael Roth     s = initialize_agent(config, socket_activation);
165750d5b3c4SMichael Roth     if (!s) {
165850d5b3c4SMichael Roth         g_critical("error initializing guest agent");
165950d5b3c4SMichael Roth         goto end;
166050d5b3c4SMichael Roth     }
1661d88495a8SMichael Roth 
1662d88495a8SMichael Roth #ifdef _WIN32
1663d88495a8SMichael Roth     if (config->daemonize) {
1664d88495a8SMichael Roth         SERVICE_TABLE_ENTRY service_table[] = {
1665d88495a8SMichael Roth             { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } };
1666d88495a8SMichael Roth         StartServiceCtrlDispatcher(service_table);
1667d88495a8SMichael Roth     } else {
16680f4d3a49SMichael Roth         ret = run_agent(s);
1669d88495a8SMichael Roth     }
1670d88495a8SMichael Roth #else
1671d88495a8SMichael Roth     ret = run_agent(s);
1672d88495a8SMichael Roth #endif
1673d88495a8SMichael Roth 
167450d5b3c4SMichael Roth     cleanup_agent(s);
16752870dc34SPaolo Bonzini 
1676e3d31039SMarc-André Lureau end:
16777a406694SMarc-André Lureau     if (config->daemonize) {
16787a406694SMarc-André Lureau         unlink(config->pid_filepath);
16792870dc34SPaolo Bonzini     }
16802e38d990SMarc-André Lureau 
16817a406694SMarc-André Lureau     config_free(config);
16822e38d990SMarc-André Lureau 
1683e3d31039SMarc-André Lureau     return ret;
16842870dc34SPaolo Bonzini }
1685