xref: /openbmc/qemu/chardev/char.c (revision f07e0ad5991afaa97649fa81b22ec7cb6c02c4f7)
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