1 | /* |
2 | * QEMU System Emulator |
3 | * |
4 | * Copyright (c) 2003-2008 Fabrice Bellard |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | * copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | * THE SOFTWARE. |
23 | */ |
24 | |
25 | #include "qemu/osdep.h" |
26 | #include "qemu/cutils.h" |
27 | #include "monitor/monitor.h" |
28 | #include "sysemu/sysemu.h" |
29 | #include "qemu/config-file.h" |
30 | #include "qemu/error-report.h" |
31 | #include "qemu/qemu-print.h" |
32 | #include "chardev/char.h" |
33 | #include "qapi/error.h" |
34 | #include "qapi/qapi-commands-char.h" |
35 | #include "qapi/qmp/qerror.h" |
36 | #include "sysemu/replay.h" |
37 | #include "qemu/help_option.h" |
38 | #include "qemu/module.h" |
39 | #include "qemu/option.h" |
40 | |
41 | #include "chardev/char-mux.h" |
42 | |
43 | /***********************************************************/ |
44 | /* character device */ |
45 | |
46 | static Object *get_chardevs_root(void) |
47 | { |
48 | return container_get(object_get_root(), "/chardevs" ); |
49 | } |
50 | |
51 | static void chr_be_event(Chardev *s, int event) |
52 | { |
53 | CharBackend *be = s->be; |
54 | |
55 | if (!be || !be->chr_event) { |
56 | return; |
57 | } |
58 | |
59 | be->chr_event(be->opaque, event); |
60 | } |
61 | |
62 | void qemu_chr_be_event(Chardev *s, int event) |
63 | { |
64 | /* Keep track if the char device is open */ |
65 | switch (event) { |
66 | case CHR_EVENT_OPENED: |
67 | s->be_open = 1; |
68 | break; |
69 | case CHR_EVENT_CLOSED: |
70 | s->be_open = 0; |
71 | break; |
72 | } |
73 | |
74 | CHARDEV_GET_CLASS(s)->chr_be_event(s, event); |
75 | } |
76 | |
77 | /* Not reporting errors from writing to logfile, as logs are |
78 | * defined to be "best effort" only */ |
79 | static void qemu_chr_write_log(Chardev *s, const uint8_t *buf, size_t len) |
80 | { |
81 | size_t done = 0; |
82 | ssize_t ret; |
83 | |
84 | if (s->logfd < 0) { |
85 | return; |
86 | } |
87 | |
88 | while (done < len) { |
89 | retry: |
90 | ret = write(s->logfd, buf + done, len - done); |
91 | if (ret == -1 && errno == EAGAIN) { |
92 | g_usleep(100); |
93 | goto retry; |
94 | } |
95 | |
96 | if (ret <= 0) { |
97 | return; |
98 | } |
99 | done += ret; |
100 | } |
101 | } |
102 | |
103 | static int qemu_chr_write_buffer(Chardev *s, |
104 | const uint8_t *buf, int len, |
105 | int *offset, bool write_all) |
106 | { |
107 | ChardevClass *cc = CHARDEV_GET_CLASS(s); |
108 | int res = 0; |
109 | *offset = 0; |
110 | |
111 | qemu_mutex_lock(&s->chr_write_lock); |
112 | while (*offset < len) { |
113 | retry: |
114 | res = cc->chr_write(s, buf + *offset, len - *offset); |
115 | if (res < 0 && errno == EAGAIN && write_all) { |
116 | g_usleep(100); |
117 | goto retry; |
118 | } |
119 | |
120 | if (res <= 0) { |
121 | break; |
122 | } |
123 | |
124 | *offset += res; |
125 | if (!write_all) { |
126 | break; |
127 | } |
128 | } |
129 | if (*offset > 0) { |
130 | qemu_chr_write_log(s, buf, *offset); |
131 | } |
132 | qemu_mutex_unlock(&s->chr_write_lock); |
133 | |
134 | return res; |
135 | } |
136 | |
137 | int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all) |
138 | { |
139 | int offset = 0; |
140 | int res; |
141 | |
142 | if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) { |
143 | replay_char_write_event_load(&res, &offset); |
144 | assert(offset <= len); |
145 | qemu_chr_write_buffer(s, buf, offset, &offset, true); |
146 | return res; |
147 | } |
148 | |
149 | res = qemu_chr_write_buffer(s, buf, len, &offset, write_all); |
150 | |
151 | if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) { |
152 | replay_char_write_event_save(res, offset); |
153 | } |
154 | |
155 | if (res < 0) { |
156 | return res; |
157 | } |
158 | return offset; |
159 | } |
160 | |
161 | int qemu_chr_be_can_write(Chardev *s) |
162 | { |
163 | CharBackend *be = s->be; |
164 | |
165 | if (!be || !be->chr_can_read) { |
166 | return 0; |
167 | } |
168 | |
169 | return be->chr_can_read(be->opaque); |
170 | } |
171 | |
172 | void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len) |
173 | { |
174 | CharBackend *be = s->be; |
175 | |
176 | if (be && be->chr_read) { |
177 | be->chr_read(be->opaque, buf, len); |
178 | } |
179 | } |
180 | |
181 | void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len) |
182 | { |
183 | if (qemu_chr_replay(s)) { |
184 | if (replay_mode == REPLAY_MODE_PLAY) { |
185 | return; |
186 | } |
187 | replay_chr_be_write(s, buf, len); |
188 | } else { |
189 | qemu_chr_be_write_impl(s, buf, len); |
190 | } |
191 | } |
192 | |
193 | void qemu_chr_be_update_read_handlers(Chardev *s, |
194 | GMainContext *context) |
195 | { |
196 | ChardevClass *cc = CHARDEV_GET_CLASS(s); |
197 | |
198 | assert(qemu_chr_has_feature(s, QEMU_CHAR_FEATURE_GCONTEXT) |
199 | || !context); |
200 | s->gcontext = context; |
201 | if (cc->chr_update_read_handler) { |
202 | cc->chr_update_read_handler(s); |
203 | } |
204 | } |
205 | |
206 | int qemu_chr_add_client(Chardev *s, int fd) |
207 | { |
208 | return CHARDEV_GET_CLASS(s)->chr_add_client ? |
209 | CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1; |
210 | } |
211 | |
212 | static void qemu_char_open(Chardev *chr, ChardevBackend *backend, |
213 | bool *be_opened, Error **errp) |
214 | { |
215 | ChardevClass *cc = CHARDEV_GET_CLASS(chr); |
216 | /* Any ChardevCommon member would work */ |
217 | ChardevCommon *common = backend ? backend->u.null.data : NULL; |
218 | |
219 | if (common && common->has_logfile) { |
220 | int flags = O_WRONLY | O_CREAT; |
221 | if (common->has_logappend && |
222 | common->logappend) { |
223 | flags |= O_APPEND; |
224 | } else { |
225 | flags |= O_TRUNC; |
226 | } |
227 | chr->logfd = qemu_open(common->logfile, flags, 0666); |
228 | if (chr->logfd < 0) { |
229 | error_setg_errno(errp, errno, |
230 | "Unable to open logfile %s" , |
231 | common->logfile); |
232 | return; |
233 | } |
234 | } |
235 | |
236 | if (cc->open) { |
237 | cc->open(chr, backend, be_opened, errp); |
238 | } |
239 | } |
240 | |
241 | static void char_init(Object *obj) |
242 | { |
243 | Chardev *chr = CHARDEV(obj); |
244 | |
245 | chr->logfd = -1; |
246 | qemu_mutex_init(&chr->chr_write_lock); |
247 | |
248 | /* |
249 | * Assume if chr_update_read_handler is implemented it will |
250 | * take the updated gcontext into account. |
251 | */ |
252 | if (CHARDEV_GET_CLASS(chr)->chr_update_read_handler) { |
253 | qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT); |
254 | } |
255 | |
256 | } |
257 | |
258 | static int null_chr_write(Chardev *chr, const uint8_t *buf, int len) |
259 | { |
260 | return len; |
261 | } |
262 | |
263 | static void char_class_init(ObjectClass *oc, void *data) |
264 | { |
265 | ChardevClass *cc = CHARDEV_CLASS(oc); |
266 | |
267 | cc->chr_write = null_chr_write; |
268 | cc->chr_be_event = chr_be_event; |
269 | } |
270 | |
271 | static void char_finalize(Object *obj) |
272 | { |
273 | Chardev *chr = CHARDEV(obj); |
274 | |
275 | if (chr->be) { |
276 | chr->be->chr = NULL; |
277 | } |
278 | g_free(chr->filename); |
279 | g_free(chr->label); |
280 | if (chr->logfd != -1) { |
281 | close(chr->logfd); |
282 | } |
283 | qemu_mutex_destroy(&chr->chr_write_lock); |
284 | } |
285 | |
286 | static const TypeInfo char_type_info = { |
287 | .name = TYPE_CHARDEV, |
288 | .parent = TYPE_OBJECT, |
289 | .instance_size = sizeof(Chardev), |
290 | .instance_init = char_init, |
291 | .instance_finalize = char_finalize, |
292 | .abstract = true, |
293 | .class_size = sizeof(ChardevClass), |
294 | .class_init = char_class_init, |
295 | }; |
296 | |
297 | static int chardev_machine_done_notify_one(Object *child, void *opaque) |
298 | { |
299 | Chardev *chr = (Chardev *)child; |
300 | ChardevClass *class = CHARDEV_GET_CLASS(chr); |
301 | |
302 | if (class->chr_machine_done) { |
303 | return class->chr_machine_done(chr); |
304 | } |
305 | |
306 | return 0; |
307 | } |
308 | |
309 | static void chardev_machine_done_hook(Notifier *notifier, void *unused) |
310 | { |
311 | int ret = object_child_foreach(get_chardevs_root(), |
312 | chardev_machine_done_notify_one, NULL); |
313 | |
314 | if (ret) { |
315 | error_report("Failed to call chardev machine_done hooks" ); |
316 | exit(1); |
317 | } |
318 | } |
319 | |
320 | static Notifier chardev_machine_done_notify = { |
321 | .notify = chardev_machine_done_hook, |
322 | }; |
323 | |
324 | static bool qemu_chr_is_busy(Chardev *s) |
325 | { |
326 | if (CHARDEV_IS_MUX(s)) { |
327 | MuxChardev *d = MUX_CHARDEV(s); |
328 | return d->mux_cnt >= 0; |
329 | } else { |
330 | return s->be != NULL; |
331 | } |
332 | } |
333 | |
334 | int qemu_chr_wait_connected(Chardev *chr, Error **errp) |
335 | { |
336 | ChardevClass *cc = CHARDEV_GET_CLASS(chr); |
337 | |
338 | if (cc->chr_wait_connected) { |
339 | return cc->chr_wait_connected(chr, errp); |
340 | } |
341 | |
342 | return 0; |
343 | } |
344 | |
345 | QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename, |
346 | bool permit_mux_mon) |
347 | { |
348 | char host[65], port[33], width[8], height[8]; |
349 | int pos; |
350 | const char *p; |
351 | QemuOpts *opts; |
352 | Error *local_err = NULL; |
353 | |
354 | opts = qemu_opts_create(qemu_find_opts("chardev" ), label, 1, &local_err); |
355 | if (local_err) { |
356 | error_report_err(local_err); |
357 | return NULL; |
358 | } |
359 | |
360 | if (strstart(filename, "mon:" , &p)) { |
361 | if (!permit_mux_mon) { |
362 | error_report("mon: isn't supported in this context" ); |
363 | return NULL; |
364 | } |
365 | filename = p; |
366 | qemu_opt_set(opts, "mux" , "on" , &error_abort); |
367 | if (strcmp(filename, "stdio" ) == 0) { |
368 | /* Monitor is muxed to stdio: do not exit on Ctrl+C by default |
369 | * but pass it to the guest. Handle this only for compat syntax, |
370 | * for -chardev syntax we have special option for this. |
371 | * This is what -nographic did, redirecting+muxing serial+monitor |
372 | * to stdio causing Ctrl+C to be passed to guest. */ |
373 | qemu_opt_set(opts, "signal" , "off" , &error_abort); |
374 | } |
375 | } |
376 | |
377 | if (strcmp(filename, "null" ) == 0 || |
378 | strcmp(filename, "pty" ) == 0 || |
379 | strcmp(filename, "msmouse" ) == 0 || |
380 | strcmp(filename, "wctablet" ) == 0 || |
381 | strcmp(filename, "braille" ) == 0 || |
382 | strcmp(filename, "testdev" ) == 0 || |
383 | strcmp(filename, "stdio" ) == 0) { |
384 | qemu_opt_set(opts, "backend" , filename, &error_abort); |
385 | return opts; |
386 | } |
387 | if (strstart(filename, "vc" , &p)) { |
388 | qemu_opt_set(opts, "backend" , "vc" , &error_abort); |
389 | if (*p == ':') { |
390 | if (sscanf(p+1, "%7[0-9]x%7[0-9]" , width, height) == 2) { |
391 | /* pixels */ |
392 | qemu_opt_set(opts, "width" , width, &error_abort); |
393 | qemu_opt_set(opts, "height" , height, &error_abort); |
394 | } else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C" , width, height) == 2) { |
395 | /* chars */ |
396 | qemu_opt_set(opts, "cols" , width, &error_abort); |
397 | qemu_opt_set(opts, "rows" , height, &error_abort); |
398 | } else { |
399 | goto fail; |
400 | } |
401 | } |
402 | return opts; |
403 | } |
404 | if (strcmp(filename, "con:" ) == 0) { |
405 | qemu_opt_set(opts, "backend" , "console" , &error_abort); |
406 | return opts; |
407 | } |
408 | if (strstart(filename, "COM" , NULL)) { |
409 | qemu_opt_set(opts, "backend" , "serial" , &error_abort); |
410 | qemu_opt_set(opts, "path" , filename, &error_abort); |
411 | return opts; |
412 | } |
413 | if (strstart(filename, "file:" , &p)) { |
414 | qemu_opt_set(opts, "backend" , "file" , &error_abort); |
415 | qemu_opt_set(opts, "path" , p, &error_abort); |
416 | return opts; |
417 | } |
418 | if (strstart(filename, "pipe:" , &p)) { |
419 | qemu_opt_set(opts, "backend" , "pipe" , &error_abort); |
420 | qemu_opt_set(opts, "path" , p, &error_abort); |
421 | return opts; |
422 | } |
423 | if (strstart(filename, "tcp:" , &p) || |
424 | strstart(filename, "telnet:" , &p) || |
425 | strstart(filename, "tn3270:" , &p) || |
426 | strstart(filename, "websocket:" , &p)) { |
427 | if (sscanf(p, "%64[^:]:%32[^,]%n" , host, port, &pos) < 2) { |
428 | host[0] = 0; |
429 | if (sscanf(p, ":%32[^,]%n" , port, &pos) < 1) |
430 | goto fail; |
431 | } |
432 | qemu_opt_set(opts, "backend" , "socket" , &error_abort); |
433 | qemu_opt_set(opts, "host" , host, &error_abort); |
434 | qemu_opt_set(opts, "port" , port, &error_abort); |
435 | if (p[pos] == ',') { |
436 | qemu_opts_do_parse(opts, p+pos+1, NULL, &local_err); |
437 | if (local_err) { |
438 | error_report_err(local_err); |
439 | goto fail; |
440 | } |
441 | } |
442 | if (strstart(filename, "telnet:" , &p)) { |
443 | qemu_opt_set(opts, "telnet" , "on" , &error_abort); |
444 | } else if (strstart(filename, "tn3270:" , &p)) { |
445 | qemu_opt_set(opts, "tn3270" , "on" , &error_abort); |
446 | } else if (strstart(filename, "websocket:" , &p)) { |
447 | qemu_opt_set(opts, "websocket" , "on" , &error_abort); |
448 | } |
449 | return opts; |
450 | } |
451 | if (strstart(filename, "udp:" , &p)) { |
452 | qemu_opt_set(opts, "backend" , "udp" , &error_abort); |
453 | if (sscanf(p, "%64[^:]:%32[^@,]%n" , host, port, &pos) < 2) { |
454 | host[0] = 0; |
455 | if (sscanf(p, ":%32[^@,]%n" , port, &pos) < 1) { |
456 | goto fail; |
457 | } |
458 | } |
459 | qemu_opt_set(opts, "host" , host, &error_abort); |
460 | qemu_opt_set(opts, "port" , port, &error_abort); |
461 | if (p[pos] == '@') { |
462 | p += pos + 1; |
463 | if (sscanf(p, "%64[^:]:%32[^,]%n" , host, port, &pos) < 2) { |
464 | host[0] = 0; |
465 | if (sscanf(p, ":%32[^,]%n" , port, &pos) < 1) { |
466 | goto fail; |
467 | } |
468 | } |
469 | qemu_opt_set(opts, "localaddr" , host, &error_abort); |
470 | qemu_opt_set(opts, "localport" , port, &error_abort); |
471 | } |
472 | return opts; |
473 | } |
474 | if (strstart(filename, "unix:" , &p)) { |
475 | qemu_opt_set(opts, "backend" , "socket" , &error_abort); |
476 | qemu_opts_do_parse(opts, p, "path" , &local_err); |
477 | if (local_err) { |
478 | error_report_err(local_err); |
479 | goto fail; |
480 | } |
481 | return opts; |
482 | } |
483 | if (strstart(filename, "/dev/parport" , NULL) || |
484 | strstart(filename, "/dev/ppi" , NULL)) { |
485 | qemu_opt_set(opts, "backend" , "parallel" , &error_abort); |
486 | qemu_opt_set(opts, "path" , filename, &error_abort); |
487 | return opts; |
488 | } |
489 | if (strstart(filename, "/dev/" , NULL)) { |
490 | qemu_opt_set(opts, "backend" , "serial" , &error_abort); |
491 | qemu_opt_set(opts, "path" , filename, &error_abort); |
492 | return opts; |
493 | } |
494 | |
495 | error_report("'%s' is not a valid char driver" , filename); |
496 | |
497 | fail: |
498 | qemu_opts_del(opts); |
499 | return NULL; |
500 | } |
501 | |
502 | void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend) |
503 | { |
504 | const char *logfile = qemu_opt_get(opts, "logfile" ); |
505 | |
506 | backend->has_logfile = logfile != NULL; |
507 | backend->logfile = g_strdup(logfile); |
508 | |
509 | backend->has_logappend = true; |
510 | backend->logappend = qemu_opt_get_bool(opts, "logappend" , false); |
511 | } |
512 | |
513 | static const ChardevClass *char_get_class(const char *driver, Error **errp) |
514 | { |
515 | ObjectClass *oc; |
516 | const ChardevClass *cc; |
517 | char *typename = g_strdup_printf("chardev-%s" , driver); |
518 | |
519 | oc = object_class_by_name(typename); |
520 | g_free(typename); |
521 | |
522 | if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) { |
523 | error_setg(errp, "'%s' is not a valid char driver name" , driver); |
524 | return NULL; |
525 | } |
526 | |
527 | if (object_class_is_abstract(oc)) { |
528 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver" , |
529 | "abstract device type" ); |
530 | return NULL; |
531 | } |
532 | |
533 | cc = CHARDEV_CLASS(oc); |
534 | if (cc->internal) { |
535 | error_setg(errp, "'%s' is not a valid char driver name" , driver); |
536 | return NULL; |
537 | } |
538 | |
539 | return cc; |
540 | } |
541 | |
542 | static const struct ChardevAlias { |
543 | const char *typename; |
544 | const char *alias; |
545 | } chardev_alias_table[] = { |
546 | #ifdef HAVE_CHARDEV_PARPORT |
547 | { "parallel" , "parport" }, |
548 | #endif |
549 | #ifdef HAVE_CHARDEV_SERIAL |
550 | { "serial" , "tty" }, |
551 | #endif |
552 | }; |
553 | |
554 | typedef struct ChadevClassFE { |
555 | void (*fn)(const char *name, void *opaque); |
556 | void *opaque; |
557 | } ChadevClassFE; |
558 | |
559 | static void |
560 | chardev_class_foreach(ObjectClass *klass, void *opaque) |
561 | { |
562 | ChadevClassFE *fe = opaque; |
563 | |
564 | assert(g_str_has_prefix(object_class_get_name(klass), "chardev-" )); |
565 | if (CHARDEV_CLASS(klass)->internal) { |
566 | return; |
567 | } |
568 | |
569 | fe->fn(object_class_get_name(klass) + 8, fe->opaque); |
570 | } |
571 | |
572 | static void |
573 | chardev_name_foreach(void (*fn)(const char *name, void *opaque), void *opaque) |
574 | { |
575 | ChadevClassFE fe = { .fn = fn, .opaque = opaque }; |
576 | int i; |
577 | |
578 | object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe); |
579 | |
580 | for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { |
581 | fn(chardev_alias_table[i].alias, opaque); |
582 | } |
583 | } |
584 | |
585 | static void |
586 | help_string_append(const char *name, void *opaque) |
587 | { |
588 | GString *str = opaque; |
589 | |
590 | g_string_append_printf(str, "\n %s" , name); |
591 | } |
592 | |
593 | static const char *chardev_alias_translate(const char *name) |
594 | { |
595 | int i; |
596 | for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { |
597 | if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) { |
598 | return chardev_alias_table[i].typename; |
599 | } |
600 | } |
601 | return name; |
602 | } |
603 | |
604 | ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) |
605 | { |
606 | Error *local_err = NULL; |
607 | const ChardevClass *cc; |
608 | ChardevBackend *backend = NULL; |
609 | const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend" )); |
610 | |
611 | if (name == NULL) { |
612 | error_setg(errp, "chardev: \"%s\" missing backend" , |
613 | qemu_opts_id(opts)); |
614 | return NULL; |
615 | } |
616 | |
617 | cc = char_get_class(name, errp); |
618 | if (cc == NULL) { |
619 | return NULL; |
620 | } |
621 | |
622 | backend = g_new0(ChardevBackend, 1); |
623 | backend->type = CHARDEV_BACKEND_KIND_NULL; |
624 | |
625 | if (cc->parse) { |
626 | cc->parse(opts, backend, &local_err); |
627 | if (local_err) { |
628 | error_propagate(errp, local_err); |
629 | qapi_free_ChardevBackend(backend); |
630 | return NULL; |
631 | } |
632 | } else { |
633 | ChardevCommon *ccom = g_new0(ChardevCommon, 1); |
634 | qemu_chr_parse_common(opts, ccom); |
635 | backend->u.null.data = ccom; /* Any ChardevCommon member would work */ |
636 | } |
637 | |
638 | return backend; |
639 | } |
640 | |
641 | Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, |
642 | Error **errp) |
643 | { |
644 | const ChardevClass *cc; |
645 | Chardev *chr = NULL; |
646 | ChardevBackend *backend = NULL; |
647 | const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend" )); |
648 | const char *id = qemu_opts_id(opts); |
649 | char *bid = NULL; |
650 | |
651 | if (name && is_help_option(name)) { |
652 | GString *str = g_string_new("" ); |
653 | |
654 | chardev_name_foreach(help_string_append, str); |
655 | |
656 | qemu_printf("Available chardev backend types: %s\n" , str->str); |
657 | g_string_free(str, true); |
658 | return NULL; |
659 | } |
660 | |
661 | if (id == NULL) { |
662 | error_setg(errp, "chardev: no id specified" ); |
663 | return NULL; |
664 | } |
665 | |
666 | backend = qemu_chr_parse_opts(opts, errp); |
667 | if (backend == NULL) { |
668 | return NULL; |
669 | } |
670 | |
671 | cc = char_get_class(name, errp); |
672 | if (cc == NULL) { |
673 | goto out; |
674 | } |
675 | |
676 | if (qemu_opt_get_bool(opts, "mux" , 0)) { |
677 | bid = g_strdup_printf("%s-base" , id); |
678 | } |
679 | |
680 | chr = qemu_chardev_new(bid ? bid : id, |
681 | object_class_get_name(OBJECT_CLASS(cc)), |
682 | backend, context, errp); |
683 | |
684 | if (chr == NULL) { |
685 | goto out; |
686 | } |
687 | |
688 | if (bid) { |
689 | Chardev *mux; |
690 | qapi_free_ChardevBackend(backend); |
691 | backend = g_new0(ChardevBackend, 1); |
692 | backend->type = CHARDEV_BACKEND_KIND_MUX; |
693 | backend->u.mux.data = g_new0(ChardevMux, 1); |
694 | backend->u.mux.data->chardev = g_strdup(bid); |
695 | mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, context, errp); |
696 | if (mux == NULL) { |
697 | object_unparent(OBJECT(chr)); |
698 | chr = NULL; |
699 | goto out; |
700 | } |
701 | chr = mux; |
702 | } |
703 | |
704 | out: |
705 | qapi_free_ChardevBackend(backend); |
706 | g_free(bid); |
707 | return chr; |
708 | } |
709 | |
710 | Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, |
711 | bool permit_mux_mon, GMainContext *context) |
712 | { |
713 | const char *p; |
714 | Chardev *chr; |
715 | QemuOpts *opts; |
716 | Error *err = NULL; |
717 | |
718 | if (strstart(filename, "chardev:" , &p)) { |
719 | return qemu_chr_find(p); |
720 | } |
721 | |
722 | opts = qemu_chr_parse_compat(label, filename, permit_mux_mon); |
723 | if (!opts) |
724 | return NULL; |
725 | |
726 | chr = qemu_chr_new_from_opts(opts, context, &err); |
727 | if (!chr) { |
728 | error_report_err(err); |
729 | goto out; |
730 | } |
731 | |
732 | if (qemu_opt_get_bool(opts, "mux" , 0)) { |
733 | assert(permit_mux_mon); |
734 | monitor_init_hmp(chr, true); |
735 | } |
736 | |
737 | out: |
738 | qemu_opts_del(opts); |
739 | return chr; |
740 | } |
741 | |
742 | static Chardev *qemu_chr_new_permit_mux_mon(const char *label, |
743 | const char *filename, |
744 | bool permit_mux_mon, |
745 | GMainContext *context) |
746 | { |
747 | Chardev *chr; |
748 | chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon, context); |
749 | if (chr) { |
750 | if (replay_mode != REPLAY_MODE_NONE) { |
751 | qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY); |
752 | } |
753 | if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) { |
754 | error_report("Replay: ioctl is not supported " |
755 | "for serial devices yet" ); |
756 | } |
757 | replay_register_char_driver(chr); |
758 | } |
759 | return chr; |
760 | } |
761 | |
762 | Chardev *qemu_chr_new(const char *label, const char *filename, |
763 | GMainContext *context) |
764 | { |
765 | return qemu_chr_new_permit_mux_mon(label, filename, false, context); |
766 | } |
767 | |
768 | Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename, |
769 | GMainContext *context) |
770 | { |
771 | return qemu_chr_new_permit_mux_mon(label, filename, true, context); |
772 | } |
773 | |
774 | static int qmp_query_chardev_foreach(Object *obj, void *data) |
775 | { |
776 | Chardev *chr = CHARDEV(obj); |
777 | ChardevInfoList **list = data; |
778 | ChardevInfoList *info = g_malloc0(sizeof(*info)); |
779 | |
780 | info->value = g_malloc0(sizeof(*info->value)); |
781 | info->value->label = g_strdup(chr->label); |
782 | info->value->filename = g_strdup(chr->filename); |
783 | info->value->frontend_open = chr->be && chr->be->fe_open; |
784 | |
785 | info->next = *list; |
786 | *list = info; |
787 | |
788 | return 0; |
789 | } |
790 | |
791 | ChardevInfoList *qmp_query_chardev(Error **errp) |
792 | { |
793 | ChardevInfoList *chr_list = NULL; |
794 | |
795 | object_child_foreach(get_chardevs_root(), |
796 | qmp_query_chardev_foreach, &chr_list); |
797 | |
798 | return chr_list; |
799 | } |
800 | |
801 | static void |
802 | qmp_prepend_backend(const char *name, void *opaque) |
803 | { |
804 | ChardevBackendInfoList **list = opaque; |
805 | ChardevBackendInfoList *info = g_malloc0(sizeof(*info)); |
806 | |
807 | info->value = g_malloc0(sizeof(*info->value)); |
808 | info->value->name = g_strdup(name); |
809 | info->next = *list; |
810 | *list = info; |
811 | } |
812 | |
813 | ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp) |
814 | { |
815 | ChardevBackendInfoList *backend_list = NULL; |
816 | |
817 | chardev_name_foreach(qmp_prepend_backend, &backend_list); |
818 | |
819 | return backend_list; |
820 | } |
821 | |
822 | Chardev *qemu_chr_find(const char *name) |
823 | { |
824 | Object *obj = object_resolve_path_component(get_chardevs_root(), name); |
825 | |
826 | return obj ? CHARDEV(obj) : NULL; |
827 | } |
828 | |
829 | QemuOptsList qemu_chardev_opts = { |
830 | .name = "chardev" , |
831 | .implied_opt_name = "backend" , |
832 | .head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head), |
833 | .desc = { |
834 | { |
835 | .name = "backend" , |
836 | .type = QEMU_OPT_STRING, |
837 | },{ |
838 | .name = "path" , |
839 | .type = QEMU_OPT_STRING, |
840 | },{ |
841 | .name = "host" , |
842 | .type = QEMU_OPT_STRING, |
843 | },{ |
844 | .name = "port" , |
845 | .type = QEMU_OPT_STRING, |
846 | },{ |
847 | .name = "fd" , |
848 | .type = QEMU_OPT_STRING, |
849 | },{ |
850 | .name = "localaddr" , |
851 | .type = QEMU_OPT_STRING, |
852 | },{ |
853 | .name = "localport" , |
854 | .type = QEMU_OPT_STRING, |
855 | },{ |
856 | .name = "to" , |
857 | .type = QEMU_OPT_NUMBER, |
858 | },{ |
859 | .name = "ipv4" , |
860 | .type = QEMU_OPT_BOOL, |
861 | },{ |
862 | .name = "ipv6" , |
863 | .type = QEMU_OPT_BOOL, |
864 | },{ |
865 | .name = "wait" , |
866 | .type = QEMU_OPT_BOOL, |
867 | },{ |
868 | .name = "server" , |
869 | .type = QEMU_OPT_BOOL, |
870 | },{ |
871 | .name = "delay" , |
872 | .type = QEMU_OPT_BOOL, |
873 | },{ |
874 | .name = "reconnect" , |
875 | .type = QEMU_OPT_NUMBER, |
876 | },{ |
877 | .name = "telnet" , |
878 | .type = QEMU_OPT_BOOL, |
879 | },{ |
880 | .name = "tn3270" , |
881 | .type = QEMU_OPT_BOOL, |
882 | },{ |
883 | .name = "tls-creds" , |
884 | .type = QEMU_OPT_STRING, |
885 | },{ |
886 | .name = "tls-authz" , |
887 | .type = QEMU_OPT_STRING, |
888 | },{ |
889 | .name = "websocket" , |
890 | .type = QEMU_OPT_BOOL, |
891 | },{ |
892 | .name = "width" , |
893 | .type = QEMU_OPT_NUMBER, |
894 | },{ |
895 | .name = "height" , |
896 | .type = QEMU_OPT_NUMBER, |
897 | },{ |
898 | .name = "cols" , |
899 | .type = QEMU_OPT_NUMBER, |
900 | },{ |
901 | .name = "rows" , |
902 | .type = QEMU_OPT_NUMBER, |
903 | },{ |
904 | .name = "mux" , |
905 | .type = QEMU_OPT_BOOL, |
906 | },{ |
907 | .name = "signal" , |
908 | .type = QEMU_OPT_BOOL, |
909 | },{ |
910 | .name = "name" , |
911 | .type = QEMU_OPT_STRING, |
912 | },{ |
913 | .name = "debug" , |
914 | .type = QEMU_OPT_NUMBER, |
915 | },{ |
916 | .name = "size" , |
917 | .type = QEMU_OPT_SIZE, |
918 | },{ |
919 | .name = "chardev" , |
920 | .type = QEMU_OPT_STRING, |
921 | },{ |
922 | .name = "append" , |
923 | .type = QEMU_OPT_BOOL, |
924 | },{ |
925 | .name = "logfile" , |
926 | .type = QEMU_OPT_STRING, |
927 | },{ |
928 | .name = "logappend" , |
929 | .type = QEMU_OPT_BOOL, |
930 | }, |
931 | { /* end of list */ } |
932 | }, |
933 | }; |
934 | |
935 | bool qemu_chr_has_feature(Chardev *chr, |
936 | ChardevFeature feature) |
937 | { |
938 | return test_bit(feature, chr->features); |
939 | } |
940 | |
941 | void qemu_chr_set_feature(Chardev *chr, |
942 | ChardevFeature feature) |
943 | { |
944 | return set_bit(feature, chr->features); |
945 | } |
946 | |
947 | Chardev *qemu_chardev_new(const char *id, const char *typename, |
948 | ChardevBackend *backend, |
949 | GMainContext *gcontext, |
950 | Error **errp) |
951 | { |
952 | Object *obj; |
953 | Chardev *chr = NULL; |
954 | Error *local_err = NULL; |
955 | bool be_opened = true; |
956 | |
957 | assert(g_str_has_prefix(typename, "chardev-" )); |
958 | |
959 | obj = object_new(typename); |
960 | chr = CHARDEV(obj); |
961 | chr->label = g_strdup(id); |
962 | chr->gcontext = gcontext; |
963 | |
964 | qemu_char_open(chr, backend, &be_opened, &local_err); |
965 | if (local_err) { |
966 | goto end; |
967 | } |
968 | |
969 | if (!chr->filename) { |
970 | chr->filename = g_strdup(typename + 8); |
971 | } |
972 | if (be_opened) { |
973 | qemu_chr_be_event(chr, CHR_EVENT_OPENED); |
974 | } |
975 | |
976 | if (id) { |
977 | object_property_add_child(get_chardevs_root(), id, obj, &local_err); |
978 | if (local_err) { |
979 | goto end; |
980 | } |
981 | object_unref(obj); |
982 | } |
983 | |
984 | end: |
985 | if (local_err) { |
986 | error_propagate(errp, local_err); |
987 | object_unref(obj); |
988 | return NULL; |
989 | } |
990 | |
991 | return chr; |
992 | } |
993 | |
994 | ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, |
995 | Error **errp) |
996 | { |
997 | const ChardevClass *cc; |
998 | ChardevReturn *ret; |
999 | Chardev *chr; |
1000 | |
1001 | cc = char_get_class(ChardevBackendKind_str(backend->type), errp); |
1002 | if (!cc) { |
1003 | return NULL; |
1004 | } |
1005 | |
1006 | chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)), |
1007 | backend, NULL, errp); |
1008 | if (!chr) { |
1009 | return NULL; |
1010 | } |
1011 | |
1012 | ret = g_new0(ChardevReturn, 1); |
1013 | if (CHARDEV_IS_PTY(chr)) { |
1014 | ret->pty = g_strdup(chr->filename + 4); |
1015 | ret->has_pty = true; |
1016 | } |
1017 | |
1018 | return ret; |
1019 | } |
1020 | |
1021 | ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, |
1022 | Error **errp) |
1023 | { |
1024 | CharBackend *be; |
1025 | const ChardevClass *cc; |
1026 | Chardev *chr, *chr_new; |
1027 | bool closed_sent = false; |
1028 | ChardevReturn *ret; |
1029 | |
1030 | chr = qemu_chr_find(id); |
1031 | if (!chr) { |
1032 | error_setg(errp, "Chardev '%s' does not exist" , id); |
1033 | return NULL; |
1034 | } |
1035 | |
1036 | if (CHARDEV_IS_MUX(chr)) { |
1037 | error_setg(errp, "Mux device hotswap not supported yet" ); |
1038 | return NULL; |
1039 | } |
1040 | |
1041 | if (qemu_chr_replay(chr)) { |
1042 | error_setg(errp, |
1043 | "Chardev '%s' cannot be changed in record/replay mode" , id); |
1044 | return NULL; |
1045 | } |
1046 | |
1047 | be = chr->be; |
1048 | if (!be) { |
1049 | /* easy case */ |
1050 | object_unparent(OBJECT(chr)); |
1051 | return qmp_chardev_add(id, backend, errp); |
1052 | } |
1053 | |
1054 | if (!be->chr_be_change) { |
1055 | error_setg(errp, "Chardev user does not support chardev hotswap" ); |
1056 | return NULL; |
1057 | } |
1058 | |
1059 | cc = char_get_class(ChardevBackendKind_str(backend->type), errp); |
1060 | if (!cc) { |
1061 | return NULL; |
1062 | } |
1063 | |
1064 | chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), |
1065 | backend, chr->gcontext, errp); |
1066 | if (!chr_new) { |
1067 | return NULL; |
1068 | } |
1069 | chr_new->label = g_strdup(id); |
1070 | |
1071 | if (chr->be_open && !chr_new->be_open) { |
1072 | qemu_chr_be_event(chr, CHR_EVENT_CLOSED); |
1073 | closed_sent = true; |
1074 | } |
1075 | |
1076 | chr->be = NULL; |
1077 | qemu_chr_fe_init(be, chr_new, &error_abort); |
1078 | |
1079 | if (be->chr_be_change(be->opaque) < 0) { |
1080 | error_setg(errp, "Chardev '%s' change failed" , chr_new->label); |
1081 | chr_new->be = NULL; |
1082 | qemu_chr_fe_init(be, chr, &error_abort); |
1083 | if (closed_sent) { |
1084 | qemu_chr_be_event(chr, CHR_EVENT_OPENED); |
1085 | } |
1086 | object_unref(OBJECT(chr_new)); |
1087 | return NULL; |
1088 | } |
1089 | |
1090 | object_unparent(OBJECT(chr)); |
1091 | object_property_add_child(get_chardevs_root(), chr_new->label, |
1092 | OBJECT(chr_new), &error_abort); |
1093 | object_unref(OBJECT(chr_new)); |
1094 | |
1095 | ret = g_new0(ChardevReturn, 1); |
1096 | if (CHARDEV_IS_PTY(chr_new)) { |
1097 | ret->pty = g_strdup(chr_new->filename + 4); |
1098 | ret->has_pty = true; |
1099 | } |
1100 | |
1101 | return ret; |
1102 | } |
1103 | |
1104 | void qmp_chardev_remove(const char *id, Error **errp) |
1105 | { |
1106 | Chardev *chr; |
1107 | |
1108 | chr = qemu_chr_find(id); |
1109 | if (chr == NULL) { |
1110 | error_setg(errp, "Chardev '%s' not found" , id); |
1111 | return; |
1112 | } |
1113 | if (qemu_chr_is_busy(chr)) { |
1114 | error_setg(errp, "Chardev '%s' is busy" , id); |
1115 | return; |
1116 | } |
1117 | if (qemu_chr_replay(chr)) { |
1118 | error_setg(errp, |
1119 | "Chardev '%s' cannot be unplugged in record/replay mode" , id); |
1120 | return; |
1121 | } |
1122 | object_unparent(OBJECT(chr)); |
1123 | } |
1124 | |
1125 | void qmp_chardev_send_break(const char *id, Error **errp) |
1126 | { |
1127 | Chardev *chr; |
1128 | |
1129 | chr = qemu_chr_find(id); |
1130 | if (chr == NULL) { |
1131 | error_setg(errp, "Chardev '%s' not found" , id); |
1132 | return; |
1133 | } |
1134 | qemu_chr_be_event(chr, CHR_EVENT_BREAK); |
1135 | } |
1136 | |
1137 | /* |
1138 | * Add a timeout callback for the chardev (in milliseconds), return |
1139 | * the GSource object created. Please use this to add timeout hook for |
1140 | * chardev instead of g_timeout_add() and g_timeout_add_seconds(), to |
1141 | * make sure the gcontext that the task bound to is correct. |
1142 | */ |
1143 | GSource *qemu_chr_timeout_add_ms(Chardev *chr, guint ms, |
1144 | GSourceFunc func, void *private) |
1145 | { |
1146 | GSource *source = g_timeout_source_new(ms); |
1147 | |
1148 | assert(func); |
1149 | g_source_set_callback(source, func, private, NULL); |
1150 | g_source_attach(source, chr->gcontext); |
1151 | |
1152 | return source; |
1153 | } |
1154 | |
1155 | void qemu_chr_cleanup(void) |
1156 | { |
1157 | object_unparent(get_chardevs_root()); |
1158 | } |
1159 | |
1160 | static void register_types(void) |
1161 | { |
1162 | type_register_static(&char_type_info); |
1163 | |
1164 | /* this must be done after machine init, since we register FEs with muxes |
1165 | * as part of realize functions like serial_isa_realizefn when -nographic |
1166 | * is specified |
1167 | */ |
1168 | qemu_add_machine_init_done_notifier(&chardev_machine_done_notify); |
1169 | } |
1170 | |
1171 | type_init(register_types); |
1172 | |