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 17 May 2007 08:01:36 -0000 @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #include @@ -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,191 @@ 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 */ + snprintf(path_buf, sizeof(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 */ + snprintf(lock_buf, sizeof(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 */ + snprintf(logdef.path_buff, sizeof(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); + snprintf(msg_buf, sizeof(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)); + snprintf(msg_buf + strlen(msg_buf), sizeof(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; + } + snprintf(msg_buf + strlen(msg_buf), sizeof(msg_buf) - strlen(msg_buf), + "%s ", lvl); + vsnprintf(msg_buf + strlen(msg_buf), sizeof(msg_buf) - strlen(msg_buf), + msg_format, ap); + + if (msg_buf[strlen(msg_buf) - 1] != '\n') + snprintf(msg_buf + strlen(msg_buf), sizeof(msg_buf) - strlen(msg_buf), "\n"); + + msg_len = strlen(msg_buf); + /* write log */ + if (write(logdef.log_fd, msg_buf, msg_len) == -1) { + /* unlock file */ + flk.l_type = F_UNLCK; + fcntl(logdef.lock_fd, F_SETLK, &flk); + vshCloseLogFile(); + vshError(ctl, FALSE, _("failed to write the log file")); + return; + } + + /* Check log size */ + if (stat(logdef.path_buff, &st) == 0) { + if (st.st_size >= MAX_LOGSIZE) { + /* rotate logs */ + for (i = ROTATE_NUM - 1; i >= 1; i--) { + snprintf(bak_old_path, sizeof(bak_old_path), "%s.%d", logdef.path_buff, i); + snprintf(bak_new_path, sizeof(bak_new_path), "%s.%d", logdef.path_buff, i+1); + if (rename(bak_old_path, bak_new_path) == -1) { + /* unlock file */ + flk.l_type = F_UNLCK; + fcntl(logdef.lock_fd, F_SETLK, &flk); + vshCloseLogFile(); + vshError(ctl, FALSE, _("failed to rename the log file")); + return; + } + } + snprintf(bak_new_path, sizeof(bak_new_path), "%s.%d", logdef.path_buff, 1); + if (rename(logdef.path_buff, bak_new_path) == -1) { + /* unlock file */ + flk.l_type = F_UNLCK; + fcntl(logdef.lock_fd, F_SETLK, &flk); + vshCloseLogFile(); + vshError(ctl, FALSE, _("failed to rename the log file")); + return; + } + } + } + + /* 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 +3579,8 @@ vshDeinit(vshControl * ctl) "failed to disconnect from the hypervisor"); } } + + vshCloseLogFile(); return TRUE; } @@ -3509,6 +3747,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;