xref: /freebsd/usr.sbin/bhyve/uart_backend.c (revision 1f903953fbf8615bb611db059417177f6cee07bd)
1d1c5d0cfSMark Johnston /*-
2d1c5d0cfSMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
3d1c5d0cfSMark Johnston  *
4d1c5d0cfSMark Johnston  * Copyright (c) 2012 NetApp, Inc.
5d1c5d0cfSMark Johnston  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6d1c5d0cfSMark Johnston  * All rights reserved.
7d1c5d0cfSMark Johnston  *
8d1c5d0cfSMark Johnston  * Redistribution and use in source and binary forms, with or without
9d1c5d0cfSMark Johnston  * modification, are permitted provided that the following conditions
10d1c5d0cfSMark Johnston  * are met:
11d1c5d0cfSMark Johnston  * 1. Redistributions of source code must retain the above copyright
12d1c5d0cfSMark Johnston  *    notice, this list of conditions and the following disclaimer.
13d1c5d0cfSMark Johnston  * 2. Redistributions in binary form must reproduce the above copyright
14d1c5d0cfSMark Johnston  *    notice, this list of conditions and the following disclaimer in the
15d1c5d0cfSMark Johnston  *    documentation and/or other materials provided with the distribution.
16d1c5d0cfSMark Johnston  *
17d1c5d0cfSMark Johnston  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18d1c5d0cfSMark Johnston  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19d1c5d0cfSMark Johnston  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20d1c5d0cfSMark Johnston  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21d1c5d0cfSMark Johnston  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22d1c5d0cfSMark Johnston  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23d1c5d0cfSMark Johnston  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24d1c5d0cfSMark Johnston  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25d1c5d0cfSMark Johnston  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26d1c5d0cfSMark Johnston  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27d1c5d0cfSMark Johnston  * SUCH DAMAGE.
28d1c5d0cfSMark Johnston  */
29d1c5d0cfSMark Johnston 
30d1c5d0cfSMark Johnston #include <sys/types.h>
31*1f903953SSHENG-YI HONG #include <sys/socket.h>
32d1c5d0cfSMark Johnston 
33d1c5d0cfSMark Johnston #include <machine/vmm.h>
34d1c5d0cfSMark Johnston #include <machine/vmm_snapshot.h>
35d1c5d0cfSMark Johnston 
36*1f903953SSHENG-YI HONG #include <netinet/in.h>
37*1f903953SSHENG-YI HONG 
38*1f903953SSHENG-YI HONG #include <arpa/inet.h>
39d1c5d0cfSMark Johnston #include <assert.h>
40d1c5d0cfSMark Johnston #include <capsicum_helpers.h>
41d1c5d0cfSMark Johnston #include <err.h>
42*1f903953SSHENG-YI HONG #include <netdb.h>
43e10b9d66SSHENG-YI HONG #include <pthread.h>
44d1c5d0cfSMark Johnston #include <stdbool.h>
45d1c5d0cfSMark Johnston #include <stdlib.h>
46d1c5d0cfSMark Johnston #include <string.h>
47d1c5d0cfSMark Johnston #include <sysexits.h>
48d1c5d0cfSMark Johnston #include <termios.h>
49d1c5d0cfSMark Johnston #include <unistd.h>
50d1c5d0cfSMark Johnston 
51d1c5d0cfSMark Johnston #include "debug.h"
52d1c5d0cfSMark Johnston #include "mevent.h"
53d1c5d0cfSMark Johnston #include "uart_backend.h"
54d1c5d0cfSMark Johnston 
55d1c5d0cfSMark Johnston struct ttyfd {
56d1c5d0cfSMark Johnston 	bool	opened;
57*1f903953SSHENG-YI HONG 	bool	is_socket;
58d1c5d0cfSMark Johnston 	int	rfd;		/* fd for reading */
59d1c5d0cfSMark Johnston 	int	wfd;		/* fd for writing, may be == rfd */
60d1c5d0cfSMark Johnston };
61d1c5d0cfSMark Johnston 
62d1c5d0cfSMark Johnston #define	FIFOSZ	16
63d1c5d0cfSMark Johnston 
64d1c5d0cfSMark Johnston struct fifo {
65d1c5d0cfSMark Johnston 	uint8_t	buf[FIFOSZ];
66d1c5d0cfSMark Johnston 	int	rindex;		/* index to read from */
67d1c5d0cfSMark Johnston 	int	windex;		/* index to write to */
68d1c5d0cfSMark Johnston 	int	num;		/* number of characters in the fifo */
69d1c5d0cfSMark Johnston 	int	size;		/* size of the fifo */
70d1c5d0cfSMark Johnston };
71d1c5d0cfSMark Johnston 
72d1c5d0cfSMark Johnston struct uart_softc {
73d1c5d0cfSMark Johnston 	struct ttyfd	tty;
74d1c5d0cfSMark Johnston 	struct fifo	rxfifo;
75d1c5d0cfSMark Johnston 	struct mevent	*mev;
76e10b9d66SSHENG-YI HONG 	pthread_mutex_t mtx;
77d1c5d0cfSMark Johnston };
78d1c5d0cfSMark Johnston 
79*1f903953SSHENG-YI HONG struct uart_socket_softc {
80*1f903953SSHENG-YI HONG 	struct uart_softc *softc;
81*1f903953SSHENG-YI HONG 	void (*drain)(int, enum ev_type, void *);
82*1f903953SSHENG-YI HONG 	void *arg;
83*1f903953SSHENG-YI HONG };
84*1f903953SSHENG-YI HONG 
85d1c5d0cfSMark Johnston static bool uart_stdio;		/* stdio in use for i/o */
86d1c5d0cfSMark Johnston static struct termios tio_stdio_orig;
87d1c5d0cfSMark Johnston 
88*1f903953SSHENG-YI HONG static void uart_tcp_disconnect(struct uart_softc *);
89*1f903953SSHENG-YI HONG 
90d1c5d0cfSMark Johnston static void
91d1c5d0cfSMark Johnston ttyclose(void)
92d1c5d0cfSMark Johnston {
93d1c5d0cfSMark Johnston 	tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
94d1c5d0cfSMark Johnston }
95d1c5d0cfSMark Johnston 
96d1c5d0cfSMark Johnston static void
97d1c5d0cfSMark Johnston ttyopen(struct ttyfd *tf)
98d1c5d0cfSMark Johnston {
99d1c5d0cfSMark Johnston 	struct termios orig, new;
100d1c5d0cfSMark Johnston 
101d1c5d0cfSMark Johnston 	tcgetattr(tf->rfd, &orig);
102d1c5d0cfSMark Johnston 	new = orig;
103d1c5d0cfSMark Johnston 	cfmakeraw(&new);
104d1c5d0cfSMark Johnston 	new.c_cflag |= CLOCAL;
105d1c5d0cfSMark Johnston 	tcsetattr(tf->rfd, TCSANOW, &new);
106d1c5d0cfSMark Johnston 	if (uart_stdio) {
107d1c5d0cfSMark Johnston 		tio_stdio_orig = orig;
108d1c5d0cfSMark Johnston 		atexit(ttyclose);
109d1c5d0cfSMark Johnston 	}
110d1c5d0cfSMark Johnston 	raw_stdio = 1;
111d1c5d0cfSMark Johnston }
112d1c5d0cfSMark Johnston 
113d1c5d0cfSMark Johnston static int
114*1f903953SSHENG-YI HONG ttyread(struct ttyfd *tf, uint8_t *ret)
115d1c5d0cfSMark Johnston {
116*1f903953SSHENG-YI HONG 	uint8_t rb;
117*1f903953SSHENG-YI HONG 	int len;
118d1c5d0cfSMark Johnston 
119*1f903953SSHENG-YI HONG 	len = read(tf->rfd, &rb, 1);
120*1f903953SSHENG-YI HONG 	if (ret && len == 1)
121*1f903953SSHENG-YI HONG 		*ret = rb;
122*1f903953SSHENG-YI HONG 
123*1f903953SSHENG-YI HONG 	return (len);
124d1c5d0cfSMark Johnston }
125d1c5d0cfSMark Johnston 
126*1f903953SSHENG-YI HONG static int
127d1c5d0cfSMark Johnston ttywrite(struct ttyfd *tf, unsigned char wb)
128d1c5d0cfSMark Johnston {
129*1f903953SSHENG-YI HONG 	return (write(tf->wfd, &wb, 1));
130d1c5d0cfSMark Johnston }
131d1c5d0cfSMark Johnston 
132d1c5d0cfSMark Johnston static bool
133d1c5d0cfSMark Johnston rxfifo_available(struct uart_softc *sc)
134d1c5d0cfSMark Johnston {
135d1c5d0cfSMark Johnston 	return (sc->rxfifo.num < sc->rxfifo.size);
136d1c5d0cfSMark Johnston }
137d1c5d0cfSMark Johnston 
138d1c5d0cfSMark Johnston int
139d1c5d0cfSMark Johnston uart_rxfifo_getchar(struct uart_softc *sc)
140d1c5d0cfSMark Johnston {
141d1c5d0cfSMark Johnston 	struct fifo *fifo;
142d1c5d0cfSMark Johnston 	int c, error, wasfull;
143d1c5d0cfSMark Johnston 
144d1c5d0cfSMark Johnston 	wasfull = 0;
145d1c5d0cfSMark Johnston 	fifo = &sc->rxfifo;
146d1c5d0cfSMark Johnston 	if (fifo->num > 0) {
147d1c5d0cfSMark Johnston 		if (!rxfifo_available(sc))
148d1c5d0cfSMark Johnston 			wasfull = 1;
149d1c5d0cfSMark Johnston 		c = fifo->buf[fifo->rindex];
150d1c5d0cfSMark Johnston 		fifo->rindex = (fifo->rindex + 1) % fifo->size;
151d1c5d0cfSMark Johnston 		fifo->num--;
152d1c5d0cfSMark Johnston 		if (wasfull) {
153d1c5d0cfSMark Johnston 			if (sc->tty.opened) {
154d1c5d0cfSMark Johnston 				error = mevent_enable(sc->mev);
155d1c5d0cfSMark Johnston 				assert(error == 0);
156d1c5d0cfSMark Johnston 			}
157d1c5d0cfSMark Johnston 		}
158d1c5d0cfSMark Johnston 		return (c);
159d1c5d0cfSMark Johnston 	} else
160d1c5d0cfSMark Johnston 		return (-1);
161d1c5d0cfSMark Johnston }
162d1c5d0cfSMark Johnston 
163d1c5d0cfSMark Johnston int
164d1c5d0cfSMark Johnston uart_rxfifo_numchars(struct uart_softc *sc)
165d1c5d0cfSMark Johnston {
166d1c5d0cfSMark Johnston 	return (sc->rxfifo.num);
167d1c5d0cfSMark Johnston }
168d1c5d0cfSMark Johnston 
169d1c5d0cfSMark Johnston static int
170d1c5d0cfSMark Johnston rxfifo_putchar(struct uart_softc *sc, uint8_t ch)
171d1c5d0cfSMark Johnston {
172d1c5d0cfSMark Johnston 	struct fifo *fifo;
173d1c5d0cfSMark Johnston 	int error;
174d1c5d0cfSMark Johnston 
175d1c5d0cfSMark Johnston 	fifo = &sc->rxfifo;
176d1c5d0cfSMark Johnston 
177d1c5d0cfSMark Johnston 	if (fifo->num < fifo->size) {
178d1c5d0cfSMark Johnston 		fifo->buf[fifo->windex] = ch;
179d1c5d0cfSMark Johnston 		fifo->windex = (fifo->windex + 1) % fifo->size;
180d1c5d0cfSMark Johnston 		fifo->num++;
181d1c5d0cfSMark Johnston 		if (!rxfifo_available(sc)) {
182d1c5d0cfSMark Johnston 			if (sc->tty.opened) {
183d1c5d0cfSMark Johnston 				/*
184d1c5d0cfSMark Johnston 				 * Disable mevent callback if the FIFO is full.
185d1c5d0cfSMark Johnston 				 */
186d1c5d0cfSMark Johnston 				error = mevent_disable(sc->mev);
187d1c5d0cfSMark Johnston 				assert(error == 0);
188d1c5d0cfSMark Johnston 			}
189d1c5d0cfSMark Johnston 		}
190d1c5d0cfSMark Johnston 		return (0);
191d1c5d0cfSMark Johnston 	} else
192d1c5d0cfSMark Johnston 		return (-1);
193d1c5d0cfSMark Johnston }
194d1c5d0cfSMark Johnston 
195d1c5d0cfSMark Johnston void
196d1c5d0cfSMark Johnston uart_rxfifo_drain(struct uart_softc *sc, bool loopback)
197d1c5d0cfSMark Johnston {
198*1f903953SSHENG-YI HONG 	uint8_t ch;
199*1f903953SSHENG-YI HONG 	int len;
200d1c5d0cfSMark Johnston 
201d1c5d0cfSMark Johnston 	if (loopback) {
202*1f903953SSHENG-YI HONG 		if (ttyread(&sc->tty, &ch) == 0 && sc->tty.is_socket)
203*1f903953SSHENG-YI HONG 			uart_tcp_disconnect(sc);
204d1c5d0cfSMark Johnston 	} else {
205*1f903953SSHENG-YI HONG 		while (rxfifo_available(sc)) {
206*1f903953SSHENG-YI HONG 			len = ttyread(&sc->tty, &ch);
207*1f903953SSHENG-YI HONG 			if (len <= 0) {
208*1f903953SSHENG-YI HONG 				/* read returning 0 means disconnected. */
209*1f903953SSHENG-YI HONG 				if (len == 0 && sc->tty.is_socket)
210*1f903953SSHENG-YI HONG 					uart_tcp_disconnect(sc);
211*1f903953SSHENG-YI HONG 				break;
212*1f903953SSHENG-YI HONG 			}
213*1f903953SSHENG-YI HONG 
214d1c5d0cfSMark Johnston 			rxfifo_putchar(sc, ch);
215d1c5d0cfSMark Johnston 		}
216d1c5d0cfSMark Johnston 	}
217*1f903953SSHENG-YI HONG }
218d1c5d0cfSMark Johnston 
219d1c5d0cfSMark Johnston int
220d1c5d0cfSMark Johnston uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback)
221d1c5d0cfSMark Johnston {
222d1c5d0cfSMark Johnston 	if (loopback) {
223d1c5d0cfSMark Johnston 		return (rxfifo_putchar(sc, ch));
224d1c5d0cfSMark Johnston 	} else if (sc->tty.opened) {
225*1f903953SSHENG-YI HONG 		/* write returning -1 means disconnected. */
226*1f903953SSHENG-YI HONG 		if (ttywrite(&sc->tty, ch) == -1 && sc->tty.is_socket)
227*1f903953SSHENG-YI HONG 			uart_tcp_disconnect(sc);
228d1c5d0cfSMark Johnston 		return (0);
229d1c5d0cfSMark Johnston 	} else {
230d1c5d0cfSMark Johnston 		/* Drop on the floor. */
231d1c5d0cfSMark Johnston 		return (0);
232d1c5d0cfSMark Johnston 	}
233d1c5d0cfSMark Johnston }
234d1c5d0cfSMark Johnston 
235d1c5d0cfSMark Johnston void
236d1c5d0cfSMark Johnston uart_rxfifo_reset(struct uart_softc *sc, int size)
237d1c5d0cfSMark Johnston {
238d1c5d0cfSMark Johnston 	char flushbuf[32];
239d1c5d0cfSMark Johnston 	struct fifo *fifo;
240d1c5d0cfSMark Johnston 	ssize_t nread;
241d1c5d0cfSMark Johnston 	int error;
242d1c5d0cfSMark Johnston 
243d1c5d0cfSMark Johnston 	fifo = &sc->rxfifo;
244d1c5d0cfSMark Johnston 	bzero(fifo, sizeof(struct fifo));
245d1c5d0cfSMark Johnston 	fifo->size = size;
246d1c5d0cfSMark Johnston 
247d1c5d0cfSMark Johnston 	if (sc->tty.opened) {
248d1c5d0cfSMark Johnston 		/*
249d1c5d0cfSMark Johnston 		 * Flush any unread input from the tty buffer.
250d1c5d0cfSMark Johnston 		 */
251d1c5d0cfSMark Johnston 		while (1) {
252d1c5d0cfSMark Johnston 			nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf));
253d1c5d0cfSMark Johnston 			if (nread != sizeof(flushbuf))
254d1c5d0cfSMark Johnston 				break;
255d1c5d0cfSMark Johnston 		}
256d1c5d0cfSMark Johnston 
257d1c5d0cfSMark Johnston 		/*
258d1c5d0cfSMark Johnston 		 * Enable mevent to trigger when new characters are available
259d1c5d0cfSMark Johnston 		 * on the tty fd.
260d1c5d0cfSMark Johnston 		 */
261d1c5d0cfSMark Johnston 		error = mevent_enable(sc->mev);
262d1c5d0cfSMark Johnston 		assert(error == 0);
263d1c5d0cfSMark Johnston 	}
264d1c5d0cfSMark Johnston }
265d1c5d0cfSMark Johnston 
266d1c5d0cfSMark Johnston int
267d1c5d0cfSMark Johnston uart_rxfifo_size(struct uart_softc *sc __unused)
268d1c5d0cfSMark Johnston {
269d1c5d0cfSMark Johnston 	return (FIFOSZ);
270d1c5d0cfSMark Johnston }
271d1c5d0cfSMark Johnston 
272d1c5d0cfSMark Johnston #ifdef BHYVE_SNAPSHOT
273d1c5d0cfSMark Johnston int
274d1c5d0cfSMark Johnston uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta)
275d1c5d0cfSMark Johnston {
276d1c5d0cfSMark Johnston 	int ret;
277d1c5d0cfSMark Johnston 
278d1c5d0cfSMark Johnston 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done);
279d1c5d0cfSMark Johnston 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done);
280d1c5d0cfSMark Johnston 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done);
281d1c5d0cfSMark Johnston 	SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done);
282d1c5d0cfSMark Johnston 	SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf),
283d1c5d0cfSMark Johnston 	    meta, ret, done);
284d1c5d0cfSMark Johnston 
285d1c5d0cfSMark Johnston done:
286d1c5d0cfSMark Johnston 	return (ret);
287d1c5d0cfSMark Johnston }
288d1c5d0cfSMark Johnston #endif
289d1c5d0cfSMark Johnston 
290*1f903953SSHENG-YI HONG /*
291*1f903953SSHENG-YI HONG  * Listen on the TCP port, wait for a connection, then accept it.
292*1f903953SSHENG-YI HONG  */
293*1f903953SSHENG-YI HONG static void
294*1f903953SSHENG-YI HONG uart_tcp_listener(int fd, enum ev_type type __unused, void *arg)
295*1f903953SSHENG-YI HONG {
296*1f903953SSHENG-YI HONG 	const static char tcp_error_msg[] = "Socket already connected\n";
297*1f903953SSHENG-YI HONG 	struct uart_socket_softc *socket_softc = (struct uart_socket_softc *)
298*1f903953SSHENG-YI HONG 	    arg;
299*1f903953SSHENG-YI HONG 	struct uart_softc *sc = socket_softc->softc;
300*1f903953SSHENG-YI HONG 	int conn_fd;
301*1f903953SSHENG-YI HONG 
302*1f903953SSHENG-YI HONG 	conn_fd = accept(fd, NULL, NULL);
303*1f903953SSHENG-YI HONG 	if (conn_fd == -1)
304*1f903953SSHENG-YI HONG 		goto clean;
305*1f903953SSHENG-YI HONG 
306*1f903953SSHENG-YI HONG 	if (fcntl(conn_fd, F_SETFL, O_NONBLOCK) != 0)
307*1f903953SSHENG-YI HONG 		goto clean;
308*1f903953SSHENG-YI HONG 
309*1f903953SSHENG-YI HONG 	pthread_mutex_lock(&sc->mtx);
310*1f903953SSHENG-YI HONG 
311*1f903953SSHENG-YI HONG 	if (sc->tty.opened) {
312*1f903953SSHENG-YI HONG 		(void)send(conn_fd, tcp_error_msg, sizeof(tcp_error_msg), 0);
313*1f903953SSHENG-YI HONG 		pthread_mutex_unlock(&sc->mtx);
314*1f903953SSHENG-YI HONG 		goto clean;
315*1f903953SSHENG-YI HONG 	} else {
316*1f903953SSHENG-YI HONG 		sc->tty.rfd = sc->tty.wfd = conn_fd;
317*1f903953SSHENG-YI HONG 		sc->tty.opened = true;
318*1f903953SSHENG-YI HONG 		sc->mev = mevent_add(sc->tty.rfd, EVF_READ, socket_softc->drain,
319*1f903953SSHENG-YI HONG 		    socket_softc->arg);
320*1f903953SSHENG-YI HONG 	}
321*1f903953SSHENG-YI HONG 
322*1f903953SSHENG-YI HONG 	pthread_mutex_unlock(&sc->mtx);
323*1f903953SSHENG-YI HONG 	return;
324*1f903953SSHENG-YI HONG 
325*1f903953SSHENG-YI HONG clean:
326*1f903953SSHENG-YI HONG 	if (conn_fd != -1)
327*1f903953SSHENG-YI HONG 		close(conn_fd);
328*1f903953SSHENG-YI HONG }
329*1f903953SSHENG-YI HONG 
330*1f903953SSHENG-YI HONG /*
331*1f903953SSHENG-YI HONG  * When a connection-oriented protocol disconnects, this handler is used to
332*1f903953SSHENG-YI HONG  * clean it up.
333*1f903953SSHENG-YI HONG  *
334*1f903953SSHENG-YI HONG  * Note that this function is a helper, so the caller is responsible for
335*1f903953SSHENG-YI HONG  * locking the softc.
336*1f903953SSHENG-YI HONG  */
337*1f903953SSHENG-YI HONG static void
338*1f903953SSHENG-YI HONG uart_tcp_disconnect(struct uart_softc *sc)
339*1f903953SSHENG-YI HONG {
340*1f903953SSHENG-YI HONG 	mevent_delete_close(sc->mev);
341*1f903953SSHENG-YI HONG 	sc->mev = NULL;
342*1f903953SSHENG-YI HONG 	sc->tty.opened = false;
343*1f903953SSHENG-YI HONG 	sc->tty.rfd = sc->tty.wfd = -1;
344*1f903953SSHENG-YI HONG }
345*1f903953SSHENG-YI HONG 
346d1c5d0cfSMark Johnston static int
347d1c5d0cfSMark Johnston uart_stdio_backend(struct uart_softc *sc)
348d1c5d0cfSMark Johnston {
349d1c5d0cfSMark Johnston #ifndef WITHOUT_CAPSICUM
350d1c5d0cfSMark Johnston 	cap_rights_t rights;
351d1c5d0cfSMark Johnston 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
352d1c5d0cfSMark Johnston #endif
353d1c5d0cfSMark Johnston 
354d1c5d0cfSMark Johnston 	if (uart_stdio)
355d1c5d0cfSMark Johnston 		return (-1);
356d1c5d0cfSMark Johnston 
357d1c5d0cfSMark Johnston 	sc->tty.rfd = STDIN_FILENO;
358d1c5d0cfSMark Johnston 	sc->tty.wfd = STDOUT_FILENO;
359d1c5d0cfSMark Johnston 	sc->tty.opened = true;
360d1c5d0cfSMark Johnston 
361d1c5d0cfSMark Johnston 	if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0)
362d1c5d0cfSMark Johnston 		return (-1);
363d1c5d0cfSMark Johnston 	if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0)
364d1c5d0cfSMark Johnston 		return (-1);
365d1c5d0cfSMark Johnston 
366d1c5d0cfSMark Johnston #ifndef WITHOUT_CAPSICUM
367d1c5d0cfSMark Johnston 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ);
368d1c5d0cfSMark Johnston 	if (caph_rights_limit(sc->tty.rfd, &rights) == -1)
369d1c5d0cfSMark Johnston 		errx(EX_OSERR, "Unable to apply rights for sandbox");
370d1c5d0cfSMark Johnston 	if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1)
371d1c5d0cfSMark Johnston 		errx(EX_OSERR, "Unable to apply rights for sandbox");
372d1c5d0cfSMark Johnston #endif
373d1c5d0cfSMark Johnston 
374d1c5d0cfSMark Johnston 	uart_stdio = true;
375d1c5d0cfSMark Johnston 
376d1c5d0cfSMark Johnston 	return (0);
377d1c5d0cfSMark Johnston }
378d1c5d0cfSMark Johnston 
379d1c5d0cfSMark Johnston static int
380d1c5d0cfSMark Johnston uart_tty_backend(struct uart_softc *sc, const char *path)
381d1c5d0cfSMark Johnston {
382d1c5d0cfSMark Johnston #ifndef WITHOUT_CAPSICUM
383d1c5d0cfSMark Johnston 	cap_rights_t rights;
384d1c5d0cfSMark Johnston 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
385d1c5d0cfSMark Johnston #endif
386d1c5d0cfSMark Johnston 	int fd;
387d1c5d0cfSMark Johnston 
388d1c5d0cfSMark Johnston 	fd = open(path, O_RDWR | O_NONBLOCK);
389d1c5d0cfSMark Johnston 	if (fd < 0)
390d1c5d0cfSMark Johnston 		return (-1);
391d1c5d0cfSMark Johnston 
392d1c5d0cfSMark Johnston 	if (!isatty(fd)) {
393d1c5d0cfSMark Johnston 		close(fd);
394d1c5d0cfSMark Johnston 		return (-1);
395d1c5d0cfSMark Johnston 	}
396d1c5d0cfSMark Johnston 
397d1c5d0cfSMark Johnston 	sc->tty.rfd = sc->tty.wfd = fd;
398d1c5d0cfSMark Johnston 	sc->tty.opened = true;
399d1c5d0cfSMark Johnston 
400d1c5d0cfSMark Johnston #ifndef WITHOUT_CAPSICUM
401d1c5d0cfSMark Johnston 	cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE);
402d1c5d0cfSMark Johnston 	if (caph_rights_limit(fd, &rights) == -1)
403d1c5d0cfSMark Johnston 		errx(EX_OSERR, "Unable to apply rights for sandbox");
404d1c5d0cfSMark Johnston 	if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1)
405d1c5d0cfSMark Johnston 		errx(EX_OSERR, "Unable to apply rights for sandbox");
406d1c5d0cfSMark Johnston #endif
407d1c5d0cfSMark Johnston 
408d1c5d0cfSMark Johnston 	return (0);
409d1c5d0cfSMark Johnston }
410d1c5d0cfSMark Johnston 
411*1f903953SSHENG-YI HONG /*
412*1f903953SSHENG-YI HONG  * Listen on the address and add it to the kqueue.
413*1f903953SSHENG-YI HONG  *
414*1f903953SSHENG-YI HONG  * If a connection is established (e.g., the TCP handler is triggered),
415*1f903953SSHENG-YI HONG  * replace the handler with the connected handler.
416*1f903953SSHENG-YI HONG  */
417*1f903953SSHENG-YI HONG static int
418*1f903953SSHENG-YI HONG uart_tcp_backend(struct uart_softc *sc, const char *path,
419*1f903953SSHENG-YI HONG     void (*drain)(int, enum ev_type, void *), void *arg)
420*1f903953SSHENG-YI HONG {
421*1f903953SSHENG-YI HONG #ifndef WITHOUT_CAPSICUM
422*1f903953SSHENG-YI HONG 	cap_rights_t rights;
423*1f903953SSHENG-YI HONG 	cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
424*1f903953SSHENG-YI HONG #endif
425*1f903953SSHENG-YI HONG 	int bind_fd = -1;
426*1f903953SSHENG-YI HONG 	char addr[256], port[6];
427*1f903953SSHENG-YI HONG 	int domain;
428*1f903953SSHENG-YI HONG 	struct addrinfo hints, *src_addr = NULL;
429*1f903953SSHENG-YI HONG 	struct uart_socket_softc *socket_softc = NULL;
430*1f903953SSHENG-YI HONG 
431*1f903953SSHENG-YI HONG 	if (sscanf(path, "tcp=[%255[^]]]:%5s", addr, port) == 2) {
432*1f903953SSHENG-YI HONG 		domain = AF_INET6;
433*1f903953SSHENG-YI HONG 	} else if (sscanf(path, "tcp=%255[^:]:%5s", addr, port) == 2) {
434*1f903953SSHENG-YI HONG 		domain = AF_INET;
435*1f903953SSHENG-YI HONG 	} else {
436*1f903953SSHENG-YI HONG 		warnx("Invalid number of parameter");
437*1f903953SSHENG-YI HONG 		goto clean;
438*1f903953SSHENG-YI HONG 	}
439*1f903953SSHENG-YI HONG 
440*1f903953SSHENG-YI HONG 	bind_fd = socket(domain, SOCK_STREAM, 0);
441*1f903953SSHENG-YI HONG 	if (bind_fd < 0)
442*1f903953SSHENG-YI HONG 		goto clean;
443*1f903953SSHENG-YI HONG 
444*1f903953SSHENG-YI HONG 	memset(&hints, 0, sizeof(hints));
445*1f903953SSHENG-YI HONG 	hints.ai_family = domain;
446*1f903953SSHENG-YI HONG 	hints.ai_socktype = SOCK_STREAM;
447*1f903953SSHENG-YI HONG 	hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
448*1f903953SSHENG-YI HONG 
449*1f903953SSHENG-YI HONG 	if (getaddrinfo(addr, port, &hints, &src_addr) != 0) {
450*1f903953SSHENG-YI HONG 		warnx("Invalid address %s:%s", addr, port);
451*1f903953SSHENG-YI HONG 		goto clean;
452*1f903953SSHENG-YI HONG 	}
453*1f903953SSHENG-YI HONG 
454*1f903953SSHENG-YI HONG 	if (bind(bind_fd, src_addr->ai_addr, src_addr->ai_addrlen) == -1) {
455*1f903953SSHENG-YI HONG 		warn(
456*1f903953SSHENG-YI HONG 		    "bind(%s:%s)",
457*1f903953SSHENG-YI HONG 		    addr, port);
458*1f903953SSHENG-YI HONG 		goto clean;
459*1f903953SSHENG-YI HONG 	}
460*1f903953SSHENG-YI HONG 
461*1f903953SSHENG-YI HONG 	freeaddrinfo(src_addr);
462*1f903953SSHENG-YI HONG 	src_addr = NULL;
463*1f903953SSHENG-YI HONG 
464*1f903953SSHENG-YI HONG 	if (fcntl(bind_fd, F_SETFL, O_NONBLOCK) == -1)
465*1f903953SSHENG-YI HONG 		goto clean;
466*1f903953SSHENG-YI HONG 
467*1f903953SSHENG-YI HONG 	if (listen(bind_fd, 1) == -1) {
468*1f903953SSHENG-YI HONG 		warnx("listen(%s:%s)", addr, port);
469*1f903953SSHENG-YI HONG 		goto clean;
470*1f903953SSHENG-YI HONG 	}
471*1f903953SSHENG-YI HONG 
472*1f903953SSHENG-YI HONG 	/*
473*1f903953SSHENG-YI HONG 	 * Set the connection softc structure, which includes both the softc
474*1f903953SSHENG-YI HONG 	 * and the drain function provided by the frontend.
475*1f903953SSHENG-YI HONG 	 */
476*1f903953SSHENG-YI HONG 	if ((socket_softc = calloc(sizeof(struct uart_socket_softc), 1)) ==
477*1f903953SSHENG-YI HONG 	    NULL)
478*1f903953SSHENG-YI HONG 		goto clean;
479*1f903953SSHENG-YI HONG 
480*1f903953SSHENG-YI HONG 	sc->tty.is_socket = true;
481*1f903953SSHENG-YI HONG 
482*1f903953SSHENG-YI HONG 	socket_softc->softc = sc;
483*1f903953SSHENG-YI HONG 	socket_softc->drain = drain;
484*1f903953SSHENG-YI HONG 	socket_softc->arg = arg;
485*1f903953SSHENG-YI HONG 
486*1f903953SSHENG-YI HONG #ifndef WITHOUT_CAPSICUM
487*1f903953SSHENG-YI HONG 	cap_rights_init(&rights, CAP_EVENT, CAP_ACCEPT, CAP_RECV, CAP_SEND,
488*1f903953SSHENG-YI HONG 	    CAP_FCNTL, CAP_IOCTL);
489*1f903953SSHENG-YI HONG 	if (caph_rights_limit(bind_fd, &rights) == -1)
490*1f903953SSHENG-YI HONG 		errx(EX_OSERR, "Unable to apply rights for sandbox");
491*1f903953SSHENG-YI HONG 	if (caph_ioctls_limit(bind_fd, cmds, nitems(cmds)) == -1)
492*1f903953SSHENG-YI HONG 		errx(EX_OSERR, "Unable to apply ioctls for sandbox");
493*1f903953SSHENG-YI HONG 	if (caph_fcntls_limit(bind_fd, CAP_FCNTL_SETFL) == -1)
494*1f903953SSHENG-YI HONG 		errx(EX_OSERR, "Unable to apply fcntls for sandbox");
495*1f903953SSHENG-YI HONG #endif
496*1f903953SSHENG-YI HONG 
497*1f903953SSHENG-YI HONG 	if ((sc->mev = mevent_add(bind_fd, EVF_READ, uart_tcp_listener,
498*1f903953SSHENG-YI HONG 	    socket_softc)) == NULL)
499*1f903953SSHENG-YI HONG 		goto clean;
500*1f903953SSHENG-YI HONG 
501*1f903953SSHENG-YI HONG 	return (0);
502*1f903953SSHENG-YI HONG 
503*1f903953SSHENG-YI HONG clean:
504*1f903953SSHENG-YI HONG 	if (bind_fd != -1)
505*1f903953SSHENG-YI HONG 		close(bind_fd);
506*1f903953SSHENG-YI HONG 	if (socket_softc != NULL)
507*1f903953SSHENG-YI HONG 		free(socket_softc);
508*1f903953SSHENG-YI HONG 	if (src_addr)
509*1f903953SSHENG-YI HONG 		freeaddrinfo(src_addr);
510*1f903953SSHENG-YI HONG 	return (-1);
511*1f903953SSHENG-YI HONG }
512*1f903953SSHENG-YI HONG 
513d1c5d0cfSMark Johnston struct uart_softc *
514d1c5d0cfSMark Johnston uart_init(void)
515d1c5d0cfSMark Johnston {
516e10b9d66SSHENG-YI HONG 	struct uart_softc *sc = calloc(1, sizeof(struct uart_softc));
517e10b9d66SSHENG-YI HONG 	if (sc == NULL)
518e10b9d66SSHENG-YI HONG 		return (NULL);
519e10b9d66SSHENG-YI HONG 
520e10b9d66SSHENG-YI HONG 	pthread_mutex_init(&sc->mtx, NULL);
521e10b9d66SSHENG-YI HONG 
522e10b9d66SSHENG-YI HONG 	return (sc);
523d1c5d0cfSMark Johnston }
524d1c5d0cfSMark Johnston 
525d1c5d0cfSMark Johnston int
526d1c5d0cfSMark Johnston uart_tty_open(struct uart_softc *sc, const char *path,
527d1c5d0cfSMark Johnston     void (*drain)(int, enum ev_type, void *), void *arg)
528d1c5d0cfSMark Johnston {
529d1c5d0cfSMark Johnston 	int retval;
530d1c5d0cfSMark Johnston 
531d1c5d0cfSMark Johnston 	if (strcmp("stdio", path) == 0)
532d1c5d0cfSMark Johnston 		retval = uart_stdio_backend(sc);
533*1f903953SSHENG-YI HONG 	else if (strncmp("tcp", path, 3) == 0)
534*1f903953SSHENG-YI HONG 		retval = uart_tcp_backend(sc, path, drain, arg);
535d1c5d0cfSMark Johnston 	else
536d1c5d0cfSMark Johnston 		retval = uart_tty_backend(sc, path);
537*1f903953SSHENG-YI HONG 
538*1f903953SSHENG-YI HONG 	/*
539*1f903953SSHENG-YI HONG 	 * A connection-oriented protocol should wait for a connection,
540*1f903953SSHENG-YI HONG 	 * so it may not listen to anything during initialization.
541*1f903953SSHENG-YI HONG 	 */
542*1f903953SSHENG-YI HONG 	if (retval == 0 && !sc->tty.is_socket) {
543d1c5d0cfSMark Johnston 		ttyopen(&sc->tty);
544d1c5d0cfSMark Johnston 		sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg);
545d1c5d0cfSMark Johnston 		assert(sc->mev != NULL);
546d1c5d0cfSMark Johnston 	}
547d1c5d0cfSMark Johnston 
548d1c5d0cfSMark Johnston 	return (retval);
549d1c5d0cfSMark Johnston }
550e10b9d66SSHENG-YI HONG 
551e10b9d66SSHENG-YI HONG void
552e10b9d66SSHENG-YI HONG uart_softc_lock(struct uart_softc *sc)
553e10b9d66SSHENG-YI HONG {
554e10b9d66SSHENG-YI HONG 	pthread_mutex_lock(&sc->mtx);
555e10b9d66SSHENG-YI HONG }
556e10b9d66SSHENG-YI HONG 
557e10b9d66SSHENG-YI HONG void
558e10b9d66SSHENG-YI HONG uart_softc_unlock(struct uart_softc *sc)
559e10b9d66SSHENG-YI HONG {
560e10b9d66SSHENG-YI HONG 	pthread_mutex_unlock(&sc->mtx);
561e10b9d66SSHENG-YI HONG }
562