src/util/virportallocator.c
src/util/virprocess.c
src/util/virrandom.c
+src/util/virrotatingfile.c
src/util/virsexpr.c
src/util/virscsi.c
src/util/virsocketaddr.c
util/virprobe.h \
util/virprocess.c util/virprocess.h \
util/virrandom.h util/virrandom.c \
+ util/virrotatingfile.h util/virrotatingfile.c \
util/virscsi.c util/virscsi.h \
util/virseclabel.c util/virseclabel.h \
util/virsexpr.c util/virsexpr.h \
virRandomInt;
+# util/virrotatingfile.h
+virRotatingFileReaderConsume;
+virRotatingFileReaderFree;
+virRotatingFileReaderNew;
+virRotatingFileReaderSeek;
+virRotatingFileWriterAppend;
+virRotatingFileWriterFree;
+virRotatingFileWriterGetINode;
+virRotatingFileWriterGetOffset;
+virRotatingFileWriterGetPath;
+virRotatingFileWriterNew;
+
+
# util/virscsi.h
virSCSIDeviceFileIterate;
virSCSIDeviceFree;
--- /dev/null
+/*
+ * virrotatingfile.c: file I/O with size rotation
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "virrotatingfile.h"
+#include "viralloc.h"
+#include "virerror.h"
+#include "virstring.h"
+#include "virfile.h"
+#include "virlog.h"
+
+VIR_LOG_INIT("util.rotatingfile");
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+#define VIR_MAX_MAX_BACKUP 32
+
+typedef struct virRotatingFileWriterEntry virRotatingFileWriterEntry;
+typedef virRotatingFileWriterEntry *virRotatingFileWriterEntryPtr;
+
+typedef struct virRotatingFileReaderEntry virRotatingFileReaderEntry;
+typedef virRotatingFileReaderEntry *virRotatingFileReaderEntryPtr;
+
+struct virRotatingFileWriterEntry {
+ int fd;
+ off_t inode;
+ off_t pos;
+ off_t len;
+};
+
+struct virRotatingFileWriter {
+ char *basepath;
+ virRotatingFileWriterEntryPtr entry;
+ size_t maxbackup;
+ mode_t mode;
+ size_t maxlen;
+};
+
+
+struct virRotatingFileReaderEntry {
+ char *path;
+ int fd;
+ off_t inode;
+};
+
+struct virRotatingFileReader {
+ virRotatingFileReaderEntryPtr *entries;
+ size_t nentries;
+ size_t current;
+};
+
+
+static void
+virRotatingFileWriterEntryFree(virRotatingFileWriterEntryPtr entry)
+{
+ if (!entry)
+ return;
+
+ VIR_FORCE_CLOSE(entry->fd);
+ VIR_FREE(entry);
+}
+
+
+static void
+virRotatingFileReaderEntryFree(virRotatingFileReaderEntryPtr entry)
+{
+ if (!entry)
+ return;
+
+ VIR_FREE(entry->path);
+ VIR_FORCE_CLOSE(entry->fd);
+ VIR_FREE(entry);
+}
+
+
+static virRotatingFileWriterEntryPtr
+virRotatingFileWriterEntryNew(const char *path,
+ mode_t mode)
+{
+ virRotatingFileWriterEntryPtr entry;
+ struct stat sb;
+
+ VIR_DEBUG("Opening %s mode=%02o", path, mode);
+
+ if (VIR_ALLOC(entry) < 0)
+ return NULL;
+
+ if ((entry->fd = open(path, O_CREAT|O_APPEND|O_WRONLY, mode)) < 0) {
+ virReportSystemError(errno,
+ _("Unable to open file: %s"), path);
+ goto error;
+ }
+
+ entry->pos = lseek(entry->fd, 0, SEEK_END);
+ if (entry->pos == (off_t)-1) {
+ virReportSystemError(errno,
+ _("Unable to determine current file offset: %s"),
+ path);
+ goto error;
+ }
+
+ if (fstat(entry->fd, &sb) < 0) {
+ virReportSystemError(errno,
+ _("Unable to determine current file inode: %s"),
+ path);
+ goto error;
+ }
+
+ entry->len = sb.st_size;
+ entry->inode = sb.st_ino;
+
+ return entry;
+
+ error:
+ virRotatingFileWriterEntryFree(entry);
+ return NULL;
+}
+
+
+static virRotatingFileReaderEntryPtr
+virRotatingFileReaderEntryNew(const char *path)
+{
+ virRotatingFileReaderEntryPtr entry;
+ struct stat sb;
+
+ VIR_DEBUG("Opening %s", path);
+
+ if (VIR_ALLOC(entry) < 0)
+ return NULL;
+
+ if ((entry->fd = open(path, O_RDONLY)) < 0) {
+ if (errno != ENOENT) {
+ virReportSystemError(errno,
+ _("Unable to open file: %s"), path);
+ goto error;
+ }
+ }
+
+ if (entry->fd != -1) {
+ if (fstat(entry->fd, &sb) < 0) {
+ virReportSystemError(errno,
+ _("Unable to determine current file inode: %s"),
+ path);
+ goto error;
+ }
+
+ entry->inode = sb.st_ino;
+ }
+
+ if (VIR_STRDUP(entry->path, path) < 0)
+ goto error;
+
+ return entry;
+
+ error:
+ virRotatingFileReaderEntryFree(entry);
+ return NULL;
+}
+
+
+static int
+virRotatingFileWriterDelete(virRotatingFileWriterPtr file)
+{
+ size_t i;
+
+ if (unlink(file->basepath) < 0 &&
+ errno != ENOENT) {
+ virReportSystemError(errno,
+ _("Unable to delete file %s"),
+ file->basepath);
+ return -1;
+ }
+
+ for (i = 0; i < file->maxbackup; i++) {
+ char *oldpath;
+ if (virAsprintf(&oldpath, "%s.%zu", file->basepath, i) < 0)
+ return -1;
+
+ if (unlink(oldpath) < 0 &&
+ errno != ENOENT) {
+ virReportSystemError(errno,
+ _("Unable to delete file %s"),
+ oldpath);
+ VIR_FREE(oldpath);
+ return -1;
+ }
+ VIR_FREE(oldpath);
+ }
+
+ return 0;
+}
+
+
+/**
+ * virRotatingFileWriterNew
+ * @path: the base path for files
+ * @maxlen: the maximum number of bytes to write before rollover
+ * @maxbackup: number of backup files to keep when rolling over
+ * @truncate: whether to truncate the current files when opening
+ * @mode: the file mode to use for creating new files
+ *
+ * Create a new object for writing data to a file with
+ * automatic rollover. If @maxbackup is zero, no backup
+ * files will be created. The primary file will just get
+ * truncated and reopened.
+ *
+ * The files will never exceed @maxlen bytes in size,
+ * but may be rolled over before they reach this size
+ * in order to avoid splitting lines
+ */
+virRotatingFileWriterPtr
+virRotatingFileWriterNew(const char *path,
+ off_t maxlen,
+ size_t maxbackup,
+ bool truncate,
+ mode_t mode)
+{
+ virRotatingFileWriterPtr file;
+
+ if (VIR_ALLOC(file) < 0)
+ goto error;
+
+ if (VIR_STRDUP(file->basepath, path) < 0)
+ goto error;
+
+ if (maxbackup > VIR_MAX_MAX_BACKUP) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Max backup %zu must be less than or equal to %d"),
+ maxbackup, VIR_MAX_MAX_BACKUP);
+ goto error;
+ }
+
+ file->mode = mode;
+ file->maxbackup = maxbackup;
+ file->maxlen = maxlen;
+
+ if (truncate &&
+ virRotatingFileWriterDelete(file) < 0)
+ goto error;
+
+ if (!(file->entry = virRotatingFileWriterEntryNew(file->basepath,
+ mode)))
+ goto error;
+
+ return file;
+
+ error:
+ virRotatingFileWriterFree(file);
+ return NULL;
+}
+
+
+/**
+ * virRotatingFileReaderNew:
+ * @path: the base path for files
+ * @maxbackup: number of backup files to read history from
+ *
+ * Create a new object for reading from a set of rolling files.
+ * I/O will start from the oldest file and proceed through
+ * files until the end of the newest one.
+ *
+ * If @maxbackup is zero the only the newest file will be read.
+ */
+virRotatingFileReaderPtr
+virRotatingFileReaderNew(const char *path,
+ size_t maxbackup)
+{
+ virRotatingFileReaderPtr file;
+ size_t i;
+
+ if (VIR_ALLOC(file) < 0)
+ goto error;
+
+ if (maxbackup > VIR_MAX_MAX_BACKUP) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Max backup %zu must be less than or equal to %d"),
+ maxbackup, VIR_MAX_MAX_BACKUP);
+ goto error;
+ }
+
+ file->nentries = maxbackup + 1;
+ if (VIR_ALLOC_N(file->entries, file->nentries) < 0)
+ goto error;
+
+ if (!(file->entries[file->nentries - 1] = virRotatingFileReaderEntryNew(path)))
+ goto error;
+
+ for (i = 0; i < maxbackup; i++) {
+ char *tmppath;
+ if (virAsprintf(&tmppath, "%s.%zu", path, i) < 0)
+ goto error;
+
+ file->entries[file->nentries - (i + 2)] = virRotatingFileReaderEntryNew(tmppath);
+ VIR_FREE(tmppath);
+ if (!file->entries[file->nentries - (i + 2)])
+ goto error;
+ }
+
+ return file;
+
+ error:
+ virRotatingFileReaderFree(file);
+ return NULL;
+}
+
+
+/**
+ * virRotatingFileWriterGetPath:
+ * @file: the file context
+ *
+ * Return the primary file path
+ */
+const char *
+virRotatingFileWriterGetPath(virRotatingFileWriterPtr file)
+{
+ return file->basepath;
+}
+
+
+/**
+ * virRotatingFileWriterGetINode:
+ * @file: the file context
+ *
+ * Return the inode of the file currently being written to
+ */
+ino_t
+virRotatingFileWriterGetINode(virRotatingFileWriterPtr file)
+{
+ return file->entry->inode;
+}
+
+
+/**
+ * virRotatingFileWriterGetOffset:
+ * @file: the file context
+ *
+ * Return the offset at which data is currently being written
+ */
+off_t
+virRotatingFileWriterGetOffset(virRotatingFileWriterPtr file)
+{
+ return file->entry->pos;
+}
+
+
+static int
+virRotatingFileWriterRollover(virRotatingFileWriterPtr file)
+{
+ size_t i;
+ char *nextpath = NULL;
+ char *thispath = NULL;
+ int ret = -1;
+
+ VIR_DEBUG("Rollover %s", file->basepath);
+ if (file->maxbackup == 0) {
+ if (unlink(file->basepath) < 0 &&
+ errno != ENOENT) {
+ virReportSystemError(errno,
+ _("Unable to remove %s"),
+ file->basepath);
+ goto cleanup;
+ }
+ } else {
+ if (virAsprintf(&nextpath, "%s.%zu", file->basepath, file->maxbackup - 1) < 0)
+ return -1;
+
+ for (i = file->maxbackup; i > 0; i--) {
+ if (i == 1) {
+ if (VIR_STRDUP(thispath, file->basepath) < 0)
+ goto cleanup;
+ } else {
+ if (virAsprintf(&thispath, "%s.%zu", file->basepath, i - 2) < 0)
+ goto cleanup;
+ }
+ VIR_DEBUG("Rollover %s -> %s", thispath, nextpath);
+
+ if (rename(thispath, nextpath) < 0 &&
+ errno != ENOENT) {
+ virReportSystemError(errno,
+ _("Unable to rename %s to %s"),
+ thispath, nextpath);
+ goto cleanup;
+ }
+
+ VIR_FREE(nextpath);
+ nextpath = thispath;
+ thispath = NULL;
+ }
+ }
+
+ VIR_DEBUG("Rollover done %s", file->basepath);
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(nextpath);
+ VIR_FREE(thispath);
+ return ret;
+}
+
+
+/**
+ * virRotatingFileWriterAppend:
+ * @file: the file context
+ * @buf: the data buffer
+ * @len: the number of bytes in @buf
+ *
+ * Append the data in @buf to the file, performing rollover
+ * of the files if their size would exceed the limit
+ *
+ * Returns the number of bytes written, or -1 on error
+ */
+ssize_t
+virRotatingFileWriterAppend(virRotatingFileWriterPtr file,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret = 0;
+ size_t i;
+ while (len) {
+ size_t towrite = len;
+ bool forceRollover = false;
+
+ if ((file->entry->pos + towrite) > file->maxlen) {
+ towrite = file->maxlen - file->entry->pos;
+
+ /*
+ * If there's a newline in the last 80 chars
+ * we're about to write, then break at that
+ * point to avoid splitting lines across
+ * separate files
+ */
+ for (i = 0; i < towrite && i < 80; i++) {
+ if (buf[towrite - i - 1] == '\n') {
+ towrite -= i;
+ forceRollover = true;
+ break;
+ }
+ }
+ }
+
+ if (towrite) {
+ if (safewrite(file->entry->fd, buf, towrite) != towrite) {
+ virReportSystemError(errno,
+ _("Unable to write to file %s"),
+ file->basepath);
+ return -1;
+ }
+
+ len -= towrite;
+ buf += towrite;
+ ret += towrite;
+ file->entry->pos += towrite;
+ file->entry->len += towrite;
+ }
+
+ if ((file->entry->pos == file->maxlen && len) ||
+ forceRollover) {
+ virRotatingFileWriterEntryPtr tmp = file->entry;
+ VIR_DEBUG("Hit max size %zu on %s (force=%d)\n",
+ file->maxlen, file->basepath, forceRollover);
+
+ if (virRotatingFileWriterRollover(file) < 0)
+ return -1;
+
+ if (!(file->entry = virRotatingFileWriterEntryNew(file->basepath,
+ file->mode)))
+ return -1;
+
+ virRotatingFileWriterEntryFree(tmp);
+ }
+ }
+
+ return ret;
+}
+
+
+/**
+ * virRotatingFileReaderSeek
+ * @file: the file context
+ * @inode: the inode of the file to seek to
+ * @offset: the offset within the file to seek to
+ *
+ * Seek to @offset in the file identified by @inode.
+ * If no file with a inode matching @inode currently
+ * exists, then seeks to the start of the oldest
+ * file, on the basis that the requested file has
+ * probably been rotated out of existance
+ */
+int
+virRotatingFileReaderSeek(virRotatingFileReaderPtr file,
+ ino_t inode,
+ off_t offset)
+{
+ size_t i;
+ off_t ret;
+
+ for (i = 0; i < file->nentries; i++) {
+ virRotatingFileReaderEntryPtr entry = file->entries[i];
+ if (entry->inode != inode ||
+ entry->fd == -1)
+ continue;
+
+ ret = lseek(entry->fd, offset, SEEK_SET);
+ if (ret == (off_t)-1) {
+ virReportSystemError(errno,
+ _("Unable to seek to inode %llu offset %llu"),
+ (unsigned long long)inode, (unsigned long long)offset);
+ return -1;
+ }
+
+ file->current = i;
+ return 0;
+ }
+
+ file->current = 0;
+ ret = lseek(file->entries[0]->fd, offset, SEEK_SET);
+ if (ret == (off_t)-1) {
+ virReportSystemError(errno,
+ _("Unable to seek to inode %llu offset %llu"),
+ (unsigned long long)inode, (unsigned long long)offset);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * virRotatingFileReaderConsume:
+ * @file: the file context
+ * @buf: the buffer to fill with data
+ * @len: the size of @buf
+ *
+ * Reads data from the file starting at the current offset.
+ * The returned data may be pulled from multiple files.
+ *
+ * Returns: the number of bytes read or -1 on error
+ */
+ssize_t
+virRotatingFileReaderConsume(virRotatingFileReaderPtr file,
+ char *buf,
+ size_t len)
+{
+ ssize_t ret = 0;
+
+ VIR_DEBUG("Consume %p %zu\n", buf, len);
+ while (len) {
+ virRotatingFileReaderEntryPtr entry;
+ ssize_t got;
+
+ if (file->current >= file->nentries)
+ break;
+
+ entry = file->entries[file->current];
+ if (entry->fd == -1) {
+ file->current++;
+ continue;
+ }
+
+ got = saferead(entry->fd, buf + ret, len);
+ if (got < 0) {
+ virReportSystemError(errno,
+ _("Unable to read from file %s"),
+ entry->path);
+ return -1;
+ }
+
+ if (got == 0) {
+ file->current++;
+ continue;
+ }
+
+ ret += got;
+ len -= got;
+ }
+
+ return ret;
+}
+
+
+/**
+ * virRotatingFileWriterFree:
+ * @file: the file context
+ *
+ * Close the current file and release all resources
+ */
+void
+virRotatingFileWriterFree(virRotatingFileWriterPtr file)
+{
+ if (!file)
+ return;
+
+ virRotatingFileWriterEntryFree(file->entry);
+ VIR_FREE(file->basepath);
+ VIR_FREE(file);
+}
+
+
+/**
+ * virRotatingFileReaderFree:
+ * @file: the file context
+ *
+ * Close the files and release all resources
+ */
+void
+virRotatingFileReaderFree(virRotatingFileReaderPtr file)
+{
+ size_t i;
+
+ if (!file)
+ return;
+
+ for (i = 0; i < file->nentries; i++)
+ virRotatingFileReaderEntryFree(file->entries[i]);
+ VIR_FREE(file->entries);
+ VIR_FREE(file);
+}
--- /dev/null
+/*
+ * virrotatingfile.h: reading/writing of auto-rotating files
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VIR_ROTATING_FILE_H__
+# define __VIR_ROTATING_FILE_H__
+
+# include "internal.h"
+
+typedef struct virRotatingFileWriter virRotatingFileWriter;
+typedef virRotatingFileWriter *virRotatingFileWriterPtr;
+
+typedef struct virRotatingFileReader virRotatingFileReader;
+typedef virRotatingFileReader *virRotatingFileReaderPtr;
+
+virRotatingFileWriterPtr virRotatingFileWriterNew(const char *path,
+ off_t maxlen,
+ size_t maxbackup,
+ bool truncate,
+ mode_t mode);
+
+virRotatingFileReaderPtr virRotatingFileReaderNew(const char *path,
+ size_t maxbackup);
+
+const char *virRotatingFileWriterGetPath(virRotatingFileWriterPtr file);
+
+ino_t virRotatingFileWriterGetINode(virRotatingFileWriterPtr file);
+off_t virRotatingFileWriterGetOffset(virRotatingFileWriterPtr file);
+
+ssize_t virRotatingFileWriterAppend(virRotatingFileWriterPtr file,
+ const char *buf,
+ size_t len);
+
+int virRotatingFileReaderSeek(virRotatingFileReaderPtr file,
+ ino_t inode,
+ off_t offset);
+
+ssize_t virRotatingFileReaderConsume(virRotatingFileReaderPtr file,
+ char *buf,
+ size_t len);
+
+void virRotatingFileWriterFree(virRotatingFileWriterPtr file);
+void virRotatingFileReaderFree(virRotatingFileReaderPtr file);
+
+#endif /* __VIR_ROTATING_FILE_H__ */
virkeycodetest \
virlockspacetest \
virlogtest \
+ virrotatingfiletest \
virstringtest \
virportallocatortest \
sysinfotest \
virpolkittest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
virpolkittest_LDADD = $(LDADDS) $(DBUS_LIBS)
+virrotatingfiletest_SOURCES = \
+ virrotatingfiletest.c testutils.h testutils.c
+virrotatingfiletest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+virrotatingfiletest_LDADD = $(LDADDS) $(DBUS_LIBS)
+
virsystemdtest_SOURCES = \
virsystemdtest.c testutils.h testutils.c
virsystemdtest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
--- /dev/null
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "virrotatingfile.h"
+#include "virlog.h"
+#include "testutils.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+VIR_LOG_INIT("tests.rotatingfiletest");
+
+#define FILENAME "virrotatingfiledata.txt"
+#define FILENAME0 "virrotatingfiledata.txt.0"
+#define FILENAME1 "virrotatingfiledata.txt.1"
+
+#define FILEBYTE 0xde
+#define FILEBYTE0 0xad
+#define FILEBYTE1 0xbe
+
+static int testRotatingFileWriterAssertOneFileSize(const char *filename,
+ off_t size)
+{
+ struct stat sb;
+
+ if (stat(filename, &sb) < 0) {
+ if (size == (off_t)-1) {
+ return 0;
+ } else {
+ fprintf(stderr, "File %s does not exist\n", filename);
+ return -1;
+ }
+ } else {
+ if (size == (off_t)-1) {
+ fprintf(stderr, "File %s should not exist\n", filename);
+ return -1;
+ } else if (sb.st_size != size) {
+ fprintf(stderr, "File %s should be %zu bytes not %zu\n",
+ filename, size, sb.st_size);
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static int testRotatingFileWriterAssertFileSizes(off_t baseSize,
+ off_t backup0Size,
+ off_t backup1Size)
+{
+ if (testRotatingFileWriterAssertOneFileSize(FILENAME, baseSize) < 0 ||
+ testRotatingFileWriterAssertOneFileSize(FILENAME0, backup0Size) < 0 ||
+ testRotatingFileWriterAssertOneFileSize(FILENAME1, backup1Size) < 0)
+ return -1;
+ return 0;
+}
+
+
+static int testRotatingFileReaderAssertBufferContent(const char *buf,
+ size_t buflen,
+ size_t nregions,
+ size_t *sizes)
+{
+ size_t i, j;
+ char bytes[] = { FILEBYTE, FILEBYTE0, FILEBYTE1 };
+ size_t total = 0;
+
+ if (nregions > ARRAY_CARDINALITY(bytes)) {
+ fprintf(stderr, "Too many regions %zu\n", nregions);
+ return -1;
+ }
+
+ for (i = 0; i < nregions; i++)
+ total += sizes[i];
+
+ if (total != buflen) {
+ fprintf(stderr, "Expected %zu bytes in file not %zu\n",
+ total, buflen);
+ return -1;
+ }
+
+ for (i = 0; i < nregions; i++) {
+ char want = bytes[nregions - (i + 1)];
+ for (j = 0; j < sizes[i]; j++) {
+ if (*buf != want) {
+ fprintf(stderr,
+ "Expected '0x%x' but got '0x%x' at region %zu byte %zu\n",
+ want & 0xff, *buf & 0xff, i, j);
+ return -1;
+ }
+ buf++;
+ }
+ }
+
+ return 0;
+}
+
+
+static int testRotatingFileInitOne(const char *filename,
+ off_t size,
+ char pattern)
+{
+ if (size == (off_t)-1) {
+ VIR_DEBUG("Deleting %s", filename);
+ unlink(filename);
+ } else {
+ VIR_DEBUG("Creating %s size %zu", filename, (size_t)size);
+ char buf[1024];
+ int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0700);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot create %s\n", filename);
+ return -1;
+ }
+ memset(buf, pattern, sizeof(buf));
+ while (size) {
+ size_t towrite = size;
+ if (towrite > sizeof(buf))
+ towrite = sizeof(buf);
+
+ if (safewrite(fd, buf, towrite) != towrite) {
+ fprintf(stderr, "Cannot write to %s\n", filename);
+ VIR_FORCE_CLOSE(fd);
+ return -1;
+ }
+ size -= towrite;
+ }
+ VIR_FORCE_CLOSE(fd);
+ }
+ return 0;
+}
+
+static int testRotatingFileInitFiles(off_t baseSize,
+ off_t backup0Size,
+ off_t backup1Size)
+{
+ if (testRotatingFileInitOne(FILENAME, baseSize, FILEBYTE) < 0 ||
+ testRotatingFileInitOne(FILENAME0, backup0Size, FILEBYTE0) < 0 ||
+ testRotatingFileInitOne(FILENAME1, backup1Size, FILEBYTE1) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int testRotatingFileWriterNew(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileWriterPtr file;
+ int ret = -1;
+ char buf[512];
+
+ if (testRotatingFileInitFiles((off_t)-1,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileWriterNew(FILENAME,
+ 1024,
+ 2,
+ false,
+ 0700);
+ if (!file)
+ goto cleanup;
+
+ if (testRotatingFileWriterAssertFileSizes(0,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ memset(buf, 0x5e, sizeof(buf));
+
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+
+ if (testRotatingFileWriterAssertFileSizes(sizeof(buf),
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileWriterFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+
+static int testRotatingFileWriterAppend(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileWriterPtr file;
+ int ret = -1;
+ char buf[512];
+
+ if (testRotatingFileInitFiles(512,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileWriterNew(FILENAME,
+ 1024,
+ 2,
+ false,
+ 0700);
+ if (!file)
+ goto cleanup;
+
+ if (testRotatingFileWriterAssertFileSizes(512,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ memset(buf, 0x5e, sizeof(buf));
+
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+
+ if (testRotatingFileWriterAssertFileSizes(1024,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileWriterFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+
+static int testRotatingFileWriterTruncate(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileWriterPtr file;
+ int ret = -1;
+ char buf[512];
+
+ if (testRotatingFileInitFiles(512,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileWriterNew(FILENAME,
+ 1024,
+ 2,
+ true,
+ 0700);
+ if (!file)
+ goto cleanup;
+
+ if (testRotatingFileWriterAssertFileSizes(0,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ memset(buf, 0x5e, sizeof(buf));
+
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+
+ if (testRotatingFileWriterAssertFileSizes(512,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileWriterFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+
+static int testRotatingFileWriterRolloverNone(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileWriterPtr file;
+ int ret = -1;
+ char buf[512];
+
+ if (testRotatingFileInitFiles((off_t)-1,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileWriterNew(FILENAME,
+ 200,
+ 0,
+ false,
+ 0700);
+ if (!file)
+ goto cleanup;
+
+ if (testRotatingFileWriterAssertFileSizes(0,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ memset(buf, 0x5e, sizeof(buf));
+
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+
+ if (testRotatingFileWriterAssertFileSizes(112,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileWriterFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+
+static int testRotatingFileWriterRolloverOne(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileWriterPtr file;
+ int ret = -1;
+ char buf[512];
+
+ if (testRotatingFileInitFiles((off_t)-1,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileWriterNew(FILENAME,
+ 1024,
+ 2,
+ false,
+ 0700);
+ if (!file)
+ goto cleanup;
+
+ if (testRotatingFileWriterAssertFileSizes(0,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ memset(buf, 0x5e, sizeof(buf));
+
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+
+ if (testRotatingFileWriterAssertFileSizes(512,
+ 1024,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileWriterFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+
+static int testRotatingFileWriterRolloverAppend(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileWriterPtr file;
+ int ret = -1;
+ char buf[512];
+
+ if (testRotatingFileInitFiles((off_t)768,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileWriterNew(FILENAME,
+ 1024,
+ 2,
+ false,
+ 0700);
+ if (!file)
+ goto cleanup;
+
+ if (testRotatingFileWriterAssertFileSizes(768,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ memset(buf, 0x5e, sizeof(buf));
+
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+
+ if (testRotatingFileWriterAssertFileSizes(256,
+ 1024,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileWriterFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+
+static int testRotatingFileWriterRolloverMany(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileWriterPtr file;
+ int ret = -1;
+ char buf[512];
+
+ if (testRotatingFileInitFiles((off_t)-1,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileWriterNew(FILENAME,
+ 1024,
+ 2,
+ false,
+ 0700);
+ if (!file)
+ goto cleanup;
+
+ if (testRotatingFileWriterAssertFileSizes(0,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ memset(buf, 0x5e, sizeof(buf));
+
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+ virRotatingFileWriterAppend(file, buf, sizeof(buf));
+
+ if (testRotatingFileWriterAssertFileSizes(512,
+ 1024,
+ 1024) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileWriterFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+
+static int testRotatingFileWriterRolloverLineBreak(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileWriterPtr file;
+ int ret = -1;
+ const char *buf = "The quick brown fox jumps over the lazy dog\n"
+ "The wizard quickly jinxed the gnomes before they vaporized\n";
+
+ if (testRotatingFileInitFiles(100,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileWriterNew(FILENAME,
+ 160,
+ 2,
+ false,
+ 0700);
+ if (!file)
+ goto cleanup;
+
+ if (testRotatingFileWriterAssertFileSizes(100,
+ (off_t)-1,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ virRotatingFileWriterAppend(file, buf, strlen(buf));
+
+ if (testRotatingFileWriterAssertFileSizes(59,
+ 144,
+ (off_t)-1) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileWriterFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+
+static int testRotatingFileReaderOne(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileReaderPtr file;
+ int ret = -1;
+ char buf[512];
+ ssize_t got;
+ size_t regions[] = { 256 };
+
+ if (testRotatingFileInitFiles(256, (off_t)-1, (off_t)-1) < 0)
+ return -1;
+
+ file = virRotatingFileReaderNew(FILENAME, 2);
+ if (!file)
+ goto cleanup;
+
+ if ((got = virRotatingFileReaderConsume(file, buf, sizeof(buf))) < 0)
+ goto cleanup;
+
+ if (testRotatingFileReaderAssertBufferContent(buf, got,
+ ARRAY_CARDINALITY(regions),
+ regions) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileReaderFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+static int testRotatingFileReaderAll(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileReaderPtr file;
+ int ret = -1;
+ char buf[768];
+ ssize_t got;
+ size_t regions[] = { 256, 256, 256 };
+
+ if (testRotatingFileInitFiles(256, 256, 256) < 0)
+ return -1;
+
+ file = virRotatingFileReaderNew(FILENAME, 2);
+ if (!file)
+ goto cleanup;
+
+ if ((got = virRotatingFileReaderConsume(file, buf, sizeof(buf))) < 0)
+ goto cleanup;
+
+ if (testRotatingFileReaderAssertBufferContent(buf, got,
+ ARRAY_CARDINALITY(regions),
+ regions) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileReaderFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+static int testRotatingFileReaderPartial(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileReaderPtr file;
+ int ret = -1;
+ char buf[600];
+ ssize_t got;
+ size_t regions[] = { 256, 256, 88 };
+
+ if (testRotatingFileInitFiles(256, 256, 256) < 0)
+ return -1;
+
+ file = virRotatingFileReaderNew(FILENAME, 2);
+ if (!file)
+ goto cleanup;
+
+ if ((got = virRotatingFileReaderConsume(file, buf, sizeof(buf))) < 0)
+ goto cleanup;
+
+ if (testRotatingFileReaderAssertBufferContent(buf, got,
+ ARRAY_CARDINALITY(regions),
+ regions) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileReaderFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+static int testRotatingFileReaderSeek(const void *data ATTRIBUTE_UNUSED)
+{
+ virRotatingFileReaderPtr file;
+ int ret = -1;
+ char buf[600];
+ ssize_t got;
+ size_t regions[] = { 156, 256 };
+ struct stat sb;
+
+ if (testRotatingFileInitFiles(256, 256, 256) < 0)
+ return -1;
+
+ file = virRotatingFileReaderNew(FILENAME, 2);
+ if (!file)
+ goto cleanup;
+
+ if (stat(FILENAME0, &sb) < 0) {
+ virReportSystemError(errno, "Cannot stat %s", FILENAME0);
+ goto cleanup;
+ }
+
+ if (virRotatingFileReaderSeek(file, sb.st_ino, 100) < 0)
+ goto cleanup;
+
+ if ((got = virRotatingFileReaderConsume(file, buf, sizeof(buf))) < 0)
+ goto cleanup;
+
+ if (testRotatingFileReaderAssertBufferContent(buf, got,
+ ARRAY_CARDINALITY(regions),
+ regions) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virRotatingFileReaderFree(file);
+ unlink(FILENAME);
+ unlink(FILENAME0);
+ unlink(FILENAME1);
+ return ret;
+}
+
+static int
+mymain(void)
+{
+ int ret = 0;
+
+ if (virtTestRun("Rotating file write new", testRotatingFileWriterNew, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file write append", testRotatingFileWriterAppend, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file write truncate", testRotatingFileWriterTruncate, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file write rollover no backup", testRotatingFileWriterRolloverNone, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file write rollover one", testRotatingFileWriterRolloverOne, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file write rollover append", testRotatingFileWriterRolloverAppend, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file write rollover many", testRotatingFileWriterRolloverMany, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file write rollover line break", testRotatingFileWriterRolloverLineBreak, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file read one", testRotatingFileReaderOne, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file read all", testRotatingFileReaderAll, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file read partial", testRotatingFileReaderPartial, NULL) < 0)
+ ret = -1;
+
+ if (virtTestRun("Rotating file read seek", testRotatingFileReaderSeek, NULL) < 0)
+ ret = -1;
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN(mymain)