xref: /openbmc/qemu/chardev/char-udp.c (revision 0221d73c)
1 /*
2  * QEMU System Emulator
3  *
4  * Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 #include "qemu/osdep.h"
26 #include "chardev/char.h"
27 #include "io/channel-socket.h"
28 #include "qapi/error.h"
29 #include "qemu/module.h"
30 #include "qemu/option.h"
31 
32 #include "chardev/char-io.h"
33 
34 /***********************************************************/
35 /* UDP Net console */
36 
37 typedef struct {
38     Chardev parent;
39     QIOChannel *ioc;
40     uint8_t buf[CHR_READ_BUF_LEN];
41     int bufcnt;
42     int bufptr;
43     int max_size;
44 } UdpChardev;
45 
46 #define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP)
47 
48 /* Called with chr_write_lock held.  */
49 static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
50 {
51     UdpChardev *s = UDP_CHARDEV(chr);
52 
53     return qio_channel_write(
54         s->ioc, (const char *)buf, len, NULL);
55 }
56 
57 static void udp_chr_flush_buffer(UdpChardev *s)
58 {
59     Chardev *chr = CHARDEV(s);
60 
61     while (s->max_size > 0 && s->bufptr < s->bufcnt) {
62         int n = MIN(s->max_size, s->bufcnt - s->bufptr);
63         qemu_chr_be_write(chr, &s->buf[s->bufptr], n);
64         s->bufptr += n;
65         s->max_size = qemu_chr_be_can_write(chr);
66     }
67 }
68 
69 static int udp_chr_read_poll(void *opaque)
70 {
71     Chardev *chr = CHARDEV(opaque);
72     UdpChardev *s = UDP_CHARDEV(opaque);
73 
74     s->max_size = qemu_chr_be_can_write(chr);
75 
76     /* If there were any stray characters in the queue process them
77      * first
78      */
79     udp_chr_flush_buffer(s);
80 
81     return s->max_size;
82 }
83 
84 static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
85 {
86     Chardev *chr = CHARDEV(opaque);
87     UdpChardev *s = UDP_CHARDEV(opaque);
88     ssize_t ret;
89 
90     if (s->max_size == 0) {
91         return TRUE;
92     }
93     ret = qio_channel_read(
94         s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
95     if (ret <= 0) {
96         remove_fd_in_watch(chr);
97         return FALSE;
98     }
99     s->bufcnt = ret;
100     s->bufptr = 0;
101     udp_chr_flush_buffer(s);
102 
103     return TRUE;
104 }
105 
106 static void udp_chr_update_read_handler(Chardev *chr)
107 {
108     UdpChardev *s = UDP_CHARDEV(chr);
109 
110     remove_fd_in_watch(chr);
111     if (s->ioc) {
112         chr->gsource = io_add_watch_poll(chr, s->ioc,
113                                            udp_chr_read_poll,
114                                            udp_chr_read, chr,
115                                            chr->gcontext);
116     }
117 }
118 
119 static void char_udp_finalize(Object *obj)
120 {
121     Chardev *chr = CHARDEV(obj);
122     UdpChardev *s = UDP_CHARDEV(obj);
123 
124     remove_fd_in_watch(chr);
125     if (s->ioc) {
126         object_unref(OBJECT(s->ioc));
127     }
128     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
129 }
130 
131 static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
132                                Error **errp)
133 {
134     const char *host = qemu_opt_get(opts, "host");
135     const char *port = qemu_opt_get(opts, "port");
136     const char *localaddr = qemu_opt_get(opts, "localaddr");
137     const char *localport = qemu_opt_get(opts, "localport");
138     bool has_local = false;
139     SocketAddressLegacy *addr;
140     ChardevUdp *udp;
141 
142     backend->type = CHARDEV_BACKEND_KIND_UDP;
143     if (host == NULL || strlen(host) == 0) {
144         host = "localhost";
145     }
146     if (port == NULL || strlen(port) == 0) {
147         error_setg(errp, "chardev: udp: remote port not specified");
148         return;
149     }
150     if (localport == NULL || strlen(localport) == 0) {
151         localport = "0";
152     } else {
153         has_local = true;
154     }
155     if (localaddr == NULL || strlen(localaddr) == 0) {
156         localaddr = "";
157     } else {
158         has_local = true;
159     }
160 
161     udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
162     qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
163 
164     addr = g_new0(SocketAddressLegacy, 1);
165     addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
166     addr->u.inet.data = g_new(InetSocketAddress, 1);
167     *addr->u.inet.data = (InetSocketAddress) {
168         .host = g_strdup(host),
169         .port = g_strdup(port),
170         .has_ipv4 = qemu_opt_get(opts, "ipv4"),
171         .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
172         .has_ipv6 = qemu_opt_get(opts, "ipv6"),
173         .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
174     };
175     udp->remote = addr;
176 
177     if (has_local) {
178         udp->has_local = true;
179         addr = g_new0(SocketAddressLegacy, 1);
180         addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
181         addr->u.inet.data = g_new(InetSocketAddress, 1);
182         *addr->u.inet.data = (InetSocketAddress) {
183             .host = g_strdup(localaddr),
184             .port = g_strdup(localport),
185         };
186         udp->local = addr;
187     }
188 }
189 
190 static void qmp_chardev_open_udp(Chardev *chr,
191                                  ChardevBackend *backend,
192                                  bool *be_opened,
193                                  Error **errp)
194 {
195     ChardevUdp *udp = backend->u.udp.data;
196     SocketAddress *local_addr = socket_address_flatten(udp->local);
197     SocketAddress *remote_addr = socket_address_flatten(udp->remote);
198     QIOChannelSocket *sioc = qio_channel_socket_new();
199     char *name;
200     UdpChardev *s = UDP_CHARDEV(chr);
201     int ret;
202 
203     ret = qio_channel_socket_dgram_sync(sioc, local_addr, remote_addr, errp);
204     qapi_free_SocketAddress(local_addr);
205     qapi_free_SocketAddress(remote_addr);
206     if (ret < 0) {
207         object_unref(OBJECT(sioc));
208         return;
209     }
210 
211     name = g_strdup_printf("chardev-udp-%s", chr->label);
212     qio_channel_set_name(QIO_CHANNEL(sioc), name);
213     g_free(name);
214 
215     s->ioc = QIO_CHANNEL(sioc);
216     /* be isn't opened until we get a connection */
217     *be_opened = false;
218 }
219 
220 static void char_udp_class_init(ObjectClass *oc, void *data)
221 {
222     ChardevClass *cc = CHARDEV_CLASS(oc);
223 
224     cc->parse = qemu_chr_parse_udp;
225     cc->open = qmp_chardev_open_udp;
226     cc->chr_write = udp_chr_write;
227     cc->chr_update_read_handler = udp_chr_update_read_handler;
228 }
229 
230 static const TypeInfo char_udp_type_info = {
231     .name = TYPE_CHARDEV_UDP,
232     .parent = TYPE_CHARDEV,
233     .instance_size = sizeof(UdpChardev),
234     .instance_finalize = char_udp_finalize,
235     .class_init = char_udp_class_init,
236 };
237 
238 static void register_types(void)
239 {
240     type_register_static(&char_udp_type_info);
241 }
242 
243 type_init(register_types);
244