1d24ca4b8SMarc-André Lureau /*
2d24ca4b8SMarc-André Lureau * QEMU System Emulator
3d24ca4b8SMarc-André Lureau *
4d24ca4b8SMarc-André Lureau * Copyright (c) 2003-2008 Fabrice Bellard
5d24ca4b8SMarc-André Lureau *
6d24ca4b8SMarc-André Lureau * Permission is hereby granted, free of charge, to any person obtaining a copy
7d24ca4b8SMarc-André Lureau * of this software and associated documentation files (the "Software"), to deal
8d24ca4b8SMarc-André Lureau * in the Software without restriction, including without limitation the rights
9d24ca4b8SMarc-André Lureau * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10d24ca4b8SMarc-André Lureau * copies of the Software, and to permit persons to whom the Software is
11d24ca4b8SMarc-André Lureau * furnished to do so, subject to the following conditions:
12d24ca4b8SMarc-André Lureau *
13d24ca4b8SMarc-André Lureau * The above copyright notice and this permission notice shall be included in
14d24ca4b8SMarc-André Lureau * all copies or substantial portions of the Software.
15d24ca4b8SMarc-André Lureau *
16d24ca4b8SMarc-André Lureau * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d24ca4b8SMarc-André Lureau * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d24ca4b8SMarc-André Lureau * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19d24ca4b8SMarc-André Lureau * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d24ca4b8SMarc-André Lureau * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21d24ca4b8SMarc-André Lureau * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22d24ca4b8SMarc-André Lureau * THE SOFTWARE.
23d24ca4b8SMarc-André Lureau */
24922a01a0SMarkus Armbruster
25d24ca4b8SMarc-André Lureau #include "qemu/osdep.h"
268228e353SMarc-André Lureau #include "chardev/char.h"
27d24ca4b8SMarc-André Lureau #include "io/channel-socket.h"
28981b06e7SJulia Suvorova #include "io/channel-websock.h"
29d24ca4b8SMarc-André Lureau #include "qemu/error-report.h"
300b8fa32fSMarkus Armbruster #include "qemu/module.h"
31922a01a0SMarkus Armbruster #include "qemu/option.h"
32d24ca4b8SMarc-André Lureau #include "qapi/error.h"
33d24ca4b8SMarc-André Lureau #include "qapi/clone-visitor.h"
349af23989SMarkus Armbruster #include "qapi/qapi-visit-sockets.h"
358ee44806SLukas Straub #include "qemu/yank.h"
366ffade79SDaniel P. Berrangé #include "trace.h"
37d24ca4b8SMarc-André Lureau
388228e353SMarc-André Lureau #include "chardev/char-io.h"
39fa670c80SMarc-André Lureau #include "chardev/char-socket.h"
40d24ca4b8SMarc-André Lureau
41d24ca4b8SMarc-André Lureau static gboolean socket_reconnect_timeout(gpointer opaque);
42ce1230c0SPeter Xu static void tcp_chr_telnet_init(Chardev *chr);
43d24ca4b8SMarc-André Lureau
tcp_chr_change_state(SocketChardev * s,TCPChardevState state)4432423ccaSDaniel P. Berrangé static void tcp_chr_change_state(SocketChardev *s, TCPChardevState state)
4532423ccaSDaniel P. Berrangé {
4632423ccaSDaniel P. Berrangé switch (state) {
4732423ccaSDaniel P. Berrangé case TCP_CHARDEV_STATE_DISCONNECTED:
4832423ccaSDaniel P. Berrangé break;
4932423ccaSDaniel P. Berrangé case TCP_CHARDEV_STATE_CONNECTING:
5032423ccaSDaniel P. Berrangé assert(s->state == TCP_CHARDEV_STATE_DISCONNECTED);
5132423ccaSDaniel P. Berrangé break;
5232423ccaSDaniel P. Berrangé case TCP_CHARDEV_STATE_CONNECTED:
5332423ccaSDaniel P. Berrangé assert(s->state == TCP_CHARDEV_STATE_CONNECTING);
5432423ccaSDaniel P. Berrangé break;
5532423ccaSDaniel P. Berrangé }
5632423ccaSDaniel P. Berrangé s->state = state;
5732423ccaSDaniel P. Berrangé }
5832423ccaSDaniel P. Berrangé
tcp_chr_reconn_timer_cancel(SocketChardev * s)592c716ba1SPeter Xu static void tcp_chr_reconn_timer_cancel(SocketChardev *s)
602c716ba1SPeter Xu {
612c716ba1SPeter Xu if (s->reconnect_timer) {
622c716ba1SPeter Xu g_source_destroy(s->reconnect_timer);
632c716ba1SPeter Xu g_source_unref(s->reconnect_timer);
642c716ba1SPeter Xu s->reconnect_timer = NULL;
652c716ba1SPeter Xu }
662c716ba1SPeter Xu }
672c716ba1SPeter Xu
qemu_chr_socket_restart_timer(Chardev * chr)68d24ca4b8SMarc-André Lureau static void qemu_chr_socket_restart_timer(Chardev *chr)
69d24ca4b8SMarc-André Lureau {
70d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
71d24ca4b8SMarc-André Lureau char *name;
72d24ca4b8SMarc-André Lureau
7332423ccaSDaniel P. Berrangé assert(s->state == TCP_CHARDEV_STATE_DISCONNECTED);
744b47373aSDaniel P. Berrangé assert(!s->reconnect_timer);
75d24ca4b8SMarc-André Lureau name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label);
762c716ba1SPeter Xu s->reconnect_timer = qemu_chr_timeout_add_ms(chr,
77*c8e2b6b4SDaniil Tatianin s->reconnect_time_ms,
782c716ba1SPeter Xu socket_reconnect_timeout,
792c716ba1SPeter Xu chr);
802c716ba1SPeter Xu g_source_set_name(s->reconnect_timer, name);
81d24ca4b8SMarc-André Lureau g_free(name);
82d24ca4b8SMarc-André Lureau }
83d24ca4b8SMarc-André Lureau
check_report_connect_error(Chardev * chr,Error * err)84d24ca4b8SMarc-André Lureau static void check_report_connect_error(Chardev *chr,
85d24ca4b8SMarc-André Lureau Error *err)
86d24ca4b8SMarc-André Lureau {
87d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
88d24ca4b8SMarc-André Lureau
89d24ca4b8SMarc-André Lureau if (!s->connect_err_reported) {
905217f188SMarkus Armbruster error_reportf_err(err,
915217f188SMarkus Armbruster "Unable to connect character device %s: ",
925217f188SMarkus Armbruster chr->label);
93d24ca4b8SMarc-André Lureau s->connect_err_reported = true;
94ed4e0d2eSlichun } else {
95ed4e0d2eSlichun error_free(err);
96d24ca4b8SMarc-André Lureau }
97d24ca4b8SMarc-André Lureau qemu_chr_socket_restart_timer(chr);
98d24ca4b8SMarc-André Lureau }
99d24ca4b8SMarc-André Lureau
100194b7f0dSDaniel P. Berrange static void tcp_chr_accept(QIONetListener *listener,
101194b7f0dSDaniel P. Berrange QIOChannelSocket *cioc,
102d24ca4b8SMarc-André Lureau void *opaque);
103d24ca4b8SMarc-André Lureau
104b0a335e3SAnton Nefedov static int tcp_chr_read_poll(void *opaque);
10578d01598SAlberto Garcia static void tcp_chr_disconnect_locked(Chardev *chr);
106b0a335e3SAnton Nefedov
107d24ca4b8SMarc-André Lureau /* Called with chr_write_lock held. */
tcp_chr_write(Chardev * chr,const uint8_t * buf,int len)108d24ca4b8SMarc-André Lureau static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
109d24ca4b8SMarc-André Lureau {
110d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
111d24ca4b8SMarc-André Lureau
11232423ccaSDaniel P. Berrangé if (s->state == TCP_CHARDEV_STATE_CONNECTED) {
113d24ca4b8SMarc-André Lureau int ret = io_channel_send_full(s->ioc, buf, len,
114d24ca4b8SMarc-André Lureau s->write_msgfds,
115d24ca4b8SMarc-André Lureau s->write_msgfds_num);
116d24ca4b8SMarc-André Lureau
11781e34930Sxinhua.Cao /* free the written msgfds in any cases
11881e34930Sxinhua.Cao * other than ret < 0 && errno == EAGAIN
11981e34930Sxinhua.Cao */
12081e34930Sxinhua.Cao if (!(ret < 0 && EAGAIN == errno)
12181e34930Sxinhua.Cao && s->write_msgfds_num) {
122d24ca4b8SMarc-André Lureau g_free(s->write_msgfds);
123d24ca4b8SMarc-André Lureau s->write_msgfds = 0;
124d24ca4b8SMarc-André Lureau s->write_msgfds_num = 0;
125d24ca4b8SMarc-André Lureau }
126d24ca4b8SMarc-André Lureau
127b0a335e3SAnton Nefedov if (ret < 0 && errno != EAGAIN) {
128b0a335e3SAnton Nefedov if (tcp_chr_read_poll(chr) <= 0) {
12927109447SDima Stepanov /* Perform disconnect and return error. */
1306ffade79SDaniel P. Berrangé trace_chr_socket_poll_err(chr, chr->label);
13178d01598SAlberto Garcia tcp_chr_disconnect_locked(chr);
132b0a335e3SAnton Nefedov } /* else let the read handler finish it properly */
133b0a335e3SAnton Nefedov }
134b0a335e3SAnton Nefedov
135d24ca4b8SMarc-André Lureau return ret;
136d24ca4b8SMarc-André Lureau } else {
13727109447SDima Stepanov /* Indicate an error. */
13827109447SDima Stepanov errno = EIO;
13927109447SDima Stepanov return -1;
140d24ca4b8SMarc-André Lureau }
141d24ca4b8SMarc-André Lureau }
142d24ca4b8SMarc-André Lureau
tcp_chr_read_poll(void * opaque)143d24ca4b8SMarc-André Lureau static int tcp_chr_read_poll(void *opaque)
144d24ca4b8SMarc-André Lureau {
145d24ca4b8SMarc-André Lureau Chardev *chr = CHARDEV(opaque);
146d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(opaque);
14732423ccaSDaniel P. Berrangé if (s->state != TCP_CHARDEV_STATE_CONNECTED) {
148d24ca4b8SMarc-André Lureau return 0;
149d24ca4b8SMarc-André Lureau }
150d24ca4b8SMarc-André Lureau s->max_size = qemu_chr_be_can_write(chr);
151d24ca4b8SMarc-André Lureau return s->max_size;
152d24ca4b8SMarc-André Lureau }
153d24ca4b8SMarc-André Lureau
tcp_chr_process_IAC_bytes(Chardev * chr,SocketChardev * s,uint8_t * buf,int * size)154d24ca4b8SMarc-André Lureau static void tcp_chr_process_IAC_bytes(Chardev *chr,
155d24ca4b8SMarc-André Lureau SocketChardev *s,
156d24ca4b8SMarc-André Lureau uint8_t *buf, int *size)
157d24ca4b8SMarc-André Lureau {
158ae92cbd5SJing Liu /* Handle any telnet or tn3270 client's basic IAC options.
159ae92cbd5SJing Liu * For telnet options, it satisfies char by char mode with no echo.
160ae92cbd5SJing Liu * For tn3270 options, it satisfies binary mode with EOR.
161ae92cbd5SJing Liu * All IAC options will be removed from the buf and the do_opt
162ae92cbd5SJing Liu * pointer will be used to track the state of the width of the
163ae92cbd5SJing Liu * IAC information.
164d24ca4b8SMarc-André Lureau *
165ae92cbd5SJing Liu * RFC854: "All TELNET commands consist of at least a two byte sequence.
166ae92cbd5SJing Liu * The commands dealing with option negotiation are three byte sequences,
167ae92cbd5SJing Liu * the third byte being the code for the option referenced."
168ae92cbd5SJing Liu * "IAC BREAK", "IAC IP", "IAC NOP" and the double IAC are two bytes.
169ae92cbd5SJing Liu * "IAC SB", "IAC SE" and "IAC EOR" are saved to split up data boundary
170ae92cbd5SJing Liu * for tn3270.
171ae92cbd5SJing Liu * NOP, Break and Interrupt Process(IP) might be encountered during a TN3270
172ae92cbd5SJing Liu * session, and NOP and IP need to be done later.
173d24ca4b8SMarc-André Lureau */
174d24ca4b8SMarc-André Lureau
175d24ca4b8SMarc-André Lureau int i;
176d24ca4b8SMarc-André Lureau int j = 0;
177d24ca4b8SMarc-André Lureau
178d24ca4b8SMarc-André Lureau for (i = 0; i < *size; i++) {
179d24ca4b8SMarc-André Lureau if (s->do_telnetopt > 1) {
180d24ca4b8SMarc-André Lureau if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
181d24ca4b8SMarc-André Lureau /* Double IAC means send an IAC */
182d24ca4b8SMarc-André Lureau if (j != i) {
183d24ca4b8SMarc-André Lureau buf[j] = buf[i];
184d24ca4b8SMarc-André Lureau }
185d24ca4b8SMarc-André Lureau j++;
186d24ca4b8SMarc-André Lureau s->do_telnetopt = 1;
187d24ca4b8SMarc-André Lureau } else {
188d24ca4b8SMarc-André Lureau if ((unsigned char)buf[i] == IAC_BREAK
189d24ca4b8SMarc-André Lureau && s->do_telnetopt == 2) {
190d24ca4b8SMarc-André Lureau /* Handle IAC break commands by sending a serial break */
191d24ca4b8SMarc-André Lureau qemu_chr_be_event(chr, CHR_EVENT_BREAK);
192d24ca4b8SMarc-André Lureau s->do_telnetopt++;
193ae92cbd5SJing Liu } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_EOR
194ae92cbd5SJing Liu || (unsigned char)buf[i] == IAC_SB
195ae92cbd5SJing Liu || (unsigned char)buf[i] == IAC_SE)
196ae92cbd5SJing Liu && s->do_telnetopt == 2) {
197ae92cbd5SJing Liu buf[j++] = IAC;
198ae92cbd5SJing Liu buf[j++] = buf[i];
199ae92cbd5SJing Liu s->do_telnetopt++;
200ae92cbd5SJing Liu } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_IP
201ae92cbd5SJing Liu || (unsigned char)buf[i] == IAC_NOP)
202ae92cbd5SJing Liu && s->do_telnetopt == 2) {
203ae92cbd5SJing Liu /* TODO: IP and NOP need to be implemented later. */
204ae92cbd5SJing Liu s->do_telnetopt++;
205d24ca4b8SMarc-André Lureau }
206d24ca4b8SMarc-André Lureau s->do_telnetopt++;
207d24ca4b8SMarc-André Lureau }
208d24ca4b8SMarc-André Lureau if (s->do_telnetopt >= 4) {
209d24ca4b8SMarc-André Lureau s->do_telnetopt = 1;
210d24ca4b8SMarc-André Lureau }
211d24ca4b8SMarc-André Lureau } else {
212d24ca4b8SMarc-André Lureau if ((unsigned char)buf[i] == IAC) {
213d24ca4b8SMarc-André Lureau s->do_telnetopt = 2;
214d24ca4b8SMarc-André Lureau } else {
215d24ca4b8SMarc-André Lureau if (j != i) {
216d24ca4b8SMarc-André Lureau buf[j] = buf[i];
217d24ca4b8SMarc-André Lureau }
218d24ca4b8SMarc-André Lureau j++;
219d24ca4b8SMarc-André Lureau }
220d24ca4b8SMarc-André Lureau }
221d24ca4b8SMarc-André Lureau }
222d24ca4b8SMarc-André Lureau *size = j;
223d24ca4b8SMarc-André Lureau }
224d24ca4b8SMarc-André Lureau
tcp_get_msgfds(Chardev * chr,int * fds,int num)225d24ca4b8SMarc-André Lureau static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
226d24ca4b8SMarc-André Lureau {
227d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
228d24ca4b8SMarc-André Lureau
229d24ca4b8SMarc-André Lureau int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
230d24ca4b8SMarc-André Lureau
231d24ca4b8SMarc-André Lureau assert(num <= TCP_MAX_FDS);
232d24ca4b8SMarc-André Lureau
233d24ca4b8SMarc-André Lureau if (to_copy) {
234d24ca4b8SMarc-André Lureau int i;
235d24ca4b8SMarc-André Lureau
236d24ca4b8SMarc-André Lureau memcpy(fds, s->read_msgfds, to_copy * sizeof(int));
237d24ca4b8SMarc-André Lureau
238d24ca4b8SMarc-André Lureau /* Close unused fds */
239d24ca4b8SMarc-André Lureau for (i = to_copy; i < s->read_msgfds_num; i++) {
240d24ca4b8SMarc-André Lureau close(s->read_msgfds[i]);
241d24ca4b8SMarc-André Lureau }
242d24ca4b8SMarc-André Lureau
243d24ca4b8SMarc-André Lureau g_free(s->read_msgfds);
244d24ca4b8SMarc-André Lureau s->read_msgfds = 0;
245d24ca4b8SMarc-André Lureau s->read_msgfds_num = 0;
246d24ca4b8SMarc-André Lureau }
247d24ca4b8SMarc-André Lureau
248d24ca4b8SMarc-André Lureau return to_copy;
249d24ca4b8SMarc-André Lureau }
250d24ca4b8SMarc-André Lureau
tcp_set_msgfds(Chardev * chr,int * fds,int num)251d24ca4b8SMarc-André Lureau static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
252d24ca4b8SMarc-André Lureau {
253d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
254d24ca4b8SMarc-André Lureau
255d24ca4b8SMarc-André Lureau /* clear old pending fd array */
256d24ca4b8SMarc-André Lureau g_free(s->write_msgfds);
257d24ca4b8SMarc-André Lureau s->write_msgfds = NULL;
258d24ca4b8SMarc-André Lureau s->write_msgfds_num = 0;
259d24ca4b8SMarc-André Lureau
26032423ccaSDaniel P. Berrangé if ((s->state != TCP_CHARDEV_STATE_CONNECTED) ||
261d24ca4b8SMarc-André Lureau !qio_channel_has_feature(s->ioc,
262d24ca4b8SMarc-André Lureau QIO_CHANNEL_FEATURE_FD_PASS)) {
263d24ca4b8SMarc-André Lureau return -1;
264d24ca4b8SMarc-André Lureau }
265d24ca4b8SMarc-André Lureau
266d24ca4b8SMarc-André Lureau if (num) {
267d24ca4b8SMarc-André Lureau s->write_msgfds = g_new(int, num);
268d24ca4b8SMarc-André Lureau memcpy(s->write_msgfds, fds, num * sizeof(int));
269d24ca4b8SMarc-André Lureau }
270d24ca4b8SMarc-André Lureau
271d24ca4b8SMarc-André Lureau s->write_msgfds_num = num;
272d24ca4b8SMarc-André Lureau
273d24ca4b8SMarc-André Lureau return 0;
274d24ca4b8SMarc-André Lureau }
275d24ca4b8SMarc-André Lureau
tcp_chr_recv(Chardev * chr,char * buf,size_t len)276d24ca4b8SMarc-André Lureau static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
277d24ca4b8SMarc-André Lureau {
278d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
279d24ca4b8SMarc-André Lureau struct iovec iov = { .iov_base = buf, .iov_len = len };
280d24ca4b8SMarc-André Lureau int ret;
281d24ca4b8SMarc-André Lureau size_t i;
282d24ca4b8SMarc-André Lureau int *msgfds = NULL;
283d24ca4b8SMarc-André Lureau size_t msgfds_num = 0;
2846ffade79SDaniel P. Berrangé Error *err = NULL;
285d24ca4b8SMarc-André Lureau
286d24ca4b8SMarc-André Lureau if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
287d24ca4b8SMarc-André Lureau ret = qio_channel_readv_full(s->ioc, &iov, 1,
288d24ca4b8SMarc-André Lureau &msgfds, &msgfds_num,
2896ffade79SDaniel P. Berrangé 0, &err);
290d24ca4b8SMarc-André Lureau } else {
291d24ca4b8SMarc-André Lureau ret = qio_channel_readv_full(s->ioc, &iov, 1,
292d24ca4b8SMarc-André Lureau NULL, NULL,
2936ffade79SDaniel P. Berrangé 0, &err);
294d24ca4b8SMarc-André Lureau }
295d24ca4b8SMarc-André Lureau
296d24ca4b8SMarc-André Lureau if (msgfds_num) {
297d24ca4b8SMarc-André Lureau /* close and clean read_msgfds */
298d24ca4b8SMarc-André Lureau for (i = 0; i < s->read_msgfds_num; i++) {
299d24ca4b8SMarc-André Lureau close(s->read_msgfds[i]);
300d24ca4b8SMarc-André Lureau }
301d24ca4b8SMarc-André Lureau
302d24ca4b8SMarc-André Lureau if (s->read_msgfds_num) {
303d24ca4b8SMarc-André Lureau g_free(s->read_msgfds);
304d24ca4b8SMarc-André Lureau }
305d24ca4b8SMarc-André Lureau
306d24ca4b8SMarc-André Lureau s->read_msgfds = msgfds;
307d24ca4b8SMarc-André Lureau s->read_msgfds_num = msgfds_num;
308d24ca4b8SMarc-André Lureau }
309d24ca4b8SMarc-André Lureau
310d24ca4b8SMarc-André Lureau for (i = 0; i < s->read_msgfds_num; i++) {
311d24ca4b8SMarc-André Lureau int fd = s->read_msgfds[i];
312d24ca4b8SMarc-André Lureau if (fd < 0) {
313d24ca4b8SMarc-André Lureau continue;
314d24ca4b8SMarc-André Lureau }
315d24ca4b8SMarc-André Lureau
316d24ca4b8SMarc-André Lureau /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */
317ff5927baSMarc-André Lureau qemu_socket_set_block(fd);
318d24ca4b8SMarc-André Lureau
319d24ca4b8SMarc-André Lureau #ifndef MSG_CMSG_CLOEXEC
320d24ca4b8SMarc-André Lureau qemu_set_cloexec(fd);
321d24ca4b8SMarc-André Lureau #endif
322d24ca4b8SMarc-André Lureau }
323d24ca4b8SMarc-André Lureau
324e8797505SRoman Kagan if (ret == QIO_CHANNEL_ERR_BLOCK) {
325e8797505SRoman Kagan errno = EAGAIN;
326e8797505SRoman Kagan ret = -1;
327e8797505SRoman Kagan } else if (ret == -1) {
3286ffade79SDaniel P. Berrangé trace_chr_socket_recv_err(chr, chr->label, error_get_pretty(err));
3296ffade79SDaniel P. Berrangé error_free(err);
330e8797505SRoman Kagan errno = EIO;
3316ffade79SDaniel P. Berrangé } else if (ret == 0) {
3326ffade79SDaniel P. Berrangé trace_chr_socket_recv_eof(chr, chr->label);
333e8797505SRoman Kagan }
334e8797505SRoman Kagan
335d24ca4b8SMarc-André Lureau return ret;
336d24ca4b8SMarc-André Lureau }
337d24ca4b8SMarc-André Lureau
tcp_chr_add_watch(Chardev * chr,GIOCondition cond)338d24ca4b8SMarc-André Lureau static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
339d24ca4b8SMarc-André Lureau {
340d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
3416585b162SPavel Dovgalyuk if (!s->ioc) {
3426585b162SPavel Dovgalyuk return NULL;
3436585b162SPavel Dovgalyuk }
344d24ca4b8SMarc-André Lureau return qio_channel_create_watch(s->ioc, cond);
345d24ca4b8SMarc-André Lureau }
346d24ca4b8SMarc-André Lureau
remove_hup_source(SocketChardev * s)347dfe9ea20SMarc-André Lureau static void remove_hup_source(SocketChardev *s)
348dfe9ea20SMarc-André Lureau {
349dfe9ea20SMarc-André Lureau if (s->hup_source != NULL) {
350dfe9ea20SMarc-André Lureau g_source_destroy(s->hup_source);
351dfe9ea20SMarc-André Lureau g_source_unref(s->hup_source);
352dfe9ea20SMarc-André Lureau s->hup_source = NULL;
353dfe9ea20SMarc-André Lureau }
354dfe9ea20SMarc-André Lureau }
355dfe9ea20SMarc-André Lureau
char_socket_yank_iochannel(void * opaque)3561a92d6d5SLukas Straub static void char_socket_yank_iochannel(void *opaque)
3571a92d6d5SLukas Straub {
3581a92d6d5SLukas Straub QIOChannel *ioc = QIO_CHANNEL(opaque);
3591a92d6d5SLukas Straub
3601a92d6d5SLukas Straub qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
3611a92d6d5SLukas Straub }
3621a92d6d5SLukas Straub
tcp_chr_free_connection(Chardev * chr)363d24ca4b8SMarc-André Lureau static void tcp_chr_free_connection(Chardev *chr)
364d24ca4b8SMarc-André Lureau {
365d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
366d24ca4b8SMarc-André Lureau int i;
367d24ca4b8SMarc-André Lureau
368d24ca4b8SMarc-André Lureau if (s->read_msgfds_num) {
369d24ca4b8SMarc-André Lureau for (i = 0; i < s->read_msgfds_num; i++) {
370d24ca4b8SMarc-André Lureau close(s->read_msgfds[i]);
371d24ca4b8SMarc-André Lureau }
372d24ca4b8SMarc-André Lureau g_free(s->read_msgfds);
373d24ca4b8SMarc-André Lureau s->read_msgfds = NULL;
374d24ca4b8SMarc-André Lureau s->read_msgfds_num = 0;
375d24ca4b8SMarc-André Lureau }
376d24ca4b8SMarc-André Lureau
377dfe9ea20SMarc-André Lureau remove_hup_source(s);
378a8aa6197SKlim Kireev
379d24ca4b8SMarc-André Lureau tcp_set_msgfds(chr, NULL, 0);
380b19456ddSzhanghailiang remove_fd_in_watch(chr);
381ebae6477SMarc-André Lureau if (s->registered_yank &&
382ebae6477SMarc-André Lureau (s->state == TCP_CHARDEV_STATE_CONNECTING
383ebae6477SMarc-André Lureau || s->state == TCP_CHARDEV_STATE_CONNECTED)) {
3848ee44806SLukas Straub yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label),
3851a92d6d5SLukas Straub char_socket_yank_iochannel,
3868ee44806SLukas Straub QIO_CHANNEL(s->sioc));
3878ee44806SLukas Straub }
388cb8ded0fSDaniel P. Berrangé
389cb8ded0fSDaniel P. Berrangé if (s->ioc) {
390cb8ded0fSDaniel P. Berrangé qio_channel_close(s->ioc, NULL);
391cb8ded0fSDaniel P. Berrangé }
392d24ca4b8SMarc-André Lureau object_unref(OBJECT(s->sioc));
393d24ca4b8SMarc-André Lureau s->sioc = NULL;
394d24ca4b8SMarc-André Lureau object_unref(OBJECT(s->ioc));
395d24ca4b8SMarc-André Lureau s->ioc = NULL;
396d24ca4b8SMarc-André Lureau g_free(chr->filename);
397d24ca4b8SMarc-André Lureau chr->filename = NULL;
39832423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED);
399d24ca4b8SMarc-André Lureau }
400d24ca4b8SMarc-André Lureau
qemu_chr_socket_protocol(SocketChardev * s)4014493b6a5SJulia Suvorova static const char *qemu_chr_socket_protocol(SocketChardev *s)
402d24ca4b8SMarc-André Lureau {
4034493b6a5SJulia Suvorova if (s->is_telnet) {
4044493b6a5SJulia Suvorova return "telnet";
4054493b6a5SJulia Suvorova }
406981b06e7SJulia Suvorova return s->is_websock ? "websocket" : "tcp";
4074493b6a5SJulia Suvorova }
4084493b6a5SJulia Suvorova
qemu_chr_socket_address(SocketChardev * s,const char * prefix)4094493b6a5SJulia Suvorova static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix)
4104493b6a5SJulia Suvorova {
4114493b6a5SJulia Suvorova switch (s->addr->type) {
412bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_INET:
413d24ca4b8SMarc-André Lureau return g_strdup_printf("%s%s:%s:%s%s", prefix,
4144493b6a5SJulia Suvorova qemu_chr_socket_protocol(s),
4154493b6a5SJulia Suvorova s->addr->u.inet.host,
4164493b6a5SJulia Suvorova s->addr->u.inet.port,
4179d902d51SPaolo Bonzini s->is_listen ? ",server=on" : "");
418d24ca4b8SMarc-André Lureau break;
419bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_UNIX:
420dea7cd17SMarkus Armbruster {
421dea7cd17SMarkus Armbruster const char *tight = "", *abstract = "";
422dea7cd17SMarkus Armbruster UnixSocketAddress *sa = &s->addr->u.q_unix;
423dea7cd17SMarkus Armbruster
4248acefc79SMarkus Armbruster #ifdef CONFIG_LINUX
425dea7cd17SMarkus Armbruster if (sa->has_abstract && sa->abstract) {
42630f80be3SMarc-André Lureau abstract = ",abstract=on";
427dea7cd17SMarkus Armbruster if (sa->has_tight && sa->tight) {
42830f80be3SMarc-André Lureau tight = ",tight=on";
429dea7cd17SMarkus Armbruster }
430dea7cd17SMarkus Armbruster }
4318acefc79SMarkus Armbruster #endif
432dea7cd17SMarkus Armbruster
433dea7cd17SMarkus Armbruster return g_strdup_printf("%sunix:%s%s%s%s", prefix, sa->path,
434dea7cd17SMarkus Armbruster abstract, tight,
4359d902d51SPaolo Bonzini s->is_listen ? ",server=on" : "");
436d24ca4b8SMarc-André Lureau break;
437dea7cd17SMarkus Armbruster }
438bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_FD:
4394493b6a5SJulia Suvorova return g_strdup_printf("%sfd:%s%s", prefix, s->addr->u.fd.str,
4409d902d51SPaolo Bonzini s->is_listen ? ",server=on" : "");
441d24ca4b8SMarc-André Lureau break;
442bd269ebcSMarkus Armbruster case SOCKET_ADDRESS_TYPE_VSOCK:
443d2e49aadSMarkus Armbruster return g_strdup_printf("%svsock:%s:%s", prefix,
4444493b6a5SJulia Suvorova s->addr->u.vsock.cid,
4454493b6a5SJulia Suvorova s->addr->u.vsock.port);
446d24ca4b8SMarc-André Lureau default:
447d24ca4b8SMarc-André Lureau abort();
448d24ca4b8SMarc-André Lureau }
449d24ca4b8SMarc-André Lureau }
450d24ca4b8SMarc-André Lureau
update_disconnected_filename(SocketChardev * s)451bbcde969SMarc-André Lureau static void update_disconnected_filename(SocketChardev *s)
452bbcde969SMarc-André Lureau {
453bbcde969SMarc-André Lureau Chardev *chr = CHARDEV(s);
454bbcde969SMarc-André Lureau
455bbcde969SMarc-André Lureau g_free(chr->filename);
45690a6d17bSMarc-André Lureau if (s->addr) {
4574493b6a5SJulia Suvorova chr->filename = qemu_chr_socket_address(s, "disconnected:");
45890a6d17bSMarc-André Lureau } else {
45990a6d17bSMarc-André Lureau chr->filename = g_strdup("disconnected:socket");
46090a6d17bSMarc-André Lureau }
461bbcde969SMarc-André Lureau }
462bbcde969SMarc-André Lureau
4639cca7578SDaniel P. Berrange /* NB may be called even if tcp_chr_connect has not been
4649cca7578SDaniel P. Berrange * reached, due to TLS or telnet initialization failure,
46532423ccaSDaniel P. Berrangé * so can *not* assume s->state == TCP_CHARDEV_STATE_CONNECTED
46678d01598SAlberto Garcia * This must be called with chr->chr_write_lock held.
4679cca7578SDaniel P. Berrange */
tcp_chr_disconnect_locked(Chardev * chr)46878d01598SAlberto Garcia static void tcp_chr_disconnect_locked(Chardev *chr)
469d24ca4b8SMarc-André Lureau {
470d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
47132423ccaSDaniel P. Berrangé bool emit_close = s->state == TCP_CHARDEV_STATE_CONNECTED;
472d24ca4b8SMarc-André Lureau
4736ffade79SDaniel P. Berrangé trace_chr_socket_disconnect(chr, chr->label);
474d24ca4b8SMarc-André Lureau tcp_chr_free_connection(chr);
475d24ca4b8SMarc-André Lureau
476194b7f0dSDaniel P. Berrange if (s->listener) {
4773da9de5cSPeter Xu qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
4783da9de5cSPeter Xu chr, NULL, chr->gcontext);
479d24ca4b8SMarc-André Lureau }
480bbcde969SMarc-André Lureau update_disconnected_filename(s);
4819cca7578SDaniel P. Berrange if (emit_close) {
482d24ca4b8SMarc-André Lureau qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
4839cca7578SDaniel P. Berrange }
484*c8e2b6b4SDaniil Tatianin if (s->reconnect_time_ms && !s->reconnect_timer) {
485d24ca4b8SMarc-André Lureau qemu_chr_socket_restart_timer(chr);
486d24ca4b8SMarc-André Lureau }
487d24ca4b8SMarc-André Lureau }
488d24ca4b8SMarc-André Lureau
tcp_chr_disconnect(Chardev * chr)48978d01598SAlberto Garcia static void tcp_chr_disconnect(Chardev *chr)
49078d01598SAlberto Garcia {
49178d01598SAlberto Garcia qemu_mutex_lock(&chr->chr_write_lock);
49278d01598SAlberto Garcia tcp_chr_disconnect_locked(chr);
49378d01598SAlberto Garcia qemu_mutex_unlock(&chr->chr_write_lock);
49478d01598SAlberto Garcia }
49578d01598SAlberto Garcia
tcp_chr_read(QIOChannel * chan,GIOCondition cond,void * opaque)496d24ca4b8SMarc-André Lureau static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
497d24ca4b8SMarc-André Lureau {
498d24ca4b8SMarc-André Lureau Chardev *chr = CHARDEV(opaque);
499d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(opaque);
500d24ca4b8SMarc-André Lureau uint8_t buf[CHR_READ_BUF_LEN];
501d24ca4b8SMarc-André Lureau int len, size;
502d24ca4b8SMarc-André Lureau
50332423ccaSDaniel P. Berrangé if ((s->state != TCP_CHARDEV_STATE_CONNECTED) ||
50432423ccaSDaniel P. Berrangé s->max_size <= 0) {
505d24ca4b8SMarc-André Lureau return TRUE;
506d24ca4b8SMarc-André Lureau }
507d24ca4b8SMarc-André Lureau len = sizeof(buf);
508e8ee827fSDaniel P. Berrangé if (len > s->max_size) {
509e8ee827fSDaniel P. Berrangé len = s->max_size;
510d24ca4b8SMarc-André Lureau }
511d24ca4b8SMarc-André Lureau size = tcp_chr_recv(chr, (void *)buf, len);
512c863fdecSDaniel P. Berrangé if (size == 0 || (size == -1 && errno != EAGAIN)) {
513d24ca4b8SMarc-André Lureau /* connection closed */
514d24ca4b8SMarc-André Lureau tcp_chr_disconnect(chr);
515d24ca4b8SMarc-André Lureau } else if (size > 0) {
516d24ca4b8SMarc-André Lureau if (s->do_telnetopt) {
517d24ca4b8SMarc-André Lureau tcp_chr_process_IAC_bytes(chr, s, buf, &size);
518d24ca4b8SMarc-André Lureau }
519d24ca4b8SMarc-André Lureau if (size > 0) {
520d24ca4b8SMarc-André Lureau qemu_chr_be_write(chr, buf, size);
521d24ca4b8SMarc-André Lureau }
522d24ca4b8SMarc-André Lureau }
523d24ca4b8SMarc-André Lureau
524d24ca4b8SMarc-André Lureau return TRUE;
525d24ca4b8SMarc-André Lureau }
526d24ca4b8SMarc-André Lureau
tcp_chr_hup(QIOChannel * channel,GIOCondition cond,void * opaque)527a8aa6197SKlim Kireev static gboolean tcp_chr_hup(QIOChannel *channel,
528a8aa6197SKlim Kireev GIOCondition cond,
529a8aa6197SKlim Kireev void *opaque)
530a8aa6197SKlim Kireev {
531a8aa6197SKlim Kireev Chardev *chr = CHARDEV(opaque);
5326ffade79SDaniel P. Berrangé trace_chr_socket_hangup(chr, chr->label);
533a8aa6197SKlim Kireev tcp_chr_disconnect(chr);
534a8aa6197SKlim Kireev return G_SOURCE_REMOVE;
535a8aa6197SKlim Kireev }
536a8aa6197SKlim Kireev
tcp_chr_sync_read(Chardev * chr,const uint8_t * buf,int len)537d24ca4b8SMarc-André Lureau static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
538d24ca4b8SMarc-André Lureau {
539d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
540d24ca4b8SMarc-André Lureau int size;
54166626503SRoman Kagan int saved_errno;
542d24ca4b8SMarc-André Lureau
54332423ccaSDaniel P. Berrangé if (s->state != TCP_CHARDEV_STATE_CONNECTED) {
544d24ca4b8SMarc-André Lureau return 0;
545d24ca4b8SMarc-André Lureau }
546d24ca4b8SMarc-André Lureau
547bcdeb9beSMarc-André Lureau qio_channel_set_blocking(s->ioc, true, NULL);
548d24ca4b8SMarc-André Lureau size = tcp_chr_recv(chr, (void *) buf, len);
54966626503SRoman Kagan saved_errno = errno;
5504d1d4602SSai Pavan Boddu if (s->state != TCP_CHARDEV_STATE_DISCONNECTED) {
551bcdeb9beSMarc-André Lureau qio_channel_set_blocking(s->ioc, false, NULL);
5524d1d4602SSai Pavan Boddu }
553d24ca4b8SMarc-André Lureau if (size == 0) {
554d24ca4b8SMarc-André Lureau /* connection closed */
555d24ca4b8SMarc-André Lureau tcp_chr_disconnect(chr);
556d24ca4b8SMarc-André Lureau }
557d24ca4b8SMarc-André Lureau
55866626503SRoman Kagan errno = saved_errno;
559d24ca4b8SMarc-André Lureau return size;
560d24ca4b8SMarc-André Lureau }
561d24ca4b8SMarc-André Lureau
qemu_chr_compute_filename(SocketChardev * s)5624493b6a5SJulia Suvorova static char *qemu_chr_compute_filename(SocketChardev *s)
563d24ca4b8SMarc-André Lureau {
5644493b6a5SJulia Suvorova struct sockaddr_storage *ss = &s->sioc->localAddr;
5654493b6a5SJulia Suvorova struct sockaddr_storage *ps = &s->sioc->remoteAddr;
5664493b6a5SJulia Suvorova socklen_t ss_len = s->sioc->localAddrLen;
5674493b6a5SJulia Suvorova socklen_t ps_len = s->sioc->remoteAddrLen;
568d24ca4b8SMarc-André Lureau char shost[NI_MAXHOST], sserv[NI_MAXSERV];
569d24ca4b8SMarc-André Lureau char phost[NI_MAXHOST], pserv[NI_MAXSERV];
570d24ca4b8SMarc-André Lureau const char *left = "", *right = "";
571d24ca4b8SMarc-André Lureau
572d24ca4b8SMarc-André Lureau switch (ss->ss_family) {
573d24ca4b8SMarc-André Lureau case AF_UNIX:
574d24ca4b8SMarc-André Lureau return g_strdup_printf("unix:%s%s",
575d24ca4b8SMarc-André Lureau ((struct sockaddr_un *)(ss))->sun_path,
5769d902d51SPaolo Bonzini s->is_listen ? ",server=on" : "");
577d24ca4b8SMarc-André Lureau case AF_INET6:
578d24ca4b8SMarc-André Lureau left = "[";
579d24ca4b8SMarc-André Lureau right = "]";
580d24ca4b8SMarc-André Lureau /* fall through */
581d24ca4b8SMarc-André Lureau case AF_INET:
582d24ca4b8SMarc-André Lureau getnameinfo((struct sockaddr *) ss, ss_len, shost, sizeof(shost),
583d24ca4b8SMarc-André Lureau sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV);
584d24ca4b8SMarc-André Lureau getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
585d24ca4b8SMarc-André Lureau pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
586d24ca4b8SMarc-André Lureau return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s",
5874493b6a5SJulia Suvorova qemu_chr_socket_protocol(s),
588d24ca4b8SMarc-André Lureau left, shost, right, sserv,
5899d902d51SPaolo Bonzini s->is_listen ? ",server=on" : "",
590d24ca4b8SMarc-André Lureau left, phost, right, pserv);
591d24ca4b8SMarc-André Lureau
592d24ca4b8SMarc-André Lureau default:
593d24ca4b8SMarc-André Lureau return g_strdup_printf("unknown");
594d24ca4b8SMarc-André Lureau }
595d24ca4b8SMarc-André Lureau }
596d24ca4b8SMarc-André Lureau
update_ioc_handlers(SocketChardev * s)597dfe9ea20SMarc-André Lureau static void update_ioc_handlers(SocketChardev *s)
598dfe9ea20SMarc-André Lureau {
599dfe9ea20SMarc-André Lureau Chardev *chr = CHARDEV(s);
600dfe9ea20SMarc-André Lureau
60132423ccaSDaniel P. Berrangé if (s->state != TCP_CHARDEV_STATE_CONNECTED) {
602dfe9ea20SMarc-André Lureau return;
603dfe9ea20SMarc-André Lureau }
604dfe9ea20SMarc-André Lureau
605dfe9ea20SMarc-André Lureau remove_fd_in_watch(chr);
606dfe9ea20SMarc-André Lureau chr->gsource = io_add_watch_poll(chr, s->ioc,
607dfe9ea20SMarc-André Lureau tcp_chr_read_poll,
608dfe9ea20SMarc-André Lureau tcp_chr_read, chr,
609dfe9ea20SMarc-André Lureau chr->gcontext);
610dfe9ea20SMarc-André Lureau
611dfe9ea20SMarc-André Lureau remove_hup_source(s);
612dfe9ea20SMarc-André Lureau s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
6138bd8b04aSDaniel P. Berrangé /*
6148bd8b04aSDaniel P. Berrangé * poll() is liable to return POLLHUP even when there is
6158bd8b04aSDaniel P. Berrangé * still incoming data available to read on the FD. If
6168bd8b04aSDaniel P. Berrangé * we have the hup_source at the same priority as the
6178bd8b04aSDaniel P. Berrangé * main io_add_watch_poll GSource, then we might end up
6188bd8b04aSDaniel P. Berrangé * processing the POLLHUP event first, closing the FD,
6198bd8b04aSDaniel P. Berrangé * and as a result silently discard data we should have
6208bd8b04aSDaniel P. Berrangé * read.
6218bd8b04aSDaniel P. Berrangé *
6228bd8b04aSDaniel P. Berrangé * By setting the hup_source to G_PRIORITY_DEFAULT + 1,
6238bd8b04aSDaniel P. Berrangé * we ensure that io_add_watch_poll GSource will always
6248bd8b04aSDaniel P. Berrangé * be dispatched first, thus guaranteeing we will be
6258bd8b04aSDaniel P. Berrangé * able to process all incoming data before closing the
6268bd8b04aSDaniel P. Berrangé * FD
6278bd8b04aSDaniel P. Berrangé */
6288bd8b04aSDaniel P. Berrangé g_source_set_priority(s->hup_source, G_PRIORITY_DEFAULT + 1);
629dfe9ea20SMarc-André Lureau g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
630dfe9ea20SMarc-André Lureau chr, NULL);
631dfe9ea20SMarc-André Lureau g_source_attach(s->hup_source, chr->gcontext);
632dfe9ea20SMarc-André Lureau }
633dfe9ea20SMarc-André Lureau
tcp_chr_connect(void * opaque)634d24ca4b8SMarc-André Lureau static void tcp_chr_connect(void *opaque)
635d24ca4b8SMarc-André Lureau {
636d24ca4b8SMarc-André Lureau Chardev *chr = CHARDEV(opaque);
637d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(opaque);
638d24ca4b8SMarc-André Lureau
639d24ca4b8SMarc-André Lureau g_free(chr->filename);
6404493b6a5SJulia Suvorova chr->filename = qemu_chr_compute_filename(s);
641d24ca4b8SMarc-André Lureau
64232423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTED);
643dfe9ea20SMarc-André Lureau update_ioc_handlers(s);
64463618135SMarc-André Lureau qemu_chr_be_event(chr, CHR_EVENT_OPENED);
645d24ca4b8SMarc-André Lureau }
646d24ca4b8SMarc-André Lureau
tcp_chr_telnet_destroy(SocketChardev * s)647ce1230c0SPeter Xu static void tcp_chr_telnet_destroy(SocketChardev *s)
648ce1230c0SPeter Xu {
649ce1230c0SPeter Xu if (s->telnet_source) {
650ce1230c0SPeter Xu g_source_destroy(s->telnet_source);
651ce1230c0SPeter Xu g_source_unref(s->telnet_source);
652ce1230c0SPeter Xu s->telnet_source = NULL;
653ce1230c0SPeter Xu }
654ce1230c0SPeter Xu }
655ce1230c0SPeter Xu
tcp_chr_update_read_handler(Chardev * chr)656bb86d05fSPeter Xu static void tcp_chr_update_read_handler(Chardev *chr)
657d24ca4b8SMarc-André Lureau {
658d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
659d24ca4b8SMarc-André Lureau
6605b774fe5SPaolo Bonzini if (s->listener && s->state == TCP_CHARDEV_STATE_DISCONNECTED) {
6613da9de5cSPeter Xu /*
6623da9de5cSPeter Xu * It's possible that chardev context is changed in
6633da9de5cSPeter Xu * qemu_chr_be_update_read_handlers(). Reset it for QIO net
6643da9de5cSPeter Xu * listener if there is.
6653da9de5cSPeter Xu */
6663da9de5cSPeter Xu qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
6673da9de5cSPeter Xu chr, NULL, chr->gcontext);
6683da9de5cSPeter Xu }
6693da9de5cSPeter Xu
670ce1230c0SPeter Xu if (s->telnet_source) {
671ce1230c0SPeter Xu tcp_chr_telnet_init(CHARDEV(s));
672ce1230c0SPeter Xu }
673ce1230c0SPeter Xu
674dfe9ea20SMarc-André Lureau update_ioc_handlers(s);
675d24ca4b8SMarc-André Lureau }
676d24ca4b8SMarc-André Lureau
tcp_chr_telnet_init_io(QIOChannel * ioc,GIOCondition cond G_GNUC_UNUSED,gpointer user_data)677d24ca4b8SMarc-André Lureau static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
678d24ca4b8SMarc-André Lureau GIOCondition cond G_GNUC_UNUSED,
679d24ca4b8SMarc-André Lureau gpointer user_data)
680d24ca4b8SMarc-André Lureau {
681ce1230c0SPeter Xu SocketChardev *s = user_data;
682ce1230c0SPeter Xu Chardev *chr = CHARDEV(s);
683ce1230c0SPeter Xu TCPChardevTelnetInit *init = s->telnet_init;
6846ffade79SDaniel P. Berrangé Error *err = NULL;
685d24ca4b8SMarc-André Lureau ssize_t ret;
686d24ca4b8SMarc-André Lureau
687ce1230c0SPeter Xu assert(init);
688ce1230c0SPeter Xu
6896ffade79SDaniel P. Berrangé ret = qio_channel_write(ioc, init->buf, init->buflen, &err);
690d24ca4b8SMarc-André Lureau if (ret < 0) {
691d24ca4b8SMarc-André Lureau if (ret == QIO_CHANNEL_ERR_BLOCK) {
692d24ca4b8SMarc-André Lureau ret = 0;
693d24ca4b8SMarc-André Lureau } else {
6946ffade79SDaniel P. Berrangé trace_chr_socket_write_err(chr, chr->label, error_get_pretty(err));
6956ffade79SDaniel P. Berrangé error_free(err);
696ce1230c0SPeter Xu tcp_chr_disconnect(chr);
6978b2ec54fSPeter Xu goto end;
698d24ca4b8SMarc-André Lureau }
699d24ca4b8SMarc-André Lureau }
700d24ca4b8SMarc-André Lureau init->buflen -= ret;
701d24ca4b8SMarc-André Lureau
702d24ca4b8SMarc-André Lureau if (init->buflen == 0) {
703ce1230c0SPeter Xu tcp_chr_connect(chr);
7048b2ec54fSPeter Xu goto end;
705d24ca4b8SMarc-André Lureau }
706d24ca4b8SMarc-André Lureau
707d24ca4b8SMarc-André Lureau memmove(init->buf, init->buf + ret, init->buflen);
708d24ca4b8SMarc-André Lureau
7098b2ec54fSPeter Xu return G_SOURCE_CONTINUE;
7108b2ec54fSPeter Xu
7118b2ec54fSPeter Xu end:
712ce1230c0SPeter Xu g_free(s->telnet_init);
713ce1230c0SPeter Xu s->telnet_init = NULL;
714ce1230c0SPeter Xu g_source_unref(s->telnet_source);
715ce1230c0SPeter Xu s->telnet_source = NULL;
7168b2ec54fSPeter Xu return G_SOURCE_REMOVE;
717d24ca4b8SMarc-André Lureau }
718d24ca4b8SMarc-André Lureau
tcp_chr_telnet_init(Chardev * chr)719d24ca4b8SMarc-André Lureau static void tcp_chr_telnet_init(Chardev *chr)
720d24ca4b8SMarc-André Lureau {
721d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
722ce1230c0SPeter Xu TCPChardevTelnetInit *init;
723d24ca4b8SMarc-André Lureau size_t n = 0;
724d24ca4b8SMarc-André Lureau
725ce1230c0SPeter Xu /* Destroy existing task */
726ce1230c0SPeter Xu tcp_chr_telnet_destroy(s);
727ce1230c0SPeter Xu
728ce1230c0SPeter Xu if (s->telnet_init) {
729ce1230c0SPeter Xu /* We are possibly during a handshake already */
730ce1230c0SPeter Xu goto cont;
731ce1230c0SPeter Xu }
732ce1230c0SPeter Xu
733ce1230c0SPeter Xu s->telnet_init = g_new0(TCPChardevTelnetInit, 1);
734ce1230c0SPeter Xu init = s->telnet_init;
735ce1230c0SPeter Xu
736d24ca4b8SMarc-André Lureau #define IACSET(x, a, b, c) \
737d24ca4b8SMarc-André Lureau do { \
738d24ca4b8SMarc-André Lureau x[n++] = a; \
739d24ca4b8SMarc-André Lureau x[n++] = b; \
740d24ca4b8SMarc-André Lureau x[n++] = c; \
741d24ca4b8SMarc-André Lureau } while (0)
742d24ca4b8SMarc-André Lureau
743ae92cbd5SJing Liu if (!s->is_tn3270) {
744ae92cbd5SJing Liu init->buflen = 12;
7450a19d879SMichael Tokarev /* Prep the telnet negotiation to put telnet in binary,
746d24ca4b8SMarc-André Lureau * no echo, single char mode */
747d24ca4b8SMarc-André Lureau IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
748d24ca4b8SMarc-André Lureau IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
749d24ca4b8SMarc-André Lureau IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
750d24ca4b8SMarc-André Lureau IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
751ae92cbd5SJing Liu } else {
752ae92cbd5SJing Liu init->buflen = 21;
7530a19d879SMichael Tokarev /* Prep the TN3270 negotiation based on RFC1576 */
754ae92cbd5SJing Liu IACSET(init->buf, 0xff, 0xfd, 0x19); /* IAC DO EOR */
755ae92cbd5SJing Liu IACSET(init->buf, 0xff, 0xfb, 0x19); /* IAC WILL EOR */
756ae92cbd5SJing Liu IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO BINARY */
757ae92cbd5SJing Liu IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL BINARY */
758ae92cbd5SJing Liu IACSET(init->buf, 0xff, 0xfd, 0x18); /* IAC DO TERMINAL TYPE */
759ae92cbd5SJing Liu IACSET(init->buf, 0xff, 0xfa, 0x18); /* IAC SB TERMINAL TYPE */
760ae92cbd5SJing Liu IACSET(init->buf, 0x01, 0xff, 0xf0); /* SEND IAC SE */
761ae92cbd5SJing Liu }
762d24ca4b8SMarc-André Lureau
763d24ca4b8SMarc-André Lureau #undef IACSET
764d24ca4b8SMarc-André Lureau
765ce1230c0SPeter Xu cont:
766ce1230c0SPeter Xu s->telnet_source = qio_channel_add_watch_source(s->ioc, G_IO_OUT,
767d24ca4b8SMarc-André Lureau tcp_chr_telnet_init_io,
768ce1230c0SPeter Xu s, NULL,
769ce1230c0SPeter Xu chr->gcontext);
770d24ca4b8SMarc-André Lureau }
771d24ca4b8SMarc-André Lureau
772d24ca4b8SMarc-André Lureau
tcp_chr_websock_handshake(QIOTask * task,gpointer user_data)773981b06e7SJulia Suvorova static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data)
774981b06e7SJulia Suvorova {
775981b06e7SJulia Suvorova Chardev *chr = user_data;
776981b06e7SJulia Suvorova SocketChardev *s = user_data;
77781cd34a3SMarc-André Lureau Error *err = NULL;
778981b06e7SJulia Suvorova
77981cd34a3SMarc-André Lureau if (qio_task_propagate_error(task, &err)) {
7806ffade79SDaniel P. Berrangé trace_chr_socket_ws_handshake_err(chr, chr->label,
7816ffade79SDaniel P. Berrangé error_get_pretty(err));
7826ffade79SDaniel P. Berrangé error_free(err);
783981b06e7SJulia Suvorova tcp_chr_disconnect(chr);
784981b06e7SJulia Suvorova } else {
785981b06e7SJulia Suvorova if (s->do_telnetopt) {
786981b06e7SJulia Suvorova tcp_chr_telnet_init(chr);
787981b06e7SJulia Suvorova } else {
788981b06e7SJulia Suvorova tcp_chr_connect(chr);
789981b06e7SJulia Suvorova }
790981b06e7SJulia Suvorova }
791981b06e7SJulia Suvorova }
792981b06e7SJulia Suvorova
793981b06e7SJulia Suvorova
tcp_chr_websock_init(Chardev * chr)794981b06e7SJulia Suvorova static void tcp_chr_websock_init(Chardev *chr)
795981b06e7SJulia Suvorova {
796981b06e7SJulia Suvorova SocketChardev *s = SOCKET_CHARDEV(chr);
797981b06e7SJulia Suvorova QIOChannelWebsock *wioc = NULL;
798981b06e7SJulia Suvorova gchar *name;
799981b06e7SJulia Suvorova
800981b06e7SJulia Suvorova wioc = qio_channel_websock_new_server(s->ioc);
801981b06e7SJulia Suvorova
802981b06e7SJulia Suvorova name = g_strdup_printf("chardev-websocket-server-%s", chr->label);
803981b06e7SJulia Suvorova qio_channel_set_name(QIO_CHANNEL(wioc), name);
804981b06e7SJulia Suvorova g_free(name);
805981b06e7SJulia Suvorova object_unref(OBJECT(s->ioc));
806981b06e7SJulia Suvorova s->ioc = QIO_CHANNEL(wioc);
807981b06e7SJulia Suvorova
808981b06e7SJulia Suvorova qio_channel_websock_handshake(wioc, tcp_chr_websock_handshake, chr, NULL);
809981b06e7SJulia Suvorova }
810981b06e7SJulia Suvorova
811981b06e7SJulia Suvorova
tcp_chr_tls_handshake(QIOTask * task,gpointer user_data)812d24ca4b8SMarc-André Lureau static void tcp_chr_tls_handshake(QIOTask *task,
813d24ca4b8SMarc-André Lureau gpointer user_data)
814d24ca4b8SMarc-André Lureau {
815d24ca4b8SMarc-André Lureau Chardev *chr = user_data;
816d24ca4b8SMarc-André Lureau SocketChardev *s = user_data;
81781cd34a3SMarc-André Lureau Error *err = NULL;
818d24ca4b8SMarc-André Lureau
81981cd34a3SMarc-André Lureau if (qio_task_propagate_error(task, &err)) {
8206ffade79SDaniel P. Berrangé trace_chr_socket_tls_handshake_err(chr, chr->label,
8216ffade79SDaniel P. Berrangé error_get_pretty(err));
8226ffade79SDaniel P. Berrangé error_free(err);
823d24ca4b8SMarc-André Lureau tcp_chr_disconnect(chr);
824d24ca4b8SMarc-André Lureau } else {
825981b06e7SJulia Suvorova if (s->is_websock) {
826981b06e7SJulia Suvorova tcp_chr_websock_init(chr);
827981b06e7SJulia Suvorova } else if (s->do_telnetopt) {
828d24ca4b8SMarc-André Lureau tcp_chr_telnet_init(chr);
829d24ca4b8SMarc-André Lureau } else {
830d24ca4b8SMarc-André Lureau tcp_chr_connect(chr);
831d24ca4b8SMarc-André Lureau }
832d24ca4b8SMarc-André Lureau }
833d24ca4b8SMarc-André Lureau }
834d24ca4b8SMarc-André Lureau
835d24ca4b8SMarc-André Lureau
tcp_chr_tls_init(Chardev * chr)836d24ca4b8SMarc-André Lureau static void tcp_chr_tls_init(Chardev *chr)
837d24ca4b8SMarc-André Lureau {
838d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
839d24ca4b8SMarc-André Lureau QIOChannelTLS *tioc;
840d24ca4b8SMarc-André Lureau gchar *name;
8416ffade79SDaniel P. Berrangé Error *err = NULL;
842d24ca4b8SMarc-André Lureau
843d24ca4b8SMarc-André Lureau if (s->is_listen) {
844d24ca4b8SMarc-André Lureau tioc = qio_channel_tls_new_server(
845d24ca4b8SMarc-André Lureau s->ioc, s->tls_creds,
846fd4a5fd4SDaniel P. Berrange s->tls_authz,
8476ffade79SDaniel P. Berrangé &err);
848d24ca4b8SMarc-André Lureau } else {
849d24ca4b8SMarc-André Lureau tioc = qio_channel_tls_new_client(
850d24ca4b8SMarc-André Lureau s->ioc, s->tls_creds,
851bd269ebcSMarkus Armbruster s->addr->u.inet.host,
8526ffade79SDaniel P. Berrangé &err);
853d24ca4b8SMarc-André Lureau }
854d24ca4b8SMarc-André Lureau if (tioc == NULL) {
8556ffade79SDaniel P. Berrangé trace_chr_socket_tls_init_err(chr, chr->label, error_get_pretty(err));
8566ffade79SDaniel P. Berrangé error_free(err);
857d24ca4b8SMarc-André Lureau tcp_chr_disconnect(chr);
858d24ca4b8SMarc-André Lureau return;
859d24ca4b8SMarc-André Lureau }
860d24ca4b8SMarc-André Lureau name = g_strdup_printf("chardev-tls-%s-%s",
861d24ca4b8SMarc-André Lureau s->is_listen ? "server" : "client",
862d24ca4b8SMarc-André Lureau chr->label);
863d24ca4b8SMarc-André Lureau qio_channel_set_name(QIO_CHANNEL(tioc), name);
864d24ca4b8SMarc-André Lureau g_free(name);
865d24ca4b8SMarc-André Lureau object_unref(OBJECT(s->ioc));
866d24ca4b8SMarc-André Lureau s->ioc = QIO_CHANNEL(tioc);
867d24ca4b8SMarc-André Lureau
868d24ca4b8SMarc-André Lureau qio_channel_tls_handshake(tioc,
869d24ca4b8SMarc-André Lureau tcp_chr_tls_handshake,
870d24ca4b8SMarc-André Lureau chr,
8711939ccdaSPeter Xu NULL,
87205b6cc4aSPeter Xu chr->gcontext);
873d24ca4b8SMarc-André Lureau }
874d24ca4b8SMarc-André Lureau
875d24ca4b8SMarc-André Lureau
tcp_chr_set_client_ioc_name(Chardev * chr,QIOChannelSocket * sioc)876d24ca4b8SMarc-André Lureau static void tcp_chr_set_client_ioc_name(Chardev *chr,
877d24ca4b8SMarc-André Lureau QIOChannelSocket *sioc)
878d24ca4b8SMarc-André Lureau {
879d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
880d24ca4b8SMarc-André Lureau char *name;
881d24ca4b8SMarc-André Lureau name = g_strdup_printf("chardev-tcp-%s-%s",
882d24ca4b8SMarc-André Lureau s->is_listen ? "server" : "client",
883d24ca4b8SMarc-André Lureau chr->label);
884d24ca4b8SMarc-André Lureau qio_channel_set_name(QIO_CHANNEL(sioc), name);
885d24ca4b8SMarc-André Lureau g_free(name);
886d24ca4b8SMarc-André Lureau
887d24ca4b8SMarc-André Lureau }
888d24ca4b8SMarc-André Lureau
tcp_chr_new_client(Chardev * chr,QIOChannelSocket * sioc)889d24ca4b8SMarc-André Lureau static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
890d24ca4b8SMarc-André Lureau {
891d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
892d24ca4b8SMarc-André Lureau
89332423ccaSDaniel P. Berrangé if (s->state != TCP_CHARDEV_STATE_CONNECTING) {
894d24ca4b8SMarc-André Lureau return -1;
895d24ca4b8SMarc-André Lureau }
896d24ca4b8SMarc-André Lureau
897d24ca4b8SMarc-André Lureau s->ioc = QIO_CHANNEL(sioc);
898d24ca4b8SMarc-André Lureau object_ref(OBJECT(sioc));
899d24ca4b8SMarc-André Lureau s->sioc = sioc;
900d24ca4b8SMarc-André Lureau object_ref(OBJECT(sioc));
901d24ca4b8SMarc-André Lureau
902d24ca4b8SMarc-André Lureau qio_channel_set_blocking(s->ioc, false, NULL);
903d24ca4b8SMarc-André Lureau
904d24ca4b8SMarc-André Lureau if (s->do_nodelay) {
905d24ca4b8SMarc-André Lureau qio_channel_set_delay(s->ioc, false);
906d24ca4b8SMarc-André Lureau }
907194b7f0dSDaniel P. Berrange if (s->listener) {
9083da9de5cSPeter Xu qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
9093da9de5cSPeter Xu NULL, chr->gcontext);
910d24ca4b8SMarc-André Lureau }
911d24ca4b8SMarc-André Lureau
912d24ca4b8SMarc-André Lureau if (s->tls_creds) {
913d24ca4b8SMarc-André Lureau tcp_chr_tls_init(chr);
914981b06e7SJulia Suvorova } else if (s->is_websock) {
915981b06e7SJulia Suvorova tcp_chr_websock_init(chr);
916981b06e7SJulia Suvorova } else if (s->do_telnetopt) {
917d24ca4b8SMarc-André Lureau tcp_chr_telnet_init(chr);
918d24ca4b8SMarc-André Lureau } else {
919d24ca4b8SMarc-André Lureau tcp_chr_connect(chr);
920d24ca4b8SMarc-André Lureau }
921d24ca4b8SMarc-André Lureau
922d24ca4b8SMarc-André Lureau return 0;
923d24ca4b8SMarc-André Lureau }
924d24ca4b8SMarc-André Lureau
925d24ca4b8SMarc-André Lureau
tcp_chr_add_client(Chardev * chr,int fd)926d24ca4b8SMarc-André Lureau static int tcp_chr_add_client(Chardev *chr, int fd)
927d24ca4b8SMarc-André Lureau {
928d24ca4b8SMarc-André Lureau int ret;
929d24ca4b8SMarc-André Lureau QIOChannelSocket *sioc;
93032423ccaSDaniel P. Berrangé SocketChardev *s = SOCKET_CHARDEV(chr);
93132423ccaSDaniel P. Berrangé
93232423ccaSDaniel P. Berrangé if (s->state != TCP_CHARDEV_STATE_DISCONNECTED) {
93332423ccaSDaniel P. Berrangé return -1;
93432423ccaSDaniel P. Berrangé }
935d24ca4b8SMarc-André Lureau
936d24ca4b8SMarc-André Lureau sioc = qio_channel_socket_new_fd(fd, NULL);
937d24ca4b8SMarc-André Lureau if (!sioc) {
938d24ca4b8SMarc-André Lureau return -1;
939d24ca4b8SMarc-André Lureau }
94032423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
941d24ca4b8SMarc-André Lureau tcp_chr_set_client_ioc_name(chr, sioc);
942ebae6477SMarc-André Lureau if (s->registered_yank) {
9438ee44806SLukas Straub yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
9441a92d6d5SLukas Straub char_socket_yank_iochannel,
9458ee44806SLukas Straub QIO_CHANNEL(sioc));
946ebae6477SMarc-André Lureau }
947d24ca4b8SMarc-André Lureau ret = tcp_chr_new_client(chr, sioc);
948d24ca4b8SMarc-André Lureau object_unref(OBJECT(sioc));
949d24ca4b8SMarc-André Lureau return ret;
950d24ca4b8SMarc-André Lureau }
951d24ca4b8SMarc-André Lureau
tcp_chr_accept(QIONetListener * listener,QIOChannelSocket * cioc,void * opaque)952194b7f0dSDaniel P. Berrange static void tcp_chr_accept(QIONetListener *listener,
953194b7f0dSDaniel P. Berrange QIOChannelSocket *cioc,
954d24ca4b8SMarc-André Lureau void *opaque)
955d24ca4b8SMarc-André Lureau {
956d24ca4b8SMarc-André Lureau Chardev *chr = CHARDEV(opaque);
95732423ccaSDaniel P. Berrangé SocketChardev *s = SOCKET_CHARDEV(chr);
958d24ca4b8SMarc-André Lureau
95932423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
960194b7f0dSDaniel P. Berrange tcp_chr_set_client_ioc_name(chr, cioc);
961ebae6477SMarc-André Lureau if (s->registered_yank) {
9628ee44806SLukas Straub yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
9631a92d6d5SLukas Straub char_socket_yank_iochannel,
9648ee44806SLukas Straub QIO_CHANNEL(cioc));
965ebae6477SMarc-André Lureau }
966194b7f0dSDaniel P. Berrange tcp_chr_new_client(chr, cioc);
967d24ca4b8SMarc-André Lureau }
968d24ca4b8SMarc-André Lureau
969efae0b92SDaniel P. Berrangé
tcp_chr_connect_client_sync(Chardev * chr,Error ** errp)970efae0b92SDaniel P. Berrangé static int tcp_chr_connect_client_sync(Chardev *chr, Error **errp)
971d24ca4b8SMarc-André Lureau {
972d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
973efae0b92SDaniel P. Berrangé QIOChannelSocket *sioc = qio_channel_socket_new();
97432423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
975d24ca4b8SMarc-André Lureau tcp_chr_set_client_ioc_name(chr, sioc);
976d24ca4b8SMarc-André Lureau if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) {
97732423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED);
978d24ca4b8SMarc-André Lureau object_unref(OBJECT(sioc));
979d24ca4b8SMarc-André Lureau return -1;
980d24ca4b8SMarc-André Lureau }
981ebae6477SMarc-André Lureau if (s->registered_yank) {
9828ee44806SLukas Straub yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
9831a92d6d5SLukas Straub char_socket_yank_iochannel,
9848ee44806SLukas Straub QIO_CHANNEL(sioc));
985ebae6477SMarc-André Lureau }
986d24ca4b8SMarc-André Lureau tcp_chr_new_client(chr, sioc);
987d24ca4b8SMarc-André Lureau object_unref(OBJECT(sioc));
988efae0b92SDaniel P. Berrangé return 0;
989efae0b92SDaniel P. Berrangé }
990efae0b92SDaniel P. Berrangé
991efae0b92SDaniel P. Berrangé
tcp_chr_accept_server_sync(Chardev * chr)992efae0b92SDaniel P. Berrangé static void tcp_chr_accept_server_sync(Chardev *chr)
993efae0b92SDaniel P. Berrangé {
994efae0b92SDaniel P. Berrangé SocketChardev *s = SOCKET_CHARDEV(chr);
995efae0b92SDaniel P. Berrangé QIOChannelSocket *sioc;
996efae0b92SDaniel P. Berrangé info_report("QEMU waiting for connection on: %s",
997efae0b92SDaniel P. Berrangé chr->filename);
99832423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
999efae0b92SDaniel P. Berrangé sioc = qio_net_listener_wait_client(s->listener);
1000efae0b92SDaniel P. Berrangé tcp_chr_set_client_ioc_name(chr, sioc);
1001ebae6477SMarc-André Lureau if (s->registered_yank) {
10028ee44806SLukas Straub yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
10031a92d6d5SLukas Straub char_socket_yank_iochannel,
10048ee44806SLukas Straub QIO_CHANNEL(sioc));
1005ebae6477SMarc-André Lureau }
1006efae0b92SDaniel P. Berrangé tcp_chr_new_client(chr, sioc);
1007efae0b92SDaniel P. Berrangé object_unref(OBJECT(sioc));
1008efae0b92SDaniel P. Berrangé }
1009efae0b92SDaniel P. Berrangé
1010efae0b92SDaniel P. Berrangé
tcp_chr_wait_connected(Chardev * chr,Error ** errp)1011efae0b92SDaniel P. Berrangé static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
1012efae0b92SDaniel P. Berrangé {
1013efae0b92SDaniel P. Berrangé SocketChardev *s = SOCKET_CHARDEV(chr);
1014d1885e54SDaniel P. Berrangé const char *opts[] = { "telnet", "tn3270", "websock", "tls-creds" };
1015d1885e54SDaniel P. Berrangé bool optset[] = { s->is_telnet, s->is_tn3270, s->is_websock, s->tls_creds };
1016d1885e54SDaniel P. Berrangé size_t i;
1017d1885e54SDaniel P. Berrangé
1018d1885e54SDaniel P. Berrangé QEMU_BUILD_BUG_ON(G_N_ELEMENTS(opts) != G_N_ELEMENTS(optset));
1019d1885e54SDaniel P. Berrangé for (i = 0; i < G_N_ELEMENTS(opts); i++) {
1020d1885e54SDaniel P. Berrangé if (optset[i]) {
1021d1885e54SDaniel P. Berrangé error_setg(errp,
1022d1885e54SDaniel P. Berrangé "'%s' option is incompatible with waiting for "
1023d1885e54SDaniel P. Berrangé "connection completion", opts[i]);
1024d1885e54SDaniel P. Berrangé return -1;
1025d1885e54SDaniel P. Berrangé }
1026d1885e54SDaniel P. Berrangé }
1027d1885e54SDaniel P. Berrangé
10284b47373aSDaniel P. Berrangé tcp_chr_reconn_timer_cancel(s);
10294b47373aSDaniel P. Berrangé
10304b47373aSDaniel P. Berrangé /*
10314b47373aSDaniel P. Berrangé * We expect states to be as follows:
10324b47373aSDaniel P. Berrangé *
10334b47373aSDaniel P. Berrangé * - server
10344b47373aSDaniel P. Berrangé * - wait -> CONNECTED
10354b47373aSDaniel P. Berrangé * - nowait -> DISCONNECTED
10364b47373aSDaniel P. Berrangé * - client
10374b47373aSDaniel P. Berrangé * - reconnect == 0 -> CONNECTED
10384b47373aSDaniel P. Berrangé * - reconnect != 0 -> CONNECTING
10394b47373aSDaniel P. Berrangé *
10404b47373aSDaniel P. Berrangé */
10414b47373aSDaniel P. Berrangé if (s->state == TCP_CHARDEV_STATE_CONNECTING) {
10424b47373aSDaniel P. Berrangé if (!s->connect_task) {
10434b47373aSDaniel P. Berrangé error_setg(errp,
10444b47373aSDaniel P. Berrangé "Unexpected 'connecting' state without connect task "
10454b47373aSDaniel P. Berrangé "while waiting for connection completion");
10464b47373aSDaniel P. Berrangé return -1;
10474b47373aSDaniel P. Berrangé }
10484b47373aSDaniel P. Berrangé /*
10494b47373aSDaniel P. Berrangé * tcp_chr_wait_connected should only ever be run from the
10504b47373aSDaniel P. Berrangé * main loop thread associated with chr->gcontext, otherwise
10514b47373aSDaniel P. Berrangé * qio_task_wait_thread has a dangerous race condition with
10524b47373aSDaniel P. Berrangé * free'ing of the s->connect_task object.
10534b47373aSDaniel P. Berrangé *
10544b47373aSDaniel P. Berrangé * Acquiring the main context doesn't 100% prove we're in
10554b47373aSDaniel P. Berrangé * the main loop thread, but it does at least guarantee
10564b47373aSDaniel P. Berrangé * that the main loop won't be executed by another thread
10574b47373aSDaniel P. Berrangé * avoiding the race condition with the task idle callback.
10584b47373aSDaniel P. Berrangé */
10594b47373aSDaniel P. Berrangé g_main_context_acquire(chr->gcontext);
10604b47373aSDaniel P. Berrangé qio_task_wait_thread(s->connect_task);
10614b47373aSDaniel P. Berrangé g_main_context_release(chr->gcontext);
10624b47373aSDaniel P. Berrangé
10634b47373aSDaniel P. Berrangé /*
10644b47373aSDaniel P. Berrangé * The completion callback (qemu_chr_socket_connected) for
10654b47373aSDaniel P. Berrangé * s->connect_task should have set this to NULL by the time
10664b47373aSDaniel P. Berrangé * qio_task_wait_thread has returned.
10674b47373aSDaniel P. Berrangé */
10684b47373aSDaniel P. Berrangé assert(!s->connect_task);
10694b47373aSDaniel P. Berrangé
10704b47373aSDaniel P. Berrangé /*
10714b47373aSDaniel P. Berrangé * NB we are *not* guaranteed to have "s->state == ..CONNECTED"
10724b47373aSDaniel P. Berrangé * at this point as this first connect may be failed, so
10734b47373aSDaniel P. Berrangé * allow the next loop to run regardless.
10744b47373aSDaniel P. Berrangé */
10754b47373aSDaniel P. Berrangé }
10764b47373aSDaniel P. Berrangé
10774b47373aSDaniel P. Berrangé while (s->state != TCP_CHARDEV_STATE_CONNECTED) {
1078efae0b92SDaniel P. Berrangé if (s->is_listen) {
1079efae0b92SDaniel P. Berrangé tcp_chr_accept_server_sync(chr);
1080efae0b92SDaniel P. Berrangé } else {
108125d93b6aSDaniel P. Berrangé Error *err = NULL;
108225d93b6aSDaniel P. Berrangé if (tcp_chr_connect_client_sync(chr, &err) < 0) {
1083*c8e2b6b4SDaniil Tatianin if (s->reconnect_time_ms) {
108425d93b6aSDaniel P. Berrangé error_free(err);
1085*c8e2b6b4SDaniil Tatianin g_usleep(s->reconnect_time_ms * 1000ULL);
108625d93b6aSDaniel P. Berrangé } else {
108725d93b6aSDaniel P. Berrangé error_propagate(errp, err);
1088efae0b92SDaniel P. Berrangé return -1;
1089efae0b92SDaniel P. Berrangé }
1090d24ca4b8SMarc-André Lureau }
1091d24ca4b8SMarc-André Lureau }
109225d93b6aSDaniel P. Berrangé }
1093d24ca4b8SMarc-André Lureau
1094d24ca4b8SMarc-André Lureau return 0;
1095d24ca4b8SMarc-André Lureau }
1096d24ca4b8SMarc-André Lureau
char_socket_finalize(Object * obj)1097d24ca4b8SMarc-André Lureau static void char_socket_finalize(Object *obj)
1098d24ca4b8SMarc-André Lureau {
1099d24ca4b8SMarc-André Lureau Chardev *chr = CHARDEV(obj);
1100d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(obj);
1101d24ca4b8SMarc-André Lureau
1102d24ca4b8SMarc-André Lureau tcp_chr_free_connection(chr);
11032c716ba1SPeter Xu tcp_chr_reconn_timer_cancel(s);
1104d24ca4b8SMarc-André Lureau qapi_free_SocketAddress(s->addr);
1105ce1230c0SPeter Xu tcp_chr_telnet_destroy(s);
1106ce1230c0SPeter Xu g_free(s->telnet_init);
1107194b7f0dSDaniel P. Berrange if (s->listener) {
11083da9de5cSPeter Xu qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
11093da9de5cSPeter Xu NULL, chr->gcontext);
1110194b7f0dSDaniel P. Berrange object_unref(OBJECT(s->listener));
1111b8a7f51fSYajun Wu s->listener = NULL;
1112d24ca4b8SMarc-André Lureau }
1113d24ca4b8SMarc-André Lureau if (s->tls_creds) {
1114d24ca4b8SMarc-André Lureau object_unref(OBJECT(s->tls_creds));
1115d24ca4b8SMarc-André Lureau }
1116fd4a5fd4SDaniel P. Berrange g_free(s->tls_authz);
11178ee44806SLukas Straub if (s->registered_yank) {
1118feb774caSLukas Straub /*
1119feb774caSLukas Straub * In the chardev-change special-case, we shouldn't unregister the yank
1120feb774caSLukas Straub * instance, as it still may be needed.
1121feb774caSLukas Straub */
1122feb774caSLukas Straub if (!chr->handover_yank_instance) {
11238ee44806SLukas Straub yank_unregister_instance(CHARDEV_YANK_INSTANCE(chr->label));
11248ee44806SLukas Straub }
1125feb774caSLukas Straub }
1126d24ca4b8SMarc-André Lureau
1127d24ca4b8SMarc-André Lureau qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
1128d24ca4b8SMarc-André Lureau }
1129d24ca4b8SMarc-André Lureau
qemu_chr_socket_connected(QIOTask * task,void * opaque)1130d24ca4b8SMarc-André Lureau static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
1131d24ca4b8SMarc-André Lureau {
1132d24ca4b8SMarc-André Lureau QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
1133d24ca4b8SMarc-André Lureau Chardev *chr = CHARDEV(opaque);
1134d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
1135d24ca4b8SMarc-André Lureau Error *err = NULL;
1136d24ca4b8SMarc-André Lureau
11374b47373aSDaniel P. Berrangé s->connect_task = NULL;
11384b47373aSDaniel P. Berrangé
1139d24ca4b8SMarc-André Lureau if (qio_task_propagate_error(task, &err)) {
114032423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED);
1141ebae6477SMarc-André Lureau if (s->registered_yank) {
11428ee44806SLukas Straub yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label),
11431a92d6d5SLukas Straub char_socket_yank_iochannel,
11448ee44806SLukas Straub QIO_CHANNEL(sioc));
1145ebae6477SMarc-André Lureau }
1146d24ca4b8SMarc-André Lureau check_report_connect_error(chr, err);
1147d24ca4b8SMarc-André Lureau goto cleanup;
1148d24ca4b8SMarc-André Lureau }
1149d24ca4b8SMarc-André Lureau
1150d24ca4b8SMarc-André Lureau s->connect_err_reported = false;
1151d24ca4b8SMarc-André Lureau tcp_chr_new_client(chr, sioc);
1152d24ca4b8SMarc-André Lureau
1153d24ca4b8SMarc-André Lureau cleanup:
1154d24ca4b8SMarc-André Lureau object_unref(OBJECT(sioc));
1155d24ca4b8SMarc-André Lureau }
1156d24ca4b8SMarc-André Lureau
11574b47373aSDaniel P. Berrangé
tcp_chr_connect_client_task(QIOTask * task,gpointer opaque)11584b47373aSDaniel P. Berrangé static void tcp_chr_connect_client_task(QIOTask *task,
11594b47373aSDaniel P. Berrangé gpointer opaque)
11604b47373aSDaniel P. Berrangé {
11614b47373aSDaniel P. Berrangé QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
11624b47373aSDaniel P. Berrangé SocketAddress *addr = opaque;
11634b47373aSDaniel P. Berrangé Error *err = NULL;
11644b47373aSDaniel P. Berrangé
11654b47373aSDaniel P. Berrangé qio_channel_socket_connect_sync(ioc, addr, &err);
11664b47373aSDaniel P. Berrangé
11674b47373aSDaniel P. Berrangé qio_task_set_error(task, err);
11684b47373aSDaniel P. Berrangé }
11694b47373aSDaniel P. Berrangé
11704b47373aSDaniel P. Berrangé
tcp_chr_connect_client_async(Chardev * chr)1171efae0b92SDaniel P. Berrangé static void tcp_chr_connect_client_async(Chardev *chr)
11723e7d4d20SPeter Xu {
11733e7d4d20SPeter Xu SocketChardev *s = SOCKET_CHARDEV(chr);
11743e7d4d20SPeter Xu QIOChannelSocket *sioc;
11753e7d4d20SPeter Xu
117632423ccaSDaniel P. Berrangé tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
11773e7d4d20SPeter Xu sioc = qio_channel_socket_new();
11783e7d4d20SPeter Xu tcp_chr_set_client_ioc_name(chr, sioc);
1179ebae6477SMarc-André Lureau if (s->registered_yank) {
11808ee44806SLukas Straub yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
11811a92d6d5SLukas Straub char_socket_yank_iochannel,
11828ee44806SLukas Straub QIO_CHANNEL(sioc));
1183ebae6477SMarc-André Lureau }
11844b47373aSDaniel P. Berrangé /*
11854b47373aSDaniel P. Berrangé * Normally code would use the qio_channel_socket_connect_async
11864b47373aSDaniel P. Berrangé * method which uses a QIOTask + qio_task_set_error internally
11874b47373aSDaniel P. Berrangé * to avoid blocking. The tcp_chr_wait_connected method, however,
11884b47373aSDaniel P. Berrangé * needs a way to synchronize with completion of the background
11894b47373aSDaniel P. Berrangé * connect task which can't be done with the QIOChannelSocket
11904b47373aSDaniel P. Berrangé * async APIs. Thus we must use QIOTask directly to implement
11914b47373aSDaniel P. Berrangé * the non-blocking concept locally.
11924b47373aSDaniel P. Berrangé */
11934b47373aSDaniel P. Berrangé s->connect_task = qio_task_new(OBJECT(sioc),
11943e7d4d20SPeter Xu qemu_chr_socket_connected,
119568066019SMarc-André Lureau object_ref(OBJECT(chr)),
119668066019SMarc-André Lureau (GDestroyNotify)object_unref);
11974b47373aSDaniel P. Berrangé qio_task_run_in_thread(s->connect_task,
11984b47373aSDaniel P. Berrangé tcp_chr_connect_client_task,
11994b47373aSDaniel P. Berrangé s->addr,
12004b47373aSDaniel P. Berrangé NULL,
12014b47373aSDaniel P. Berrangé chr->gcontext);
12023e7d4d20SPeter Xu }
12033e7d4d20SPeter Xu
socket_reconnect_timeout(gpointer opaque)1204d24ca4b8SMarc-André Lureau static gboolean socket_reconnect_timeout(gpointer opaque)
1205d24ca4b8SMarc-André Lureau {
1206d24ca4b8SMarc-André Lureau Chardev *chr = CHARDEV(opaque);
1207d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(opaque);
1208d24ca4b8SMarc-André Lureau
120978d01598SAlberto Garcia qemu_mutex_lock(&chr->chr_write_lock);
12102c716ba1SPeter Xu g_source_unref(s->reconnect_timer);
12112c716ba1SPeter Xu s->reconnect_timer = NULL;
121278d01598SAlberto Garcia qemu_mutex_unlock(&chr->chr_write_lock);
1213d24ca4b8SMarc-André Lureau
1214d24ca4b8SMarc-André Lureau if (chr->be_open) {
1215d24ca4b8SMarc-André Lureau return false;
1216d24ca4b8SMarc-André Lureau }
1217d24ca4b8SMarc-André Lureau
1218efae0b92SDaniel P. Berrangé tcp_chr_connect_client_async(chr);
1219d24ca4b8SMarc-André Lureau
1220d24ca4b8SMarc-André Lureau return false;
1221d24ca4b8SMarc-André Lureau }
1222d24ca4b8SMarc-André Lureau
12231645984bSDaniel P. Berrangé
qmp_chardev_open_socket_server(Chardev * chr,bool is_telnet,bool is_waitconnect,Error ** errp)1224b5e18e51SDaniel P. Berrangé static int qmp_chardev_open_socket_server(Chardev *chr,
1225b5e18e51SDaniel P. Berrangé bool is_telnet,
1226b5e18e51SDaniel P. Berrangé bool is_waitconnect,
1227b5e18e51SDaniel P. Berrangé Error **errp)
1228b5e18e51SDaniel P. Berrangé {
1229b5e18e51SDaniel P. Berrangé SocketChardev *s = SOCKET_CHARDEV(chr);
1230b5e18e51SDaniel P. Berrangé char *name;
1231b5e18e51SDaniel P. Berrangé if (is_telnet) {
1232b5e18e51SDaniel P. Berrangé s->do_telnetopt = 1;
1233b5e18e51SDaniel P. Berrangé }
1234b5e18e51SDaniel P. Berrangé s->listener = qio_net_listener_new();
1235b5e18e51SDaniel P. Berrangé
1236b5e18e51SDaniel P. Berrangé name = g_strdup_printf("chardev-tcp-listener-%s", chr->label);
1237b5e18e51SDaniel P. Berrangé qio_net_listener_set_name(s->listener, name);
1238b5e18e51SDaniel P. Berrangé g_free(name);
1239b5e18e51SDaniel P. Berrangé
12401b87751fSMarc-André Lureau if (s->addr->type == SOCKET_ADDRESS_TYPE_FD && !*s->addr->u.fd.str) {
12411b87751fSMarc-André Lureau goto skip_listen;
12421b87751fSMarc-André Lureau }
12431b87751fSMarc-André Lureau
1244fc8135c6SJuan Quintela if (qio_net_listener_open_sync(s->listener, s->addr, 1, errp) < 0) {
1245b5e18e51SDaniel P. Berrangé object_unref(OBJECT(s->listener));
1246b5e18e51SDaniel P. Berrangé s->listener = NULL;
1247b5e18e51SDaniel P. Berrangé return -1;
1248b5e18e51SDaniel P. Berrangé }
1249b5e18e51SDaniel P. Berrangé
1250b5e18e51SDaniel P. Berrangé qapi_free_SocketAddress(s->addr);
1251b5e18e51SDaniel P. Berrangé s->addr = socket_local_address(s->listener->sioc[0]->fd, errp);
12521b87751fSMarc-André Lureau
12531b87751fSMarc-André Lureau skip_listen:
1254b5e18e51SDaniel P. Berrangé update_disconnected_filename(s);
1255b5e18e51SDaniel P. Berrangé
1256b5e18e51SDaniel P. Berrangé if (is_waitconnect) {
1257b5e18e51SDaniel P. Berrangé tcp_chr_accept_server_sync(chr);
1258b5e18e51SDaniel P. Berrangé } else {
1259b5e18e51SDaniel P. Berrangé qio_net_listener_set_client_func_full(s->listener,
1260b5e18e51SDaniel P. Berrangé tcp_chr_accept,
1261b5e18e51SDaniel P. Berrangé chr, NULL,
1262b5e18e51SDaniel P. Berrangé chr->gcontext);
1263b5e18e51SDaniel P. Berrangé }
1264b5e18e51SDaniel P. Berrangé
1265b5e18e51SDaniel P. Berrangé return 0;
1266b5e18e51SDaniel P. Berrangé }
1267b5e18e51SDaniel P. Berrangé
1268b5e18e51SDaniel P. Berrangé
qmp_chardev_open_socket_client(Chardev * chr,int64_t reconnect_ms,Error ** errp)1269b5e18e51SDaniel P. Berrangé static int qmp_chardev_open_socket_client(Chardev *chr,
1270*c8e2b6b4SDaniil Tatianin int64_t reconnect_ms,
1271b5e18e51SDaniel P. Berrangé Error **errp)
1272b5e18e51SDaniel P. Berrangé {
1273b5e18e51SDaniel P. Berrangé SocketChardev *s = SOCKET_CHARDEV(chr);
1274b5e18e51SDaniel P. Berrangé
1275*c8e2b6b4SDaniil Tatianin if (reconnect_ms > 0) {
1276*c8e2b6b4SDaniil Tatianin s->reconnect_time_ms = reconnect_ms;
1277b5e18e51SDaniel P. Berrangé tcp_chr_connect_client_async(chr);
1278b5e18e51SDaniel P. Berrangé return 0;
1279b5e18e51SDaniel P. Berrangé } else {
1280b5e18e51SDaniel P. Berrangé return tcp_chr_connect_client_sync(chr, errp);
1281b5e18e51SDaniel P. Berrangé }
1282b5e18e51SDaniel P. Berrangé }
1283b5e18e51SDaniel P. Berrangé
1284b5e18e51SDaniel P. Berrangé
qmp_chardev_validate_socket(ChardevSocket * sock,SocketAddress * addr,Error ** errp)12851645984bSDaniel P. Berrangé static bool qmp_chardev_validate_socket(ChardevSocket *sock,
12861645984bSDaniel P. Berrangé SocketAddress *addr,
12871645984bSDaniel P. Berrangé Error **errp)
12881645984bSDaniel P. Berrangé {
12891645984bSDaniel P. Berrangé /* Validate any options which have a dependency on address type */
12901645984bSDaniel P. Berrangé switch (addr->type) {
12911645984bSDaniel P. Berrangé case SOCKET_ADDRESS_TYPE_FD:
12921645984bSDaniel P. Berrangé if (sock->has_reconnect) {
12931645984bSDaniel P. Berrangé error_setg(errp,
12941645984bSDaniel P. Berrangé "'reconnect' option is incompatible with "
12951645984bSDaniel P. Berrangé "'fd' address type");
12961645984bSDaniel P. Berrangé return false;
12971645984bSDaniel P. Berrangé }
12988de69efaSMarkus Armbruster if (sock->tls_creds &&
12991645984bSDaniel P. Berrangé !(sock->has_server && sock->server)) {
13001645984bSDaniel P. Berrangé error_setg(errp,
13011645984bSDaniel P. Berrangé "'tls_creds' option is incompatible with "
13021645984bSDaniel P. Berrangé "'fd' address type as client");
13031645984bSDaniel P. Berrangé return false;
13041645984bSDaniel P. Berrangé }
13051645984bSDaniel P. Berrangé break;
13061645984bSDaniel P. Berrangé
13071645984bSDaniel P. Berrangé case SOCKET_ADDRESS_TYPE_UNIX:
13088de69efaSMarkus Armbruster if (sock->tls_creds) {
13091645984bSDaniel P. Berrangé error_setg(errp,
13101645984bSDaniel P. Berrangé "'tls_creds' option is incompatible with "
13111645984bSDaniel P. Berrangé "'unix' address type");
13121645984bSDaniel P. Berrangé return false;
13131645984bSDaniel P. Berrangé }
13141645984bSDaniel P. Berrangé break;
13151645984bSDaniel P. Berrangé
13161645984bSDaniel P. Berrangé case SOCKET_ADDRESS_TYPE_INET:
13171645984bSDaniel P. Berrangé break;
13181645984bSDaniel P. Berrangé
13191645984bSDaniel P. Berrangé case SOCKET_ADDRESS_TYPE_VSOCK:
13208de69efaSMarkus Armbruster if (sock->tls_creds) {
13211645984bSDaniel P. Berrangé error_setg(errp,
13221645984bSDaniel P. Berrangé "'tls_creds' option is incompatible with "
13231645984bSDaniel P. Berrangé "'vsock' address type");
13241645984bSDaniel P. Berrangé return false;
13251645984bSDaniel P. Berrangé }
13261645984bSDaniel P. Berrangé
13271645984bSDaniel P. Berrangé default:
13281645984bSDaniel P. Berrangé break;
13291645984bSDaniel P. Berrangé }
13301645984bSDaniel P. Berrangé
13318de69efaSMarkus Armbruster if (sock->tls_authz && !sock->tls_creds) {
1332fd4a5fd4SDaniel P. Berrange error_setg(errp, "'tls_authz' option requires 'tls_creds' option");
1333fd4a5fd4SDaniel P. Berrange return false;
1334fd4a5fd4SDaniel P. Berrange }
1335fd4a5fd4SDaniel P. Berrange
13360a19d879SMichael Tokarev /* Validate any options which have a dependency on client vs server */
13374a0582f6SDaniel P. Berrangé if (!sock->has_server || sock->server) {
13384a0582f6SDaniel P. Berrangé if (sock->has_reconnect) {
13394a0582f6SDaniel P. Berrangé error_setg(errp,
13404a0582f6SDaniel P. Berrangé "'reconnect' option is incompatible with "
13414a0582f6SDaniel P. Berrangé "socket in server listen mode");
13424a0582f6SDaniel P. Berrangé return false;
13434a0582f6SDaniel P. Berrangé }
13444a0582f6SDaniel P. Berrangé } else {
13451645984bSDaniel P. Berrangé if (sock->has_websocket && sock->websocket) {
13461645984bSDaniel P. Berrangé error_setg(errp, "%s", "Websocket client is not implemented");
13471645984bSDaniel P. Berrangé return false;
13481645984bSDaniel P. Berrangé }
1349767abe7fSDaniel P. Berrangé if (sock->has_wait) {
1350767abe7fSDaniel P. Berrangé error_setg(errp, "%s",
1351767abe7fSDaniel P. Berrangé "'wait' option is incompatible with "
1352767abe7fSDaniel P. Berrangé "socket in client connect mode");
1353767abe7fSDaniel P. Berrangé return false;
1354767abe7fSDaniel P. Berrangé }
13551645984bSDaniel P. Berrangé }
13561645984bSDaniel P. Berrangé
1357*c8e2b6b4SDaniil Tatianin if (sock->has_reconnect_ms && sock->has_reconnect) {
1358*c8e2b6b4SDaniil Tatianin error_setg(errp,
1359*c8e2b6b4SDaniil Tatianin "'reconnect' and 'reconnect-ms' are mutually exclusive");
1360*c8e2b6b4SDaniil Tatianin return false;
1361*c8e2b6b4SDaniil Tatianin }
1362*c8e2b6b4SDaniil Tatianin
13631645984bSDaniel P. Berrangé return true;
13641645984bSDaniel P. Berrangé }
13651645984bSDaniel P. Berrangé
13661645984bSDaniel P. Berrangé
qmp_chardev_open_socket(Chardev * chr,ChardevBackend * backend,bool * be_opened,Error ** errp)1367d24ca4b8SMarc-André Lureau static void qmp_chardev_open_socket(Chardev *chr,
1368d24ca4b8SMarc-André Lureau ChardevBackend *backend,
1369d24ca4b8SMarc-André Lureau bool *be_opened,
1370d24ca4b8SMarc-André Lureau Error **errp)
1371d24ca4b8SMarc-André Lureau {
1372d24ca4b8SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(chr);
1373d24ca4b8SMarc-André Lureau ChardevSocket *sock = backend->u.socket.data;
1374d24ca4b8SMarc-André Lureau bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
1375d24ca4b8SMarc-André Lureau bool is_listen = sock->has_server ? sock->server : true;
1376d24ca4b8SMarc-André Lureau bool is_telnet = sock->has_telnet ? sock->telnet : false;
1377ae92cbd5SJing Liu bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false;
1378d24ca4b8SMarc-André Lureau bool is_waitconnect = sock->has_wait ? sock->wait : false;
1379981b06e7SJulia Suvorova bool is_websock = sock->has_websocket ? sock->websocket : false;
1380*c8e2b6b4SDaniil Tatianin int64_t reconnect_ms = 0;
1381bd269ebcSMarkus Armbruster SocketAddress *addr;
1382d24ca4b8SMarc-André Lureau
1383d24ca4b8SMarc-André Lureau s->is_listen = is_listen;
1384d24ca4b8SMarc-André Lureau s->is_telnet = is_telnet;
1385ae92cbd5SJing Liu s->is_tn3270 = is_tn3270;
1386981b06e7SJulia Suvorova s->is_websock = is_websock;
1387d24ca4b8SMarc-André Lureau s->do_nodelay = do_nodelay;
1388d24ca4b8SMarc-André Lureau if (sock->tls_creds) {
1389d24ca4b8SMarc-André Lureau Object *creds;
1390d24ca4b8SMarc-André Lureau creds = object_resolve_path_component(
1391d24ca4b8SMarc-André Lureau object_get_objects_root(), sock->tls_creds);
1392d24ca4b8SMarc-André Lureau if (!creds) {
1393d24ca4b8SMarc-André Lureau error_setg(errp, "No TLS credentials with id '%s'",
1394d24ca4b8SMarc-André Lureau sock->tls_creds);
1395e154fd79SDaniel P. Berrangé return;
1396d24ca4b8SMarc-André Lureau }
1397d24ca4b8SMarc-André Lureau s->tls_creds = (QCryptoTLSCreds *)
1398d24ca4b8SMarc-André Lureau object_dynamic_cast(creds,
1399d24ca4b8SMarc-André Lureau TYPE_QCRYPTO_TLS_CREDS);
1400d24ca4b8SMarc-André Lureau if (!s->tls_creds) {
1401d24ca4b8SMarc-André Lureau error_setg(errp, "Object with id '%s' is not TLS credentials",
1402d24ca4b8SMarc-André Lureau sock->tls_creds);
1403e154fd79SDaniel P. Berrangé return;
1404d24ca4b8SMarc-André Lureau }
1405d24ca4b8SMarc-André Lureau object_ref(OBJECT(s->tls_creds));
14068612df2eSPhilippe Mathieu-Daudé if (!qcrypto_tls_creds_check_endpoint(s->tls_creds,
14078612df2eSPhilippe Mathieu-Daudé is_listen
14088612df2eSPhilippe Mathieu-Daudé ? QCRYPTO_TLS_CREDS_ENDPOINT_SERVER
14098612df2eSPhilippe Mathieu-Daudé : QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
14108612df2eSPhilippe Mathieu-Daudé errp)) {
1411e154fd79SDaniel P. Berrangé return;
1412d24ca4b8SMarc-André Lureau }
1413d24ca4b8SMarc-André Lureau }
1414fd4a5fd4SDaniel P. Berrange s->tls_authz = g_strdup(sock->tls_authz);
1415d24ca4b8SMarc-André Lureau
1416bd269ebcSMarkus Armbruster s->addr = addr = socket_address_flatten(sock->addr);
1417d24ca4b8SMarc-André Lureau
14181645984bSDaniel P. Berrangé if (!qmp_chardev_validate_socket(sock, addr, errp)) {
1419e154fd79SDaniel P. Berrangé return;
14204591bd46SMarc-André Lureau }
14211645984bSDaniel P. Berrangé
1422d24ca4b8SMarc-André Lureau qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE);
1423120fa5e0SBin Meng #ifndef _WIN32
1424ca0b64e5SMarkus Armbruster /* TODO SOCKET_ADDRESS_FD where fd has AF_UNIX */
1425bd269ebcSMarkus Armbruster if (addr->type == SOCKET_ADDRESS_TYPE_UNIX) {
1426d24ca4b8SMarc-André Lureau qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
1427d24ca4b8SMarc-André Lureau }
1428120fa5e0SBin Meng #endif
1429d24ca4b8SMarc-André Lureau
1430feb774caSLukas Straub /*
1431feb774caSLukas Straub * In the chardev-change special-case, we shouldn't register a new yank
1432feb774caSLukas Straub * instance, as there already may be one.
1433feb774caSLukas Straub */
1434feb774caSLukas Straub if (!chr->handover_yank_instance) {
14358ee44806SLukas Straub if (!yank_register_instance(CHARDEV_YANK_INSTANCE(chr->label), errp)) {
14368ee44806SLukas Straub return;
14378ee44806SLukas Straub }
1438feb774caSLukas Straub }
14398ee44806SLukas Straub s->registered_yank = true;
14408ee44806SLukas Straub
1441d24ca4b8SMarc-André Lureau /* be isn't opened until we get a connection */
1442d24ca4b8SMarc-André Lureau *be_opened = false;
1443d24ca4b8SMarc-André Lureau
1444bbcde969SMarc-André Lureau update_disconnected_filename(s);
1445d24ca4b8SMarc-André Lureau
1446d24ca4b8SMarc-André Lureau if (s->is_listen) {
1447b5e18e51SDaniel P. Berrangé if (qmp_chardev_open_socket_server(chr, is_telnet || is_tn3270,
1448b5e18e51SDaniel P. Berrangé is_waitconnect, errp) < 0) {
1449e154fd79SDaniel P. Berrangé return;
1450d24ca4b8SMarc-André Lureau }
1451efae0b92SDaniel P. Berrangé } else {
1452*c8e2b6b4SDaniil Tatianin if (sock->has_reconnect) {
1453*c8e2b6b4SDaniil Tatianin reconnect_ms = sock->reconnect * 1000ULL;
1454*c8e2b6b4SDaniil Tatianin } else if (sock->has_reconnect_ms) {
1455*c8e2b6b4SDaniil Tatianin reconnect_ms = sock->reconnect_ms;
1456*c8e2b6b4SDaniil Tatianin }
1457*c8e2b6b4SDaniil Tatianin
1458*c8e2b6b4SDaniil Tatianin if (qmp_chardev_open_socket_client(chr, reconnect_ms, errp) < 0) {
1459d24ca4b8SMarc-André Lureau return;
1460e154fd79SDaniel P. Berrangé }
1461d24ca4b8SMarc-André Lureau }
1462d24ca4b8SMarc-André Lureau }
1463d24ca4b8SMarc-André Lureau
qemu_chr_parse_socket(QemuOpts * opts,ChardevBackend * backend,Error ** errp)1464d24ca4b8SMarc-André Lureau static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
1465d24ca4b8SMarc-André Lureau Error **errp)
1466d24ca4b8SMarc-André Lureau {
1467d24ca4b8SMarc-André Lureau const char *path = qemu_opt_get(opts, "path");
1468d24ca4b8SMarc-André Lureau const char *host = qemu_opt_get(opts, "host");
1469d24ca4b8SMarc-André Lureau const char *port = qemu_opt_get(opts, "port");
14700935700fSDaniel P. Berrange const char *fd = qemu_opt_get(opts, "fd");
14718acefc79SMarkus Armbruster #ifdef CONFIG_LINUX
1472776b97d3Sxiaoqiang zhao bool tight = qemu_opt_get_bool(opts, "tight", true);
1473776b97d3Sxiaoqiang zhao bool abstract = qemu_opt_get_bool(opts, "abstract", false);
14748acefc79SMarkus Armbruster #endif
1475dfd100f2SMarkus Armbruster SocketAddressLegacy *addr;
1476d24ca4b8SMarc-André Lureau ChardevSocket *sock;
1477d24ca4b8SMarc-André Lureau
14781b87751fSMarc-André Lureau if ((!!path + !!fd + !!host) > 1) {
14799bb4060cSDaniel P. Berrange error_setg(errp,
14801b87751fSMarc-André Lureau "None or one of 'path', 'fd' or 'host' option required.");
1481d24ca4b8SMarc-André Lureau return;
1482d24ca4b8SMarc-André Lureau }
14839bb4060cSDaniel P. Berrange
14841645984bSDaniel P. Berrangé if (host && !port) {
1485d24ca4b8SMarc-André Lureau error_setg(errp, "chardev: socket: no port given");
1486d24ca4b8SMarc-André Lureau return;
1487d24ca4b8SMarc-André Lureau }
1488d24ca4b8SMarc-André Lureau
14891645984bSDaniel P. Berrangé backend->type = CHARDEV_BACKEND_KIND_SOCKET;
1490d24ca4b8SMarc-André Lureau sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
1491d24ca4b8SMarc-André Lureau qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock));
1492d24ca4b8SMarc-André Lureau
1493a9b1315fSPaolo Bonzini if (qemu_opt_get(opts, "delay") && qemu_opt_get(opts, "nodelay")) {
1494a9b1315fSPaolo Bonzini error_setg(errp, "'delay' and 'nodelay' are mutually exclusive");
1495a9b1315fSPaolo Bonzini return;
1496a9b1315fSPaolo Bonzini }
1497a9b1315fSPaolo Bonzini sock->has_nodelay =
1498a9b1315fSPaolo Bonzini qemu_opt_get(opts, "delay") ||
1499a9b1315fSPaolo Bonzini qemu_opt_get(opts, "nodelay");
1500a9b1315fSPaolo Bonzini sock->nodelay =
1501a9b1315fSPaolo Bonzini !qemu_opt_get_bool(opts, "delay", true) ||
1502a9b1315fSPaolo Bonzini qemu_opt_get_bool(opts, "nodelay", false);
1503a9b1315fSPaolo Bonzini
15045981c3a2SDaniel P. Berrangé /*
15055981c3a2SDaniel P. Berrangé * We have different default to QMP for 'server', hence
15065981c3a2SDaniel P. Berrangé * we can't just check for existence of 'server'
15075981c3a2SDaniel P. Berrangé */
1508d24ca4b8SMarc-André Lureau sock->has_server = true;
15095981c3a2SDaniel P. Berrangé sock->server = qemu_opt_get_bool(opts, "server", false);
15105981c3a2SDaniel P. Berrangé sock->has_telnet = qemu_opt_get(opts, "telnet");
15115981c3a2SDaniel P. Berrangé sock->telnet = qemu_opt_get_bool(opts, "telnet", false);
15125981c3a2SDaniel P. Berrangé sock->has_tn3270 = qemu_opt_get(opts, "tn3270");
15135981c3a2SDaniel P. Berrangé sock->tn3270 = qemu_opt_get_bool(opts, "tn3270", false);
15145981c3a2SDaniel P. Berrangé sock->has_websocket = qemu_opt_get(opts, "websocket");
15155981c3a2SDaniel P. Berrangé sock->websocket = qemu_opt_get_bool(opts, "websocket", false);
1516767abe7fSDaniel P. Berrangé /*
1517767abe7fSDaniel P. Berrangé * We have different default to QMP for 'wait' when 'server'
1518767abe7fSDaniel P. Berrangé * is set, hence we can't just check for existence of 'wait'
1519767abe7fSDaniel P. Berrangé */
15205981c3a2SDaniel P. Berrangé sock->has_wait = qemu_opt_find(opts, "wait") || sock->server;
15215981c3a2SDaniel P. Berrangé sock->wait = qemu_opt_get_bool(opts, "wait", true);
15223b023756SMarc-André Lureau sock->has_reconnect = qemu_opt_find(opts, "reconnect");
15235981c3a2SDaniel P. Berrangé sock->reconnect = qemu_opt_get_number(opts, "reconnect", 0);
1524*c8e2b6b4SDaniil Tatianin sock->has_reconnect_ms = qemu_opt_find(opts, "reconnect-ms");
1525*c8e2b6b4SDaniil Tatianin sock->reconnect_ms = qemu_opt_get_number(opts, "reconnect-ms", 0);
1526*c8e2b6b4SDaniil Tatianin
15275981c3a2SDaniel P. Berrangé sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds"));
1528fd4a5fd4SDaniel P. Berrange sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz"));
1529d24ca4b8SMarc-André Lureau
1530dfd100f2SMarkus Armbruster addr = g_new0(SocketAddressLegacy, 1);
1531d24ca4b8SMarc-André Lureau if (path) {
1532d24ca4b8SMarc-André Lureau UnixSocketAddress *q_unix;
1533935a867cSMarkus Armbruster addr->type = SOCKET_ADDRESS_TYPE_UNIX;
1534d24ca4b8SMarc-André Lureau q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
1535d24ca4b8SMarc-André Lureau q_unix->path = g_strdup(path);
15368acefc79SMarkus Armbruster #ifdef CONFIG_LINUX
1537b08cc97dSMarkus Armbruster q_unix->has_tight = true;
1538776b97d3Sxiaoqiang zhao q_unix->tight = tight;
1539b08cc97dSMarkus Armbruster q_unix->has_abstract = true;
1540776b97d3Sxiaoqiang zhao q_unix->abstract = abstract;
15418acefc79SMarkus Armbruster #endif
15429bb4060cSDaniel P. Berrange } else if (host) {
1543935a867cSMarkus Armbruster addr->type = SOCKET_ADDRESS_TYPE_INET;
1544d24ca4b8SMarc-André Lureau addr->u.inet.data = g_new(InetSocketAddress, 1);
1545d24ca4b8SMarc-André Lureau *addr->u.inet.data = (InetSocketAddress) {
1546d24ca4b8SMarc-André Lureau .host = g_strdup(host),
1547d24ca4b8SMarc-André Lureau .port = g_strdup(port),
1548d24ca4b8SMarc-André Lureau .has_to = qemu_opt_get(opts, "to"),
1549d24ca4b8SMarc-André Lureau .to = qemu_opt_get_number(opts, "to", 0),
1550d24ca4b8SMarc-André Lureau .has_ipv4 = qemu_opt_get(opts, "ipv4"),
1551d24ca4b8SMarc-André Lureau .ipv4 = qemu_opt_get_bool(opts, "ipv4", 0),
1552d24ca4b8SMarc-André Lureau .has_ipv6 = qemu_opt_get(opts, "ipv6"),
1553d24ca4b8SMarc-André Lureau .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
1554d24ca4b8SMarc-André Lureau };
15551b87751fSMarc-André Lureau } else {
1556935a867cSMarkus Armbruster addr->type = SOCKET_ADDRESS_TYPE_FD;
15574edb196eSMarkus Armbruster addr->u.fd.data = g_new(FdSocketAddress, 1);
15580935700fSDaniel P. Berrange addr->u.fd.data->str = g_strdup(fd);
1559d24ca4b8SMarc-André Lureau }
1560d24ca4b8SMarc-André Lureau sock->addr = addr;
1561d24ca4b8SMarc-André Lureau }
1562d24ca4b8SMarc-André Lureau
1563123676e9SMarc-André Lureau static void
char_socket_get_addr(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)1564123676e9SMarc-André Lureau char_socket_get_addr(Object *obj, Visitor *v, const char *name,
1565123676e9SMarc-André Lureau void *opaque, Error **errp)
1566123676e9SMarc-André Lureau {
1567123676e9SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(obj);
1568123676e9SMarc-André Lureau
1569123676e9SMarc-André Lureau visit_type_SocketAddress(v, name, &s->addr, errp);
1570123676e9SMarc-André Lureau }
1571123676e9SMarc-André Lureau
1572da2d19b0SMarc-André Lureau static bool
char_socket_get_connected(Object * obj,Error ** errp)1573da2d19b0SMarc-André Lureau char_socket_get_connected(Object *obj, Error **errp)
1574da2d19b0SMarc-André Lureau {
1575da2d19b0SMarc-André Lureau SocketChardev *s = SOCKET_CHARDEV(obj);
1576da2d19b0SMarc-André Lureau
157732423ccaSDaniel P. Berrangé return s->state == TCP_CHARDEV_STATE_CONNECTED;
1578da2d19b0SMarc-André Lureau }
1579da2d19b0SMarc-André Lureau
char_socket_class_init(ObjectClass * oc,void * data)1580d24ca4b8SMarc-André Lureau static void char_socket_class_init(ObjectClass *oc, void *data)
1581d24ca4b8SMarc-André Lureau {
1582d24ca4b8SMarc-André Lureau ChardevClass *cc = CHARDEV_CLASS(oc);
1583d24ca4b8SMarc-André Lureau
1584feb774caSLukas Straub cc->supports_yank = true;
1585feb774caSLukas Straub
1586d24ca4b8SMarc-André Lureau cc->parse = qemu_chr_parse_socket;
1587d24ca4b8SMarc-André Lureau cc->open = qmp_chardev_open_socket;
1588d24ca4b8SMarc-André Lureau cc->chr_wait_connected = tcp_chr_wait_connected;
1589d24ca4b8SMarc-André Lureau cc->chr_write = tcp_chr_write;
1590d24ca4b8SMarc-André Lureau cc->chr_sync_read = tcp_chr_sync_read;
1591d24ca4b8SMarc-André Lureau cc->chr_disconnect = tcp_chr_disconnect;
1592d24ca4b8SMarc-André Lureau cc->get_msgfds = tcp_get_msgfds;
1593d24ca4b8SMarc-André Lureau cc->set_msgfds = tcp_set_msgfds;
1594d24ca4b8SMarc-André Lureau cc->chr_add_client = tcp_chr_add_client;
1595d24ca4b8SMarc-André Lureau cc->chr_add_watch = tcp_chr_add_watch;
1596d24ca4b8SMarc-André Lureau cc->chr_update_read_handler = tcp_chr_update_read_handler;
1597123676e9SMarc-André Lureau
1598123676e9SMarc-André Lureau object_class_property_add(oc, "addr", "SocketAddress",
1599123676e9SMarc-André Lureau char_socket_get_addr, NULL,
1600d2623129SMarkus Armbruster NULL, NULL);
1601da2d19b0SMarc-André Lureau
1602da2d19b0SMarc-André Lureau object_class_property_add_bool(oc, "connected", char_socket_get_connected,
1603d2623129SMarkus Armbruster NULL);
1604d24ca4b8SMarc-André Lureau }
1605d24ca4b8SMarc-André Lureau
1606d24ca4b8SMarc-André Lureau static const TypeInfo char_socket_type_info = {
1607d24ca4b8SMarc-André Lureau .name = TYPE_CHARDEV_SOCKET,
1608d24ca4b8SMarc-André Lureau .parent = TYPE_CHARDEV,
1609d24ca4b8SMarc-André Lureau .instance_size = sizeof(SocketChardev),
1610d24ca4b8SMarc-André Lureau .instance_finalize = char_socket_finalize,
1611d24ca4b8SMarc-André Lureau .class_init = char_socket_class_init,
1612d24ca4b8SMarc-André Lureau };
1613d24ca4b8SMarc-André Lureau
register_types(void)1614d24ca4b8SMarc-André Lureau static void register_types(void)
1615d24ca4b8SMarc-André Lureau {
1616d24ca4b8SMarc-André Lureau type_register_static(&char_socket_type_info);
1617d24ca4b8SMarc-André Lureau }
1618d24ca4b8SMarc-André Lureau
1619d24ca4b8SMarc-André Lureau type_init(register_types);
1620