[PATCH 1/4] Initial implementation of low level logging routines.

david at tethera.net david at tethera.net
Sun Oct 24 14:01:03 PDT 2010


From: David Bremner <bremner at unb.ca>

notmuch_log_open: open a log file; return an opaque "log descriptor"

notmuch_log_words: log words, escaping spaces and newlines, and appending a newline.

Log format is space delimited, and line oriented.  Escaping log text
is currently based on a slightly simplified version of
json_quote_chararray in ../json.c. This is probably overkill.

notmuch_log_transaction_(start|finish): log a transaction identifier
and depth.

Nested transactions are supported, so that this aligns with
notmuch_message_(freeze|thaw).  The are not too well tested though,
since I don't know when they arise.
---
 lib/Makefile.local |    1 +
 lib/log-private.h  |   13 +++
 lib/log.c          |  289 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/notmuch.h      |   23 ++++
 notmuch-client.h   |    4 +
 notmuch-config.c   |   15 +++
 6 files changed, 345 insertions(+), 0 deletions(-)
 create mode 100644 lib/log-private.h
 create mode 100644 lib/log.c

diff --git a/lib/Makefile.local b/lib/Makefile.local
index a60ef98..d7e8f41 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -46,6 +46,7 @@ extra_cflags += -I$(dir) -fPIC
 libnotmuch_c_srcs =		\
 	$(notmuch_compat_srcs)	\
 	$(dir)/libsha1.c	\
+	$(dir)/log.c		\
 	$(dir)/message-file.c	\
 	$(dir)/messages.c	\
 	$(dir)/sha1.c		\
diff --git a/lib/log-private.h b/lib/log-private.h
new file mode 100644
index 0000000..1af05dd
--- /dev/null
+++ b/lib/log-private.h
@@ -0,0 +1,13 @@
+
+#ifndef NOTMUCH_LOG_PRIVATE_H
+#define NOTMUCH_LOG_PRIVATE_H
+#include "notmuch.h"
+struct _notmuch_log {
+  int file_desc;
+  char *buffer;
+  char *txn_id;
+  int txn_depth;
+  notmuch_log_buffering_t buffering;
+};
+
+#endif
diff --git a/lib/log.c b/lib/log.c
new file mode 100644
index 0000000..31f1e62
--- /dev/null
+++ b/lib/log.c
@@ -0,0 +1,289 @@
+
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2010 David Bremner
+ *
+ * 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/ .
+ *
+ * Author: David Bremner <david at tethera.net>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <talloc.h>
+#include <stdarg.h>
+
+#include "log-private.h"
+#include "notmuch.h"
+
+/*
+ * Return a file descriptor to the open log file, or -1 if an error
+ * occurs.
+ *
+ */
+
+notmuch_log_t *
+notmuch_log_open (void *ctx,const char *path, notmuch_log_buffering_t buffering)
+{
+    int fd;
+    notmuch_log_t *log;
+
+    log=talloc (ctx, notmuch_log_t);
+    if (log==NULL)
+	return NULL;
+
+    fd = open (path, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+    if (fd < 0) {
+	fprintf (stderr, "Failed to open %s: %s\n",
+		 path, strerror (errno));
+	return NULL;
+    }
+
+    log->file_desc = fd;
+    log->buffer = NULL;
+    log->txn_id = NULL;
+    log->txn_depth = 0;
+    log->buffering = buffering;
+    return log;
+}
+
+static notmuch_status_t
+_log_write (int file_desc, const char *buffer, size_t len){
+
+    struct flock lock;
+
+    lock.l_type = F_WRLCK;
+    lock.l_whence = SEEK_SET;
+    lock.l_start = 0;
+    lock.l_len = 0;
+
+    if (fcntl (file_desc, F_SETLKW, &lock) != 0) {
+	fprintf (stderr, "Failed to lock  %s\n",
+		 strerror (errno));
+
+	return NOTMUCH_STATUS_FILE_ERROR;
+    }
+
+    while (len > 0)
+    {
+	int written;
+
+	written = write(file_desc, buffer, len);
+	if (written < 0 || (written == 0 && errno !=0))
+	{
+	    fprintf (stderr, "Failed to write %zd characters: %s\n",
+		     len, strerror (errno));
+
+	    return NOTMUCH_STATUS_FILE_ERROR;
+	}
+
+	len -= written;
+	buffer += written;
+
+    }
+
+    if (fdatasync (file_desc) != 0) {
+	fprintf (stderr, "Failed to sync:  %s\n",
+		 strerror (errno));
+
+	return NOTMUCH_STATUS_FILE_ERROR;
+    }
+
+    lock.l_type=F_UNLCK;
+
+    if (fcntl (file_desc, F_SETLK, &lock) != 0) {
+	fprintf (stderr, "Failed to unlock:  %s\n",
+		 strerror (errno));
+
+	return NOTMUCH_STATUS_FILE_ERROR;
+    }
+
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
+void
+notmuch_log_sync (notmuch_log_t *log)
+{
+    if (log->buffer) {
+	_log_write(log->file_desc, log->buffer, strlen (log->buffer));
+	talloc_free(log->buffer);
+	log->buffer=NULL;
+    }
+
+};
+
+
+/* This function was derived from the print_string_ptr function of
+ * cJSON (http://cjson.sourceforge.net/) and is used by permission of
+ * the following license:
+ *
+ * Copyright (c) 2009 Dave Gamble
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static char *
+_log_escape_chararray(const void *ctx, const char *str, const size_t len)
+{
+    const char *ptr;
+    char *ptr2;
+    char *out;
+    size_t loop;
+    size_t required;
+
+    for (loop = 0, required = 0, ptr = str;
+	 loop < len;
+	 loop++, required++, ptr++) {
+	if ((unsigned char)(*ptr) <= ' ')
+	    required++;
+    }
+
+    /*
+     * + 1 for trailing NULL.
+     */
+    out = talloc_array (ctx, char, required + 1);
+
+    ptr = str;
+    ptr2 = out;
+
+    for (loop = 0; loop < len; loop++) {
+	if ((unsigned char)(*ptr) > 32 && *ptr != '\"' && *ptr != '\\') {
+		*ptr2++ = *ptr++;
+	    } else {
+		*ptr2++ = '\\';
+		switch (*ptr++) {
+		    case ' ':   *ptr2++ = ' ';	break;
+		    case '\\':	*ptr2++ = '\\';	break;
+		    case '\b':	*ptr2++ = 'b';	break;
+		    case '\f':	*ptr2++ = 'f';	break;
+		    case '\n':	*ptr2++ = 'n';	break;
+		    case '\r':	*ptr2++ = 'r';	break;
+		    case '\t':	*ptr2++ = 't';	break;
+		    default:	 ptr2--;	break;
+		}
+	    }
+    }
+    *ptr2++ = '\0';
+
+    return out;
+}
+
+static char*
+_log_escape_string (void *ctx, const char *str)
+{
+    return _log_escape_chararray (ctx, str, strlen(str));
+}
+
+/*
+ * Log a series of strings as a space delimited line.  Spaces and
+ * backslashes will be escaped by adding a backslash in front.
+ */
+
+static notmuch_status_t
+_log_append_string (notmuch_log_t *log, const char *str)
+{
+    if (log->buffer == NULL) {
+	log->buffer = talloc_strdup (log, str);
+    } else {
+	log->buffer = talloc_strdup_append (log->buffer, str);
+    }
+
+    if (log->buffer == NULL){
+	return NOTMUCH_STATUS_OUT_OF_MEMORY;
+    }
+
+    if (log->buffering == NOTMUCH_LOG_BUFFER_NONE)
+	notmuch_log_sync(log);
+
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
+
+/*
+ *  Log a null terminated list of strings as a single space delimited log line.
+ *  spaces and newlines are escaped.
+ */
+
+void
+notmuch_log_words (notmuch_log_t *log, const char *word, ...)
+{
+    va_list args;
+    const char *str;
+    char *timestamp;
+
+    timestamp = talloc_asprintf (log, "%ld", (long)time(NULL));
+    _log_append_string(log,timestamp);
+    talloc_free(timestamp);
+
+    va_start (args, word);
+
+    for (str = word; str != NULL; str = va_arg (args, const char *)) {
+	_log_append_string (log, " ");
+	_log_append_string(log, _log_escape_string (log, str));
+    }
+
+    va_end(args);
+
+    _log_append_string(log, "\n");
+    if (log->buffering <= NOTMUCH_LOG_BUFFER_LINE)
+	notmuch_log_sync(log);
+
+};
+
+void
+notmuch_log_start_transaction (notmuch_log_t *log)
+{
+    /* XXX This should probably be something like a uuid */
+    if (log->txn_depth == 0) {
+	log->txn_id = talloc_asprintf (log, "%ld", random());
+    }
+    log->txn_depth++;
+
+    notmuch_log_words(log,"TX_START",
+		      talloc_asprintf (log, "%s.%d", log->txn_id, log->txn_depth));
+};
+
+void
+notmuch_log_finish_transaction (notmuch_log_t *log)
+{
+    notmuch_log_words(log,"TX_END",
+		      talloc_asprintf (log, "%s.%d", log->txn_id, log->txn_depth));
+
+    log->txn_depth--;
+
+    if (log->txn_depth == 0){
+	talloc_free (log->txn_id);
+	log->txn_id = NULL;
+    }
+};
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 505ad19..1da84aa 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -119,6 +119,7 @@ typedef struct _notmuch_message notmuch_message_t;
 typedef struct _notmuch_tags notmuch_tags_t;
 typedef struct _notmuch_directory notmuch_directory_t;
 typedef struct _notmuch_filenames notmuch_filenames_t;
+typedef struct _notmuch_log notmuch_log_t;
 
 /* Create a new, empty notmuch database located at 'path'.
  *
@@ -1123,6 +1124,28 @@ notmuch_filenames_move_to_next (notmuch_filenames_t *filenames);
 void
 notmuch_filenames_destroy (notmuch_filenames_t *filenames);
 
+
+typedef enum _notmuch_log_buffer {
+  NOTMUCH_LOG_BUFFER_NONE = 0,
+  NOTMUCH_LOG_BUFFER_LINE = 1,
+  NOTMUCH_LOG_BUFFER_USER = 2
+} notmuch_log_buffering_t;
+
+notmuch_log_t *
+notmuch_log_open (void *ctx, const char *path, notmuch_log_buffering_t buffering);
+
+
+void
+notmuch_log_sync (notmuch_log_t *log);
+
+void
+notmuch_log_words (notmuch_log_t *log, const char *word, ...);
+void
+notmuch_log_start_transaction (notmuch_log_t *log);
+
+void
+notmuch_log_finish_transaction (notmuch_log_t *log);
+
 NOTMUCH_END_DECLS
 
 #endif
diff --git a/notmuch-client.h b/notmuch-client.h
index 20be43b..0be679d 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -191,6 +191,10 @@ notmuch_config_set_new_tags (notmuch_config_t *config,
 			     const char *new_tags[],
 			     size_t length);
 
+const char *
+notmuch_config_get_log_path (notmuch_config_t *config,
+			     const char *name);
+
 notmuch_bool_t
 debugger_is_active (void);
 
diff --git a/notmuch-config.c b/notmuch-config.c
index cf30603..c01e8f4 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -562,3 +562,18 @@ notmuch_config_set_new_tags (notmuch_config_t *config,
     config->new_tags = NULL;
 }
 
+const char *
+notmuch_config_get_log_path (notmuch_config_t *config, const char *name)
+{
+    char *path, *rpath;
+
+    path= g_key_file_get_string (config->key_file,
+				 "log", name, NULL);
+    if (path != NULL) {
+	rpath = talloc_strdup (config, path);
+	free (path);
+	return rpath;
+    } else {
+	return NULL;
+    }
+}
-- 
1.7.1



More information about the notmuch mailing list