*.tp
*.vr
*.d
--
- *.[oa]
++*.o
+*~
#######################################################################
# BLOCK_OBJS is code used by both qemu system emulation and qemu-img
- BLOCK_OBJS=cutils.o osdep.o
-BLOCK_OBJS=cutils.o qemu-malloc.o
++BLOCK_OBJS=cutils.o osdep.o qemu-malloc.o
BLOCK_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o
BLOCK_OBJS+=block-dmg.o block-bochs.o block-vpc.o block-vvfat.o
- BLOCK_OBJS+=block-qcow2.o block-parallels.o
+ BLOCK_OBJS+=block-qcow2.o block-parallels.o block-nbd.o
+ BLOCK_OBJS+=nbd.o block.o aio.o
+
+ ifdef CONFIG_WIN32
+ BLOCK_OBJS += block-raw-win32.o
+ else
+ BLOCK_OBJS += block-raw-posix.o
+ endif
######################################################################
# libqemu_common.a: Target independent part of system emulation. The
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
+ qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o osdep.o $(BLOCK_OBJS)
+ $(CC) $(LDFLAGS) -o $@ $^ -lz $(LIBS)
+
# dyngen host tool
-dyngen$(EXESUF): dyngen.c
+dyngen$(EXESUF): dyngen.c osdep.o
$(HOST_CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
clean:
size_t i;
char *u = r + sizeof (qemu_prefix) - 1;
- strcpy (r, qemu_prefix);
- strcat (r, s);
+ pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix);
+ pstrcat (r, len + sizeof (qemu_prefix), s);
for (i = 0; i < len; ++i) {
- u[i] = toupper (u[i]);
+ u[i] = CTYPE(toupper, u[i]);
}
}
return r;
/* copy while upper-casing, including trailing zero */
for (i = 0; i <= preflen; ++i) {
- optname[i + sizeof (qemu_prefix) - 1] = toupper (prefix[i]);
+ optname[i + sizeof (qemu_prefix) - 1] = CTYPE(toupper, prefix[i]);
}
- strcat (optname, "_");
- strcat (optname, opt->name);
+ pstrcat (optname, optlen, "_");
+ pstrcat (optname, optlen, opt->name);
def = 1;
switch (opt->tag) {
return 0;
}
--static void cow_flush(BlockDriverState *bs)
++static int cow_flush(BlockDriverState *bs)
{
BDRVCowState *s = bs->opaque;
-- fsync(s->fd);
++ if (fsync(s->fd))
++ return errno;
++ return 0;
}
BlockDriver bdrv_cow = {
.bdrv_aio_read = raw_aio_read,
.bdrv_aio_write = raw_aio_write,
.bdrv_aio_cancel = raw_aio_cancel,
++ .bdrv_aio_flush = raw_aio_flush,
.aiocb_size = sizeof(RawAIOCB),
- .protocol_name = "file",
+ #endif
.bdrv_pread = raw_pread,
.bdrv_pwrite = raw_pwrite,
.bdrv_truncate = raw_truncate,
.bdrv_aio_read = raw_aio_read,
.bdrv_aio_write = raw_aio_write,
.bdrv_aio_cancel = raw_aio_cancel,
+ .bdrv_aio_flush = raw_aio_flush,
.aiocb_size = sizeof(RawAIOCB),
+ #endif
.bdrv_pread = raw_pread,
.bdrv_pwrite = raw_pwrite,
.bdrv_getlength = raw_getlength,
/* XXX: if more than one async I/O it is not correct */
CancelIo(s->hfile);
qemu_aio_release(acb);
- #endif
}
- #endif /* #if 0 */
+ #endif /* #if WIN32_AIO */
-static void raw_flush(BlockDriverState *bs)
+static int raw_flush(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
- FlushFileBuffers(s->hfile);
+ int ret;
+ ret = FlushFileBuffers(s->hfile);
+ if (ret)
+ return -EIO;
+ return 0;
}
static void raw_close(BlockDriverState *bs)
if (parent_open)
// Parent must be opened as RO.
flags = BDRV_O_RDONLY;
- fprintf(stderr, "(VMDK) image open: flags=0x%x filename=%s\n", flags, bs->filename);
- ret = bdrv_file_open(&s->hd, filename, flags);
+ ret = bdrv_file_open(&s->hd, filename, flags | BDRV_O_EXTENDABLE);
if (ret < 0)
return ret;
if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic))
qemu_free(s->l1_table);
qemu_free(s->l2_cache);
- bdrv_delete(s->hd);
// try to close parent image, if exist
vmdk_parent_close(s->hd);
+ bdrv_delete(s->hd);
}
-static void vmdk_flush(BlockDriverState *bs)
+static int vmdk_flush(BlockDriverState *bs)
{
BDRVVmdkState *s = bs->opaque;
- bdrv_flush(s->hd);
+ return bdrv_flush(s->hd);
}
BlockDriver bdrv_vmdk = {
}
}
-
+
+static int bdrv_rw_badreq_sectors(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors)
+{
+ return (
+ nb_sectors < 0 ||
+ nb_sectors > bs->total_sectors ||
+ sector_num > bs->total_sectors - nb_sectors
+ ) && !bs->extendable;
+}
+
+static int bdrv_rw_badreq_bytes(BlockDriverState *bs,
+ int64_t offset, int count)
+{
+ int64_t size = bs->total_sectors << SECTOR_BITS;
+ return (
+ count < 0 ||
+ count > size ||
+ offset > size - count
+ ) && !bs->extendable;
-
+}
++
static void bdrv_register(BlockDriver *bdrv)
{
if (!bdrv->bdrv_aio_read) {
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_pread)
- return bdrv_pread_em(bs, offset, buf1, count1);
+ if (bdrv_rw_badreq_bytes(bs, offset, count1))
+ return -EDOM;
+ if (!drv->bdrv_pread)
+ return bdrv_pread_em(bs, offset, buf1, count1);
return drv->bdrv_pread(bs, offset, buf1, count1);
}
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_pwrite)
- return bdrv_pwrite_em(bs, offset, buf1, count1);
+ if (bdrv_rw_badreq_bytes(bs, offset, count1))
+ return -EDOM;
+ if (!drv->bdrv_pwrite)
+ return bdrv_pwrite_em(bs, offset, buf1, count1);
return drv->bdrv_pwrite(bs, offset, buf1, count1);
}
return bs->device_name;
}
-void bdrv_flush(BlockDriverState *bs)
+int bdrv_flush(BlockDriverState *bs)
{
- if (bs->drv->bdrv_flush)
+ int ret = 0;
- bs->drv->bdrv_flush(bs);
- if (bs->backing_hd)
- bdrv_flush(bs->backing_hd);
+ if (bs->drv->bdrv_flush)
+ ret = bs->drv->bdrv_flush(bs);
+ if (!ret && bs->backing_hd)
+ ret = bdrv_flush(bs->backing_hd);
+ return ret;
}
- #ifndef QEMU_IMG
-void bdrv_flush_all(void)
++int bdrv_flush_all(void)
+ {
+ BlockDriverState *bs;
++ int ret = 0, ret1;
+
+ for (bs = bdrv_first; bs != NULL; bs = bs->next)
+ if (bs->drv && !bdrv_is_read_only(bs) &&
- (!bdrv_is_removable(bs) || bdrv_is_inserted(bs)))
- bdrv_flush(bs);
++ (!bdrv_is_removable(bs) || bdrv_is_inserted(bs))) {
++ ret1 = bdrv_flush(bs);
++ if (ret1) ret = ret1;
++ }
++
++ return ret;
+ }
+
+ /*
+ * Returns true iff the specified sector is present in the disk image. Drivers
+ * not implementing the functionality are assumed to not support backing files,
+ * hence all their sectors are reported as allocated.
+ *
+ * 'pnum' is set to the number of sectors (including and immediately following
+ * the specified sector) that are known to be in the same
+ * allocated/unallocated state.
+ *
+ * 'nb_sectors' is the max value 'pnum' should be set to.
+ */
+ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
+ int *pnum)
+ {
+ int64_t n;
+ if (!bs->drv->bdrv_is_allocated) {
+ if (sector_num >= bs->total_sectors) {
+ *pnum = 0;
+ return 0;
+ }
+ n = bs->total_sectors - sector_num;
+ *pnum = (n < nb_sectors) ? (n) : (nb_sectors);
+ return 1;
+ }
+ return bs->drv->bdrv_is_allocated(bs, sector_num, nb_sectors, pnum);
+ }
+
void bdrv_info(void)
{
BlockDriverState *bs;
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
- if (!drv->bdrv_write_compressed)
- return -ENOTSUP;
+ if (bdrv_rw_badreq_sectors(bs, sector_num, nb_sectors))
+ return -EDOM;
+ if (!drv->bdrv_write_compressed)
+ return -ENOTSUP;
return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
}
drv->bdrv_aio_cancel(acb);
}
+BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (!drv)
+ return NULL;
+
+ return drv->bdrv_aio_flush(bs, cb, opaque);
+}
-
/**************************************************************/
/* async block device emulation */
return async_ret;
}
++static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
++ BlockDriverCompletionFunc *cb, void *opaque)
++{
++ BlockDriverAIOCBSync *acb;
++ int ret;
++
++ acb = qemu_aio_get(bs, cb, opaque);
++ if (!acb->bh)
++ acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
++ ret = bdrv_flush(bs);
++ acb->ret = ret;
++ qemu_bh_schedule(acb->bh);
++ return &acb->common;
++}
++
void bdrv_init(void)
{
bdrv_register(&bdrv_raw);
it (default for
bdrv_file_open()) */
#define BDRV_O_DIRECT 0x0020
+#define BDRV_O_EXTENDABLE 0x0080 /* allow writes out of original size range;
+ only effective for some drivers */
- #ifndef QEMU_IMG
void bdrv_info(void);
void bdrv_info_stats(void);
- #endif
void bdrv_init(void);
BlockDriver *bdrv_find_format(const char *format_name);
BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque);
+BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque);
void bdrv_aio_cancel(BlockDriverAIOCB *acb);
- void qemu_aio_init(void);
- void qemu_aio_poll(void);
- void qemu_aio_flush(void);
- void qemu_aio_wait_start(void);
- void qemu_aio_wait(void);
- void qemu_aio_wait_end(void);
-
int qemu_key_check(BlockDriverState *bs, const char *name);
/* Ensure contents are flushed to disk. */
-void bdrv_flush(BlockDriverState *bs);
-void bdrv_flush_all(void);
+int bdrv_flush(BlockDriverState *bs);
++int bdrv_flush_all(void);
+
+ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
+ int *pnum);
#define BDRV_TYPE_HD 0
#define BDRV_TYPE_CDROM 1
esac
done
- if [ "$bsd" = "yes" ] ; then
- AIOLIBS="-lrt"
- elif [ "$darwin" = "yes" -o "$mingw32" = "yes" ] ; then
- AIOLIBS=
- else
- # Some Linux architectures (e.g. s390) don't imply -lpthread automatically.
- AIOLIBS="-lrt -lpthread"
- fi
+
# default flags for all hosts
- CFLAGS="$CFLAGS -Wall -O2 -g -fno-strict-aliasing"
+ CFLAGS="$CFLAGS -O2 -g -fno-strict-aliasing"
+ CFLAGS="$CFLAGS -Wall -Wundef -Wendif-labels -Wwrite-strings"
LDFLAGS="$LDFLAGS -g"
if test "$werror" = "yes" ; then
CFLAGS="$CFLAGS -Werror"
#define ASC_ILLEGAL_OPCODE 0x20
#define ASC_LOGICAL_BLOCK_OOR 0x21
#define ASC_INV_FIELD_IN_CMD_PACKET 0x24
+#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28
+ #define ASC_INCOMPATIBLE_FORMAT 0x30
#define ASC_MEDIUM_NOT_PRESENT 0x3a
#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39
static inline void ide_set_irq(IDEState *s)
{
BMDMAState *bm = s->bmdma;
- if (!s->bs) return; /* yikes */
++ if (!s->bs) return; /* ouch! (see ide_flush_cb) */
if (!(s->cmd & IDE_CMD_DISABLE_IRQ)) {
if (bm) {
bm->status |= BM_STATUS_INT;
return;
}
- if (!s->bs) return; /* yikes */
++ if (!s->bs) return; /* ouch! (see ide_flush_cb) */
+
n = s->io_buffer_size >> 9;
sector_num = ide_get_sector(s);
if (n > 0) {
return;
}
- if (!s->bs) return; /* yikes */
++ if (!s->bs) return; /* ouch! (see ide_flush_cb) */
+
n = s->io_buffer_size >> 9;
sector_num = ide_get_sector(s);
if (n > 0) {
ide_dma_start(s, ide_write_dma_cb);
}
- if (!s->bs) return; /* yikes */
+static void ide_device_utterly_broken(IDEState *s) {
+ s->status |= BUSY_STAT;
+ s->bs = NULL;
+ /* This prevents all future commands from working. All of the
+ * asynchronous callbacks (and ide_set_irq, as a safety measure)
+ * check to see whether this has happened and bail if so.
+ */
+}
+
+static void ide_flush_cb(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+
- s->status = READY_STAT;
++ if (!s->bs) return; /* ouch! (see ide_flush_cb) */
+
+ if (ret) {
+ /* We are completely doomed. The IDE spec does not permit us
+ * to return an error from a flush except via a protocol which
+ * requires us to say where the error is and which
+ * contemplates the guest repeating the flush attempt to
+ * attempt flush the remaining data. We can't support that
+ * because f(data)sync (which is what the block drivers use
+ * eventually) doesn't report the necessary information or
+ * give us the necessary control. So we make the disk vanish.
+ */
+ ide_device_utterly_broken(s);
+ return;
+ }
+ else
++ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+}
+
static void ide_atapi_cmd_ok(IDEState *s)
{
s->error = 0;
IDEState *s = bm->ide_if;
int data_offset, n;
- if (!s->bs) return; /* yikes */
++ if (!s->bs) return; /* ouch! (see ide_flush_cb) */
+
if (ret < 0) {
ide_atapi_io_error(s, ret);
goto eot;
IDEState *s = opaque;
uint64_t nb_sectors;
- if (!s->bs) return; /* yikes */
++ if (!s->bs) return; /* ouch! (see ide_flush_cb) */
+
/* XXX: send interrupt too */
bdrv_get_geometry(s->bs, &nb_sectors);
s->nb_sectors = nb_sectors;
printf("ide: CMD=%02x\n", val);
#endif
s = ide_if->cur_drive;
- /* ignore commands to non existant device */
- /* ignore commands to non existant slave */
- if (s != ide_if && !s->bs)
++ /* ignore commands to non existant device */
+ if (!s->bs)
break;
+ /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */
+ if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET)
+ break;
+
switch(val) {
case WIN_IDENTIFY:
if (s->bs && !s->is_cdrom) {
#include <malloc.h>
#endif
+#define define_readwrite(rw, cnst) \
+ssize_t qemu_##rw(int fd, cnst void *buf, size_t count) { \
+ ssize_t got, done; \
+ done = 0; \
+ while (count>0) { \
+ got = rw(fd, buf, count); \
+ if (got == 0) { errno = 0; return done; } \
+ if (got < 0) { \
+ if (errno==EINTR) continue; \
+ return done ? done : -1; \
+ } \
+ assert(got <= count); \
+ done += got; \
+ count -= got; \
+ buf = (cnst char*)buf + got; \
+ } \
+ return done; \
+} \
+int qemu_##rw##_ok(int fd, cnst void *buf, size_t count) { \
+ ssize_t got; \
+ got = qemu_##rw(fd, buf, count); \
+ if (got == count) return 1; \
+ return -1; \
+}
+define_readwrite(read, /* empty */)
+define_readwrite(write, const)
+
+ #include "qemu_socket.h"
+
#if defined(_WIN32)
void *qemu_memalign(size_t alignment, size_t size)
{
int stristart(const char *str, const char *val, const char **ptr);
time_t mktimegm(struct tm *tm);
+#define CTYPE(isfoobar,argumentchar) (isfoobar((unsigned char)(argumentchar)))
+ /* One must not pass a plain `char' to isupper, toupper, et al. If
+ * it has the top bit set (ie, is negative if your chars are
+ * signed), undefined behaviour results. The <ctype.h> functions
+ * are defined to take the value of an unsigned char, as an int.
+ * So use this macro. You may pass toupper et al for isfoobar.
+ * Do not pass EOF as a character to this macro. If you might have
+ * EOF then you ought to have it in an int representing an unsigned
+ * char, which is safe for the ctype macros directly. Or test separately.
+ * Obviously don't use this for floating point things like isnan! */
+
void *qemu_malloc(size_t size);
+ void *qemu_realloc(void *ptr, size_t size);
void *qemu_mallocz(size_t size);
void qemu_free(void *ptr);
char *qemu_strdup(const char *str);
#define EINTR WSAEINTR
#define EINPROGRESS WSAEINPROGRESS
+#ifndef NO_UNIX_SOCKETS
+#define NO_UNIX_SOCKETS 1
+#endif
+
+ int inet_aton(const char *cp, struct in_addr *ia);
+
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
-
+#ifndef NO_UNIX_SOCKETS
+ #include <arpa/inet.h>
+ #include <netdb.h>
#include <sys/un.h>
+#endif
#define socket_error() errno
#define closesocket(s) close(s)
#if defined(__linux__)
#include <dirent.h>
#include <sys/ioctl.h>
+/* Some versions of usbdevice_fs.h need __user to be defined for them. */
+/* This may (harmlessly) conflict with a definition in linux/compiler.h. */
+#define __user
+ #include <signal.h>
+
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
- #include <signal.h>
+ #include "hw/usb.h"
/* We redefine it to avoid version problems */
struct usb_ctrltransfer {
product_id, product_name, speed);
}
the_end:
- fclose(f);
+ if (f)
+ fclose(f);
+ return ret;
+ }
+
+ /*
+ * Read sys file-system device file
+ *
+ * @line address of buffer to put file contents in
+ * @line_size size of line
+ * @device_file path to device file (printf format string)
+ * @device_name device being opened (inserted into device_file)
+ *
+ * @return 0 failed, 1 succeeded ('line' contains data)
+ */
+ static int usb_host_read_file(char *line, size_t line_size, const char *device_file, const char *device_name)
+ {
+ FILE *f;
+ int ret = 0;
+ char filename[PATH_MAX];
+
+ snprintf(filename, PATH_MAX, device_file, device_name);
+ f = fopen(filename, "r");
+ if (f) {
- fgets(line, line_size, f);
++ if (fgets(line, line_size, f) != NULL &&
++ !ferror(f))
++ ret = 1;
+ fclose(f);
- ret = 1;
+ } else {
+ term_printf("husb: could not open %s\n", filename);
+ }
+
+ return ret;
+ }
+
+ /*
+ * Use /sys/bus/usb/devices/ directory to determine host's USB
+ * devices.
+ *
+ * This code is based on Robert Schiele's original patches posted to
+ * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
+ */
+ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
+ {
+ DIR *dir = 0;
+ char line[1024];
+ int bus_num, addr, speed, class_id, product_id, vendor_id;
+ int ret = 0;
+ char product_name[512];
+ struct dirent *de;
+
+ dir = opendir(USBSYSBUS_PATH "/devices");
+ if (!dir) {
+ perror("husb: cannot open devices directory");
+ goto the_end;
+ }
+
+ while ((de = readdir(dir))) {
+ if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
+ char *tmpstr = de->d_name;
+ if (!strncmp(de->d_name, "usb", 3))
+ tmpstr += 3;
+ bus_num = atoi(tmpstr);
+
+ if (!usb_host_read_file(line, sizeof(line), USBSYSBUS_PATH "/devices/%s/devnum", de->d_name))
+ goto the_end;
+ if (sscanf(line, "%d", &addr) != 1)
+ goto the_end;
+
+ if (!usb_host_read_file(line, sizeof(line), USBSYSBUS_PATH "/devices/%s/bDeviceClass", de->d_name))
+ goto the_end;
+ if (sscanf(line, "%x", &class_id) != 1)
+ goto the_end;
+
+ if (!usb_host_read_file(line, sizeof(line), USBSYSBUS_PATH "/devices/%s/idVendor", de->d_name))
+ goto the_end;
+ if (sscanf(line, "%x", &vendor_id) != 1)
+ goto the_end;
+
+ if (!usb_host_read_file(line, sizeof(line), USBSYSBUS_PATH "/devices/%s/idProduct", de->d_name))
+ goto the_end;
+ if (sscanf(line, "%x", &product_id) != 1)
+ goto the_end;
+
+ if (!usb_host_read_file(line, sizeof(line), USBSYSBUS_PATH "/devices/%s/product", de->d_name)) {
+ *product_name = 0;
+ } else {
+ if (strlen(line) > 0)
+ line[strlen(line) - 1] = '\0';
+ pstrcpy(product_name, sizeof(product_name), line);
+ }
+
+ if (!usb_host_read_file(line, sizeof(line), USBSYSBUS_PATH "/devices/%s/speed", de->d_name))
+ goto the_end;
+ if (!strcmp(line, "480\n"))
+ speed = USB_SPEED_HIGH;
+ else if (!strcmp(line, "1.5\n"))
+ speed = USB_SPEED_LOW;
+ else
+ speed = USB_SPEED_FULL;
+
+ ret = func(opaque, bus_num, addr, class_id, vendor_id,
+ product_id, product_name, speed);
+ if (ret)
+ goto the_end;
+ }
+ }
+ the_end:
+ if (dir)
+ closedir(dir);
return ret;
}
return len1 - len;
}
- void socket_set_nonblock(int fd)
- {
- unsigned long opt = 1;
- ioctlsocket(fd, FIONBIO, &opt);
- }
-
#else
-static int unix_write(int fd, const uint8_t *buf, int len1)
-{
- int ret, len;
-
- len = len1;
- while (len > 0) {
- ret = write(fd, buf, len);
- if (ret < 0) {
- if (errno != EINTR && errno != EAGAIN)
- return -1;
- } else if (ret == 0) {
- break;
- } else {
- buf += ret;
- len -= ret;
- }
- }
- return len1 - len;
-}
-
static inline int send_all(int fd, const uint8_t *buf, int len1)
{
- return unix_write(fd, buf, len1);
+ return qemu_write(fd, buf, len1);
}
- void socket_set_nonblock(int fd)
- {
- int f;
- f = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, f | O_NONBLOCK);
- }
+
#endif /* !_WIN32 */
#ifndef _WIN32
}
#endif
- #if defined(__linux__) || defined(__sun__)
+ #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+
+ typedef struct {
+ int fd;
+ int connected;
+ int polling;
+ int read_bytes;
+ QEMUTimer *timer;
+ } PtyCharDriver;
+
+ static void pty_chr_update_read_handler(CharDriverState *chr);
+ static void pty_chr_state(CharDriverState *chr, int connected);
+
+ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+ {
+ PtyCharDriver *s = chr->opaque;
+
+ if (!s->connected) {
+ /* guest sends data, check for (re-)connect */
+ pty_chr_update_read_handler(chr);
+ return 0;
+ }
- return unix_write(s->fd, buf, len);
++ return qemu_write(s->fd, buf, len);
+ }
+
+ static int pty_chr_read_poll(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ s->read_bytes = qemu_chr_can_read(chr);
+ return s->read_bytes;
+ }
+
+ static void pty_chr_read(void *opaque)
+ {
+ CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+ int size, len;
+ uint8_t buf[1024];
+
+ len = sizeof(buf);
+ if (len > s->read_bytes)
+ len = s->read_bytes;
+ if (len == 0)
+ return;
+ size = read(s->fd, buf, len);
+ if ((size == -1 && errno == EIO) ||
+ (size == 0)) {
+ pty_chr_state(chr, 0);
+ return;
+ }
+ if (size > 0) {
+ pty_chr_state(chr, 1);
+ qemu_chr_read(chr, buf, size);
+ }
+ }
+
+ static void pty_chr_update_read_handler(CharDriverState *chr)
+ {
+ PtyCharDriver *s = chr->opaque;
+
+ qemu_set_fd_handler2(s->fd, pty_chr_read_poll,
+ pty_chr_read, NULL, chr);
+ s->polling = 1;
+ /*
+ * Short timeout here: just need wait long enougth that qemu makes
+ * it through the poll loop once. When reconnected we want a
+ * short timeout so we notice it almost instantly. Otherwise
+ * read() gives us -EIO instantly, making pty_chr_state() reset the
+ * timeout to the normal (much longer) poll interval before the
+ * timer triggers.
+ */
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10);
+ }
+
+ static void pty_chr_state(CharDriverState *chr, int connected)
+ {
+ PtyCharDriver *s = chr->opaque;
+
+ if (!connected) {
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ s->connected = 0;
+ s->polling = 0;
+ /* (re-)connect poll interval for idle guests: once per second.
+ * We check more frequently in case the guests sends data to
+ * the virtual device linked to our pty. */
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
+ } else {
+ if (!s->connected)
+ qemu_chr_reset(chr);
+ s->connected = 1;
+ }
+ }
+
+ static void pty_chr_timer(void *opaque)
+ {
+ struct CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ if (s->connected)
+ return;
+ if (s->polling) {
+ /* If we arrive here without polling being cleared due
+ * read returning -EIO, then we are (re-)connected */
+ pty_chr_state(chr, 1);
+ return;
+ }
+
+ /* Next poll ... */
+ pty_chr_update_read_handler(chr);
+ }
+
+ static void pty_chr_close(struct CharDriverState *chr)
+ {
+ PtyCharDriver *s = chr->opaque;
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ close(s->fd);
+ qemu_free(s);
+ }
+
static CharDriverState *qemu_chr_open_pty(void)
{
+ CharDriverState *chr;
+ PtyCharDriver *s;
struct termios tty;
- int master_fd, slave_fd;
+ int slave_fd;
+ #if defined(__OpenBSD__)
+ char pty_name[PATH_MAX];
+ #define q_ptsname(x) pty_name
+ #else
+ char *pty_name = NULL;
+ #define q_ptsname(x) ptsname(x)
+ #endif
- if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) < 0) {
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ if (!chr)
+ return NULL;
+ s = qemu_mallocz(sizeof(PtyCharDriver));
+ if (!s) {
+ qemu_free(chr);
+ return NULL;
+ }
+
+ if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) {
return NULL;
}