xref: /openbmc/qemu/qga/channel-posix.c (revision c5ea91da443b458352c1b629b490ee6631775cb4)
14459bf38SPeter Maydell #include "qemu/osdep.h"
2b9947c9cSMarc-André Lureau #include "qemu/cutils.h"
3125b310eSMichael Roth #include <termios.h>
4da34e65cSMarkus Armbruster #include "qapi/error.h"
51de7afc9SPaolo Bonzini #include "qemu/sockets.h"
6dc03272dSMichael S. Tsirkin #include "channel.h"
7b9947c9cSMarc-André Lureau #include "cutils.h"
8125b310eSMichael Roth 
9e61ab1daSAndreas Färber #ifdef CONFIG_SOLARIS
10e61ab1daSAndreas Färber #include <stropts.h>
11e61ab1daSAndreas Färber #endif
12e61ab1daSAndreas Färber 
13125b310eSMichael Roth #define GA_CHANNEL_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
14125b310eSMichael Roth 
15125b310eSMichael Roth struct GAChannel {
16125b310eSMichael Roth     GIOChannel *listen_channel;
17125b310eSMichael Roth     GIOChannel *client_channel;
18125b310eSMichael Roth     GAChannelMethod method;
19125b310eSMichael Roth     GAChannelCallback event_cb;
20125b310eSMichael Roth     gpointer user_data;
21125b310eSMichael Roth };
22125b310eSMichael Roth 
23125b310eSMichael Roth static int ga_channel_client_add(GAChannel *c, int fd);
24125b310eSMichael Roth 
ga_channel_listen_accept(GIOChannel * channel,GIOCondition condition,gpointer data)25125b310eSMichael Roth static gboolean ga_channel_listen_accept(GIOChannel *channel,
26125b310eSMichael Roth                                          GIOCondition condition, gpointer data)
27125b310eSMichael Roth {
28125b310eSMichael Roth     GAChannel *c = data;
29125b310eSMichael Roth     int ret, client_fd;
30125b310eSMichael Roth     bool accepted = false;
31125b310eSMichael Roth 
32125b310eSMichael Roth     g_assert(channel != NULL);
33125b310eSMichael Roth 
34b8093d38SStefan Hajnoczi     client_fd = qemu_accept(g_io_channel_unix_get_fd(channel), NULL, NULL);
35125b310eSMichael Roth     if (client_fd == -1) {
36125b310eSMichael Roth         g_warning("error converting fd to gsocket: %s", strerror(errno));
37125b310eSMichael Roth         goto out;
38125b310eSMichael Roth     }
39ff5927baSMarc-André Lureau     qemu_socket_set_nonblock(client_fd);
40125b310eSMichael Roth     ret = ga_channel_client_add(c, client_fd);
41125b310eSMichael Roth     if (ret) {
42125b310eSMichael Roth         g_warning("error setting up connection");
4332c16620SMarkus Armbruster         close(client_fd);
44125b310eSMichael Roth         goto out;
45125b310eSMichael Roth     }
46125b310eSMichael Roth     accepted = true;
47125b310eSMichael Roth 
48125b310eSMichael Roth out:
49125b310eSMichael Roth     /* only accept 1 connection at a time */
50125b310eSMichael Roth     return !accepted;
51125b310eSMichael Roth }
52125b310eSMichael Roth 
53125b310eSMichael Roth /* start polling for readable events on listen fd, new==true
54125b310eSMichael Roth  * indicates we should use the existing s->listen_channel
55125b310eSMichael Roth  */
ga_channel_listen_add(GAChannel * c,int listen_fd,bool create)56125b310eSMichael Roth static void ga_channel_listen_add(GAChannel *c, int listen_fd, bool create)
57125b310eSMichael Roth {
58125b310eSMichael Roth     if (create) {
59125b310eSMichael Roth         c->listen_channel = g_io_channel_unix_new(listen_fd);
60125b310eSMichael Roth     }
61125b310eSMichael Roth     g_io_add_watch(c->listen_channel, G_IO_IN, ga_channel_listen_accept, c);
62125b310eSMichael Roth }
63125b310eSMichael Roth 
ga_channel_listen_close(GAChannel * c)64125b310eSMichael Roth static void ga_channel_listen_close(GAChannel *c)
65125b310eSMichael Roth {
66125b310eSMichael Roth     g_assert(c->listen_channel);
67125b310eSMichael Roth     g_io_channel_shutdown(c->listen_channel, true, NULL);
68125b310eSMichael Roth     g_io_channel_unref(c->listen_channel);
69125b310eSMichael Roth     c->listen_channel = NULL;
70125b310eSMichael Roth }
71125b310eSMichael Roth 
72125b310eSMichael Roth /* cleanup state for closed connection/session, start accepting new
73125b310eSMichael Roth  * connections if we're in listening mode
74125b310eSMichael Roth  */
ga_channel_client_close(GAChannel * c)75125b310eSMichael Roth static void ga_channel_client_close(GAChannel *c)
76125b310eSMichael Roth {
77125b310eSMichael Roth     g_assert(c->client_channel);
78125b310eSMichael Roth     g_io_channel_shutdown(c->client_channel, true, NULL);
79125b310eSMichael Roth     g_io_channel_unref(c->client_channel);
80125b310eSMichael Roth     c->client_channel = NULL;
81f06b2031SStefan Hajnoczi     if (c->listen_channel) {
82125b310eSMichael Roth         ga_channel_listen_add(c, 0, false);
83125b310eSMichael Roth     }
84125b310eSMichael Roth }
85125b310eSMichael Roth 
ga_channel_client_event(GIOChannel * channel,GIOCondition condition,gpointer data)86125b310eSMichael Roth static gboolean ga_channel_client_event(GIOChannel *channel,
87125b310eSMichael Roth                                         GIOCondition condition, gpointer data)
88125b310eSMichael Roth {
89125b310eSMichael Roth     GAChannel *c = data;
90125b310eSMichael Roth     gboolean client_cont;
91125b310eSMichael Roth 
92125b310eSMichael Roth     g_assert(c);
93125b310eSMichael Roth     if (c->event_cb) {
94125b310eSMichael Roth         client_cont = c->event_cb(condition, c->user_data);
95125b310eSMichael Roth         if (!client_cont) {
96125b310eSMichael Roth             ga_channel_client_close(c);
97125b310eSMichael Roth             return false;
98125b310eSMichael Roth         }
99125b310eSMichael Roth     }
100125b310eSMichael Roth     return true;
101125b310eSMichael Roth }
102125b310eSMichael Roth 
ga_channel_client_add(GAChannel * c,int fd)103125b310eSMichael Roth static int ga_channel_client_add(GAChannel *c, int fd)
104125b310eSMichael Roth {
105125b310eSMichael Roth     GIOChannel *client_channel;
106125b310eSMichael Roth     GError *err = NULL;
107125b310eSMichael Roth 
108125b310eSMichael Roth     g_assert(c && !c->client_channel);
109125b310eSMichael Roth     client_channel = g_io_channel_unix_new(fd);
110125b310eSMichael Roth     g_assert(client_channel);
111125b310eSMichael Roth     g_io_channel_set_encoding(client_channel, NULL, &err);
112125b310eSMichael Roth     if (err != NULL) {
113125b310eSMichael Roth         g_warning("error setting channel encoding to binary");
114125b310eSMichael Roth         g_error_free(err);
115125b310eSMichael Roth         return -1;
116125b310eSMichael Roth     }
117125b310eSMichael Roth     g_io_add_watch(client_channel, G_IO_IN | G_IO_HUP,
118125b310eSMichael Roth                    ga_channel_client_event, c);
119125b310eSMichael Roth     c->client_channel = client_channel;
120125b310eSMichael Roth     return 0;
121125b310eSMichael Roth }
122125b310eSMichael Roth 
ga_channel_open(GAChannel * c,const gchar * path,GAChannelMethod method,int fd,Error ** errp)12326de2296SStefan Hajnoczi static gboolean ga_channel_open(GAChannel *c, const gchar *path,
12487ed8b2cSMarc-André Lureau                                 GAChannelMethod method, int fd, Error **errp)
125125b310eSMichael Roth {
126125b310eSMichael Roth     int ret;
127125b310eSMichael Roth     c->method = method;
128125b310eSMichael Roth 
129125b310eSMichael Roth     switch (c->method) {
130125b310eSMichael Roth     case GA_CHANNEL_VIRTIO_SERIAL: {
13126de2296SStefan Hajnoczi         assert(fd < 0);
132b9947c9cSMarc-André Lureau         fd = qga_open_cloexec(
133b9947c9cSMarc-André Lureau             path,
134e61ab1daSAndreas Färber #ifndef CONFIG_SOLARIS
135b9947c9cSMarc-André Lureau             O_ASYNC |
136e61ab1daSAndreas Färber #endif
137b9947c9cSMarc-André Lureau             O_RDWR | O_NONBLOCK,
138b9947c9cSMarc-André Lureau             0
139e61ab1daSAndreas Färber         );
140125b310eSMichael Roth         if (fd == -1) {
1413845ffffSBjørn Forsman             error_setg_errno(errp, errno, "error opening channel '%s'", path);
1427868181fSMarkus Armbruster             return false;
143125b310eSMichael Roth         }
144e61ab1daSAndreas Färber #ifdef CONFIG_SOLARIS
145e61ab1daSAndreas Färber         ret = ioctl(fd, I_SETSIG, S_OUTPUT | S_INPUT | S_HIPRI);
146e61ab1daSAndreas Färber         if (ret == -1) {
14787ed8b2cSMarc-André Lureau             error_setg_errno(errp, errno, "error setting event mask for channel");
1487868181fSMarkus Armbruster             close(fd);
1497868181fSMarkus Armbruster             return false;
150e61ab1daSAndreas Färber         }
151e61ab1daSAndreas Färber #endif
152c6cd588bSAlexander Ivanov #ifdef __FreeBSD__
153c6cd588bSAlexander Ivanov         /*
154c6cd588bSAlexander Ivanov          * In the default state channel sends echo of every command to a
155*01dc0651SMichael Tokarev          * client. The client program doesn't expect this and raises an
156c6cd588bSAlexander Ivanov          * error. Suppress echo by resetting ECHO terminal flag.
157c6cd588bSAlexander Ivanov          */
158c6cd588bSAlexander Ivanov         struct termios tio;
159c6cd588bSAlexander Ivanov         if (tcgetattr(fd, &tio) < 0) {
160c6cd588bSAlexander Ivanov             error_setg_errno(errp, errno, "error getting channel termios attrs");
161c6cd588bSAlexander Ivanov             close(fd);
162c6cd588bSAlexander Ivanov             return false;
163c6cd588bSAlexander Ivanov         }
164c6cd588bSAlexander Ivanov         tio.c_lflag &= ~ECHO;
165c6cd588bSAlexander Ivanov         if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) {
166c6cd588bSAlexander Ivanov             error_setg_errno(errp, errno, "error setting channel termios attrs");
167c6cd588bSAlexander Ivanov             close(fd);
168c6cd588bSAlexander Ivanov             return false;
169c6cd588bSAlexander Ivanov         }
170c6cd588bSAlexander Ivanov #endif /* __FreeBSD__ */
171125b310eSMichael Roth         ret = ga_channel_client_add(c, fd);
172125b310eSMichael Roth         if (ret) {
17387ed8b2cSMarc-André Lureau             error_setg(errp, "error adding channel to main loop");
174d4f4a3efSMarkus Armbruster             close(fd);
175125b310eSMichael Roth             return false;
176125b310eSMichael Roth         }
177125b310eSMichael Roth         break;
178125b310eSMichael Roth     }
179125b310eSMichael Roth     case GA_CHANNEL_ISA_SERIAL: {
180125b310eSMichael Roth         struct termios tio;
18126de2296SStefan Hajnoczi 
18226de2296SStefan Hajnoczi         assert(fd < 0);
183b9947c9cSMarc-André Lureau         fd = qga_open_cloexec(path, O_RDWR | O_NOCTTY | O_NONBLOCK, 0);
184125b310eSMichael Roth         if (fd == -1) {
1853845ffffSBjørn Forsman             error_setg_errno(errp, errno, "error opening channel '%s'", path);
1867868181fSMarkus Armbruster             return false;
187125b310eSMichael Roth         }
188125b310eSMichael Roth         tcgetattr(fd, &tio);
189125b310eSMichael Roth         /* set up serial port for non-canonical, dumb byte streaming */
190125b310eSMichael Roth         tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
191125b310eSMichael Roth                          INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
192125b310eSMichael Roth                          IMAXBEL);
193125b310eSMichael Roth         tio.c_oflag = 0;
194125b310eSMichael Roth         tio.c_lflag = 0;
195125b310eSMichael Roth         tio.c_cflag |= GA_CHANNEL_BAUDRATE_DEFAULT;
196125b310eSMichael Roth         /* 1 available byte min or reads will block (we'll set non-blocking
197125b310eSMichael Roth          * elsewhere, else we have to deal with read()=0 instead)
198125b310eSMichael Roth          */
199125b310eSMichael Roth         tio.c_cc[VMIN] = 1;
200125b310eSMichael Roth         tio.c_cc[VTIME] = 0;
201125b310eSMichael Roth         /* flush everything waiting for read/xmit, it's garbage at this point */
202125b310eSMichael Roth         tcflush(fd, TCIFLUSH);
203125b310eSMichael Roth         tcsetattr(fd, TCSANOW, &tio);
204125b310eSMichael Roth         ret = ga_channel_client_add(c, fd);
205125b310eSMichael Roth         if (ret) {
20687ed8b2cSMarc-André Lureau             error_setg(errp, "error adding channel to main loop");
2077868181fSMarkus Armbruster             close(fd);
2087868181fSMarkus Armbruster             return false;
209125b310eSMichael Roth         }
210125b310eSMichael Roth         break;
211125b310eSMichael Roth     }
212125b310eSMichael Roth     case GA_CHANNEL_UNIX_LISTEN: {
21326de2296SStefan Hajnoczi         if (fd < 0) {
21487ed8b2cSMarc-André Lureau             fd = unix_listen(path, errp);
21587ed8b2cSMarc-André Lureau             if (fd < 0) {
216125b310eSMichael Roth                 return false;
217125b310eSMichael Roth             }
21826de2296SStefan Hajnoczi         }
219125b310eSMichael Roth         ga_channel_listen_add(c, fd, true);
220125b310eSMichael Roth         break;
221125b310eSMichael Roth     }
222586ef5deSStefan Hajnoczi     case GA_CHANNEL_VSOCK_LISTEN: {
22326de2296SStefan Hajnoczi         if (fd < 0) {
224586ef5deSStefan Hajnoczi             SocketAddress *addr;
225586ef5deSStefan Hajnoczi             char *addr_str;
226586ef5deSStefan Hajnoczi 
227586ef5deSStefan Hajnoczi             addr_str = g_strdup_printf("vsock:%s", path);
22887ed8b2cSMarc-André Lureau             addr = socket_parse(addr_str, errp);
229586ef5deSStefan Hajnoczi             g_free(addr_str);
23087ed8b2cSMarc-André Lureau             if (!addr) {
231586ef5deSStefan Hajnoczi                 return false;
232586ef5deSStefan Hajnoczi             }
233586ef5deSStefan Hajnoczi 
23487ed8b2cSMarc-André Lureau             fd = socket_listen(addr, 1, errp);
235586ef5deSStefan Hajnoczi             qapi_free_SocketAddress(addr);
23687ed8b2cSMarc-André Lureau             if (fd < 0) {
237586ef5deSStefan Hajnoczi                 return false;
238586ef5deSStefan Hajnoczi             }
23926de2296SStefan Hajnoczi         }
240586ef5deSStefan Hajnoczi         ga_channel_listen_add(c, fd, true);
241586ef5deSStefan Hajnoczi         break;
242586ef5deSStefan Hajnoczi     }
243125b310eSMichael Roth     default:
24487ed8b2cSMarc-André Lureau         error_setg(errp, "error binding/listening to specified socket");
245125b310eSMichael Roth         return false;
246125b310eSMichael Roth     }
247125b310eSMichael Roth 
248125b310eSMichael Roth     return true;
249125b310eSMichael Roth }
250125b310eSMichael Roth 
ga_channel_write_all(GAChannel * c,const gchar * buf,gsize size)251125b310eSMichael Roth GIOStatus ga_channel_write_all(GAChannel *c, const gchar *buf, gsize size)
252125b310eSMichael Roth {
253125b310eSMichael Roth     GError *err = NULL;
254125b310eSMichael Roth     gsize written = 0;
255125b310eSMichael Roth     GIOStatus status = G_IO_STATUS_NORMAL;
256125b310eSMichael Roth 
257125b310eSMichael Roth     while (size) {
258f74df9bfSYuri Pudgorodskiy         g_debug("sending data, count: %d", (int)size);
259125b310eSMichael Roth         status = g_io_channel_write_chars(c->client_channel, buf, size,
260125b310eSMichael Roth                                           &written, &err);
261f74df9bfSYuri Pudgorodskiy         if (status == G_IO_STATUS_NORMAL) {
262125b310eSMichael Roth             size -= written;
263f74df9bfSYuri Pudgorodskiy             buf += written;
264f74df9bfSYuri Pudgorodskiy         } else if (status != G_IO_STATUS_AGAIN) {
265f74df9bfSYuri Pudgorodskiy             g_warning("error writing to channel: %s", err->message);
266f74df9bfSYuri Pudgorodskiy             return status;
267f74df9bfSYuri Pudgorodskiy         }
268125b310eSMichael Roth     }
269125b310eSMichael Roth 
270f74df9bfSYuri Pudgorodskiy     do {
271125b310eSMichael Roth         status = g_io_channel_flush(c->client_channel, &err);
272f74df9bfSYuri Pudgorodskiy     } while (status == G_IO_STATUS_AGAIN);
273f74df9bfSYuri Pudgorodskiy 
274f74df9bfSYuri Pudgorodskiy     if (status != G_IO_STATUS_NORMAL) {
275125b310eSMichael Roth         g_warning("error flushing channel: %s", err->message);
276125b310eSMichael Roth     }
277125b310eSMichael Roth 
278125b310eSMichael Roth     return status;
279125b310eSMichael Roth }
280125b310eSMichael Roth 
ga_channel_read(GAChannel * c,gchar * buf,gsize size,gsize * count)281125b310eSMichael Roth GIOStatus ga_channel_read(GAChannel *c, gchar *buf, gsize size, gsize *count)
282125b310eSMichael Roth {
283125b310eSMichael Roth     return g_io_channel_read_chars(c->client_channel, buf, size, count, NULL);
284125b310eSMichael Roth }
285125b310eSMichael Roth 
ga_channel_new(GAChannelMethod method,const gchar * path,int listen_fd,GAChannelCallback cb,gpointer opaque)286125b310eSMichael Roth GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
28726de2296SStefan Hajnoczi                           int listen_fd, GAChannelCallback cb, gpointer opaque)
288125b310eSMichael Roth {
28987ed8b2cSMarc-André Lureau     Error *err = NULL;
290f3a06403SMarkus Armbruster     GAChannel *c = g_new0(GAChannel, 1);
291125b310eSMichael Roth     c->event_cb = cb;
292125b310eSMichael Roth     c->user_data = opaque;
293125b310eSMichael Roth 
29487ed8b2cSMarc-André Lureau     if (!ga_channel_open(c, path, method, listen_fd, &err)) {
29587ed8b2cSMarc-André Lureau         g_critical("%s", error_get_pretty(err));
29687ed8b2cSMarc-André Lureau         error_free(err);
297125b310eSMichael Roth         ga_channel_free(c);
298125b310eSMichael Roth         return NULL;
299125b310eSMichael Roth     }
300125b310eSMichael Roth 
301125b310eSMichael Roth     return c;
302125b310eSMichael Roth }
303125b310eSMichael Roth 
ga_channel_free(GAChannel * c)304125b310eSMichael Roth void ga_channel_free(GAChannel *c)
305125b310eSMichael Roth {
306f06b2031SStefan Hajnoczi     if (c->listen_channel) {
307125b310eSMichael Roth         ga_channel_listen_close(c);
308125b310eSMichael Roth     }
309125b310eSMichael Roth     if (c->client_channel) {
310125b310eSMichael Roth         ga_channel_client_close(c);
311125b310eSMichael Roth     }
312125b310eSMichael Roth     g_free(c);
313125b310eSMichael Roth }
314