new file mode 100644
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) Citrix 2016
+ * Author(s): Wei Liu <wei.liu2@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+#define _GNU_SOURCE
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "utils.h"
+#include "logfile.h"
+
+static void logfile_entry_free(struct logfile_entry *entry)
+{
+ if (!entry) return;
+
+ if (entry->fd >= 0) close(entry->fd);
+ free(entry);
+}
+
+void logfile_free(struct logfile *f)
+{
+ if (!f) return;
+
+ logfile_entry_free(f->entry);
+
+ free(f->basepath);
+ free(f);
+}
+
+static struct logfile_entry *logfile_entry_new(const char *path, mode_t mode)
+{
+ struct logfile_entry *entry;
+ struct stat sb;
+
+ entry = calloc(1, sizeof(*entry));
+ if (!entry) goto err;
+
+ entry->fd = open(path, O_CREAT|O_APPEND|O_WRONLY|O_CLOEXEC, mode);
+ if (entry->fd < 0) {
+ dolog(LOG_ERR, "Failed to open log file %s", path);
+ goto err;
+ }
+
+ entry->pos = lseek(entry->fd, 0, SEEK_END);
+ if (entry->pos == (off_t)-1) {
+ dolog(LOG_ERR, "Unable to determine current file offset in %s",
+ path);
+ goto err;
+ }
+
+ if (fstat(entry->fd, &sb) < 0) {
+ dolog(LOG_ERR, "Unable to fstat current file %s", path);
+ goto err;
+ }
+
+ entry->len = sb.st_size;
+ return entry;
+
+err:
+ logfile_entry_free(entry);
+ return NULL;
+}
+
+struct logfile *logfile_new(const char *path, mode_t mode)
+{
+ struct logfile *logfile;
+
+ logfile = calloc(1, sizeof(*logfile));
+ if (!logfile) goto err;
+
+ logfile->basepath = strdup(path);
+ if (!logfile->basepath) {
+ dolog(LOG_ERR, "Failed to strdup %s", path);
+ goto err;
+ }
+
+ logfile->mode = mode;
+ logfile->maxlen = LOGFILE_MAX_SIZE;
+
+
+ logfile->entry = logfile_entry_new(logfile->basepath, mode);
+
+ if (!logfile->entry) goto err;
+
+ return logfile;
+err:
+ logfile_free(logfile);
+ return NULL;
+}
+
+static int logfile_rollover(struct logfile *file)
+{
+ struct logfile_entry *old = file->entry, *new = NULL;
+ char *this = NULL;
+ char *next = NULL;
+ unsigned i;
+
+ if (asprintf(&next, "%s.%u", file->basepath,
+ LOGFILE_MAX_BACKUP_NUM) < 0) {
+ dolog(LOG_ERR, "Failed to asprintf %s.%u",
+ file->basepath, LOGFILE_MAX_BACKUP_NUM);
+ goto err;
+ }
+
+ for (i = LOGFILE_MAX_BACKUP_NUM; i > 0; i--) {
+ if (i == 1) {
+ this = strdup(file->basepath);
+ if (!this) {
+ dolog(LOG_ERR, "Failed to strdup %s",
+ file->basepath);
+ goto err;
+ }
+ } else {
+ if (asprintf(&this, "%s.%u", file->basepath, i-1) < 0) {
+ dolog(LOG_ERR, "Failed to asprintf %s.%u",
+ file->basepath, i-1);
+ goto err;
+ }
+ }
+
+ if (rename(this, next) < 0 && errno != ENOENT) {
+ dolog(LOG_ERR, "Failed to rename %s to %s",
+ this, next);
+ goto err;
+ }
+
+ free(next);
+ next = this;
+ this = NULL;
+ }
+
+ new = logfile_entry_new(file->basepath, file->mode);
+ if (!new) goto err;
+
+ file->entry = new;
+ logfile_entry_free(old);
+
+ return 0;
+err:
+ free(this);
+ free(next);
+ return -1;
+}
+
+static ssize_t write_all(int fd, const char* buf, size_t len)
+{
+ ssize_t written = 0;
+ while (len) {
+ ssize_t r = write(fd, buf, len);
+ if (r < 0 && errno == EINTR)
+ continue;
+ if (r < 0)
+ return r;
+ len -= r;
+ buf += r;
+ written += r;
+ }
+
+ return written;
+}
+
+ssize_t logfile_append(struct logfile *file, const char *buf,
+ size_t len)
+{
+ ssize_t ret = 0;
+
+ while (len) {
+ struct logfile_entry *entry = file->entry;
+ size_t towrite = len;
+ bool rollover = false;
+
+ if (entry->pos > file->maxlen) {
+ rollover = true;
+ towrite = 0;
+ } else if (entry->pos + towrite > file->maxlen) {
+ towrite = file->maxlen - entry->pos;
+ }
+
+ if (towrite) {
+ if (write_all(entry->fd, buf, towrite) != towrite) {
+ dolog(LOG_ERR, "Unable to write file %s",
+ file->basepath);
+ return -1;
+ }
+
+ len -= towrite;
+ buf += towrite;
+ ret += towrite;
+ entry->pos += towrite;
+ entry->len += towrite;
+ }
+
+ if ((entry->pos == file->maxlen && len) || rollover) {
+ dolog(LOG_DEBUG, "Roll over %s (maxlen %zu)",
+ file->basepath, file->maxlen);
+ if (logfile_rollover(file))
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * indent-tabs-mode: t
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
new file mode 100644
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) Citrix 2016
+ * Author(s): Wei Liu <wei.liu2@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef XENCONSOLED_LOGFILE_H
+#define XENCONSOLED_LOGFILE_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define LOGFILE_MAX_BACKUP_NUM 5
+#define LOGFILE_MAX_SIZE (1024*128)
+
+/* Append only abstraction for log file */
+struct logfile_entry {
+ int fd;
+ off_t pos;
+ off_t len;
+};
+
+struct logfile {
+ char *basepath;
+ mode_t mode;
+ off_t maxlen;
+ struct logfile_entry *entry;
+};
+
+struct logfile *logfile_new(const char *path, mode_t mode);
+void logfile_free(struct logfile *f);
+ssize_t logfile_append(struct logfile *file, const char *buf,
+ size_t len);
+
+#endif /* XENCONSOLED_LOGFILE_H */
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * indent-tabs-mode: t
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
It will be used to handle hypervsior log and guest console log. Signed-off-by: Wei Liu <wei.liu2@citrix.com> --- tools/console/daemon/logfile.c | 229 +++++++++++++++++++++++++++++++++++++++++ tools/console/daemon/logfile.h | 57 ++++++++++ 2 files changed, 286 insertions(+) create mode 100644 tools/console/daemon/logfile.c create mode 100644 tools/console/daemon/logfile.h