1178fe0aeSMarc-André Lureau /*
2178fe0aeSMarc-André Lureau * QEMU System Emulator
3178fe0aeSMarc-André Lureau *
4178fe0aeSMarc-André Lureau * Copyright (c) 2003-2008 Fabrice Bellard
5178fe0aeSMarc-André Lureau *
6178fe0aeSMarc-André Lureau * Permission is hereby granted, free of charge, to any person obtaining a copy
7178fe0aeSMarc-André Lureau * of this software and associated documentation files (the "Software"), to deal
8178fe0aeSMarc-André Lureau * in the Software without restriction, including without limitation the rights
9178fe0aeSMarc-André Lureau * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10178fe0aeSMarc-André Lureau * copies of the Software, and to permit persons to whom the Software is
11178fe0aeSMarc-André Lureau * furnished to do so, subject to the following conditions:
12178fe0aeSMarc-André Lureau *
13178fe0aeSMarc-André Lureau * The above copyright notice and this permission notice shall be included in
14178fe0aeSMarc-André Lureau * all copies or substantial portions of the Software.
15178fe0aeSMarc-André Lureau *
16178fe0aeSMarc-André Lureau * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17178fe0aeSMarc-André Lureau * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18178fe0aeSMarc-André Lureau * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19178fe0aeSMarc-André Lureau * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20178fe0aeSMarc-André Lureau * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21178fe0aeSMarc-André Lureau * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22178fe0aeSMarc-André Lureau * THE SOFTWARE.
23178fe0aeSMarc-André Lureau */
24e688df6bSMarkus Armbruster
25178fe0aeSMarc-André Lureau #include "qemu/osdep.h"
26178fe0aeSMarc-André Lureau #include "qemu/cutils.h"
27178fe0aeSMarc-André Lureau #include "monitor/monitor.h"
28c3054a6eSMarkus Armbruster #include "monitor/qmp-helpers.h"
29213dcb06SMarc-André Lureau #include "qemu/config-file.h"
30178fe0aeSMarc-André Lureau #include "qemu/error-report.h"
31ef2fd6f1SMarkus Armbruster #include "qemu/qemu-print.h"
328228e353SMarc-André Lureau #include "chardev/char.h"
33e688df6bSMarkus Armbruster #include "qapi/error.h"
349af23989SMarkus Armbruster #include "qapi/qapi-commands-char.h"
350dd13589SMarkus Armbruster #include "qapi/qmp/qerror.h"
36178fe0aeSMarc-André Lureau #include "sysemu/replay.h"
37178fe0aeSMarc-André Lureau #include "qemu/help_option.h"
380b8fa32fSMarkus Armbruster #include "qemu/module.h"
39922a01a0SMarkus Armbruster #include "qemu/option.h"
401e419ee6SMarc-André Lureau #include "qemu/id.h"
412158fa1bSLukas Straub #include "qemu/coroutine.h"
42feb774caSLukas Straub #include "qemu/yank.h"
43178fe0aeSMarc-André Lureau
44ffa0f7ebSPhilippe Mathieu-Daudé #include "chardev-internal.h"
45df85a78bSMarc-André Lureau
46178fe0aeSMarc-André Lureau /***********************************************************/
47178fe0aeSMarc-André Lureau /* character device */
48178fe0aeSMarc-André Lureau
get_chardevs_root(void)4930827badSPhilippe Mathieu-Daudé Object *get_chardevs_root(void)
502f5d45a1SMarc-André Lureau {
512f5d45a1SMarc-André Lureau return container_get(object_get_root(), "/chardevs");
522f5d45a1SMarc-André Lureau }
53178fe0aeSMarc-André Lureau
chr_be_event(Chardev * s,QEMUChrEvent event)542fa9044aSPaolo Bonzini static void chr_be_event(Chardev *s, QEMUChrEvent event)
55178fe0aeSMarc-André Lureau {
56178fe0aeSMarc-André Lureau CharBackend *be = s->be;
57178fe0aeSMarc-André Lureau
58d09c4a47SMarc-André Lureau if (!be || !be->chr_event) {
59d09c4a47SMarc-André Lureau return;
60d09c4a47SMarc-André Lureau }
61d09c4a47SMarc-André Lureau
62d09c4a47SMarc-André Lureau be->chr_event(be->opaque, event);
63d09c4a47SMarc-André Lureau }
64d09c4a47SMarc-André Lureau
qemu_chr_be_event(Chardev * s,QEMUChrEvent event)652fa9044aSPaolo Bonzini void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
66d09c4a47SMarc-André Lureau {
67178fe0aeSMarc-André Lureau /* Keep track if the char device is open */
68178fe0aeSMarc-André Lureau switch (event) {
69178fe0aeSMarc-André Lureau case CHR_EVENT_OPENED:
70178fe0aeSMarc-André Lureau s->be_open = 1;
71178fe0aeSMarc-André Lureau break;
72178fe0aeSMarc-André Lureau case CHR_EVENT_CLOSED:
73178fe0aeSMarc-André Lureau s->be_open = 0;
74178fe0aeSMarc-André Lureau break;
7571f8d3b0SPhilippe Mathieu-Daudé case CHR_EVENT_BREAK:
7671f8d3b0SPhilippe Mathieu-Daudé case CHR_EVENT_MUX_IN:
7771f8d3b0SPhilippe Mathieu-Daudé case CHR_EVENT_MUX_OUT:
7871f8d3b0SPhilippe Mathieu-Daudé /* Ignore */
7971f8d3b0SPhilippe Mathieu-Daudé break;
80178fe0aeSMarc-André Lureau }
81178fe0aeSMarc-André Lureau
82d09c4a47SMarc-André Lureau CHARDEV_GET_CLASS(s)->chr_be_event(s, event);
83178fe0aeSMarc-André Lureau }
84178fe0aeSMarc-André Lureau
85178fe0aeSMarc-André Lureau /* Not reporting errors from writing to logfile, as logs are
86178fe0aeSMarc-André Lureau * defined to be "best effort" only */
qemu_chr_write_log(Chardev * s,const uint8_t * buf,size_t len)87a9b1ca38SMarc-André Lureau static void qemu_chr_write_log(Chardev *s, const uint8_t *buf, size_t len)
88178fe0aeSMarc-André Lureau {
89178fe0aeSMarc-André Lureau size_t done = 0;
90178fe0aeSMarc-André Lureau ssize_t ret;
91178fe0aeSMarc-André Lureau
92178fe0aeSMarc-André Lureau if (s->logfd < 0) {
93178fe0aeSMarc-André Lureau return;
94178fe0aeSMarc-André Lureau }
95178fe0aeSMarc-André Lureau
96178fe0aeSMarc-André Lureau while (done < len) {
97178fe0aeSMarc-André Lureau retry:
98178fe0aeSMarc-André Lureau ret = write(s->logfd, buf + done, len - done);
99178fe0aeSMarc-André Lureau if (ret == -1 && errno == EAGAIN) {
100178fe0aeSMarc-André Lureau g_usleep(100);
101178fe0aeSMarc-André Lureau goto retry;
102178fe0aeSMarc-André Lureau }
103178fe0aeSMarc-André Lureau
104178fe0aeSMarc-André Lureau if (ret <= 0) {
105178fe0aeSMarc-André Lureau return;
106178fe0aeSMarc-André Lureau }
107178fe0aeSMarc-André Lureau done += ret;
108178fe0aeSMarc-André Lureau }
109178fe0aeSMarc-André Lureau }
110178fe0aeSMarc-André Lureau
qemu_chr_write_buffer(Chardev * s,const uint8_t * buf,int len,int * offset,bool write_all)111a9b1ca38SMarc-André Lureau static int qemu_chr_write_buffer(Chardev *s,
112c90e9392SMarc-André Lureau const uint8_t *buf, int len,
113c90e9392SMarc-André Lureau int *offset, bool write_all)
114178fe0aeSMarc-André Lureau {
115178fe0aeSMarc-André Lureau ChardevClass *cc = CHARDEV_GET_CLASS(s);
116178fe0aeSMarc-André Lureau int res = 0;
117178fe0aeSMarc-André Lureau *offset = 0;
118178fe0aeSMarc-André Lureau
119178fe0aeSMarc-André Lureau qemu_mutex_lock(&s->chr_write_lock);
120178fe0aeSMarc-André Lureau while (*offset < len) {
121178fe0aeSMarc-André Lureau retry:
122178fe0aeSMarc-André Lureau res = cc->chr_write(s, buf + *offset, len - *offset);
123c90e9392SMarc-André Lureau if (res < 0 && errno == EAGAIN && write_all) {
1242158fa1bSLukas Straub if (qemu_in_coroutine()) {
1252158fa1bSLukas Straub qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
1262158fa1bSLukas Straub } else {
127178fe0aeSMarc-André Lureau g_usleep(100);
1282158fa1bSLukas Straub }
129178fe0aeSMarc-André Lureau goto retry;
130178fe0aeSMarc-André Lureau }
131178fe0aeSMarc-André Lureau
132178fe0aeSMarc-André Lureau if (res <= 0) {
133178fe0aeSMarc-André Lureau break;
134178fe0aeSMarc-André Lureau }
135178fe0aeSMarc-André Lureau
136178fe0aeSMarc-André Lureau *offset += res;
137c90e9392SMarc-André Lureau if (!write_all) {
138c90e9392SMarc-André Lureau break;
139c90e9392SMarc-André Lureau }
140178fe0aeSMarc-André Lureau }
141178fe0aeSMarc-André Lureau if (*offset > 0) {
142d96aa5dbSDaniel P. Berrangé /*
143d96aa5dbSDaniel P. Berrangé * If some data was written by backend, we should
144d96aa5dbSDaniel P. Berrangé * only log what was actually written. This method
145d96aa5dbSDaniel P. Berrangé * may be invoked again to write the remaining
146d96aa5dbSDaniel P. Berrangé * method, thus we'll log the remainder at that time.
147d96aa5dbSDaniel P. Berrangé */
148a9b1ca38SMarc-André Lureau qemu_chr_write_log(s, buf, *offset);
149d96aa5dbSDaniel P. Berrangé } else if (res < 0) {
150d96aa5dbSDaniel P. Berrangé /*
151d96aa5dbSDaniel P. Berrangé * If a fatal error was reported by the backend,
152d96aa5dbSDaniel P. Berrangé * assume this method won't be invoked again with
153d96aa5dbSDaniel P. Berrangé * this buffer, so log it all right away.
154d96aa5dbSDaniel P. Berrangé */
155d96aa5dbSDaniel P. Berrangé qemu_chr_write_log(s, buf, len);
156178fe0aeSMarc-André Lureau }
157178fe0aeSMarc-André Lureau qemu_mutex_unlock(&s->chr_write_lock);
158178fe0aeSMarc-André Lureau
159178fe0aeSMarc-André Lureau return res;
160178fe0aeSMarc-André Lureau }
161178fe0aeSMarc-André Lureau
qemu_chr_write(Chardev * s,const uint8_t * buf,int len,bool write_all)1624d43a603SMarc-André Lureau int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all)
163178fe0aeSMarc-André Lureau {
164c90e9392SMarc-André Lureau int offset = 0;
165178fe0aeSMarc-André Lureau int res;
166178fe0aeSMarc-André Lureau
167178fe0aeSMarc-André Lureau if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
168178fe0aeSMarc-André Lureau replay_char_write_event_load(&res, &offset);
169178fe0aeSMarc-André Lureau assert(offset <= len);
170a9b1ca38SMarc-André Lureau qemu_chr_write_buffer(s, buf, offset, &offset, true);
171178fe0aeSMarc-André Lureau return res;
172178fe0aeSMarc-André Lureau }
173178fe0aeSMarc-André Lureau
1747b571ef3SAlex Bennée if (replay_mode == REPLAY_MODE_RECORD) {
1757b571ef3SAlex Bennée /*
1767b571ef3SAlex Bennée * When recording we don't want temporary conditions to
1777b571ef3SAlex Bennée * perturb the result. By ensuring we write everything we can
1787b571ef3SAlex Bennée * while recording we avoid playback being out of sync if it
1797b571ef3SAlex Bennée * doesn't encounter the same temporary conditions (usually
1807b571ef3SAlex Bennée * triggered by external programs not reading the chardev fast
1817b571ef3SAlex Bennée * enough and pipes filling up).
1827b571ef3SAlex Bennée */
1837b571ef3SAlex Bennée write_all = true;
1847b571ef3SAlex Bennée }
1857b571ef3SAlex Bennée
186a9b1ca38SMarc-André Lureau res = qemu_chr_write_buffer(s, buf, len, &offset, write_all);
187178fe0aeSMarc-André Lureau
188178fe0aeSMarc-André Lureau if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
189178fe0aeSMarc-André Lureau replay_char_write_event_save(res, offset);
190178fe0aeSMarc-André Lureau }
191178fe0aeSMarc-André Lureau
192178fe0aeSMarc-André Lureau if (res < 0) {
193178fe0aeSMarc-André Lureau return res;
194178fe0aeSMarc-André Lureau }
195178fe0aeSMarc-André Lureau return offset;
196178fe0aeSMarc-André Lureau }
197178fe0aeSMarc-André Lureau
qemu_chr_be_can_write(Chardev * s)198178fe0aeSMarc-André Lureau int qemu_chr_be_can_write(Chardev *s)
199178fe0aeSMarc-André Lureau {
200178fe0aeSMarc-André Lureau CharBackend *be = s->be;
201178fe0aeSMarc-André Lureau
202178fe0aeSMarc-André Lureau if (!be || !be->chr_can_read) {
203178fe0aeSMarc-André Lureau return 0;
204178fe0aeSMarc-André Lureau }
205178fe0aeSMarc-André Lureau
206178fe0aeSMarc-André Lureau return be->chr_can_read(be->opaque);
207178fe0aeSMarc-André Lureau }
208178fe0aeSMarc-André Lureau
qemu_chr_be_write_impl(Chardev * s,const uint8_t * buf,int len)2098f9abdf5SArwed Meyer void qemu_chr_be_write_impl(Chardev *s, const uint8_t *buf, int len)
210178fe0aeSMarc-André Lureau {
211178fe0aeSMarc-André Lureau CharBackend *be = s->be;
212178fe0aeSMarc-André Lureau
213178fe0aeSMarc-André Lureau if (be && be->chr_read) {
214178fe0aeSMarc-André Lureau be->chr_read(be->opaque, buf, len);
215178fe0aeSMarc-André Lureau }
216178fe0aeSMarc-André Lureau }
217178fe0aeSMarc-André Lureau
qemu_chr_be_write(Chardev * s,const uint8_t * buf,int len)2188f9abdf5SArwed Meyer void qemu_chr_be_write(Chardev *s, const uint8_t *buf, int len)
219178fe0aeSMarc-André Lureau {
220178fe0aeSMarc-André Lureau if (qemu_chr_replay(s)) {
221178fe0aeSMarc-André Lureau if (replay_mode == REPLAY_MODE_PLAY) {
222178fe0aeSMarc-André Lureau return;
223178fe0aeSMarc-André Lureau }
224178fe0aeSMarc-André Lureau replay_chr_be_write(s, buf, len);
225178fe0aeSMarc-André Lureau } else {
226178fe0aeSMarc-André Lureau qemu_chr_be_write_impl(s, buf, len);
227178fe0aeSMarc-André Lureau }
228178fe0aeSMarc-André Lureau }
229178fe0aeSMarc-André Lureau
qemu_chr_be_update_read_handlers(Chardev * s,GMainContext * context)23007241c20SPeter Xu void qemu_chr_be_update_read_handlers(Chardev *s,
23107241c20SPeter Xu GMainContext *context)
23207241c20SPeter Xu {
23307241c20SPeter Xu ChardevClass *cc = CHARDEV_GET_CLASS(s);
23407241c20SPeter Xu
2359ac3788bSMarc-André Lureau assert(qemu_chr_has_feature(s, QEMU_CHAR_FEATURE_GCONTEXT)
2369ac3788bSMarc-André Lureau || !context);
23795eeeba6SPeter Xu s->gcontext = context;
23807241c20SPeter Xu if (cc->chr_update_read_handler) {
239bb86d05fSPeter Xu cc->chr_update_read_handler(s);
24007241c20SPeter Xu }
24107241c20SPeter Xu }
24207241c20SPeter Xu
qemu_chr_add_client(Chardev * s,int fd)243178fe0aeSMarc-André Lureau int qemu_chr_add_client(Chardev *s, int fd)
244178fe0aeSMarc-André Lureau {
245178fe0aeSMarc-André Lureau return CHARDEV_GET_CLASS(s)->chr_add_client ?
246178fe0aeSMarc-André Lureau CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1;
247178fe0aeSMarc-André Lureau }
248178fe0aeSMarc-André Lureau
qemu_char_open(Chardev * chr,ChardevBackend * backend,bool * be_opened,Error ** errp)249178fe0aeSMarc-André Lureau static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
250178fe0aeSMarc-André Lureau bool *be_opened, Error **errp)
251178fe0aeSMarc-André Lureau {
252178fe0aeSMarc-André Lureau ChardevClass *cc = CHARDEV_GET_CLASS(chr);
253178fe0aeSMarc-André Lureau /* Any ChardevCommon member would work */
254178fe0aeSMarc-André Lureau ChardevCommon *common = backend ? backend->u.null.data : NULL;
255178fe0aeSMarc-André Lureau
2568de69efaSMarkus Armbruster if (common && common->logfile) {
257692277f3SMichal Privoznik int flags = O_WRONLY;
258178fe0aeSMarc-André Lureau if (common->has_logappend &&
259178fe0aeSMarc-André Lureau common->logappend) {
260178fe0aeSMarc-André Lureau flags |= O_APPEND;
261178fe0aeSMarc-André Lureau } else {
262178fe0aeSMarc-André Lureau flags |= O_TRUNC;
263178fe0aeSMarc-André Lureau }
264692277f3SMichal Privoznik chr->logfd = qemu_create(common->logfile, flags, 0666, errp);
265178fe0aeSMarc-André Lureau if (chr->logfd < 0) {
266178fe0aeSMarc-André Lureau return;
267178fe0aeSMarc-André Lureau }
268178fe0aeSMarc-André Lureau }
269178fe0aeSMarc-André Lureau
270178fe0aeSMarc-André Lureau if (cc->open) {
271178fe0aeSMarc-André Lureau cc->open(chr, backend, be_opened, errp);
272178fe0aeSMarc-André Lureau }
273178fe0aeSMarc-André Lureau }
274178fe0aeSMarc-André Lureau
char_init(Object * obj)275178fe0aeSMarc-André Lureau static void char_init(Object *obj)
276178fe0aeSMarc-André Lureau {
277178fe0aeSMarc-André Lureau Chardev *chr = CHARDEV(obj);
278178fe0aeSMarc-André Lureau
279feb774caSLukas Straub chr->handover_yank_instance = false;
280178fe0aeSMarc-André Lureau chr->logfd = -1;
281178fe0aeSMarc-André Lureau qemu_mutex_init(&chr->chr_write_lock);
2829ac3788bSMarc-André Lureau
2839ac3788bSMarc-André Lureau /*
2849ac3788bSMarc-André Lureau * Assume if chr_update_read_handler is implemented it will
2859ac3788bSMarc-André Lureau * take the updated gcontext into account.
2869ac3788bSMarc-André Lureau */
2879ac3788bSMarc-André Lureau if (CHARDEV_GET_CLASS(chr)->chr_update_read_handler) {
2889ac3788bSMarc-André Lureau qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT);
2899ac3788bSMarc-André Lureau }
2909ac3788bSMarc-André Lureau
291178fe0aeSMarc-André Lureau }
292178fe0aeSMarc-André Lureau
null_chr_write(Chardev * chr,const uint8_t * buf,int len)293eb314a94SMarc-André Lureau static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
294eb314a94SMarc-André Lureau {
295eb314a94SMarc-André Lureau return len;
296eb314a94SMarc-André Lureau }
297eb314a94SMarc-André Lureau
char_class_init(ObjectClass * oc,void * data)298eb314a94SMarc-André Lureau static void char_class_init(ObjectClass *oc, void *data)
299eb314a94SMarc-André Lureau {
300eb314a94SMarc-André Lureau ChardevClass *cc = CHARDEV_CLASS(oc);
301eb314a94SMarc-André Lureau
302eb314a94SMarc-André Lureau cc->chr_write = null_chr_write;
303d09c4a47SMarc-André Lureau cc->chr_be_event = chr_be_event;
304eb314a94SMarc-André Lureau }
305eb314a94SMarc-André Lureau
char_finalize(Object * obj)306178fe0aeSMarc-André Lureau static void char_finalize(Object *obj)
307178fe0aeSMarc-André Lureau {
308178fe0aeSMarc-André Lureau Chardev *chr = CHARDEV(obj);
309178fe0aeSMarc-André Lureau
310178fe0aeSMarc-André Lureau if (chr->be) {
311178fe0aeSMarc-André Lureau chr->be->chr = NULL;
312178fe0aeSMarc-André Lureau }
313178fe0aeSMarc-André Lureau g_free(chr->filename);
314178fe0aeSMarc-André Lureau g_free(chr->label);
315178fe0aeSMarc-André Lureau if (chr->logfd != -1) {
316178fe0aeSMarc-André Lureau close(chr->logfd);
317178fe0aeSMarc-André Lureau }
318178fe0aeSMarc-André Lureau qemu_mutex_destroy(&chr->chr_write_lock);
319178fe0aeSMarc-André Lureau }
320178fe0aeSMarc-André Lureau
321178fe0aeSMarc-André Lureau static const TypeInfo char_type_info = {
322178fe0aeSMarc-André Lureau .name = TYPE_CHARDEV,
323178fe0aeSMarc-André Lureau .parent = TYPE_OBJECT,
324178fe0aeSMarc-André Lureau .instance_size = sizeof(Chardev),
325178fe0aeSMarc-André Lureau .instance_init = char_init,
326178fe0aeSMarc-André Lureau .instance_finalize = char_finalize,
327178fe0aeSMarc-André Lureau .abstract = true,
328178fe0aeSMarc-André Lureau .class_size = sizeof(ChardevClass),
329eb314a94SMarc-André Lureau .class_init = char_class_init,
330178fe0aeSMarc-André Lureau };
331178fe0aeSMarc-André Lureau
qemu_chr_is_busy(Chardev * s)332178fe0aeSMarc-André Lureau static bool qemu_chr_is_busy(Chardev *s)
333178fe0aeSMarc-André Lureau {
334178fe0aeSMarc-André Lureau if (CHARDEV_IS_MUX(s)) {
335178fe0aeSMarc-André Lureau MuxChardev *d = MUX_CHARDEV(s);
336005b6d51SRoman Penyaev return d->mux_bitset != 0;
337178fe0aeSMarc-André Lureau } else {
338178fe0aeSMarc-André Lureau return s->be != NULL;
339178fe0aeSMarc-André Lureau }
340178fe0aeSMarc-André Lureau }
341178fe0aeSMarc-André Lureau
qemu_chr_wait_connected(Chardev * chr,Error ** errp)342d24ca4b8SMarc-André Lureau int qemu_chr_wait_connected(Chardev *chr, Error **errp)
343178fe0aeSMarc-André Lureau {
344178fe0aeSMarc-André Lureau ChardevClass *cc = CHARDEV_GET_CLASS(chr);
345178fe0aeSMarc-André Lureau
346178fe0aeSMarc-André Lureau if (cc->chr_wait_connected) {
347178fe0aeSMarc-André Lureau return cc->chr_wait_connected(chr, errp);
348178fe0aeSMarc-André Lureau }
349178fe0aeSMarc-André Lureau
350178fe0aeSMarc-André Lureau return 0;
351178fe0aeSMarc-André Lureau }
352178fe0aeSMarc-André Lureau
qemu_chr_parse_compat(const char * label,const char * filename,bool permit_mux_mon)35395e30b2aSMarc-André Lureau QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
35495e30b2aSMarc-André Lureau bool permit_mux_mon)
355178fe0aeSMarc-André Lureau {
356178fe0aeSMarc-André Lureau char host[65], port[33], width[8], height[8];
357178fe0aeSMarc-André Lureau int pos;
358178fe0aeSMarc-André Lureau const char *p;
359178fe0aeSMarc-André Lureau QemuOpts *opts;
360178fe0aeSMarc-André Lureau Error *local_err = NULL;
361178fe0aeSMarc-André Lureau
362178fe0aeSMarc-André Lureau opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
363178fe0aeSMarc-André Lureau if (local_err) {
364178fe0aeSMarc-André Lureau error_report_err(local_err);
365178fe0aeSMarc-André Lureau return NULL;
366178fe0aeSMarc-André Lureau }
367178fe0aeSMarc-André Lureau
368178fe0aeSMarc-André Lureau if (strstart(filename, "mon:", &p)) {
36995e30b2aSMarc-André Lureau if (!permit_mux_mon) {
37095e30b2aSMarc-André Lureau error_report("mon: isn't supported in this context");
37195e30b2aSMarc-André Lureau return NULL;
37295e30b2aSMarc-André Lureau }
373178fe0aeSMarc-André Lureau filename = p;
374178fe0aeSMarc-André Lureau qemu_opt_set(opts, "mux", "on", &error_abort);
375178fe0aeSMarc-André Lureau if (strcmp(filename, "stdio") == 0) {
376178fe0aeSMarc-André Lureau /* Monitor is muxed to stdio: do not exit on Ctrl+C by default
377178fe0aeSMarc-André Lureau * but pass it to the guest. Handle this only for compat syntax,
378178fe0aeSMarc-André Lureau * for -chardev syntax we have special option for this.
379178fe0aeSMarc-André Lureau * This is what -nographic did, redirecting+muxing serial+monitor
380178fe0aeSMarc-André Lureau * to stdio causing Ctrl+C to be passed to guest. */
381178fe0aeSMarc-André Lureau qemu_opt_set(opts, "signal", "off", &error_abort);
382178fe0aeSMarc-André Lureau }
383178fe0aeSMarc-André Lureau }
384178fe0aeSMarc-André Lureau
385178fe0aeSMarc-André Lureau if (strcmp(filename, "null") == 0 ||
386178fe0aeSMarc-André Lureau strcmp(filename, "pty") == 0 ||
387178fe0aeSMarc-André Lureau strcmp(filename, "msmouse") == 0 ||
388378af961SAnatoli Huseu1 strcmp(filename, "wctablet") == 0 ||
389178fe0aeSMarc-André Lureau strcmp(filename, "braille") == 0 ||
390178fe0aeSMarc-André Lureau strcmp(filename, "testdev") == 0 ||
391178fe0aeSMarc-André Lureau strcmp(filename, "stdio") == 0) {
392178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", filename, &error_abort);
393178fe0aeSMarc-André Lureau return opts;
394178fe0aeSMarc-André Lureau }
395178fe0aeSMarc-André Lureau if (strstart(filename, "vc", &p)) {
396178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", "vc", &error_abort);
397178fe0aeSMarc-André Lureau if (*p == ':') {
398178fe0aeSMarc-André Lureau if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) {
399178fe0aeSMarc-André Lureau /* pixels */
400178fe0aeSMarc-André Lureau qemu_opt_set(opts, "width", width, &error_abort);
401178fe0aeSMarc-André Lureau qemu_opt_set(opts, "height", height, &error_abort);
402178fe0aeSMarc-André Lureau } else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) {
403178fe0aeSMarc-André Lureau /* chars */
404178fe0aeSMarc-André Lureau qemu_opt_set(opts, "cols", width, &error_abort);
405178fe0aeSMarc-André Lureau qemu_opt_set(opts, "rows", height, &error_abort);
406178fe0aeSMarc-André Lureau } else {
407178fe0aeSMarc-André Lureau goto fail;
408178fe0aeSMarc-André Lureau }
409178fe0aeSMarc-André Lureau }
410178fe0aeSMarc-André Lureau return opts;
411178fe0aeSMarc-André Lureau }
412178fe0aeSMarc-André Lureau if (strcmp(filename, "con:") == 0) {
413178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", "console", &error_abort);
414178fe0aeSMarc-André Lureau return opts;
415178fe0aeSMarc-André Lureau }
416178fe0aeSMarc-André Lureau if (strstart(filename, "COM", NULL)) {
417178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", "serial", &error_abort);
418178fe0aeSMarc-André Lureau qemu_opt_set(opts, "path", filename, &error_abort);
419178fe0aeSMarc-André Lureau return opts;
420178fe0aeSMarc-André Lureau }
421178fe0aeSMarc-André Lureau if (strstart(filename, "file:", &p)) {
422178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", "file", &error_abort);
423178fe0aeSMarc-André Lureau qemu_opt_set(opts, "path", p, &error_abort);
424178fe0aeSMarc-André Lureau return opts;
425178fe0aeSMarc-André Lureau }
426178fe0aeSMarc-André Lureau if (strstart(filename, "pipe:", &p)) {
427178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", "pipe", &error_abort);
428178fe0aeSMarc-André Lureau qemu_opt_set(opts, "path", p, &error_abort);
429178fe0aeSMarc-André Lureau return opts;
430178fe0aeSMarc-André Lureau }
431b74cb876SOctavian Purdila if (strstart(filename, "pty:", &p)) {
432b74cb876SOctavian Purdila qemu_opt_set(opts, "backend", "pty", &error_abort);
433b74cb876SOctavian Purdila qemu_opt_set(opts, "path", p, &error_abort);
434b74cb876SOctavian Purdila return opts;
435b74cb876SOctavian Purdila }
436178fe0aeSMarc-André Lureau if (strstart(filename, "tcp:", &p) ||
437ae92cbd5SJing Liu strstart(filename, "telnet:", &p) ||
438981b06e7SJulia Suvorova strstart(filename, "tn3270:", &p) ||
439981b06e7SJulia Suvorova strstart(filename, "websocket:", &p)) {
440178fe0aeSMarc-André Lureau if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
441178fe0aeSMarc-André Lureau host[0] = 0;
442178fe0aeSMarc-André Lureau if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
443178fe0aeSMarc-André Lureau goto fail;
444178fe0aeSMarc-André Lureau }
445178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", "socket", &error_abort);
446178fe0aeSMarc-André Lureau qemu_opt_set(opts, "host", host, &error_abort);
447178fe0aeSMarc-André Lureau qemu_opt_set(opts, "port", port, &error_abort);
448178fe0aeSMarc-André Lureau if (p[pos] == ',') {
449235e59cfSMarkus Armbruster if (!qemu_opts_do_parse(opts, p + pos + 1, NULL, &local_err)) {
450178fe0aeSMarc-André Lureau error_report_err(local_err);
451178fe0aeSMarc-André Lureau goto fail;
452178fe0aeSMarc-André Lureau }
453178fe0aeSMarc-André Lureau }
454ae92cbd5SJing Liu if (strstart(filename, "telnet:", &p)) {
455178fe0aeSMarc-André Lureau qemu_opt_set(opts, "telnet", "on", &error_abort);
456ae92cbd5SJing Liu } else if (strstart(filename, "tn3270:", &p)) {
457ae92cbd5SJing Liu qemu_opt_set(opts, "tn3270", "on", &error_abort);
458981b06e7SJulia Suvorova } else if (strstart(filename, "websocket:", &p)) {
459981b06e7SJulia Suvorova qemu_opt_set(opts, "websocket", "on", &error_abort);
460ae92cbd5SJing Liu }
461178fe0aeSMarc-André Lureau return opts;
462178fe0aeSMarc-André Lureau }
463178fe0aeSMarc-André Lureau if (strstart(filename, "udp:", &p)) {
464178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", "udp", &error_abort);
465178fe0aeSMarc-André Lureau if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
466178fe0aeSMarc-André Lureau host[0] = 0;
467178fe0aeSMarc-André Lureau if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
468178fe0aeSMarc-André Lureau goto fail;
469178fe0aeSMarc-André Lureau }
470178fe0aeSMarc-André Lureau }
471178fe0aeSMarc-André Lureau qemu_opt_set(opts, "host", host, &error_abort);
472178fe0aeSMarc-André Lureau qemu_opt_set(opts, "port", port, &error_abort);
473178fe0aeSMarc-André Lureau if (p[pos] == '@') {
474178fe0aeSMarc-André Lureau p += pos + 1;
475178fe0aeSMarc-André Lureau if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
476178fe0aeSMarc-André Lureau host[0] = 0;
477178fe0aeSMarc-André Lureau if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) {
478178fe0aeSMarc-André Lureau goto fail;
479178fe0aeSMarc-André Lureau }
480178fe0aeSMarc-André Lureau }
481178fe0aeSMarc-André Lureau qemu_opt_set(opts, "localaddr", host, &error_abort);
482178fe0aeSMarc-André Lureau qemu_opt_set(opts, "localport", port, &error_abort);
483178fe0aeSMarc-André Lureau }
484178fe0aeSMarc-André Lureau return opts;
485178fe0aeSMarc-André Lureau }
486178fe0aeSMarc-André Lureau if (strstart(filename, "unix:", &p)) {
487178fe0aeSMarc-André Lureau qemu_opt_set(opts, "backend", "socket", &error_abort);
488235e59cfSMarkus Armbruster if (!qemu_opts_do_parse(opts, p, "path", &local_err)) {
489178fe0aeSMarc-André Lureau error_report_err(local_err);
490178fe0aeSMarc-André Lureau goto fail;
491178fe0aeSMarc-André Lureau }
492178fe0aeSMarc-André Lureau return opts;
493178fe0aeSMarc-André Lureau }
494178fe0aeSMarc-André Lureau if (strstart(filename, "/dev/parport", NULL) ||
495178fe0aeSMarc-André Lureau strstart(filename, "/dev/ppi", NULL)) {
49673119c28SMarc-André Lureau qemu_opt_set(opts, "backend", "parallel", &error_abort);
497178fe0aeSMarc-André Lureau qemu_opt_set(opts, "path", filename, &error_abort);
498178fe0aeSMarc-André Lureau return opts;
499178fe0aeSMarc-André Lureau }
500178fe0aeSMarc-André Lureau if (strstart(filename, "/dev/", NULL)) {
50173119c28SMarc-André Lureau qemu_opt_set(opts, "backend", "serial", &error_abort);
502178fe0aeSMarc-André Lureau qemu_opt_set(opts, "path", filename, &error_abort);
503178fe0aeSMarc-André Lureau return opts;
504178fe0aeSMarc-André Lureau }
505178fe0aeSMarc-André Lureau
5060bf62dc8SDaniel P. Berrangé error_report("'%s' is not a valid char driver", filename);
5070bf62dc8SDaniel P. Berrangé
508178fe0aeSMarc-André Lureau fail:
509178fe0aeSMarc-André Lureau qemu_opts_del(opts);
510178fe0aeSMarc-André Lureau return NULL;
511178fe0aeSMarc-André Lureau }
512178fe0aeSMarc-André Lureau
qemu_chr_parse_common(QemuOpts * opts,ChardevCommon * backend)513178fe0aeSMarc-André Lureau void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
514178fe0aeSMarc-André Lureau {
515178fe0aeSMarc-André Lureau const char *logfile = qemu_opt_get(opts, "logfile");
516178fe0aeSMarc-André Lureau
517e1f98fe4SMarc-André Lureau backend->logfile = g_strdup(logfile);
518178fe0aeSMarc-André Lureau backend->has_logappend = true;
519178fe0aeSMarc-André Lureau backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
520178fe0aeSMarc-André Lureau }
521178fe0aeSMarc-André Lureau
char_get_class(const char * driver,Error ** errp)522178fe0aeSMarc-André Lureau static const ChardevClass *char_get_class(const char *driver, Error **errp)
523178fe0aeSMarc-André Lureau {
524178fe0aeSMarc-André Lureau ObjectClass *oc;
525178fe0aeSMarc-André Lureau const ChardevClass *cc;
526178fe0aeSMarc-André Lureau char *typename = g_strdup_printf("chardev-%s", driver);
527178fe0aeSMarc-André Lureau
528ef138c77SGerd Hoffmann oc = module_object_class_by_name(typename);
529178fe0aeSMarc-André Lureau g_free(typename);
530178fe0aeSMarc-André Lureau
531178fe0aeSMarc-André Lureau if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) {
532178fe0aeSMarc-André Lureau error_setg(errp, "'%s' is not a valid char driver name", driver);
533178fe0aeSMarc-André Lureau return NULL;
534178fe0aeSMarc-André Lureau }
535178fe0aeSMarc-André Lureau
536178fe0aeSMarc-André Lureau if (object_class_is_abstract(oc)) {
537178fe0aeSMarc-André Lureau error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
5384ad87cd4SMichael Tokarev "a non-abstract device type");
539178fe0aeSMarc-André Lureau return NULL;
540178fe0aeSMarc-André Lureau }
541178fe0aeSMarc-André Lureau
542178fe0aeSMarc-André Lureau cc = CHARDEV_CLASS(oc);
543178fe0aeSMarc-André Lureau if (cc->internal) {
544178fe0aeSMarc-André Lureau error_setg(errp, "'%s' is not a valid char driver name", driver);
545178fe0aeSMarc-André Lureau return NULL;
546178fe0aeSMarc-André Lureau }
547178fe0aeSMarc-André Lureau
548178fe0aeSMarc-André Lureau return cc;
549178fe0aeSMarc-André Lureau }
550178fe0aeSMarc-André Lureau
551178fe0aeSMarc-André Lureau typedef struct ChadevClassFE {
552f3b70e07SKevin Wolf void (*fn)(const char *name, void *opaque);
553178fe0aeSMarc-André Lureau void *opaque;
554178fe0aeSMarc-André Lureau } ChadevClassFE;
555178fe0aeSMarc-André Lureau
556178fe0aeSMarc-André Lureau static void
chardev_class_foreach(ObjectClass * klass,void * opaque)557178fe0aeSMarc-André Lureau chardev_class_foreach(ObjectClass *klass, void *opaque)
558178fe0aeSMarc-André Lureau {
559178fe0aeSMarc-André Lureau ChadevClassFE *fe = opaque;
560178fe0aeSMarc-André Lureau
561178fe0aeSMarc-André Lureau assert(g_str_has_prefix(object_class_get_name(klass), "chardev-"));
562178fe0aeSMarc-André Lureau if (CHARDEV_CLASS(klass)->internal) {
563178fe0aeSMarc-André Lureau return;
564178fe0aeSMarc-André Lureau }
565178fe0aeSMarc-André Lureau
566f3b70e07SKevin Wolf fe->fn(object_class_get_name(klass) + 8, fe->opaque);
567178fe0aeSMarc-André Lureau }
568178fe0aeSMarc-André Lureau
569178fe0aeSMarc-André Lureau static void
chardev_name_foreach(void (* fn)(const char * name,void * opaque),void * opaque)570f3b70e07SKevin Wolf chardev_name_foreach(void (*fn)(const char *name, void *opaque),
5711254bd39SKevin Wolf void *opaque)
572178fe0aeSMarc-André Lureau {
573178fe0aeSMarc-André Lureau ChadevClassFE fe = { .fn = fn, .opaque = opaque };
574178fe0aeSMarc-André Lureau
575178fe0aeSMarc-André Lureau object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe);
576178fe0aeSMarc-André Lureau }
577178fe0aeSMarc-André Lureau
578178fe0aeSMarc-André Lureau static void
help_string_append(const char * name,void * opaque)579f3b70e07SKevin Wolf help_string_append(const char *name, void *opaque)
580178fe0aeSMarc-André Lureau {
581178fe0aeSMarc-André Lureau GString *str = opaque;
582178fe0aeSMarc-André Lureau
583178fe0aeSMarc-André Lureau g_string_append_printf(str, "\n %s", name);
584178fe0aeSMarc-André Lureau }
585178fe0aeSMarc-André Lureau
qemu_chr_parse_opts(QemuOpts * opts,Error ** errp)58675b60160SAnton Nefedov ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp)
587178fe0aeSMarc-André Lureau {
588178fe0aeSMarc-André Lureau Error *local_err = NULL;
589178fe0aeSMarc-André Lureau const ChardevClass *cc;
590178fe0aeSMarc-André Lureau ChardevBackend *backend = NULL;
5916f9f6308SPaolo Bonzini const char *name = qemu_opt_get(opts, "backend");
592178fe0aeSMarc-André Lureau
593178fe0aeSMarc-André Lureau if (name == NULL) {
594178fe0aeSMarc-André Lureau error_setg(errp, "chardev: \"%s\" missing backend",
595178fe0aeSMarc-André Lureau qemu_opts_id(opts));
596178fe0aeSMarc-André Lureau return NULL;
597178fe0aeSMarc-André Lureau }
598178fe0aeSMarc-André Lureau
599313e45b5SAnton Nefedov cc = char_get_class(name, errp);
600313e45b5SAnton Nefedov if (cc == NULL) {
601313e45b5SAnton Nefedov return NULL;
602313e45b5SAnton Nefedov }
603313e45b5SAnton Nefedov
604313e45b5SAnton Nefedov backend = g_new0(ChardevBackend, 1);
605313e45b5SAnton Nefedov backend->type = CHARDEV_BACKEND_KIND_NULL;
606313e45b5SAnton Nefedov
607313e45b5SAnton Nefedov if (cc->parse) {
608313e45b5SAnton Nefedov cc->parse(opts, backend, &local_err);
609313e45b5SAnton Nefedov if (local_err) {
610313e45b5SAnton Nefedov error_propagate(errp, local_err);
611313e45b5SAnton Nefedov qapi_free_ChardevBackend(backend);
612313e45b5SAnton Nefedov return NULL;
613313e45b5SAnton Nefedov }
614313e45b5SAnton Nefedov } else {
615313e45b5SAnton Nefedov ChardevCommon *ccom = g_new0(ChardevCommon, 1);
616313e45b5SAnton Nefedov qemu_chr_parse_common(opts, ccom);
617313e45b5SAnton Nefedov backend->u.null.data = ccom; /* Any ChardevCommon member would work */
618313e45b5SAnton Nefedov }
619313e45b5SAnton Nefedov
620313e45b5SAnton Nefedov return backend;
621313e45b5SAnton Nefedov }
622313e45b5SAnton Nefedov
qemu_chardev_set_replay(Chardev * chr,Error ** errp)6234c193bb1SNicholas Piggin static void qemu_chardev_set_replay(Chardev *chr, Error **errp)
6244c193bb1SNicholas Piggin {
6254c193bb1SNicholas Piggin if (replay_mode != REPLAY_MODE_NONE) {
6264c193bb1SNicholas Piggin if (CHARDEV_GET_CLASS(chr)->chr_ioctl) {
6274c193bb1SNicholas Piggin error_setg(errp, "Replay: ioctl is not supported "
6284c193bb1SNicholas Piggin "for serial devices yet");
6294c193bb1SNicholas Piggin return;
6304c193bb1SNicholas Piggin }
6314c193bb1SNicholas Piggin qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
6324c193bb1SNicholas Piggin replay_register_char_driver(chr);
6334c193bb1SNicholas Piggin }
6344c193bb1SNicholas Piggin }
6354c193bb1SNicholas Piggin
do_qemu_chr_new_from_opts(QemuOpts * opts,GMainContext * context,bool replay,Error ** errp)636*3c8ab23fSNicholas Piggin static Chardev *do_qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
6374c193bb1SNicholas Piggin bool replay, Error **errp)
638313e45b5SAnton Nefedov {
639313e45b5SAnton Nefedov const ChardevClass *cc;
6404c193bb1SNicholas Piggin Chardev *base = NULL, *chr = NULL;
641313e45b5SAnton Nefedov ChardevBackend *backend = NULL;
6426f9f6308SPaolo Bonzini const char *name = qemu_opt_get(opts, "backend");
643313e45b5SAnton Nefedov const char *id = qemu_opts_id(opts);
644313e45b5SAnton Nefedov char *bid = NULL;
645313e45b5SAnton Nefedov
646313e45b5SAnton Nefedov if (name && is_help_option(name)) {
647178fe0aeSMarc-André Lureau GString *str = g_string_new("");
648178fe0aeSMarc-André Lureau
649178fe0aeSMarc-André Lureau chardev_name_foreach(help_string_append, str);
650178fe0aeSMarc-André Lureau
651ef2fd6f1SMarkus Armbruster qemu_printf("Available chardev backend types: %s\n", str->str);
652178fe0aeSMarc-André Lureau g_string_free(str, true);
6530ec846bfSAnton Nefedov return NULL;
654178fe0aeSMarc-André Lureau }
655178fe0aeSMarc-André Lureau
656178fe0aeSMarc-André Lureau if (id == NULL) {
657178fe0aeSMarc-André Lureau error_setg(errp, "chardev: no id specified");
658178fe0aeSMarc-André Lureau return NULL;
659178fe0aeSMarc-André Lureau }
660178fe0aeSMarc-André Lureau
661313e45b5SAnton Nefedov backend = qemu_chr_parse_opts(opts, errp);
662313e45b5SAnton Nefedov if (backend == NULL) {
663313e45b5SAnton Nefedov return NULL;
664178fe0aeSMarc-André Lureau }
665178fe0aeSMarc-André Lureau
666178fe0aeSMarc-André Lureau cc = char_get_class(name, errp);
667178fe0aeSMarc-André Lureau if (cc == NULL) {
668313e45b5SAnton Nefedov goto out;
669178fe0aeSMarc-André Lureau }
670178fe0aeSMarc-André Lureau
671178fe0aeSMarc-André Lureau if (qemu_opt_get_bool(opts, "mux", 0)) {
672178fe0aeSMarc-André Lureau bid = g_strdup_printf("%s-base", id);
673178fe0aeSMarc-André Lureau }
674178fe0aeSMarc-André Lureau
675c5749f7cSMarc-André Lureau chr = qemu_chardev_new(bid ? bid : id,
676178fe0aeSMarc-André Lureau object_class_get_name(OBJECT_CLASS(cc)),
6774ad6f6cbSPaolo Bonzini backend, context, errp);
678178fe0aeSMarc-André Lureau if (chr == NULL) {
679178fe0aeSMarc-André Lureau goto out;
680178fe0aeSMarc-André Lureau }
681178fe0aeSMarc-André Lureau
6824c193bb1SNicholas Piggin base = chr;
683178fe0aeSMarc-André Lureau if (bid) {
684178fe0aeSMarc-André Lureau Chardev *mux;
685178fe0aeSMarc-André Lureau qapi_free_ChardevBackend(backend);
686178fe0aeSMarc-André Lureau backend = g_new0(ChardevBackend, 1);
687178fe0aeSMarc-André Lureau backend->type = CHARDEV_BACKEND_KIND_MUX;
688178fe0aeSMarc-André Lureau backend->u.mux.data = g_new0(ChardevMux, 1);
689178fe0aeSMarc-André Lureau backend->u.mux.data->chardev = g_strdup(bid);
6904ad6f6cbSPaolo Bonzini mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, context, errp);
691178fe0aeSMarc-André Lureau if (mux == NULL) {
6922f5d45a1SMarc-André Lureau object_unparent(OBJECT(chr));
693178fe0aeSMarc-André Lureau chr = NULL;
694178fe0aeSMarc-André Lureau goto out;
695178fe0aeSMarc-André Lureau }
696178fe0aeSMarc-André Lureau chr = mux;
697178fe0aeSMarc-André Lureau }
698178fe0aeSMarc-André Lureau
699178fe0aeSMarc-André Lureau out:
700178fe0aeSMarc-André Lureau qapi_free_ChardevBackend(backend);
701178fe0aeSMarc-André Lureau g_free(bid);
7024c193bb1SNicholas Piggin
7034c193bb1SNicholas Piggin if (replay && base) {
7044c193bb1SNicholas Piggin /* RR should be set on the base device, not the mux */
7054c193bb1SNicholas Piggin qemu_chardev_set_replay(base, errp);
7064c193bb1SNicholas Piggin }
7074c193bb1SNicholas Piggin
708178fe0aeSMarc-André Lureau return chr;
709178fe0aeSMarc-André Lureau }
710178fe0aeSMarc-André Lureau
qemu_chr_new_from_opts(QemuOpts * opts,GMainContext * context,Error ** errp)7114c193bb1SNicholas Piggin Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
7124c193bb1SNicholas Piggin Error **errp)
7134c193bb1SNicholas Piggin {
7144c193bb1SNicholas Piggin /* XXX: should this really not record/replay? */
715*3c8ab23fSNicholas Piggin return do_qemu_chr_new_from_opts(opts, context, false, errp);
7164c193bb1SNicholas Piggin }
7174c193bb1SNicholas Piggin
qemu_chr_new_from_name(const char * label,const char * filename,bool permit_mux_mon,GMainContext * context,bool replay)718*3c8ab23fSNicholas Piggin static Chardev *qemu_chr_new_from_name(const char *label, const char *filename,
719*3c8ab23fSNicholas Piggin bool permit_mux_mon,
720*3c8ab23fSNicholas Piggin GMainContext *context, bool replay)
721178fe0aeSMarc-André Lureau {
722178fe0aeSMarc-André Lureau const char *p;
723178fe0aeSMarc-André Lureau Chardev *chr;
724178fe0aeSMarc-André Lureau QemuOpts *opts;
725178fe0aeSMarc-André Lureau Error *err = NULL;
726178fe0aeSMarc-André Lureau
727178fe0aeSMarc-André Lureau if (strstart(filename, "chardev:", &p)) {
7284c193bb1SNicholas Piggin chr = qemu_chr_find(p);
729651b3862SNicholas Piggin if (replay && chr) {
7304c193bb1SNicholas Piggin qemu_chardev_set_replay(chr, &err);
7314c193bb1SNicholas Piggin if (err) {
7324c193bb1SNicholas Piggin error_report_err(err);
7334c193bb1SNicholas Piggin return NULL;
7344c193bb1SNicholas Piggin }
7354c193bb1SNicholas Piggin }
7364c193bb1SNicholas Piggin return chr;
737178fe0aeSMarc-André Lureau }
738178fe0aeSMarc-André Lureau
73995e30b2aSMarc-André Lureau opts = qemu_chr_parse_compat(label, filename, permit_mux_mon);
740178fe0aeSMarc-André Lureau if (!opts)
741178fe0aeSMarc-André Lureau return NULL;
742178fe0aeSMarc-André Lureau
743*3c8ab23fSNicholas Piggin chr = do_qemu_chr_new_from_opts(opts, context, replay, &err);
74495e30b2aSMarc-André Lureau if (!chr) {
745178fe0aeSMarc-André Lureau error_report_err(err);
74695e30b2aSMarc-André Lureau goto out;
747178fe0aeSMarc-André Lureau }
74895e30b2aSMarc-André Lureau
74995e30b2aSMarc-André Lureau if (qemu_opt_get_bool(opts, "mux", 0)) {
75095e30b2aSMarc-André Lureau assert(permit_mux_mon);
7518e9119a8SKevin Wolf monitor_init_hmp(chr, true, &err);
7528e9119a8SKevin Wolf if (err) {
7538e9119a8SKevin Wolf error_report_err(err);
7548e9119a8SKevin Wolf object_unparent(OBJECT(chr));
7558e9119a8SKevin Wolf chr = NULL;
7568e9119a8SKevin Wolf goto out;
7578e9119a8SKevin Wolf }
758178fe0aeSMarc-André Lureau }
75995e30b2aSMarc-André Lureau
76095e30b2aSMarc-André Lureau out:
761178fe0aeSMarc-André Lureau qemu_opts_del(opts);
762178fe0aeSMarc-André Lureau return chr;
763178fe0aeSMarc-André Lureau }
764178fe0aeSMarc-André Lureau
qemu_chr_new_noreplay(const char * label,const char * filename,bool permit_mux_mon,GMainContext * context)7654c193bb1SNicholas Piggin Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
7664c193bb1SNicholas Piggin bool permit_mux_mon, GMainContext *context)
7674c193bb1SNicholas Piggin {
768*3c8ab23fSNicholas Piggin return qemu_chr_new_from_name(label, filename, permit_mux_mon, context,
769*3c8ab23fSNicholas Piggin false);
7704c193bb1SNicholas Piggin }
7714c193bb1SNicholas Piggin
qemu_chr_new_permit_mux_mon(const char * label,const char * filename,bool permit_mux_mon,GMainContext * context)77295e30b2aSMarc-André Lureau static Chardev *qemu_chr_new_permit_mux_mon(const char *label,
77395e30b2aSMarc-André Lureau const char *filename,
7744ad6f6cbSPaolo Bonzini bool permit_mux_mon,
7754ad6f6cbSPaolo Bonzini GMainContext *context)
776178fe0aeSMarc-André Lureau {
777*3c8ab23fSNicholas Piggin return qemu_chr_new_from_name(label, filename, permit_mux_mon, context,
778*3c8ab23fSNicholas Piggin true);
779178fe0aeSMarc-André Lureau }
780178fe0aeSMarc-André Lureau
qemu_chr_new(const char * label,const char * filename,GMainContext * context)7814ad6f6cbSPaolo Bonzini Chardev *qemu_chr_new(const char *label, const char *filename,
7824ad6f6cbSPaolo Bonzini GMainContext *context)
78395e30b2aSMarc-André Lureau {
7844ad6f6cbSPaolo Bonzini return qemu_chr_new_permit_mux_mon(label, filename, false, context);
78595e30b2aSMarc-André Lureau }
78695e30b2aSMarc-André Lureau
qemu_chr_new_mux_mon(const char * label,const char * filename,GMainContext * context)7874ad6f6cbSPaolo Bonzini Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename,
7884ad6f6cbSPaolo Bonzini GMainContext *context)
78995e30b2aSMarc-André Lureau {
7904ad6f6cbSPaolo Bonzini return qemu_chr_new_permit_mux_mon(label, filename, true, context);
79195e30b2aSMarc-André Lureau }
79295e30b2aSMarc-André Lureau
qmp_query_chardev_foreach(Object * obj,void * data)7936061162eSMarc-André Lureau static int qmp_query_chardev_foreach(Object *obj, void *data)
794178fe0aeSMarc-André Lureau {
7956061162eSMarc-André Lureau Chardev *chr = CHARDEV(obj);
7966061162eSMarc-André Lureau ChardevInfoList **list = data;
79754aa3de7SEric Blake ChardevInfo *value = g_malloc0(sizeof(*value));
7986061162eSMarc-André Lureau
79954aa3de7SEric Blake value->label = g_strdup(chr->label);
80054aa3de7SEric Blake value->filename = g_strdup(chr->filename);
80167b5595dSAlex Bennée value->frontend_open = chr->be && chr->be->fe_is_open;
802178fe0aeSMarc-André Lureau
80354aa3de7SEric Blake QAPI_LIST_PREPEND(*list, value);
8046061162eSMarc-André Lureau
8056061162eSMarc-André Lureau return 0;
806178fe0aeSMarc-André Lureau }
807178fe0aeSMarc-André Lureau
qmp_query_chardev(Error ** errp)8086061162eSMarc-André Lureau ChardevInfoList *qmp_query_chardev(Error **errp)
8096061162eSMarc-André Lureau {
8106061162eSMarc-André Lureau ChardevInfoList *chr_list = NULL;
8116061162eSMarc-André Lureau
8126061162eSMarc-André Lureau object_child_foreach(get_chardevs_root(),
8136061162eSMarc-André Lureau qmp_query_chardev_foreach, &chr_list);
8146061162eSMarc-André Lureau
815178fe0aeSMarc-André Lureau return chr_list;
816178fe0aeSMarc-André Lureau }
817178fe0aeSMarc-André Lureau
818178fe0aeSMarc-André Lureau static void
qmp_prepend_backend(const char * name,void * opaque)819f3b70e07SKevin Wolf qmp_prepend_backend(const char *name, void *opaque)
820178fe0aeSMarc-André Lureau {
821178fe0aeSMarc-André Lureau ChardevBackendInfoList **list = opaque;
8221254bd39SKevin Wolf ChardevBackendInfo *value;
823178fe0aeSMarc-André Lureau
8241254bd39SKevin Wolf value = g_new0(ChardevBackendInfo, 1);
82554aa3de7SEric Blake value->name = g_strdup(name);
82654aa3de7SEric Blake QAPI_LIST_PREPEND(*list, value);
827178fe0aeSMarc-André Lureau }
828178fe0aeSMarc-André Lureau
qmp_query_chardev_backends(Error ** errp)829178fe0aeSMarc-André Lureau ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
830178fe0aeSMarc-André Lureau {
831178fe0aeSMarc-André Lureau ChardevBackendInfoList *backend_list = NULL;
832178fe0aeSMarc-André Lureau
833178fe0aeSMarc-André Lureau chardev_name_foreach(qmp_prepend_backend, &backend_list);
834178fe0aeSMarc-André Lureau
835178fe0aeSMarc-André Lureau return backend_list;
836178fe0aeSMarc-André Lureau }
837178fe0aeSMarc-André Lureau
qemu_chr_find(const char * name)838178fe0aeSMarc-André Lureau Chardev *qemu_chr_find(const char *name)
839178fe0aeSMarc-André Lureau {
8406061162eSMarc-André Lureau Object *obj = object_resolve_path_component(get_chardevs_root(), name);
841178fe0aeSMarc-André Lureau
8426061162eSMarc-André Lureau return obj ? CHARDEV(obj) : NULL;
843178fe0aeSMarc-André Lureau }
844178fe0aeSMarc-André Lureau
845178fe0aeSMarc-André Lureau QemuOptsList qemu_chardev_opts = {
846178fe0aeSMarc-André Lureau .name = "chardev",
847178fe0aeSMarc-André Lureau .implied_opt_name = "backend",
848178fe0aeSMarc-André Lureau .head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
849178fe0aeSMarc-André Lureau .desc = {
850178fe0aeSMarc-André Lureau {
851178fe0aeSMarc-André Lureau .name = "backend",
852178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
853178fe0aeSMarc-André Lureau },{
854178fe0aeSMarc-André Lureau .name = "path",
855178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
856178fe0aeSMarc-André Lureau },{
8575b18a6bfSPeter Maydell .name = "input-path",
8585b18a6bfSPeter Maydell .type = QEMU_OPT_STRING,
8595b18a6bfSPeter Maydell },{
860178fe0aeSMarc-André Lureau .name = "host",
861178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
862178fe0aeSMarc-André Lureau },{
863178fe0aeSMarc-André Lureau .name = "port",
864178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
865178fe0aeSMarc-André Lureau },{
8660935700fSDaniel P. Berrange .name = "fd",
8670935700fSDaniel P. Berrange .type = QEMU_OPT_STRING,
8680935700fSDaniel P. Berrange },{
869178fe0aeSMarc-André Lureau .name = "localaddr",
870178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
871178fe0aeSMarc-André Lureau },{
872178fe0aeSMarc-André Lureau .name = "localport",
873178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
874178fe0aeSMarc-André Lureau },{
875178fe0aeSMarc-André Lureau .name = "to",
876178fe0aeSMarc-André Lureau .type = QEMU_OPT_NUMBER,
877178fe0aeSMarc-André Lureau },{
878178fe0aeSMarc-André Lureau .name = "ipv4",
879178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
880178fe0aeSMarc-André Lureau },{
881178fe0aeSMarc-André Lureau .name = "ipv6",
882178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
883178fe0aeSMarc-André Lureau },{
884178fe0aeSMarc-André Lureau .name = "wait",
885178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
886178fe0aeSMarc-André Lureau },{
887178fe0aeSMarc-André Lureau .name = "server",
888178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
889178fe0aeSMarc-André Lureau },{
890178fe0aeSMarc-André Lureau .name = "delay",
891178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
892178fe0aeSMarc-André Lureau },{
893a9b1315fSPaolo Bonzini .name = "nodelay",
894a9b1315fSPaolo Bonzini .type = QEMU_OPT_BOOL,
895a9b1315fSPaolo Bonzini },{
896178fe0aeSMarc-André Lureau .name = "reconnect",
897178fe0aeSMarc-André Lureau .type = QEMU_OPT_NUMBER,
898178fe0aeSMarc-André Lureau },{
899c8e2b6b4SDaniil Tatianin .name = "reconnect-ms",
900c8e2b6b4SDaniil Tatianin .type = QEMU_OPT_NUMBER,
901c8e2b6b4SDaniil Tatianin },{
902178fe0aeSMarc-André Lureau .name = "telnet",
903178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
904178fe0aeSMarc-André Lureau },{
905ae92cbd5SJing Liu .name = "tn3270",
906ae92cbd5SJing Liu .type = QEMU_OPT_BOOL,
907ae92cbd5SJing Liu },{
908178fe0aeSMarc-André Lureau .name = "tls-creds",
909178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
910178fe0aeSMarc-André Lureau },{
911fd4a5fd4SDaniel P. Berrange .name = "tls-authz",
912fd4a5fd4SDaniel P. Berrange .type = QEMU_OPT_STRING,
913fd4a5fd4SDaniel P. Berrange },{
914981b06e7SJulia Suvorova .name = "websocket",
915981b06e7SJulia Suvorova .type = QEMU_OPT_BOOL,
916981b06e7SJulia Suvorova },{
917178fe0aeSMarc-André Lureau .name = "width",
918178fe0aeSMarc-André Lureau .type = QEMU_OPT_NUMBER,
919178fe0aeSMarc-André Lureau },{
920178fe0aeSMarc-André Lureau .name = "height",
921178fe0aeSMarc-André Lureau .type = QEMU_OPT_NUMBER,
922178fe0aeSMarc-André Lureau },{
923178fe0aeSMarc-André Lureau .name = "cols",
924178fe0aeSMarc-André Lureau .type = QEMU_OPT_NUMBER,
925178fe0aeSMarc-André Lureau },{
926178fe0aeSMarc-André Lureau .name = "rows",
927178fe0aeSMarc-André Lureau .type = QEMU_OPT_NUMBER,
928178fe0aeSMarc-André Lureau },{
929178fe0aeSMarc-André Lureau .name = "mux",
930178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
931178fe0aeSMarc-André Lureau },{
932178fe0aeSMarc-André Lureau .name = "signal",
933178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
934178fe0aeSMarc-André Lureau },{
935178fe0aeSMarc-André Lureau .name = "name",
936178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
937178fe0aeSMarc-André Lureau },{
938178fe0aeSMarc-André Lureau .name = "debug",
939178fe0aeSMarc-André Lureau .type = QEMU_OPT_NUMBER,
940178fe0aeSMarc-André Lureau },{
941178fe0aeSMarc-André Lureau .name = "size",
942178fe0aeSMarc-André Lureau .type = QEMU_OPT_SIZE,
943178fe0aeSMarc-André Lureau },{
944178fe0aeSMarc-André Lureau .name = "chardev",
945178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
946178fe0aeSMarc-André Lureau },{
947178fe0aeSMarc-André Lureau .name = "append",
948178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
949178fe0aeSMarc-André Lureau },{
950178fe0aeSMarc-André Lureau .name = "logfile",
951178fe0aeSMarc-André Lureau .type = QEMU_OPT_STRING,
952178fe0aeSMarc-André Lureau },{
953178fe0aeSMarc-André Lureau .name = "logappend",
954178fe0aeSMarc-André Lureau .type = QEMU_OPT_BOOL,
95556081919SGerd Hoffmann },{
95656081919SGerd Hoffmann .name = "mouse",
95756081919SGerd Hoffmann .type = QEMU_OPT_BOOL,
958f0349f4dSGerd Hoffmann },{
959f0349f4dSGerd Hoffmann .name = "clipboard",
960f0349f4dSGerd Hoffmann .type = QEMU_OPT_BOOL,
9618acefc79SMarkus Armbruster #ifdef CONFIG_LINUX
962776b97d3Sxiaoqiang zhao },{
963776b97d3Sxiaoqiang zhao .name = "tight",
964776b97d3Sxiaoqiang zhao .type = QEMU_OPT_BOOL,
965776b97d3Sxiaoqiang zhao .def_value_str = "on",
966776b97d3Sxiaoqiang zhao },{
967776b97d3Sxiaoqiang zhao .name = "abstract",
968776b97d3Sxiaoqiang zhao .type = QEMU_OPT_BOOL,
9698acefc79SMarkus Armbruster #endif
970178fe0aeSMarc-André Lureau },
971178fe0aeSMarc-André Lureau { /* end of list */ }
972178fe0aeSMarc-André Lureau },
973178fe0aeSMarc-André Lureau };
974178fe0aeSMarc-André Lureau
qemu_chr_has_feature(Chardev * chr,ChardevFeature feature)975178fe0aeSMarc-André Lureau bool qemu_chr_has_feature(Chardev *chr,
976178fe0aeSMarc-André Lureau ChardevFeature feature)
977178fe0aeSMarc-André Lureau {
978178fe0aeSMarc-André Lureau return test_bit(feature, chr->features);
979178fe0aeSMarc-André Lureau }
980178fe0aeSMarc-André Lureau
qemu_chr_set_feature(Chardev * chr,ChardevFeature feature)981178fe0aeSMarc-André Lureau void qemu_chr_set_feature(Chardev *chr,
982178fe0aeSMarc-André Lureau ChardevFeature feature)
983178fe0aeSMarc-André Lureau {
984178fe0aeSMarc-André Lureau return set_bit(feature, chr->features);
985178fe0aeSMarc-André Lureau }
986178fe0aeSMarc-André Lureau
chardev_new(const char * id,const char * typename,ChardevBackend * backend,GMainContext * gcontext,bool handover_yank_instance,Error ** errp)9871e419ee6SMarc-André Lureau static Chardev *chardev_new(const char *id, const char *typename,
9882f5d45a1SMarc-André Lureau ChardevBackend *backend,
9894ad6f6cbSPaolo Bonzini GMainContext *gcontext,
990feb774caSLukas Straub bool handover_yank_instance,
9912f5d45a1SMarc-André Lureau Error **errp)
992178fe0aeSMarc-André Lureau {
9932f5d45a1SMarc-André Lureau Object *obj;
994178fe0aeSMarc-André Lureau Chardev *chr = NULL;
995178fe0aeSMarc-André Lureau Error *local_err = NULL;
996178fe0aeSMarc-André Lureau bool be_opened = true;
997178fe0aeSMarc-André Lureau
998178fe0aeSMarc-André Lureau assert(g_str_has_prefix(typename, "chardev-"));
999f36b0efdSLukas Straub assert(id);
1000178fe0aeSMarc-André Lureau
10012f5d45a1SMarc-André Lureau obj = object_new(typename);
10022f5d45a1SMarc-André Lureau chr = CHARDEV(obj);
1003feb774caSLukas Straub chr->handover_yank_instance = handover_yank_instance;
1004178fe0aeSMarc-André Lureau chr->label = g_strdup(id);
10054ad6f6cbSPaolo Bonzini chr->gcontext = gcontext;
1006178fe0aeSMarc-André Lureau
1007178fe0aeSMarc-André Lureau qemu_char_open(chr, backend, &be_opened, &local_err);
1008178fe0aeSMarc-André Lureau if (local_err) {
1009789fd693SLukas Straub error_propagate(errp, local_err);
1010789fd693SLukas Straub object_unref(obj);
1011789fd693SLukas Straub return NULL;
1012178fe0aeSMarc-André Lureau }
1013178fe0aeSMarc-André Lureau
1014178fe0aeSMarc-André Lureau if (!chr->filename) {
1015178fe0aeSMarc-André Lureau chr->filename = g_strdup(typename + 8);
1016178fe0aeSMarc-André Lureau }
1017178fe0aeSMarc-André Lureau if (be_opened) {
1018178fe0aeSMarc-André Lureau qemu_chr_be_event(chr, CHR_EVENT_OPENED);
1019178fe0aeSMarc-André Lureau }
1020178fe0aeSMarc-André Lureau
1021178fe0aeSMarc-André Lureau return chr;
1022178fe0aeSMarc-André Lureau }
1023178fe0aeSMarc-André Lureau
qemu_chardev_new(const char * id,const char * typename,ChardevBackend * backend,GMainContext * gcontext,Error ** errp)10241e419ee6SMarc-André Lureau Chardev *qemu_chardev_new(const char *id, const char *typename,
10251e419ee6SMarc-André Lureau ChardevBackend *backend,
10261e419ee6SMarc-André Lureau GMainContext *gcontext,
10271e419ee6SMarc-André Lureau Error **errp)
10281e419ee6SMarc-André Lureau {
1029789fd693SLukas Straub Chardev *chr;
10301e419ee6SMarc-André Lureau g_autofree char *genid = NULL;
10311e419ee6SMarc-André Lureau
10321e419ee6SMarc-André Lureau if (!id) {
10331e419ee6SMarc-André Lureau genid = id_generate(ID_CHR);
10341e419ee6SMarc-André Lureau id = genid;
10351e419ee6SMarc-André Lureau }
10361e419ee6SMarc-André Lureau
1037feb774caSLukas Straub chr = chardev_new(id, typename, backend, gcontext, false, errp);
1038789fd693SLukas Straub if (!chr) {
1039789fd693SLukas Straub return NULL;
1040789fd693SLukas Straub }
1041789fd693SLukas Straub
1042789fd693SLukas Straub if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr),
1043789fd693SLukas Straub errp)) {
1044789fd693SLukas Straub object_unref(OBJECT(chr));
1045789fd693SLukas Straub return NULL;
1046789fd693SLukas Straub }
1047789fd693SLukas Straub object_unref(OBJECT(chr));
1048789fd693SLukas Straub
1049789fd693SLukas Straub return chr;
10501e419ee6SMarc-André Lureau }
10511e419ee6SMarc-André Lureau
qmp_chardev_add(const char * id,ChardevBackend * backend,Error ** errp)1052178fe0aeSMarc-André Lureau ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
1053178fe0aeSMarc-André Lureau Error **errp)
1054178fe0aeSMarc-André Lureau {
105564195b0dSMarc-André Lureau ERRP_GUARD();
1056178fe0aeSMarc-André Lureau const ChardevClass *cc;
1057178fe0aeSMarc-André Lureau ChardevReturn *ret;
105864195b0dSMarc-André Lureau g_autoptr(Chardev) chr = NULL;
1059178fe0aeSMarc-André Lureau
1060a68403b0SMarc-André Lureau if (qemu_chr_find(id)) {
1061a68403b0SMarc-André Lureau error_setg(errp, "Chardev with id '%s' already exists", id);
1062a68403b0SMarc-André Lureau return NULL;
1063a68403b0SMarc-André Lureau }
1064a68403b0SMarc-André Lureau
1065977c736fSMarkus Armbruster cc = char_get_class(ChardevBackendKind_str(backend->type), errp);
1066178fe0aeSMarc-André Lureau if (!cc) {
106764195b0dSMarc-André Lureau goto err;
1068178fe0aeSMarc-André Lureau }
1069178fe0aeSMarc-André Lureau
10701e419ee6SMarc-André Lureau chr = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
1071feb774caSLukas Straub backend, NULL, false, errp);
1072178fe0aeSMarc-André Lureau if (!chr) {
107364195b0dSMarc-André Lureau goto err;
1074178fe0aeSMarc-André Lureau }
1075178fe0aeSMarc-André Lureau
1076789fd693SLukas Straub if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr),
1077789fd693SLukas Straub errp)) {
107864195b0dSMarc-André Lureau goto err;
1079789fd693SLukas Straub }
1080789fd693SLukas Straub
1081178fe0aeSMarc-André Lureau ret = g_new0(ChardevReturn, 1);
1082178fe0aeSMarc-André Lureau if (CHARDEV_IS_PTY(chr)) {
1083178fe0aeSMarc-André Lureau ret->pty = g_strdup(chr->filename + 4);
1084178fe0aeSMarc-André Lureau }
1085178fe0aeSMarc-André Lureau
1086178fe0aeSMarc-André Lureau return ret;
108764195b0dSMarc-André Lureau
108864195b0dSMarc-André Lureau err:
108964195b0dSMarc-André Lureau error_prepend(errp, "Failed to add chardev '%s': ", id);
109064195b0dSMarc-André Lureau return NULL;
1091178fe0aeSMarc-André Lureau }
1092178fe0aeSMarc-André Lureau
qmp_chardev_change(const char * id,ChardevBackend * backend,Error ** errp)10937bb86085SAnton Nefedov ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
10947bb86085SAnton Nefedov Error **errp)
10957bb86085SAnton Nefedov {
10967bb86085SAnton Nefedov CharBackend *be;
1097feb774caSLukas Straub const ChardevClass *cc, *cc_new;
10987bb86085SAnton Nefedov Chardev *chr, *chr_new;
10997bb86085SAnton Nefedov bool closed_sent = false;
1100feb774caSLukas Straub bool handover_yank_instance;
11017bb86085SAnton Nefedov ChardevReturn *ret;
11027bb86085SAnton Nefedov
11037bb86085SAnton Nefedov chr = qemu_chr_find(id);
11047bb86085SAnton Nefedov if (!chr) {
11057bb86085SAnton Nefedov error_setg(errp, "Chardev '%s' does not exist", id);
11067bb86085SAnton Nefedov return NULL;
11077bb86085SAnton Nefedov }
11087bb86085SAnton Nefedov
11097bb86085SAnton Nefedov if (CHARDEV_IS_MUX(chr)) {
11107bb86085SAnton Nefedov error_setg(errp, "Mux device hotswap not supported yet");
11117bb86085SAnton Nefedov return NULL;
11127bb86085SAnton Nefedov }
11137bb86085SAnton Nefedov
11147bb86085SAnton Nefedov if (qemu_chr_replay(chr)) {
11157bb86085SAnton Nefedov error_setg(errp,
11167bb86085SAnton Nefedov "Chardev '%s' cannot be changed in record/replay mode", id);
11177bb86085SAnton Nefedov return NULL;
11187bb86085SAnton Nefedov }
11197bb86085SAnton Nefedov
11207bb86085SAnton Nefedov be = chr->be;
11217bb86085SAnton Nefedov if (!be) {
11227bb86085SAnton Nefedov /* easy case */
11237bb86085SAnton Nefedov object_unparent(OBJECT(chr));
11247bb86085SAnton Nefedov return qmp_chardev_add(id, backend, errp);
11257bb86085SAnton Nefedov }
11267bb86085SAnton Nefedov
11277bb86085SAnton Nefedov if (!be->chr_be_change) {
11287bb86085SAnton Nefedov error_setg(errp, "Chardev user does not support chardev hotswap");
11297bb86085SAnton Nefedov return NULL;
11307bb86085SAnton Nefedov }
11317bb86085SAnton Nefedov
1132feb774caSLukas Straub cc = CHARDEV_GET_CLASS(chr);
1133feb774caSLukas Straub cc_new = char_get_class(ChardevBackendKind_str(backend->type), errp);
1134feb774caSLukas Straub if (!cc_new) {
11357bb86085SAnton Nefedov return NULL;
11367bb86085SAnton Nefedov }
11377bb86085SAnton Nefedov
1138feb774caSLukas Straub /*
1139feb774caSLukas Straub * The new chardev should not register a yank instance if the current
1140feb774caSLukas Straub * chardev has registered one already.
1141feb774caSLukas Straub */
1142feb774caSLukas Straub handover_yank_instance = cc->supports_yank && cc_new->supports_yank;
1143feb774caSLukas Straub
1144feb774caSLukas Straub chr_new = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc_new)),
1145feb774caSLukas Straub backend, chr->gcontext, handover_yank_instance, errp);
11467bb86085SAnton Nefedov if (!chr_new) {
11477bb86085SAnton Nefedov return NULL;
11487bb86085SAnton Nefedov }
11497bb86085SAnton Nefedov
11507bb86085SAnton Nefedov if (chr->be_open && !chr_new->be_open) {
11517bb86085SAnton Nefedov qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
11527bb86085SAnton Nefedov closed_sent = true;
11537bb86085SAnton Nefedov }
11547bb86085SAnton Nefedov
11557bb86085SAnton Nefedov chr->be = NULL;
11567bb86085SAnton Nefedov qemu_chr_fe_init(be, chr_new, &error_abort);
11577bb86085SAnton Nefedov
11587bb86085SAnton Nefedov if (be->chr_be_change(be->opaque) < 0) {
11597bb86085SAnton Nefedov error_setg(errp, "Chardev '%s' change failed", chr_new->label);
11607bb86085SAnton Nefedov chr_new->be = NULL;
11617bb86085SAnton Nefedov qemu_chr_fe_init(be, chr, &error_abort);
11627bb86085SAnton Nefedov if (closed_sent) {
11637bb86085SAnton Nefedov qemu_chr_be_event(chr, CHR_EVENT_OPENED);
11647bb86085SAnton Nefedov }
11657bb86085SAnton Nefedov object_unref(OBJECT(chr_new));
11667bb86085SAnton Nefedov return NULL;
11677bb86085SAnton Nefedov }
11687bb86085SAnton Nefedov
11690a19d879SMichael Tokarev /* change successful, clean up */
1170feb774caSLukas Straub chr_new->handover_yank_instance = false;
1171feb774caSLukas Straub
1172feb774caSLukas Straub /*
1173feb774caSLukas Straub * When the old chardev is freed, it should not unregister the yank
1174feb774caSLukas Straub * instance if the new chardev needs it.
1175feb774caSLukas Straub */
1176feb774caSLukas Straub chr->handover_yank_instance = handover_yank_instance;
1177feb774caSLukas Straub
11787bb86085SAnton Nefedov object_unparent(OBJECT(chr));
11797bb86085SAnton Nefedov object_property_add_child(get_chardevs_root(), chr_new->label,
1180d2623129SMarkus Armbruster OBJECT(chr_new));
11817bb86085SAnton Nefedov object_unref(OBJECT(chr_new));
11827bb86085SAnton Nefedov
11837bb86085SAnton Nefedov ret = g_new0(ChardevReturn, 1);
11847bb86085SAnton Nefedov if (CHARDEV_IS_PTY(chr_new)) {
11857bb86085SAnton Nefedov ret->pty = g_strdup(chr_new->filename + 4);
11867bb86085SAnton Nefedov }
11877bb86085SAnton Nefedov
11887bb86085SAnton Nefedov return ret;
11897bb86085SAnton Nefedov }
11907bb86085SAnton Nefedov
qmp_chardev_remove(const char * id,Error ** errp)1191178fe0aeSMarc-André Lureau void qmp_chardev_remove(const char *id, Error **errp)
1192178fe0aeSMarc-André Lureau {
1193178fe0aeSMarc-André Lureau Chardev *chr;
1194178fe0aeSMarc-André Lureau
1195178fe0aeSMarc-André Lureau chr = qemu_chr_find(id);
1196178fe0aeSMarc-André Lureau if (chr == NULL) {
1197178fe0aeSMarc-André Lureau error_setg(errp, "Chardev '%s' not found", id);
1198178fe0aeSMarc-André Lureau return;
1199178fe0aeSMarc-André Lureau }
1200178fe0aeSMarc-André Lureau if (qemu_chr_is_busy(chr)) {
1201178fe0aeSMarc-André Lureau error_setg(errp, "Chardev '%s' is busy", id);
1202178fe0aeSMarc-André Lureau return;
1203178fe0aeSMarc-André Lureau }
1204178fe0aeSMarc-André Lureau if (qemu_chr_replay(chr)) {
1205178fe0aeSMarc-André Lureau error_setg(errp,
1206178fe0aeSMarc-André Lureau "Chardev '%s' cannot be unplugged in record/replay mode", id);
1207178fe0aeSMarc-André Lureau return;
1208178fe0aeSMarc-André Lureau }
12092f5d45a1SMarc-André Lureau object_unparent(OBJECT(chr));
1210178fe0aeSMarc-André Lureau }
1211178fe0aeSMarc-André Lureau
qmp_chardev_send_break(const char * id,Error ** errp)1212bd1d5ad9SStefan Fritsch void qmp_chardev_send_break(const char *id, Error **errp)
1213bd1d5ad9SStefan Fritsch {
1214bd1d5ad9SStefan Fritsch Chardev *chr;
1215bd1d5ad9SStefan Fritsch
1216bd1d5ad9SStefan Fritsch chr = qemu_chr_find(id);
1217bd1d5ad9SStefan Fritsch if (chr == NULL) {
1218bd1d5ad9SStefan Fritsch error_setg(errp, "Chardev '%s' not found", id);
1219bd1d5ad9SStefan Fritsch return;
1220bd1d5ad9SStefan Fritsch }
1221bd1d5ad9SStefan Fritsch qemu_chr_be_event(chr, CHR_EVENT_BREAK);
1222bd1d5ad9SStefan Fritsch }
1223bd1d5ad9SStefan Fritsch
qmp_add_client_char(int fd,bool has_skipauth,bool skipauth,bool has_tls,bool tls,const char * protocol,Error ** errp)1224c3054a6eSMarkus Armbruster bool qmp_add_client_char(int fd, bool has_skipauth, bool skipauth,
1225c3054a6eSMarkus Armbruster bool has_tls, bool tls, const char *protocol,
1226c3054a6eSMarkus Armbruster Error **errp)
1227c3054a6eSMarkus Armbruster {
1228c3054a6eSMarkus Armbruster Chardev *s = qemu_chr_find(protocol);
1229c3054a6eSMarkus Armbruster
1230c3054a6eSMarkus Armbruster if (!s) {
1231c3054a6eSMarkus Armbruster error_setg(errp, "protocol '%s' is invalid", protocol);
1232c3054a6eSMarkus Armbruster return false;
1233c3054a6eSMarkus Armbruster }
1234c3054a6eSMarkus Armbruster if (qemu_chr_add_client(s, fd) < 0) {
1235c3054a6eSMarkus Armbruster error_setg(errp, "failed to add client");
1236c3054a6eSMarkus Armbruster return false;
1237c3054a6eSMarkus Armbruster }
1238c3054a6eSMarkus Armbruster return true;
1239c3054a6eSMarkus Armbruster }
1240c3054a6eSMarkus Armbruster
12412c716ba1SPeter Xu /*
12422c716ba1SPeter Xu * Add a timeout callback for the chardev (in milliseconds), return
12432c716ba1SPeter Xu * the GSource object created. Please use this to add timeout hook for
12442c716ba1SPeter Xu * chardev instead of g_timeout_add() and g_timeout_add_seconds(), to
12452c716ba1SPeter Xu * make sure the gcontext that the task bound to is correct.
12462c716ba1SPeter Xu */
qemu_chr_timeout_add_ms(Chardev * chr,guint ms,GSourceFunc func,void * private)12472c716ba1SPeter Xu GSource *qemu_chr_timeout_add_ms(Chardev *chr, guint ms,
12482c716ba1SPeter Xu GSourceFunc func, void *private)
12492c716ba1SPeter Xu {
12502c716ba1SPeter Xu GSource *source = g_timeout_source_new(ms);
12512c716ba1SPeter Xu
12522c716ba1SPeter Xu assert(func);
12532c716ba1SPeter Xu g_source_set_callback(source, func, private, NULL);
12542c716ba1SPeter Xu g_source_attach(source, chr->gcontext);
12552c716ba1SPeter Xu
12562c716ba1SPeter Xu return source;
12572c716ba1SPeter Xu }
12582c716ba1SPeter Xu
qemu_chr_cleanup(void)1259178fe0aeSMarc-André Lureau void qemu_chr_cleanup(void)
1260178fe0aeSMarc-André Lureau {
12612f5d45a1SMarc-André Lureau object_unparent(get_chardevs_root());
1262178fe0aeSMarc-André Lureau }
1263178fe0aeSMarc-André Lureau
register_types(void)1264178fe0aeSMarc-André Lureau static void register_types(void)
1265178fe0aeSMarc-André Lureau {
1266178fe0aeSMarc-André Lureau type_register_static(&char_type_info);
1267178fe0aeSMarc-André Lureau }
1268178fe0aeSMarc-André Lureau
1269178fe0aeSMarc-André Lureau type_init(register_types);
1270