From: Ian Jackson Date: Thu, 16 Oct 2008 16:37:38 +0000 (+0100) Subject: Merge branch 'upstream' into qemu X-Git-Tag: t.master-before-merge~71^2~1 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=76996ac46291a4ba6ede82b7ab64a5ae42059697;p=qemu-xen-4.2-testing.git Merge branch 'upstream' into qemu Conflicts: .gitignore Makefile audio/sys-queue.h block-raw-posix.c block.c block.h configure hw/ide.c hw/usb-uhci.c osdep.c osdep.h qemu_socket.h usb-linux.c vl.c --- 76996ac46291a4ba6ede82b7ab64a5ae42059697 diff --cc .gitignore index d47fcc4c1,e70ebab45..76a2e648e --- a/.gitignore +++ b/.gitignore @@@ -26,6 -30,4 +30,5 @@@ qemu-nbd. *.tp *.vr *.d -- - *.[oa] ++*.o +*~ diff --cc Makefile index 103aaf84c,36b36cd26..269338724 --- a/Makefile +++ b/Makefile @@@ -34,10 -47,17 +47,17 @@@ recurse-all: $(SUBDIR_RULES ####################################################################### # 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 @@@ -156,11 -188,11 +188,11 @@@ qemu-img$(EXESUF): qemu-img.o qemu-tool %.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: diff --cc audio/audio.c index 0273ac1b5,b3ac10e12..cca77dd35 --- a/audio/audio.c +++ b/audio/audio.c @@@ -231,11 -211,11 +211,11 @@@ static char *audio_alloc_prefix (const 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; @@@ -489,10 -470,10 +470,10 @@@ static void audio_process_options (cons /* 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) { diff --cc block-cow.c index 6c0d65771,9e7b64602..777d8a581 --- a/block-cow.c +++ b/block-cow.c @@@ -246,10 -246,10 +246,12 @@@ static int cow_create(const char *filen 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 = { diff --cc block-raw-posix.c index c9f01255b,83a358cd4..92b4038e7 --- a/block-raw-posix.c +++ b/block-raw-posix.c @@@ -602,8 -894,8 +910,9 @@@ BlockDriver bdrv_raw = .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, @@@ -957,8 -1249,8 +1268,9 @@@ BlockDriver bdrv_host_device = .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, diff --cc block-raw-win32.c index c94ad0e29,fd4a9e3a4..2bcebb0b4 --- a/block-raw-win32.c +++ b/block-raw-win32.c @@@ -265,18 -259,13 +259,17 @@@ static void raw_aio_cancel(BlockDriverA /* 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) diff --cc block-vmdk.c index e49db6b91,c49b67193..f3b54fd24 --- a/block-vmdk.c +++ b/block-vmdk.c @@@ -376,9 -377,8 +377,8 @@@ static int vmdk_open(BlockDriverState * 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)) @@@ -810,15 -810,15 +812,15 @@@ static void vmdk_close(BlockDriverStat 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 = { diff --cc block.c index fcaf70323,3eb452616..95dc4fab8 --- a/block.c +++ b/block.c @@@ -125,28 -122,7 +124,28 @@@ void path_combine(char *dest, int dest_ } } + +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) { @@@ -716,10 -690,8 +727,10 @@@ int bdrv_pread(BlockDriverState *bs, in 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); } @@@ -733,10 -705,8 +744,10 @@@ int bdrv_pwrite(BlockDriverState *bs, i 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); } @@@ -917,17 -887,51 +928,58 @@@ const char *bdrv_get_device_name(BlockD return bs->device_name; } -void bdrv_flush(BlockDriverState *bs) +int bdrv_flush(BlockDriverState *bs) { + int ret = 0; - if (bs->drv->bdrv_flush) + if (bs->drv->bdrv_flush) - bs->drv->bdrv_flush(bs); - if (bs->backing_hd) - bdrv_flush(bs->backing_hd); + 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; @@@ -1003,10 -1006,8 +1054,10 @@@ int bdrv_write_compressed(BlockDriverSt 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); } @@@ -1210,18 -1207,7 +1261,17 @@@ void bdrv_aio_cancel(BlockDriverAIOCB * 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 */ @@@ -1372,7 -1303,6 +1367,21 @@@ static int bdrv_write_em(BlockDriverSta 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); diff --cc block.h index 56ca9d191,f0129130b..218c7901d --- a/block.h +++ b/block.h @@@ -45,13 -48,9 +48,11 @@@ typedef struct QEMUSnapshotInfo 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); @@@ -88,21 -87,16 +89,18 @@@ BlockDriverAIOCB *bdrv_aio_read(BlockDr 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 diff --cc configure index f138bec28,ba80f1c55..c15ffd181 --- a/configure +++ b/configure @@@ -336,17 -354,9 +354,10 @@@ for opt d 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" diff --cc hw/ide.c index 68c6f8596,33e8b3932..8cf4fdc92 --- a/hw/ide.c +++ b/hw/ide.c @@@ -351,7 -351,7 +351,8 @@@ #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 @@@ -734,7 -749,6 +752,7 @@@ static inline void ide_dma_submit_check 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; @@@ -922,8 -928,6 +940,8 @@@ static void ide_read_dma_cb(void *opaqu 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) { @@@ -1048,8 -1039,6 +1066,8 @@@ static void ide_write_dma_cb(void *opaq 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) { @@@ -1096,39 -1085,6 +1114,39 @@@ static void ide_sector_write_dma(IDESta ide_dma_start(s, ide_write_dma_cb); } +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; + - if (!s->bs) return; /* yikes */ ++ 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; ++ s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s); +} + static void ide_atapi_cmd_ok(IDEState *s) { s->error = 0; @@@ -1355,8 -1311,6 +1373,8 @@@ static void ide_atapi_cmd_read_dma_cb(v 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; @@@ -1936,8 -1974,6 +2039,8 @@@ static void cdrom_change_cb(void *opaqu 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; @@@ -2041,10 -2081,14 +2149,14 @@@ static void ide_ioport_write(void *opaq 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) { diff --cc osdep.c index c78dc2be5,683aad0f0..b53b9559f --- a/osdep.c +++ b/osdep.c @@@ -46,33 -45,8 +46,35 @@@ #include #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) { diff --cc qemu-common.h index 964e203ac,cc95fe62b..1c5f0a4b7 --- a/qemu-common.h +++ b/qemu-common.h @@@ -86,18 -86,8 +86,19 @@@ int strstart(const char *str, const cha 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 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); diff --cc qemu_socket.h index 060904136,ef2d88bfe..659583779 --- a/qemu_socket.h +++ b/qemu_socket.h @@@ -14,19 -14,16 +14,22 @@@ #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 #include #include - +#ifndef NO_UNIX_SOCKETS + #include + #include #include +#endif #define socket_error() errno #define closesocket(s) close(s) diff --cc usb-linux.c index 5b573f45f,6113a0c5f..a48567103 --- a/usb-linux.c +++ b/usb-linux.c @@@ -28,12 -37,11 +37,14 @@@ #if defined(__linux__) #include #include +/* 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 + #include #include - #include + #include "hw/usb.h" /* We redefine it to avoid version problems */ struct usb_ctrltransfer { @@@ -807,7 -1141,115 +1144,116 @@@ static int usb_host_scan_dev(void *opaq 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; } diff --cc vl.c index 86dbf8341,e8410a8a1..bb1cc8d21 --- a/vl.c +++ b/vl.c @@@ -1991,25 -2128,32 +2128,13 @@@ static int send_all(int fd, const uint8 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 @@@ -2312,13 -2453,153 +2437,153 @@@ void cfmakeraw (struct termios *termios } #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; }