root / qemu-ga.c @ 2542bfd5
History | View | Annotate | Download (17.5 kB)
1 | 48ff7a62 | Michael Roth | /*
|
---|---|---|---|
2 | 48ff7a62 | Michael Roth | * QEMU Guest Agent
|
3 | 48ff7a62 | Michael Roth | *
|
4 | 48ff7a62 | Michael Roth | * Copyright IBM Corp. 2011
|
5 | 48ff7a62 | Michael Roth | *
|
6 | 48ff7a62 | Michael Roth | * Authors:
|
7 | 48ff7a62 | Michael Roth | * Adam Litke <aglitke@linux.vnet.ibm.com>
|
8 | 48ff7a62 | Michael Roth | * Michael Roth <mdroth@linux.vnet.ibm.com>
|
9 | 48ff7a62 | Michael Roth | *
|
10 | 48ff7a62 | Michael Roth | * This work is licensed under the terms of the GNU GPL, version 2 or later.
|
11 | 48ff7a62 | Michael Roth | * See the COPYING file in the top-level directory.
|
12 | 48ff7a62 | Michael Roth | */
|
13 | 48ff7a62 | Michael Roth | #include <stdlib.h> |
14 | 48ff7a62 | Michael Roth | #include <stdio.h> |
15 | 48ff7a62 | Michael Roth | #include <stdbool.h> |
16 | 48ff7a62 | Michael Roth | #include <glib.h> |
17 | 48ff7a62 | Michael Roth | #include <getopt.h> |
18 | 48ff7a62 | Michael Roth | #include <termios.h> |
19 | 48ff7a62 | Michael Roth | #include <syslog.h> |
20 | 48ff7a62 | Michael Roth | #include "qemu_socket.h" |
21 | 48ff7a62 | Michael Roth | #include "json-streamer.h" |
22 | 48ff7a62 | Michael Roth | #include "json-parser.h" |
23 | 48ff7a62 | Michael Roth | #include "qint.h" |
24 | 48ff7a62 | Michael Roth | #include "qjson.h" |
25 | 48ff7a62 | Michael Roth | #include "qga/guest-agent-core.h" |
26 | 48ff7a62 | Michael Roth | #include "module.h" |
27 | 48ff7a62 | Michael Roth | #include "signal.h" |
28 | 48ff7a62 | Michael Roth | #include "qerror.h" |
29 | 48ff7a62 | Michael Roth | #include "error_int.h" |
30 | 48ff7a62 | Michael Roth | |
31 | 48ff7a62 | Michael Roth | #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" |
32 | 48ff7a62 | Michael Roth | #define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid" |
33 | 48ff7a62 | Michael Roth | #define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */ |
34 | 48ff7a62 | Michael Roth | #define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */ |
35 | 48ff7a62 | Michael Roth | |
36 | 48ff7a62 | Michael Roth | struct GAState {
|
37 | 48ff7a62 | Michael Roth | JSONMessageParser parser; |
38 | 48ff7a62 | Michael Roth | GMainLoop *main_loop; |
39 | 48ff7a62 | Michael Roth | GIOChannel *conn_channel; |
40 | 48ff7a62 | Michael Roth | GIOChannel *listen_channel; |
41 | 48ff7a62 | Michael Roth | const char *path; |
42 | 48ff7a62 | Michael Roth | const char *method; |
43 | 48ff7a62 | Michael Roth | bool virtio; /* fastpath to check for virtio to deal with poll() quirks */ |
44 | 48ff7a62 | Michael Roth | GACommandState *command_state; |
45 | 48ff7a62 | Michael Roth | GLogLevelFlags log_level; |
46 | 48ff7a62 | Michael Roth | FILE *log_file; |
47 | 48ff7a62 | Michael Roth | bool logging_enabled;
|
48 | 48ff7a62 | Michael Roth | }; |
49 | 48ff7a62 | Michael Roth | |
50 | 48ff7a62 | Michael Roth | static struct GAState *ga_state; |
51 | 48ff7a62 | Michael Roth | |
52 | 48ff7a62 | Michael Roth | static void quit_handler(int sig) |
53 | 48ff7a62 | Michael Roth | { |
54 | 2542bfd5 | Stefan Weil | g_debug("received signal num %d, quitting", sig);
|
55 | 48ff7a62 | Michael Roth | |
56 | 48ff7a62 | Michael Roth | if (g_main_loop_is_running(ga_state->main_loop)) {
|
57 | 48ff7a62 | Michael Roth | g_main_loop_quit(ga_state->main_loop); |
58 | 48ff7a62 | Michael Roth | } |
59 | 48ff7a62 | Michael Roth | } |
60 | 48ff7a62 | Michael Roth | |
61 | 48ff7a62 | Michael Roth | static void register_signal_handlers(void) |
62 | 48ff7a62 | Michael Roth | { |
63 | 48ff7a62 | Michael Roth | struct sigaction sigact;
|
64 | 48ff7a62 | Michael Roth | int ret;
|
65 | 48ff7a62 | Michael Roth | |
66 | 48ff7a62 | Michael Roth | memset(&sigact, 0, sizeof(struct sigaction)); |
67 | 48ff7a62 | Michael Roth | sigact.sa_handler = quit_handler; |
68 | 48ff7a62 | Michael Roth | |
69 | 48ff7a62 | Michael Roth | ret = sigaction(SIGINT, &sigact, NULL);
|
70 | 48ff7a62 | Michael Roth | if (ret == -1) { |
71 | 48ff7a62 | Michael Roth | g_error("error configuring signal handler: %s", strerror(errno));
|
72 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
73 | 48ff7a62 | Michael Roth | } |
74 | 48ff7a62 | Michael Roth | ret = sigaction(SIGTERM, &sigact, NULL);
|
75 | 48ff7a62 | Michael Roth | if (ret == -1) { |
76 | 48ff7a62 | Michael Roth | g_error("error configuring signal handler: %s", strerror(errno));
|
77 | 48ff7a62 | Michael Roth | } |
78 | 48ff7a62 | Michael Roth | } |
79 | 48ff7a62 | Michael Roth | |
80 | 48ff7a62 | Michael Roth | static void usage(const char *cmd) |
81 | 48ff7a62 | Michael Roth | { |
82 | 48ff7a62 | Michael Roth | printf( |
83 | 48ff7a62 | Michael Roth | "Usage: %s -c <channel_opts>\n"
|
84 | 48ff7a62 | Michael Roth | "QEMU Guest Agent %s\n"
|
85 | 48ff7a62 | Michael Roth | "\n"
|
86 | 48ff7a62 | Michael Roth | " -m, --method transport method: one of unix-listen, virtio-serial, or\n"
|
87 | 48ff7a62 | Michael Roth | " isa-serial (virtio-serial is the default)\n"
|
88 | 48ff7a62 | Michael Roth | " -p, --path device/socket path (%s is the default for virtio-serial)\n"
|
89 | 48ff7a62 | Michael Roth | " -l, --logfile set logfile path, logs to stderr by default\n"
|
90 | 48ff7a62 | Michael Roth | " -f, --pidfile specify pidfile (default is %s)\n"
|
91 | 48ff7a62 | Michael Roth | " -v, --verbose log extra debugging information\n"
|
92 | 48ff7a62 | Michael Roth | " -V, --version print version information and exit\n"
|
93 | 48ff7a62 | Michael Roth | " -d, --daemonize become a daemon\n"
|
94 | 48ff7a62 | Michael Roth | " -h, --help display this help and exit\n"
|
95 | 48ff7a62 | Michael Roth | "\n"
|
96 | 48ff7a62 | Michael Roth | "Report bugs to <mdroth@linux.vnet.ibm.com>\n"
|
97 | 48ff7a62 | Michael Roth | , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT); |
98 | 48ff7a62 | Michael Roth | } |
99 | 48ff7a62 | Michael Roth | |
100 | 48ff7a62 | Michael Roth | static void conn_channel_close(GAState *s); |
101 | 48ff7a62 | Michael Roth | |
102 | 48ff7a62 | Michael Roth | static const char *ga_log_level_str(GLogLevelFlags level) |
103 | 48ff7a62 | Michael Roth | { |
104 | 48ff7a62 | Michael Roth | switch (level & G_LOG_LEVEL_MASK) {
|
105 | 48ff7a62 | Michael Roth | case G_LOG_LEVEL_ERROR:
|
106 | 48ff7a62 | Michael Roth | return "error"; |
107 | 48ff7a62 | Michael Roth | case G_LOG_LEVEL_CRITICAL:
|
108 | 48ff7a62 | Michael Roth | return "critical"; |
109 | 48ff7a62 | Michael Roth | case G_LOG_LEVEL_WARNING:
|
110 | 48ff7a62 | Michael Roth | return "warning"; |
111 | 48ff7a62 | Michael Roth | case G_LOG_LEVEL_MESSAGE:
|
112 | 48ff7a62 | Michael Roth | return "message"; |
113 | 48ff7a62 | Michael Roth | case G_LOG_LEVEL_INFO:
|
114 | 48ff7a62 | Michael Roth | return "info"; |
115 | 48ff7a62 | Michael Roth | case G_LOG_LEVEL_DEBUG:
|
116 | 48ff7a62 | Michael Roth | return "debug"; |
117 | 48ff7a62 | Michael Roth | default:
|
118 | 48ff7a62 | Michael Roth | return "user"; |
119 | 48ff7a62 | Michael Roth | } |
120 | 48ff7a62 | Michael Roth | } |
121 | 48ff7a62 | Michael Roth | |
122 | 48ff7a62 | Michael Roth | bool ga_logging_enabled(GAState *s)
|
123 | 48ff7a62 | Michael Roth | { |
124 | 48ff7a62 | Michael Roth | return s->logging_enabled;
|
125 | 48ff7a62 | Michael Roth | } |
126 | 48ff7a62 | Michael Roth | |
127 | 48ff7a62 | Michael Roth | void ga_disable_logging(GAState *s)
|
128 | 48ff7a62 | Michael Roth | { |
129 | 48ff7a62 | Michael Roth | s->logging_enabled = false;
|
130 | 48ff7a62 | Michael Roth | } |
131 | 48ff7a62 | Michael Roth | |
132 | 48ff7a62 | Michael Roth | void ga_enable_logging(GAState *s)
|
133 | 48ff7a62 | Michael Roth | { |
134 | 48ff7a62 | Michael Roth | s->logging_enabled = true;
|
135 | 48ff7a62 | Michael Roth | } |
136 | 48ff7a62 | Michael Roth | |
137 | 48ff7a62 | Michael Roth | static void ga_log(const gchar *domain, GLogLevelFlags level, |
138 | 48ff7a62 | Michael Roth | const gchar *msg, gpointer opaque)
|
139 | 48ff7a62 | Michael Roth | { |
140 | 48ff7a62 | Michael Roth | GAState *s = opaque; |
141 | 48ff7a62 | Michael Roth | GTimeVal time; |
142 | 48ff7a62 | Michael Roth | const char *level_str = ga_log_level_str(level); |
143 | 48ff7a62 | Michael Roth | |
144 | 48ff7a62 | Michael Roth | if (!ga_logging_enabled(s)) {
|
145 | 48ff7a62 | Michael Roth | return;
|
146 | 48ff7a62 | Michael Roth | } |
147 | 48ff7a62 | Michael Roth | |
148 | 48ff7a62 | Michael Roth | level &= G_LOG_LEVEL_MASK; |
149 | 8f477478 | Michael Roth | if (domain && strcmp(domain, "syslog") == 0) { |
150 | 48ff7a62 | Michael Roth | syslog(LOG_INFO, "%s: %s", level_str, msg);
|
151 | 48ff7a62 | Michael Roth | } else if (level & s->log_level) { |
152 | 48ff7a62 | Michael Roth | g_get_current_time(&time); |
153 | 48ff7a62 | Michael Roth | fprintf(s->log_file, |
154 | 48ff7a62 | Michael Roth | "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
|
155 | 48ff7a62 | Michael Roth | fflush(s->log_file); |
156 | 48ff7a62 | Michael Roth | } |
157 | 48ff7a62 | Michael Roth | } |
158 | 48ff7a62 | Michael Roth | |
159 | 48ff7a62 | Michael Roth | static void become_daemon(const char *pidfile) |
160 | 48ff7a62 | Michael Roth | { |
161 | 48ff7a62 | Michael Roth | pid_t pid, sid; |
162 | 48ff7a62 | Michael Roth | int pidfd;
|
163 | 48ff7a62 | Michael Roth | char *pidstr = NULL; |
164 | 48ff7a62 | Michael Roth | |
165 | 48ff7a62 | Michael Roth | pid = fork(); |
166 | 48ff7a62 | Michael Roth | if (pid < 0) { |
167 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
168 | 48ff7a62 | Michael Roth | } |
169 | 48ff7a62 | Michael Roth | if (pid > 0) { |
170 | 48ff7a62 | Michael Roth | exit(EXIT_SUCCESS); |
171 | 48ff7a62 | Michael Roth | } |
172 | 48ff7a62 | Michael Roth | |
173 | 48ff7a62 | Michael Roth | pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR); |
174 | 48ff7a62 | Michael Roth | if (pidfd == -1) { |
175 | 48ff7a62 | Michael Roth | g_critical("Cannot create pid file, %s", strerror(errno));
|
176 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
177 | 48ff7a62 | Michael Roth | } |
178 | 48ff7a62 | Michael Roth | |
179 | 48ff7a62 | Michael Roth | if (asprintf(&pidstr, "%d", getpid()) == -1) { |
180 | 48ff7a62 | Michael Roth | g_critical("Cannot allocate memory");
|
181 | 48ff7a62 | Michael Roth | goto fail;
|
182 | 48ff7a62 | Michael Roth | } |
183 | 48ff7a62 | Michael Roth | if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
|
184 | 48ff7a62 | Michael Roth | free(pidstr); |
185 | 48ff7a62 | Michael Roth | g_critical("Failed to write pid file");
|
186 | 48ff7a62 | Michael Roth | goto fail;
|
187 | 48ff7a62 | Michael Roth | } |
188 | 48ff7a62 | Michael Roth | |
189 | 48ff7a62 | Michael Roth | umask(0);
|
190 | 48ff7a62 | Michael Roth | sid = setsid(); |
191 | 48ff7a62 | Michael Roth | if (sid < 0) { |
192 | 48ff7a62 | Michael Roth | goto fail;
|
193 | 48ff7a62 | Michael Roth | } |
194 | 48ff7a62 | Michael Roth | if ((chdir("/")) < 0) { |
195 | 48ff7a62 | Michael Roth | goto fail;
|
196 | 48ff7a62 | Michael Roth | } |
197 | 48ff7a62 | Michael Roth | |
198 | 48ff7a62 | Michael Roth | close(STDIN_FILENO); |
199 | 48ff7a62 | Michael Roth | close(STDOUT_FILENO); |
200 | 48ff7a62 | Michael Roth | close(STDERR_FILENO); |
201 | 48ff7a62 | Michael Roth | free(pidstr); |
202 | 48ff7a62 | Michael Roth | return;
|
203 | 48ff7a62 | Michael Roth | |
204 | 48ff7a62 | Michael Roth | fail:
|
205 | 48ff7a62 | Michael Roth | unlink(pidfile); |
206 | 48ff7a62 | Michael Roth | g_critical("failed to daemonize");
|
207 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
208 | 48ff7a62 | Michael Roth | } |
209 | 48ff7a62 | Michael Roth | |
210 | 48ff7a62 | Michael Roth | static int conn_channel_send_buf(GIOChannel *channel, const char *buf, |
211 | 48ff7a62 | Michael Roth | gsize count) |
212 | 48ff7a62 | Michael Roth | { |
213 | 48ff7a62 | Michael Roth | GError *err = NULL;
|
214 | 48ff7a62 | Michael Roth | gsize written = 0;
|
215 | 48ff7a62 | Michael Roth | GIOStatus status; |
216 | 48ff7a62 | Michael Roth | |
217 | 48ff7a62 | Michael Roth | while (count) {
|
218 | 48ff7a62 | Michael Roth | status = g_io_channel_write_chars(channel, buf, count, &written, &err); |
219 | 48ff7a62 | Michael Roth | g_debug("sending data, count: %d", (int)count); |
220 | 48ff7a62 | Michael Roth | if (err != NULL) { |
221 | 48ff7a62 | Michael Roth | g_warning("error sending newline: %s", err->message);
|
222 | 48ff7a62 | Michael Roth | return err->code;
|
223 | 48ff7a62 | Michael Roth | } |
224 | 48ff7a62 | Michael Roth | if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) {
|
225 | 48ff7a62 | Michael Roth | return -EPIPE;
|
226 | 48ff7a62 | Michael Roth | } |
227 | 48ff7a62 | Michael Roth | |
228 | 48ff7a62 | Michael Roth | if (status == G_IO_STATUS_NORMAL) {
|
229 | 48ff7a62 | Michael Roth | count -= written; |
230 | 48ff7a62 | Michael Roth | } |
231 | 48ff7a62 | Michael Roth | } |
232 | 48ff7a62 | Michael Roth | |
233 | 48ff7a62 | Michael Roth | return 0; |
234 | 48ff7a62 | Michael Roth | } |
235 | 48ff7a62 | Michael Roth | |
236 | 48ff7a62 | Michael Roth | static int conn_channel_send_payload(GIOChannel *channel, QObject *payload) |
237 | 48ff7a62 | Michael Roth | { |
238 | 48ff7a62 | Michael Roth | int ret = 0; |
239 | 48ff7a62 | Michael Roth | const char *buf; |
240 | 48ff7a62 | Michael Roth | QString *payload_qstr; |
241 | 48ff7a62 | Michael Roth | GError *err = NULL;
|
242 | 48ff7a62 | Michael Roth | |
243 | 48ff7a62 | Michael Roth | g_assert(payload && channel); |
244 | 48ff7a62 | Michael Roth | |
245 | 48ff7a62 | Michael Roth | payload_qstr = qobject_to_json(payload); |
246 | 48ff7a62 | Michael Roth | if (!payload_qstr) {
|
247 | 48ff7a62 | Michael Roth | return -EINVAL;
|
248 | 48ff7a62 | Michael Roth | } |
249 | 48ff7a62 | Michael Roth | |
250 | 48ff7a62 | Michael Roth | qstring_append_chr(payload_qstr, '\n');
|
251 | 48ff7a62 | Michael Roth | buf = qstring_get_str(payload_qstr); |
252 | 48ff7a62 | Michael Roth | ret = conn_channel_send_buf(channel, buf, strlen(buf)); |
253 | 48ff7a62 | Michael Roth | if (ret) {
|
254 | 48ff7a62 | Michael Roth | goto out_free;
|
255 | 48ff7a62 | Michael Roth | } |
256 | 48ff7a62 | Michael Roth | |
257 | 48ff7a62 | Michael Roth | g_io_channel_flush(channel, &err); |
258 | 48ff7a62 | Michael Roth | if (err != NULL) { |
259 | 48ff7a62 | Michael Roth | g_warning("error flushing payload: %s", err->message);
|
260 | 48ff7a62 | Michael Roth | ret = err->code; |
261 | 48ff7a62 | Michael Roth | goto out_free;
|
262 | 48ff7a62 | Michael Roth | } |
263 | 48ff7a62 | Michael Roth | |
264 | 48ff7a62 | Michael Roth | out_free:
|
265 | 48ff7a62 | Michael Roth | QDECREF(payload_qstr); |
266 | 48ff7a62 | Michael Roth | if (err) {
|
267 | 48ff7a62 | Michael Roth | g_error_free(err); |
268 | 48ff7a62 | Michael Roth | } |
269 | 48ff7a62 | Michael Roth | return ret;
|
270 | 48ff7a62 | Michael Roth | } |
271 | 48ff7a62 | Michael Roth | |
272 | 48ff7a62 | Michael Roth | static void process_command(GAState *s, QDict *req) |
273 | 48ff7a62 | Michael Roth | { |
274 | 48ff7a62 | Michael Roth | QObject *rsp = NULL;
|
275 | 48ff7a62 | Michael Roth | int ret;
|
276 | 48ff7a62 | Michael Roth | |
277 | 48ff7a62 | Michael Roth | g_assert(req); |
278 | 48ff7a62 | Michael Roth | g_debug("processing command");
|
279 | 48ff7a62 | Michael Roth | rsp = qmp_dispatch(QOBJECT(req)); |
280 | 48ff7a62 | Michael Roth | if (rsp) {
|
281 | 48ff7a62 | Michael Roth | ret = conn_channel_send_payload(s->conn_channel, rsp); |
282 | 48ff7a62 | Michael Roth | if (ret) {
|
283 | 48ff7a62 | Michael Roth | g_warning("error sending payload: %s", strerror(ret));
|
284 | 48ff7a62 | Michael Roth | } |
285 | 48ff7a62 | Michael Roth | qobject_decref(rsp); |
286 | 48ff7a62 | Michael Roth | } else {
|
287 | 48ff7a62 | Michael Roth | g_warning("error getting response");
|
288 | 48ff7a62 | Michael Roth | } |
289 | 48ff7a62 | Michael Roth | } |
290 | 48ff7a62 | Michael Roth | |
291 | 48ff7a62 | Michael Roth | /* handle requests/control events coming in over the channel */
|
292 | 48ff7a62 | Michael Roth | static void process_event(JSONMessageParser *parser, QList *tokens) |
293 | 48ff7a62 | Michael Roth | { |
294 | 48ff7a62 | Michael Roth | GAState *s = container_of(parser, GAState, parser); |
295 | 48ff7a62 | Michael Roth | QObject *obj; |
296 | 48ff7a62 | Michael Roth | QDict *qdict; |
297 | 48ff7a62 | Michael Roth | Error *err = NULL;
|
298 | 48ff7a62 | Michael Roth | int ret;
|
299 | 48ff7a62 | Michael Roth | |
300 | 48ff7a62 | Michael Roth | g_assert(s && parser); |
301 | 48ff7a62 | Michael Roth | |
302 | 48ff7a62 | Michael Roth | g_debug("process_event: called");
|
303 | 48ff7a62 | Michael Roth | obj = json_parser_parse_err(tokens, NULL, &err);
|
304 | 48ff7a62 | Michael Roth | if (err || !obj || qobject_type(obj) != QTYPE_QDICT) {
|
305 | 48ff7a62 | Michael Roth | qobject_decref(obj); |
306 | 48ff7a62 | Michael Roth | qdict = qdict_new(); |
307 | 48ff7a62 | Michael Roth | if (!err) {
|
308 | 48ff7a62 | Michael Roth | g_warning("failed to parse event: unknown error");
|
309 | 48ff7a62 | Michael Roth | error_set(&err, QERR_JSON_PARSING); |
310 | 48ff7a62 | Michael Roth | } else {
|
311 | 48ff7a62 | Michael Roth | g_warning("failed to parse event: %s", error_get_pretty(err));
|
312 | 48ff7a62 | Michael Roth | } |
313 | 48ff7a62 | Michael Roth | qdict_put_obj(qdict, "error", error_get_qobject(err));
|
314 | 48ff7a62 | Michael Roth | error_free(err); |
315 | 48ff7a62 | Michael Roth | } else {
|
316 | 48ff7a62 | Michael Roth | qdict = qobject_to_qdict(obj); |
317 | 48ff7a62 | Michael Roth | } |
318 | 48ff7a62 | Michael Roth | |
319 | 48ff7a62 | Michael Roth | g_assert(qdict); |
320 | 48ff7a62 | Michael Roth | |
321 | 48ff7a62 | Michael Roth | /* handle host->guest commands */
|
322 | 48ff7a62 | Michael Roth | if (qdict_haskey(qdict, "execute")) { |
323 | 48ff7a62 | Michael Roth | process_command(s, qdict); |
324 | 48ff7a62 | Michael Roth | } else {
|
325 | 48ff7a62 | Michael Roth | if (!qdict_haskey(qdict, "error")) { |
326 | 48ff7a62 | Michael Roth | QDECREF(qdict); |
327 | 48ff7a62 | Michael Roth | qdict = qdict_new(); |
328 | 48ff7a62 | Michael Roth | g_warning("unrecognized payload format");
|
329 | 48ff7a62 | Michael Roth | error_set(&err, QERR_UNSUPPORTED); |
330 | 48ff7a62 | Michael Roth | qdict_put_obj(qdict, "error", error_get_qobject(err));
|
331 | 48ff7a62 | Michael Roth | error_free(err); |
332 | 48ff7a62 | Michael Roth | } |
333 | 48ff7a62 | Michael Roth | ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict)); |
334 | 48ff7a62 | Michael Roth | if (ret) {
|
335 | 48ff7a62 | Michael Roth | g_warning("error sending payload: %s", strerror(ret));
|
336 | 48ff7a62 | Michael Roth | } |
337 | 48ff7a62 | Michael Roth | } |
338 | 48ff7a62 | Michael Roth | |
339 | 48ff7a62 | Michael Roth | QDECREF(qdict); |
340 | 48ff7a62 | Michael Roth | } |
341 | 48ff7a62 | Michael Roth | |
342 | 48ff7a62 | Michael Roth | static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition,
|
343 | 48ff7a62 | Michael Roth | gpointer data) |
344 | 48ff7a62 | Michael Roth | { |
345 | 48ff7a62 | Michael Roth | GAState *s = data; |
346 | 48ff7a62 | Michael Roth | gchar buf[1024];
|
347 | 48ff7a62 | Michael Roth | gsize count; |
348 | 48ff7a62 | Michael Roth | GError *err = NULL;
|
349 | 48ff7a62 | Michael Roth | memset(buf, 0, 1024); |
350 | 48ff7a62 | Michael Roth | GIOStatus status = g_io_channel_read_chars(channel, buf, 1024,
|
351 | 48ff7a62 | Michael Roth | &count, &err); |
352 | 48ff7a62 | Michael Roth | if (err != NULL) { |
353 | 48ff7a62 | Michael Roth | g_warning("error reading channel: %s", err->message);
|
354 | 48ff7a62 | Michael Roth | conn_channel_close(s); |
355 | 48ff7a62 | Michael Roth | g_error_free(err); |
356 | 48ff7a62 | Michael Roth | return false; |
357 | 48ff7a62 | Michael Roth | } |
358 | 48ff7a62 | Michael Roth | switch (status) {
|
359 | 48ff7a62 | Michael Roth | case G_IO_STATUS_ERROR:
|
360 | 48ff7a62 | Michael Roth | g_warning("problem");
|
361 | 48ff7a62 | Michael Roth | return false; |
362 | 48ff7a62 | Michael Roth | case G_IO_STATUS_NORMAL:
|
363 | 48ff7a62 | Michael Roth | g_debug("read data, count: %d, data: %s", (int)count, buf); |
364 | 48ff7a62 | Michael Roth | json_message_parser_feed(&s->parser, (char *)buf, (int)count); |
365 | 48ff7a62 | Michael Roth | case G_IO_STATUS_AGAIN:
|
366 | 48ff7a62 | Michael Roth | /* virtio causes us to spin here when no process is attached to
|
367 | 48ff7a62 | Michael Roth | * host-side chardev. sleep a bit to mitigate this
|
368 | 48ff7a62 | Michael Roth | */
|
369 | 48ff7a62 | Michael Roth | if (s->virtio) {
|
370 | 48ff7a62 | Michael Roth | usleep(100*1000); |
371 | 48ff7a62 | Michael Roth | } |
372 | 48ff7a62 | Michael Roth | return true; |
373 | 48ff7a62 | Michael Roth | case G_IO_STATUS_EOF:
|
374 | 48ff7a62 | Michael Roth | g_debug("received EOF");
|
375 | 48ff7a62 | Michael Roth | conn_channel_close(s); |
376 | 48ff7a62 | Michael Roth | if (s->virtio) {
|
377 | 48ff7a62 | Michael Roth | return true; |
378 | 48ff7a62 | Michael Roth | } |
379 | 48ff7a62 | Michael Roth | return false; |
380 | 48ff7a62 | Michael Roth | default:
|
381 | 48ff7a62 | Michael Roth | g_warning("unknown channel read status, closing");
|
382 | 48ff7a62 | Michael Roth | conn_channel_close(s); |
383 | 48ff7a62 | Michael Roth | return false; |
384 | 48ff7a62 | Michael Roth | } |
385 | 48ff7a62 | Michael Roth | return true; |
386 | 48ff7a62 | Michael Roth | } |
387 | 48ff7a62 | Michael Roth | |
388 | 48ff7a62 | Michael Roth | static int conn_channel_add(GAState *s, int fd) |
389 | 48ff7a62 | Michael Roth | { |
390 | 48ff7a62 | Michael Roth | GIOChannel *conn_channel; |
391 | 48ff7a62 | Michael Roth | GError *err = NULL;
|
392 | 48ff7a62 | Michael Roth | |
393 | 48ff7a62 | Michael Roth | g_assert(s && !s->conn_channel); |
394 | 48ff7a62 | Michael Roth | conn_channel = g_io_channel_unix_new(fd); |
395 | 48ff7a62 | Michael Roth | g_assert(conn_channel); |
396 | 48ff7a62 | Michael Roth | g_io_channel_set_encoding(conn_channel, NULL, &err);
|
397 | 48ff7a62 | Michael Roth | if (err != NULL) { |
398 | 48ff7a62 | Michael Roth | g_warning("error setting channel encoding to binary");
|
399 | 48ff7a62 | Michael Roth | g_error_free(err); |
400 | 48ff7a62 | Michael Roth | return -1; |
401 | 48ff7a62 | Michael Roth | } |
402 | 48ff7a62 | Michael Roth | g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP, |
403 | 48ff7a62 | Michael Roth | conn_channel_read, s); |
404 | 48ff7a62 | Michael Roth | s->conn_channel = conn_channel; |
405 | 48ff7a62 | Michael Roth | return 0; |
406 | 48ff7a62 | Michael Roth | } |
407 | 48ff7a62 | Michael Roth | |
408 | 48ff7a62 | Michael Roth | static gboolean listen_channel_accept(GIOChannel *channel,
|
409 | 48ff7a62 | Michael Roth | GIOCondition condition, gpointer data) |
410 | 48ff7a62 | Michael Roth | { |
411 | 48ff7a62 | Michael Roth | GAState *s = data; |
412 | 48ff7a62 | Michael Roth | g_assert(channel != NULL);
|
413 | 1fc7bd4a | Anthony Liguori | int ret, conn_fd;
|
414 | 48ff7a62 | Michael Roth | bool accepted = false; |
415 | 1fc7bd4a | Anthony Liguori | struct sockaddr_un addr;
|
416 | 1fc7bd4a | Anthony Liguori | socklen_t addrlen = sizeof(addr);
|
417 | 48ff7a62 | Michael Roth | |
418 | 1fc7bd4a | Anthony Liguori | conn_fd = qemu_accept(g_io_channel_unix_get_fd(s->listen_channel), |
419 | 1fc7bd4a | Anthony Liguori | (struct sockaddr *)&addr, &addrlen);
|
420 | 1fc7bd4a | Anthony Liguori | if (conn_fd == -1) { |
421 | 1fc7bd4a | Anthony Liguori | g_warning("error converting fd to gsocket: %s", strerror(errno));
|
422 | 48ff7a62 | Michael Roth | goto out;
|
423 | 48ff7a62 | Michael Roth | } |
424 | 1fc7bd4a | Anthony Liguori | fcntl(conn_fd, F_SETFL, O_NONBLOCK); |
425 | 1fc7bd4a | Anthony Liguori | ret = conn_channel_add(s, conn_fd); |
426 | 48ff7a62 | Michael Roth | if (ret) {
|
427 | 48ff7a62 | Michael Roth | g_warning("error setting up connection");
|
428 | 48ff7a62 | Michael Roth | goto out;
|
429 | 48ff7a62 | Michael Roth | } |
430 | 48ff7a62 | Michael Roth | accepted = true;
|
431 | 48ff7a62 | Michael Roth | |
432 | 48ff7a62 | Michael Roth | out:
|
433 | 48ff7a62 | Michael Roth | /* only accept 1 connection at a time */
|
434 | 48ff7a62 | Michael Roth | return !accepted;
|
435 | 48ff7a62 | Michael Roth | } |
436 | 48ff7a62 | Michael Roth | |
437 | 48ff7a62 | Michael Roth | /* start polling for readable events on listen fd, new==true
|
438 | 48ff7a62 | Michael Roth | * indicates we should use the existing s->listen_channel
|
439 | 48ff7a62 | Michael Roth | */
|
440 | 48ff7a62 | Michael Roth | static int listen_channel_add(GAState *s, int listen_fd, bool new) |
441 | 48ff7a62 | Michael Roth | { |
442 | 48ff7a62 | Michael Roth | if (new) {
|
443 | 48ff7a62 | Michael Roth | s->listen_channel = g_io_channel_unix_new(listen_fd); |
444 | 48ff7a62 | Michael Roth | } |
445 | 48ff7a62 | Michael Roth | g_io_add_watch(s->listen_channel, G_IO_IN, |
446 | 48ff7a62 | Michael Roth | listen_channel_accept, s); |
447 | 48ff7a62 | Michael Roth | return 0; |
448 | 48ff7a62 | Michael Roth | } |
449 | 48ff7a62 | Michael Roth | |
450 | 48ff7a62 | Michael Roth | /* cleanup state for closed connection/session, start accepting new
|
451 | 48ff7a62 | Michael Roth | * connections if we're in listening mode
|
452 | 48ff7a62 | Michael Roth | */
|
453 | 48ff7a62 | Michael Roth | static void conn_channel_close(GAState *s) |
454 | 48ff7a62 | Michael Roth | { |
455 | 48ff7a62 | Michael Roth | if (strcmp(s->method, "unix-listen") == 0) { |
456 | 48ff7a62 | Michael Roth | g_io_channel_shutdown(s->conn_channel, true, NULL); |
457 | 48ff7a62 | Michael Roth | listen_channel_add(s, 0, false); |
458 | 48ff7a62 | Michael Roth | } else if (strcmp(s->method, "virtio-serial") == 0) { |
459 | 48ff7a62 | Michael Roth | /* we spin on EOF for virtio-serial, so back off a bit. also,
|
460 | 48ff7a62 | Michael Roth | * dont close the connection in this case, it'll resume normal
|
461 | 48ff7a62 | Michael Roth | * operation when another process connects to host chardev
|
462 | 48ff7a62 | Michael Roth | */
|
463 | 48ff7a62 | Michael Roth | usleep(100*1000); |
464 | 48ff7a62 | Michael Roth | goto out_noclose;
|
465 | 48ff7a62 | Michael Roth | } |
466 | 48ff7a62 | Michael Roth | g_io_channel_unref(s->conn_channel); |
467 | 48ff7a62 | Michael Roth | s->conn_channel = NULL;
|
468 | 48ff7a62 | Michael Roth | out_noclose:
|
469 | 48ff7a62 | Michael Roth | return;
|
470 | 48ff7a62 | Michael Roth | } |
471 | 48ff7a62 | Michael Roth | |
472 | 48ff7a62 | Michael Roth | static void init_guest_agent(GAState *s) |
473 | 48ff7a62 | Michael Roth | { |
474 | 48ff7a62 | Michael Roth | struct termios tio;
|
475 | 48ff7a62 | Michael Roth | int ret, fd;
|
476 | 48ff7a62 | Michael Roth | |
477 | 48ff7a62 | Michael Roth | if (s->method == NULL) { |
478 | 48ff7a62 | Michael Roth | /* try virtio-serial as our default */
|
479 | 48ff7a62 | Michael Roth | s->method = "virtio-serial";
|
480 | 48ff7a62 | Michael Roth | } |
481 | 48ff7a62 | Michael Roth | |
482 | 48ff7a62 | Michael Roth | if (s->path == NULL) { |
483 | 48ff7a62 | Michael Roth | if (strcmp(s->method, "virtio-serial") != 0) { |
484 | 48ff7a62 | Michael Roth | g_critical("must specify a path for this channel");
|
485 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
486 | 48ff7a62 | Michael Roth | } |
487 | 48ff7a62 | Michael Roth | /* try the default path for the virtio-serial port */
|
488 | 48ff7a62 | Michael Roth | s->path = QGA_VIRTIO_PATH_DEFAULT; |
489 | 48ff7a62 | Michael Roth | } |
490 | 48ff7a62 | Michael Roth | |
491 | 48ff7a62 | Michael Roth | if (strcmp(s->method, "virtio-serial") == 0) { |
492 | 48ff7a62 | Michael Roth | s->virtio = true;
|
493 | 48ff7a62 | Michael Roth | fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC); |
494 | 48ff7a62 | Michael Roth | if (fd == -1) { |
495 | 48ff7a62 | Michael Roth | g_critical("error opening channel: %s", strerror(errno));
|
496 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
497 | 48ff7a62 | Michael Roth | } |
498 | 48ff7a62 | Michael Roth | ret = conn_channel_add(s, fd); |
499 | 48ff7a62 | Michael Roth | if (ret) {
|
500 | 48ff7a62 | Michael Roth | g_critical("error adding channel to main loop");
|
501 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
502 | 48ff7a62 | Michael Roth | } |
503 | 48ff7a62 | Michael Roth | } else if (strcmp(s->method, "isa-serial") == 0) { |
504 | 48ff7a62 | Michael Roth | fd = qemu_open(s->path, O_RDWR | O_NOCTTY); |
505 | 48ff7a62 | Michael Roth | if (fd == -1) { |
506 | 48ff7a62 | Michael Roth | g_critical("error opening channel: %s", strerror(errno));
|
507 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
508 | 48ff7a62 | Michael Roth | } |
509 | 48ff7a62 | Michael Roth | tcgetattr(fd, &tio); |
510 | 48ff7a62 | Michael Roth | /* set up serial port for non-canonical, dumb byte streaming */
|
511 | 48ff7a62 | Michael Roth | tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | |
512 | 48ff7a62 | Michael Roth | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | |
513 | 48ff7a62 | Michael Roth | IMAXBEL); |
514 | 48ff7a62 | Michael Roth | tio.c_oflag = 0;
|
515 | 48ff7a62 | Michael Roth | tio.c_lflag = 0;
|
516 | 48ff7a62 | Michael Roth | tio.c_cflag |= QGA_BAUDRATE_DEFAULT; |
517 | 48ff7a62 | Michael Roth | /* 1 available byte min or reads will block (we'll set non-blocking
|
518 | 48ff7a62 | Michael Roth | * elsewhere, else we have to deal with read()=0 instead)
|
519 | 48ff7a62 | Michael Roth | */
|
520 | 48ff7a62 | Michael Roth | tio.c_cc[VMIN] = 1;
|
521 | 48ff7a62 | Michael Roth | tio.c_cc[VTIME] = 0;
|
522 | 48ff7a62 | Michael Roth | /* flush everything waiting for read/xmit, it's garbage at this point */
|
523 | 48ff7a62 | Michael Roth | tcflush(fd, TCIFLUSH); |
524 | 48ff7a62 | Michael Roth | tcsetattr(fd, TCSANOW, &tio); |
525 | 48ff7a62 | Michael Roth | ret = conn_channel_add(s, fd); |
526 | 48ff7a62 | Michael Roth | if (ret) {
|
527 | 48ff7a62 | Michael Roth | g_error("error adding channel to main loop");
|
528 | 48ff7a62 | Michael Roth | } |
529 | 48ff7a62 | Michael Roth | } else if (strcmp(s->method, "unix-listen") == 0) { |
530 | 48ff7a62 | Michael Roth | fd = unix_listen(s->path, NULL, strlen(s->path));
|
531 | 48ff7a62 | Michael Roth | if (fd == -1) { |
532 | 48ff7a62 | Michael Roth | g_critical("error opening path: %s", strerror(errno));
|
533 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
534 | 48ff7a62 | Michael Roth | } |
535 | 48ff7a62 | Michael Roth | ret = listen_channel_add(s, fd, true);
|
536 | 48ff7a62 | Michael Roth | if (ret) {
|
537 | 48ff7a62 | Michael Roth | g_critical("error binding/listening to specified socket");
|
538 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
539 | 48ff7a62 | Michael Roth | } |
540 | 48ff7a62 | Michael Roth | } else {
|
541 | 48ff7a62 | Michael Roth | g_critical("unsupported channel method/type: %s", s->method);
|
542 | 48ff7a62 | Michael Roth | exit(EXIT_FAILURE); |
543 | 48ff7a62 | Michael Roth | } |
544 | 48ff7a62 | Michael Roth | |
545 | 48ff7a62 | Michael Roth | json_message_parser_init(&s->parser, process_event); |
546 | 48ff7a62 | Michael Roth | s->main_loop = g_main_loop_new(NULL, false); |
547 | 48ff7a62 | Michael Roth | } |
548 | 48ff7a62 | Michael Roth | |
549 | 48ff7a62 | Michael Roth | int main(int argc, char **argv) |
550 | 48ff7a62 | Michael Roth | { |
551 | 48ff7a62 | Michael Roth | const char *sopt = "hVvdm:p:l:f:"; |
552 | 48ff7a62 | Michael Roth | const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT; |
553 | 48ff7a62 | Michael Roth | const struct option lopt[] = { |
554 | 48ff7a62 | Michael Roth | { "help", 0, NULL, 'h' }, |
555 | 48ff7a62 | Michael Roth | { "version", 0, NULL, 'V' }, |
556 | 48ff7a62 | Michael Roth | { "logfile", 0, NULL, 'l' }, |
557 | 48ff7a62 | Michael Roth | { "pidfile", 0, NULL, 'f' }, |
558 | 48ff7a62 | Michael Roth | { "verbose", 0, NULL, 'v' }, |
559 | 48ff7a62 | Michael Roth | { "method", 0, NULL, 'm' }, |
560 | 48ff7a62 | Michael Roth | { "path", 0, NULL, 'p' }, |
561 | 48ff7a62 | Michael Roth | { "daemonize", 0, NULL, 'd' }, |
562 | 48ff7a62 | Michael Roth | { NULL, 0, NULL, 0 } |
563 | 48ff7a62 | Michael Roth | }; |
564 | 48ff7a62 | Michael Roth | int opt_ind = 0, ch, daemonize = 0; |
565 | 48ff7a62 | Michael Roth | GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; |
566 | 48ff7a62 | Michael Roth | FILE *log_file = stderr; |
567 | 48ff7a62 | Michael Roth | GAState *s; |
568 | 48ff7a62 | Michael Roth | |
569 | 48ff7a62 | Michael Roth | while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { |
570 | 48ff7a62 | Michael Roth | switch (ch) {
|
571 | 48ff7a62 | Michael Roth | case 'm': |
572 | 48ff7a62 | Michael Roth | method = optarg; |
573 | 48ff7a62 | Michael Roth | break;
|
574 | 48ff7a62 | Michael Roth | case 'p': |
575 | 48ff7a62 | Michael Roth | path = optarg; |
576 | 48ff7a62 | Michael Roth | break;
|
577 | 48ff7a62 | Michael Roth | case 'l': |
578 | 48ff7a62 | Michael Roth | log_file = fopen(optarg, "a");
|
579 | 48ff7a62 | Michael Roth | if (!log_file) {
|
580 | 48ff7a62 | Michael Roth | g_critical("unable to open specified log file: %s",
|
581 | 48ff7a62 | Michael Roth | strerror(errno)); |
582 | 48ff7a62 | Michael Roth | return EXIT_FAILURE;
|
583 | 48ff7a62 | Michael Roth | } |
584 | 48ff7a62 | Michael Roth | break;
|
585 | 48ff7a62 | Michael Roth | case 'f': |
586 | 48ff7a62 | Michael Roth | pidfile = optarg; |
587 | 48ff7a62 | Michael Roth | break;
|
588 | 48ff7a62 | Michael Roth | case 'v': |
589 | 48ff7a62 | Michael Roth | /* enable all log levels */
|
590 | 48ff7a62 | Michael Roth | log_level = G_LOG_LEVEL_MASK; |
591 | 48ff7a62 | Michael Roth | break;
|
592 | 48ff7a62 | Michael Roth | case 'V': |
593 | 48ff7a62 | Michael Roth | printf("QEMU Guest Agent %s\n", QGA_VERSION);
|
594 | 48ff7a62 | Michael Roth | return 0; |
595 | 48ff7a62 | Michael Roth | case 'd': |
596 | 48ff7a62 | Michael Roth | daemonize = 1;
|
597 | 48ff7a62 | Michael Roth | break;
|
598 | 48ff7a62 | Michael Roth | case 'h': |
599 | 48ff7a62 | Michael Roth | usage(argv[0]);
|
600 | 48ff7a62 | Michael Roth | return 0; |
601 | 48ff7a62 | Michael Roth | case '?': |
602 | 48ff7a62 | Michael Roth | g_print("Unknown option, try '%s --help' for more information.\n",
|
603 | 48ff7a62 | Michael Roth | argv[0]);
|
604 | 48ff7a62 | Michael Roth | return EXIT_FAILURE;
|
605 | 48ff7a62 | Michael Roth | } |
606 | 48ff7a62 | Michael Roth | } |
607 | 48ff7a62 | Michael Roth | |
608 | 48ff7a62 | Michael Roth | if (daemonize) {
|
609 | 48ff7a62 | Michael Roth | g_debug("starting daemon");
|
610 | 48ff7a62 | Michael Roth | become_daemon(pidfile); |
611 | 48ff7a62 | Michael Roth | } |
612 | 48ff7a62 | Michael Roth | |
613 | 7267c094 | Anthony Liguori | s = g_malloc0(sizeof(GAState));
|
614 | 48ff7a62 | Michael Roth | s->conn_channel = NULL;
|
615 | 48ff7a62 | Michael Roth | s->path = path; |
616 | 48ff7a62 | Michael Roth | s->method = method; |
617 | 48ff7a62 | Michael Roth | s->log_file = log_file; |
618 | 48ff7a62 | Michael Roth | s->log_level = log_level; |
619 | 48ff7a62 | Michael Roth | g_log_set_default_handler(ga_log, s); |
620 | 48ff7a62 | Michael Roth | g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
|
621 | 48ff7a62 | Michael Roth | s->logging_enabled = true;
|
622 | e3d4d252 | Michael Roth | s->command_state = ga_command_state_new(); |
623 | e3d4d252 | Michael Roth | ga_command_state_init(s, s->command_state); |
624 | e3d4d252 | Michael Roth | ga_command_state_init_all(s->command_state); |
625 | 48ff7a62 | Michael Roth | ga_state = s; |
626 | 48ff7a62 | Michael Roth | |
627 | 48ff7a62 | Michael Roth | module_call_init(MODULE_INIT_QAPI); |
628 | 48ff7a62 | Michael Roth | init_guest_agent(ga_state); |
629 | 48ff7a62 | Michael Roth | register_signal_handlers(); |
630 | 48ff7a62 | Michael Roth | |
631 | 48ff7a62 | Michael Roth | g_main_loop_run(ga_state->main_loop); |
632 | 48ff7a62 | Michael Roth | |
633 | e3d4d252 | Michael Roth | ga_command_state_cleanup_all(ga_state->command_state); |
634 | 48ff7a62 | Michael Roth | unlink(pidfile); |
635 | 48ff7a62 | Michael Roth | |
636 | 48ff7a62 | Michael Roth | return 0; |
637 | 48ff7a62 | Michael Roth | } |