qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

char-udp.c (7263B)


      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 #include "qom/object.h"
     34 
     35 /***********************************************************/
     36 /* UDP Net console */
     37 
     38 struct UdpChardev {
     39     Chardev parent;
     40     QIOChannel *ioc;
     41     uint8_t buf[CHR_READ_BUF_LEN];
     42     int bufcnt;
     43     int bufptr;
     44     int max_size;
     45 };
     46 typedef struct UdpChardev UdpChardev;
     47 
     48 DECLARE_INSTANCE_CHECKER(UdpChardev, UDP_CHARDEV,
     49                          TYPE_CHARDEV_UDP)
     50 
     51 /* Called with chr_write_lock held.  */
     52 static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
     53 {
     54     UdpChardev *s = UDP_CHARDEV(chr);
     55 
     56     return qio_channel_write(
     57         s->ioc, (const char *)buf, len, NULL);
     58 }
     59 
     60 static void udp_chr_flush_buffer(UdpChardev *s)
     61 {
     62     Chardev *chr = CHARDEV(s);
     63 
     64     while (s->max_size > 0 && s->bufptr < s->bufcnt) {
     65         int n = MIN(s->max_size, s->bufcnt - s->bufptr);
     66         qemu_chr_be_write(chr, &s->buf[s->bufptr], n);
     67         s->bufptr += n;
     68         s->max_size = qemu_chr_be_can_write(chr);
     69     }
     70 }
     71 
     72 static int udp_chr_read_poll(void *opaque)
     73 {
     74     Chardev *chr = CHARDEV(opaque);
     75     UdpChardev *s = UDP_CHARDEV(opaque);
     76 
     77     s->max_size = qemu_chr_be_can_write(chr);
     78 
     79     /* If there were any stray characters in the queue process them
     80      * first
     81      */
     82     udp_chr_flush_buffer(s);
     83 
     84     return s->max_size;
     85 }
     86 
     87 static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
     88 {
     89     Chardev *chr = CHARDEV(opaque);
     90     UdpChardev *s = UDP_CHARDEV(opaque);
     91     ssize_t ret;
     92 
     93     if (s->max_size == 0) {
     94         return TRUE;
     95     }
     96     ret = qio_channel_read(
     97         s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
     98     if (ret <= 0) {
     99         remove_fd_in_watch(chr);
    100         return FALSE;
    101     }
    102     s->bufcnt = ret;
    103     s->bufptr = 0;
    104     udp_chr_flush_buffer(s);
    105 
    106     return TRUE;
    107 }
    108 
    109 static void udp_chr_update_read_handler(Chardev *chr)
    110 {
    111     UdpChardev *s = UDP_CHARDEV(chr);
    112 
    113     remove_fd_in_watch(chr);
    114     if (s->ioc) {
    115         chr->gsource = io_add_watch_poll(chr, s->ioc,
    116                                            udp_chr_read_poll,
    117                                            udp_chr_read, chr,
    118                                            chr->gcontext);
    119     }
    120 }
    121 
    122 static void char_udp_finalize(Object *obj)
    123 {
    124     Chardev *chr = CHARDEV(obj);
    125     UdpChardev *s = UDP_CHARDEV(obj);
    126 
    127     remove_fd_in_watch(chr);
    128     if (s->ioc) {
    129         object_unref(OBJECT(s->ioc));
    130     }
    131     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
    132 }
    133 
    134 static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
    135                                Error **errp)
    136 {
    137     const char *host = qemu_opt_get(opts, "host");
    138     const char *port = qemu_opt_get(opts, "port");
    139     const char *localaddr = qemu_opt_get(opts, "localaddr");
    140     const char *localport = qemu_opt_get(opts, "localport");
    141     bool has_local = false;
    142     SocketAddressLegacy *addr;
    143     ChardevUdp *udp;
    144 
    145     backend->type = CHARDEV_BACKEND_KIND_UDP;
    146     if (host == NULL || strlen(host) == 0) {
    147         host = "localhost";
    148     }
    149     if (port == NULL || strlen(port) == 0) {
    150         error_setg(errp, "chardev: udp: remote port not specified");
    151         return;
    152     }
    153     if (localport == NULL || strlen(localport) == 0) {
    154         localport = "0";
    155     } else {
    156         has_local = true;
    157     }
    158     if (localaddr == NULL || strlen(localaddr) == 0) {
    159         localaddr = "";
    160     } else {
    161         has_local = true;
    162     }
    163 
    164     udp = backend->u.udp.data = g_new0(ChardevUdp, 1);
    165     qemu_chr_parse_common(opts, qapi_ChardevUdp_base(udp));
    166 
    167     addr = g_new0(SocketAddressLegacy, 1);
    168     addr->type = SOCKET_ADDRESS_TYPE_INET;
    169     addr->u.inet.data = g_new(InetSocketAddress, 1);
    170     *addr->u.inet.data = (InetSocketAddress) {
    171         .host = g_strdup(host),
    172         .port = g_strdup(port),
    173         .has_ipv4 = qemu_opt_get(opts, "ipv4"),
    174         .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
    175         .has_ipv6 = qemu_opt_get(opts, "ipv6"),
    176         .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
    177     };
    178     udp->remote = addr;
    179 
    180     if (has_local) {
    181         udp->has_local = true;
    182         addr = g_new0(SocketAddressLegacy, 1);
    183         addr->type = SOCKET_ADDRESS_TYPE_INET;
    184         addr->u.inet.data = g_new(InetSocketAddress, 1);
    185         *addr->u.inet.data = (InetSocketAddress) {
    186             .host = g_strdup(localaddr),
    187             .port = g_strdup(localport),
    188         };
    189         udp->local = addr;
    190     }
    191 }
    192 
    193 static void qmp_chardev_open_udp(Chardev *chr,
    194                                  ChardevBackend *backend,
    195                                  bool *be_opened,
    196                                  Error **errp)
    197 {
    198     ChardevUdp *udp = backend->u.udp.data;
    199     SocketAddress *local_addr = socket_address_flatten(udp->local);
    200     SocketAddress *remote_addr = socket_address_flatten(udp->remote);
    201     QIOChannelSocket *sioc = qio_channel_socket_new();
    202     char *name;
    203     UdpChardev *s = UDP_CHARDEV(chr);
    204     int ret;
    205 
    206     ret = qio_channel_socket_dgram_sync(sioc, local_addr, remote_addr, errp);
    207     qapi_free_SocketAddress(local_addr);
    208     qapi_free_SocketAddress(remote_addr);
    209     if (ret < 0) {
    210         object_unref(OBJECT(sioc));
    211         return;
    212     }
    213 
    214     name = g_strdup_printf("chardev-udp-%s", chr->label);
    215     qio_channel_set_name(QIO_CHANNEL(sioc), name);
    216     g_free(name);
    217 
    218     s->ioc = QIO_CHANNEL(sioc);
    219     /* be isn't opened until we get a connection */
    220     *be_opened = false;
    221 }
    222 
    223 static void char_udp_class_init(ObjectClass *oc, void *data)
    224 {
    225     ChardevClass *cc = CHARDEV_CLASS(oc);
    226 
    227     cc->parse = qemu_chr_parse_udp;
    228     cc->open = qmp_chardev_open_udp;
    229     cc->chr_write = udp_chr_write;
    230     cc->chr_update_read_handler = udp_chr_update_read_handler;
    231 }
    232 
    233 static const TypeInfo char_udp_type_info = {
    234     .name = TYPE_CHARDEV_UDP,
    235     .parent = TYPE_CHARDEV,
    236     .instance_size = sizeof(UdpChardev),
    237     .instance_finalize = char_udp_finalize,
    238     .class_init = char_udp_class_init,
    239 };
    240 
    241 static void register_types(void)
    242 {
    243     type_register_static(&char_udp_type_info);
    244 }
    245 
    246 type_init(register_types);