*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qapi/error.h"
#include "chardev/char.h"
#include "io/channel-file.h"
#include "qemu/qemu-print.h"
#include "chardev/char-io.h"
+#include "qom/object.h"
-typedef struct {
+struct PtyChardev {
Chardev parent;
QIOChannel *ioc;
int read_bytes;
int connected;
GSource *timer_src;
-} PtyChardev;
+};
+typedef struct PtyChardev PtyChardev;
-#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
+DECLARE_INSTANCE_CHECKER(PtyChardev, PTY_CHARDEV,
+ TYPE_CHARDEV_PTY)
static void pty_chr_state(Chardev *chr, int connected);
pfd.fd = fioc->fd;
pfd.events = G_IO_OUT;
pfd.revents = 0;
- do {
- rc = g_poll(&pfd, 1, 0);
- } while (rc == -1 && errno == EINTR);
+ rc = RETRY_ON_EINTR(g_poll(&pfd, 1, 0));
assert(rc >= 0);
if (pfd.revents & G_IO_HUP) {
static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
PtyChardev *s = PTY_CHARDEV(chr);
+ GPollFD pfd;
+ int rc;
- if (!s->connected) {
- return len;
+ if (s->connected) {
+ return io_channel_send(s->ioc, buf, len);
}
- return io_channel_send(s->ioc, buf, len);
+
+ /*
+ * The other side might already be re-connected, but the timer might
+ * not have fired yet. So let's check here whether we can write again:
+ */
+ pfd.fd = QIO_CHANNEL_FILE(s->ioc)->fd;
+ pfd.events = G_IO_OUT;
+ pfd.revents = 0;
+ rc = RETRY_ON_EINTR(g_poll(&pfd, 1, 0));
+ g_assert(rc >= 0);
+ if (!(pfd.revents & G_IO_HUP) && (pfd.revents & G_IO_OUT)) {
+ io_channel_send(s->ioc, buf, len);
+ }
+
+ return len;
}
static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
+#if defined HAVE_PTY_H
+# include <pty.h>
+#elif defined CONFIG_BSD
+# include <termios.h>
+# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+# include <libutil.h>
+# else
+# include <util.h>
+# endif
+#elif defined CONFIG_SOLARIS
+# include <termios.h>
+# include <stropts.h>
+#else
+# include <termios.h>
+#endif
+
+#ifdef __sun__
+
+#if !defined(HAVE_OPENPTY)
+/* Once illumos has openpty(), this is going to be removed. */
+static int openpty(int *amaster, int *aslave, char *name,
+ struct termios *termp, struct winsize *winp)
+{
+ const char *slave;
+ int mfd = -1, sfd = -1;
+
+ *amaster = *aslave = -1;
+
+ mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
+ if (mfd < 0) {
+ goto err;
+ }
+
+ if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) {
+ goto err;
+ }
+
+ if ((slave = ptsname(mfd)) == NULL) {
+ goto err;
+ }
+
+ if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) {
+ goto err;
+ }
+
+ if (ioctl(sfd, I_PUSH, "ptem") == -1 ||
+ (termp != NULL && tcgetattr(sfd, termp) < 0)) {
+ goto err;
+ }
+
+ *amaster = mfd;
+ *aslave = sfd;
+
+ if (winp) {
+ ioctl(sfd, TIOCSWINSZ, winp);
+ }
+
+ return 0;
+
+err:
+ if (sfd != -1) {
+ close(sfd);
+ }
+ close(mfd);
+ return -1;
+}
+#endif
+
+static void cfmakeraw (struct termios *termios_p)
+{
+ termios_p->c_iflag &=
+ ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+ termios_p->c_oflag &= ~OPOST;
+ termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ termios_p->c_cflag &= ~(CSIZE | PARENB);
+ termios_p->c_cflag |= CS8;
+
+ termios_p->c_cc[VMIN] = 0;
+ termios_p->c_cc[VTIME] = 0;
+}
+#endif
+
+/* like openpty() but also makes it raw; return master fd */
+static int qemu_openpty_raw(int *aslave, char *pty_name)
+{
+ int amaster;
+ struct termios tty;
+#if defined(__OpenBSD__) || defined(__DragonFly__)
+ char pty_buf[PATH_MAX];
+#define q_ptsname(x) pty_buf
+#else
+ char *pty_buf = NULL;
+#define q_ptsname(x) ptsname(x)
+#endif
+
+ if (openpty(&amaster, aslave, pty_buf, NULL, NULL) < 0) {
+ return -1;
+ }
+
+ /* Set raw attributes on the pty. */
+ tcgetattr(*aslave, &tty);
+ cfmakeraw(&tty);
+ tcsetattr(*aslave, TCSAFLUSH, &tty);
+
+ if (pty_name) {
+ strcpy(pty_name, q_ptsname(amaster));
+ }
+
+ return amaster;
+}
+
static void char_pty_open(Chardev *chr,
ChardevBackend *backend,
bool *be_opened,
}
close(slave_fd);
- qemu_set_nonblock(master_fd);
+ if (!g_unix_set_fd_nonblocking(master_fd, true, NULL)) {
+ error_setg_errno(errp, errno, "Failed to set FD nonblocking");
+ return;
+ }
chr->filename = g_strdup_printf("pty:%s", pty_name);
qemu_printf("char device redirected to %s (label %s)\n",
s = PTY_CHARDEV(chr);
s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
name = g_strdup_printf("chardev-pty-%s", chr->label);
- qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
+ qio_channel_set_name(s->ioc, name);
g_free(name);
s->timer_src = NULL;
*be_opened = false;