Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 0A41040D152 for ; Sun, 24 Oct 2010 14:02:08 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -2.6 X-Spam-Level: X-Spam-Status: No, score=-2.6 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, RCVD_IN_DNSWL_LOW=-0.7] autolearn=ham Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Re-v-T3Ks9Tz for ; Sun, 24 Oct 2010 14:01:51 -0700 (PDT) Received: from tempo.its.unb.ca (tempo.its.unb.ca [131.202.1.21]) by olra.theworths.org (Postfix) with ESMTP id BBEC840D157 for ; Sun, 24 Oct 2010 14:01:14 -0700 (PDT) Received: from rocinante.cs.unb.ca (fctnnbsc30w-142167176217.pppoe-dynamic.High-Speed.nb.bellaliant.net [142.167.176.217]) (authenticated bits=0) by tempo.its.unb.ca (8.13.8/8.13.8) with ESMTP id o9OL1BEg017810 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NO); Sun, 24 Oct 2010 18:01:14 -0300 Received: from bremner by rocinante.cs.unb.ca with local (Exim 4.72) (envelope-from ) id 1PA7gl-0006Ok-Ci; Sun, 24 Oct 2010 18:01:11 -0300 From: david@tethera.net To: notmuch@notmuchmail.org Subject: [PATCH 1/4] Initial implementation of low level logging routines. Date: Sun, 24 Oct 2010 18:01:03 -0300 Message-Id: <1287954066-24512-2-git-send-email-david@tethera.net> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1286803617-17328-1-git-send-email-david@tethera.net> References: <1286803617-17328-1-git-send-email-david@tethera.net> Cc: David Bremner X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 24 Oct 2010 21:02:08 -0000 From: David Bremner 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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