Index: src/virsh.c
===================================================================
RCS file: /data/cvs/libvirt/src/virsh.c,v
retrieving revision 1.75
diff -u -p -r1.75 virsh.c
--- src/virsh.c	3 May 2007 16:03:02 -0000	1.75
+++ src/virsh.c	15 May 2007 07:38:58 -0000
@@ -28,6 +28,9 @@
 #include <ctype.h>
 #include <fcntl.h>
 #include <locale.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <poll.h>
 
 #include <libxml/parser.h>
 #include <libxml/tree.h>
@@ -55,6 +58,48 @@ static char *progname;
         ((((int) ((T)->tv_sec - (U)->tv_sec)) * 1000000.0 + \
           ((int) ((T)->tv_usec - (U)->tv_usec))) / 1000.0)
 
+/**
+ * The log configuration
+ */
+#define MAX_LOGSIZE   (1024 * 1024)
+#define ROTATE_NUM    5
+#define MSG_BUFFER    4096
+#define SIGN_NAME     "virsh"
+#define DIR_NAME      ".virsh"
+#define FILE_NAME     "virsh.log"
+#define LOCK_NAME     ".log.lck"
+#define LOCK_TIMER    100  /* mili seconds */
+#define DIR_MODE      (S_IWUSR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)  /* 0755 */
+#define FILE_MODE     (S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)                                /* 0644 */
+#define LOCK_MODE     (S_IWUSR | S_IRUSR)                                                    /* 0600 */
+#define LVL_DEBUG     "DEBUG"
+#define LVL_INFO      "INFO"
+#define LVL_NOTICE    "NOTICE"
+#define LVL_WARNING   "WARNING"
+#define LVL_ERROR     "ERROR"
+
+/**
+ * vshErrorLevel:
+ *
+ * Indicates the level of an log massage
+ */
+typedef enum {
+    VSH_ERR_DEBUG = 0,
+    VSH_ERR_INFO,
+    VSH_ERR_NOTICE,
+    VSH_ERR_WARNING,
+    VSH_ERR_ERROR
+} vshErrorLevel;
+
+/*
+ * vshLogDef - log definition
+ */
+typedef struct {
+    int log_fd;   /* log file descriptor */
+    int lock_fd;  /* lock file descriptor */
+    char path_buff[PATH_MAX + NAME_MAX];  /* log path buffer */
+} vshLogDef;
+
 /*
  * The error handler for virtsh
  */
@@ -178,12 +223,16 @@ typedef struct __vshControl {
 
 
 static vshCmdDef commands[];
+static vshLogDef logdef;
 
 static void vshError(vshControl * ctl, int doexit, const char *format, ...)
     ATTRIBUTE_FORMAT(printf, 3, 4);
 static int vshInit(vshControl * ctl);
 static int vshDeinit(vshControl * ctl);
 static void vshUsage(vshControl * ctl, const char *cmdname);
+static void vshOpenLogFile(vshControl *ctl);
+static void vshOutputLogFile(vshControl *ctl, int log_level, const char *format, va_list ap);
+static void vshCloseLogFile(void);
 
 static int vshParseArgv(vshControl * ctl, int argc, char **argv);
 
@@ -3100,11 +3149,10 @@ vshDebug(vshControl * ctl, int level, co
 {
     va_list ap;
 
-    if (level > ctl->debug)
-        return;
-
     va_start(ap, format);
-    vfprintf(stdout, format, ap);
+    if (level <= ctl->debug)
+        vfprintf(stdout, format, ap);
+    vshOutputLogFile(ctl, VSH_ERR_DEBUG, format, ap);
     va_end(ap);
 }
 
@@ -3134,6 +3182,7 @@ vshError(vshControl * ctl, int doexit, c
 
     va_start(ap, format);
     vfprintf(stderr, format, ap);
+    vshOutputLogFile(ctl, VSH_ERR_ERROR, format, ap);
     va_end(ap);
 
     fputc('\n', stderr);
@@ -3194,6 +3243,8 @@ vshInit(vshControl * ctl)
 
     ctl->uid = getuid();
 
+    vshOpenLogFile(ctl);
+
     /* set up the library error handler */
     virSetErrorFunc(NULL, virshErrorHandler);
 
@@ -3213,6 +3264,168 @@ vshInit(vshControl * ctl)
     return TRUE;
 }
 
+/**
+ * vshOpenLogFile:
+ *
+ * Open log file.
+ */
+static void
+vshOpenLogFile(vshControl *ctl)
+{
+    struct stat st;
+    char path_buf[PATH_MAX];
+    char lock_buf[PATH_MAX + NAME_MAX];
+
+    /* make log directory */
+    memset(&st, 0x00, sizeof(struct stat));
+    sprintf(path_buf, "%s/%s", getenv("HOME"), DIR_NAME);
+    if (stat(path_buf, &st) == -1) {
+        switch (errno) {
+            case ENOENT:
+                mkdir(path_buf, DIR_MODE);
+                break;
+            default:
+                vshError(ctl, FALSE, _("failed to get the log directory information"));
+                break;
+        }
+    }
+
+    /* lock file open */
+    sprintf(lock_buf, "%s/%s", path_buf, LOCK_NAME);
+    if ((logdef.lock_fd = open(lock_buf, O_WRONLY | O_CREAT, LOCK_MODE)) < 0) {
+        vshError(ctl, FALSE, _("failed to open the log lock file"));
+    }
+    /* log file open */
+    sprintf(logdef.path_buff, "%s/%s", path_buf, FILE_NAME);
+    if ((logdef.log_fd = open(logdef.path_buff, O_WRONLY | O_APPEND | O_CREAT | O_SYNC, FILE_MODE)) < 0) {
+        vshError(ctl, FALSE, _("failed to open the log file"));
+    }
+}
+
+/**
+ * vshOutputLogFile:
+ *
+ * Outputting an error to log file.
+ */
+static void
+vshOutputLogFile(vshControl *ctl, int log_level, const char *msg_format, va_list ap)
+{
+    int msg_len = 0;
+    int i = 0;
+    char bak_old_path[PATH_MAX + NAME_MAX];
+    char bak_new_path[PATH_MAX + NAME_MAX];
+    char msg_buf[MSG_BUFFER];
+    const char *lvl = "";
+    struct stat st;
+    struct flock flk;
+    struct timeval stTimeval;
+    struct tm *stTm;
+
+    if (logdef.lock_fd == -1 || logdef.log_fd == -1)
+        return;
+
+    /* lock */
+    memset(&flk, 0x00, sizeof(struct flock));
+    flk.l_type = F_WRLCK;
+    while (fcntl(logdef.lock_fd, F_SETLK, &flk) < 0) {
+        switch (errno) {
+            case EAGAIN:
+            case EACCES:
+                poll(NULL, 0, LOCK_TIMER);
+                break;
+            default:
+                vshCloseLogFile();
+                vshError(ctl, FALSE, _("failed to lock the log file"));
+                return;
+        }
+    }
+
+    /**
+     * create log format
+     *
+     * [YYYY.MM.DD HH:MM:SS SIGNATURE PID] LOG_LEVEL message
+    */
+    gettimeofday(&stTimeval, NULL);
+    stTm = localtime(&stTimeval.tv_sec);
+    sprintf(msg_buf, "[%d.%02d.%02d %02d:%02d:%02d ",
+            (1900 + stTm->tm_year),
+            (1 + stTm->tm_mon),
+            (stTm->tm_mday),
+            (stTm->tm_hour),
+            (stTm->tm_min),
+            (stTm->tm_sec));
+    sprintf(msg_buf + strlen(msg_buf), "%s %d] ", SIGN_NAME, getpid());
+    switch (log_level) {
+        case VSH_ERR_DEBUG:
+            lvl = LVL_DEBUG;
+            break;
+        case VSH_ERR_INFO:
+            lvl = LVL_INFO;
+            break;
+        case VSH_ERR_NOTICE:
+            lvl = LVL_INFO;
+            break;
+        case VSH_ERR_WARNING:
+            lvl = LVL_WARNING;
+            break;
+        case VSH_ERR_ERROR:
+            lvl = LVL_ERROR;
+            break;
+        default:
+            lvl = LVL_DEBUG;
+            break;
+    }
+    sprintf(msg_buf + strlen(msg_buf), "%s ", lvl);
+    vsprintf(msg_buf + strlen(msg_buf), msg_format, ap);
+
+    if (msg_buf[strlen(msg_buf) - 1] != '\n')
+        sprintf(msg_buf + strlen(msg_buf), "\n");
+
+    msg_len = strlen(msg_buf);
+    /* write log */
+    write(logdef.log_fd, msg_buf, msg_len);
+
+    /* Check log size */
+    memset(&st, 0x00, sizeof(struct stat));
+    if (stat(logdef.path_buff, &st) == 0) {
+        if (st.st_size >= MAX_LOGSIZE) {
+            /* rotate logs */
+            for (i = ROTATE_NUM - 1; i >= 1; i--) {
+                sprintf(bak_old_path, "%s.%d", logdef.path_buff, i);
+                sprintf(bak_new_path, "%s.%d", logdef.path_buff, i+1);
+                rename(bak_old_path, bak_new_path);
+            }
+            sprintf(bak_new_path, "%s.%d", logdef.path_buff, 1);
+            rename(logdef.path_buff, bak_new_path);
+        }
+    }
+
+    /* unlock file */
+    flk.l_type = F_UNLCK;
+    fcntl(logdef.lock_fd, F_SETLK, &flk);
+}
+
+/**
+ * vshCloseLogFile:
+ *
+ * Close log file.
+ */
+static void
+vshCloseLogFile()
+{
+    /* log file close */
+    if (logdef.log_fd >= 0) {
+        close(logdef.log_fd);
+        logdef.log_fd = -1;
+    }
+
+    /* lock file close */
+    if (logdef.lock_fd >= 0) {
+        close(logdef.lock_fd);
+        logdef.lock_fd = -1;
+    }
+}
+
 /* -----------------
  * Readline stuff
  * -----------------
@@ -3343,6 +3556,8 @@ vshDeinit(vshControl * ctl)
                      "failed to disconnect from the hypervisor");
         }
     }
+
+    vshCloseLogFile();
     return TRUE;
 }
 
@@ -3509,6 +3724,10 @@ main(int argc, char **argv)
     char *defaultConn;
     int ret = TRUE;
 
+    /* Initialize log file descriptor */
+    logdef.log_fd = -1;
+    logdef.lock_fd = -1;
+
     if (!setlocale(LC_ALL, "")) {
         perror("setlocale");
         return -1;
