ld_log_db.c

Go to the documentation of this file.
00001 #include "ld_private.h"
00002 
00003 #include "ac_app_context.h"
00004 #include "application_config.h"
00005 #include "er_errors.h"
00006 #include "ld_create.h"
00007 #include "ut_compress.h"
00008 
00009 #include "common/logging.h"
00010 #include "common/platform_config.h"
00011 #include "common/threading.h"
00012 
00013 #include <glib/gprintf.h>
00014 
00015 #include <errno.h>
00016 #include <stdarg.h> // va_list
00017 #include <stdlib.h>
00018 #include <string.h> /* memset() */
00019 #include <time.h>
00020 
00021 static void 
00022 log_db_close_session (LogDb * self)
00023 {
00024   if (self->db) {
00025     destroy_sql_statements(self);
00026 
00027     // Note that prepared statements and BLOB handles must be
00028     // freed separately.
00029     int errCode = sqlite3_close(self->db);
00030 #if __DO_LOGGING__
00031     if (errCode) {
00032       // A close failure is probably a programming error, so we
00033       // shall log it.
00034       logg("sqlite3_close failure %d", errCode);
00035     }
00036 #endif
00037     self->db = NULL;
00038   }
00039 }
00040 
00041 static gboolean 
00042 log_db_open_session (LogDb * self, GError ** error)
00043 {
00044   // This still allocates a handle, except for those cases in which
00045   // the memory for the handle cannot be allocated. We can hence get
00046   // an error message if "db" is non-NULL.
00047   int errCode = sqlite3_open(LOGDB_FILE, &self->db);
00048   if (errCode) {
00049     if (error)
00050       *error = gx_error_new(domain_cl2app, code_database_open, 
00051           "error opening database '%s': %s (%d)", 
00052           LOGDB_FILE, 
00053           sqlite_get_error_string(self->db), errCode);
00054     if (self->db) {
00055       log_db_close_session(self);
00056     }
00057     return FALSE;
00058   }
00059 
00060   if (!prepare_sql_statements(self, error)) {
00061     log_db_close_session(self);
00062     return FALSE;
00063   }
00064   
00065   return TRUE;
00066 }
00067 
00068 // text:: A zero-terminated string, in UTF-8. Ownership is not
00069 //        taken, and the string need not persist after the call.
00070 static gboolean log_text_to_db(LogDb* self, 
00071              const char* text,
00072              sqlite3_stmt* stmt,
00073              const char* errorFmt,
00074              GError** error)
00075 {
00076   assert_error_unset(error);
00077 
00078   logt(text);
00079 
00080   // strftime('%s', 'now') evaluates to Unix time in SQL so we could
00081   // possibly evaluate that, but this might only happen during
00082   // preparation, which is not okay in the general case.
00083   time_t now = time(NULL);
00084   if (now == -1) {
00085     er_log_errno(er_FATAL, "time()");
00086   }
00087   if (sqlite3_bind_int(stmt, 1, now)) {
00088     goto fail;
00089   }
00090 
00091   // If we use SQLITE_TRANSIENT instead of SQLITE_STATIC, the text has
00092   // to persist "during all subsequent calls to sqlite3_step() on the
00093   // statement handle".
00094   if (sqlite3_bind_text(stmt, 2, text, strlen(text), SQLITE_STATIC)) {
00095     goto fail;
00096   }
00097 
00098 #define set_sqlite_error {            \
00099     if (error)                \
00100       *error = gx_error_new(domain_cl2app, code_database_command, errorFmt, \
00101           sqlite3_errmsg(self->db), sqlite3_errcode(self->db)); \
00102   }
00103 
00104   if (sqlite3_step(stmt) != SQLITE_DONE) {
00105     set_sqlite_error; // capture original error
00106     sqlite3_reset(stmt); // necessary due to SQLITE_STATIC
00107     return FALSE;
00108   }
00109 
00110   if (sqlite3_reset(stmt)) {
00111     goto fail;
00112   }
00113 
00114   return TRUE;
00115 
00116  fail:
00117   set_sqlite_error;
00118   return FALSE;
00119 }
00120 
00121 LogDb * 
00122 LogDb_new (GError ** error)
00123 {
00124   assert_error_unset(error);
00125 
00126   if (G_UNLIKELY(!ensure_log_db_created(error)))
00127     return NULL;
00128 
00129   LogDb* self = g_try_new0(LogDb, 1);
00130   if (G_UNLIKELY(!self)) {
00131     if (error) *error = gx_error_no_memory;
00132     return NULL;
00133   }
00134   
00135   if (G_UNLIKELY(!log_db_open_session(self, error))) {
00136     LogDb_destroy(self);
00137     return NULL;
00138   }
00139   
00140   return self;
00141 }
00142 
00143 void 
00144 LogDb_destroy (LogDb * self)
00145 {
00146   if (self) {
00147     log_db_close_session(self);
00148     g_free(self);
00149   }
00150 }
00151 
00152 gboolean 
00153 log_db_log_status_direct (LogDb * self, GError ** error, const char * text)
00154 {
00155   return log_text_to_db(self, text, self->stmts.statusStmt,
00156       "failed to log status: %s (%d)", error);
00157 }
00158 
00159 gboolean 
00160 log_db_log_status (LogDb * self, GError ** error, const char * fmt, ...)
00161 {
00162   {
00163     gchar* buf = NULL;
00164 
00165     SET_TRAP_OOM(goto nomemory);
00166     {
00167       va_list argp;
00168       va_start(argp, fmt);
00169       g_vasprintf(&buf, fmt, argp);
00170       va_end(argp); // any cleanup
00171     }
00172     UNSET_TRAP_OOM();
00173 
00174     gboolean res = log_db_log_status_direct(self, error, buf);
00175     g_free(buf);
00176     return res;
00177 
00178 #if HAVE_TRAP_OOM
00179   nomemory:
00180     g_free(buf);
00181     if (error) *error = gx_error_no_memory;
00182     return FALSE;
00183 #endif
00184   }
00185 }
00186 
00187 gboolean 
00188 log_db_take_snapshot (LogDb * self, gchar * pathname, 
00189           gboolean * renamed, GError ** error)
00190 {
00191   *renamed = FALSE;
00192 
00193   log_db_close_session(self);
00194 
00195   if (rename(LOGDB_FILE, pathname)) {
00196     if (error)
00197       *error = gx_error_new(domain_posix, errno, 
00198           "failed to rename '%s' as '%s': %s (%d)", 
00199           LOGDB_FILE, pathname, strerror(errno), errno);
00200     return FALSE;
00201   }
00202   *renamed = TRUE;
00203 
00204   if (g_file_test(LOGDB_FILE, G_FILE_TEST_EXISTS)) {
00205     logg("Oops, file '%s' still exists!", LOGDB_FILE);
00206     er_fatal_general;
00207   }
00208 
00209 #if __FEATURE_COMPRESS_LOGS__
00210   if (ac_STATIC_GET(compress_logs)) {
00211     logt("compressing logfile");
00212     if (!compress_file(pathname, error)) {
00213       return FALSE;
00214     }
00215   }
00216 #endif
00217 
00218   if (!create_log_db(error)) {
00219     return FALSE;
00220   }
00221 
00222   if (!log_db_open_session(self, error)) {
00223     return FALSE;
00224   }
00225 
00226   return TRUE;
00227 }
00228 
00229 /**
00230 
00231 Copyright 2010 Helsinki Institute for Information Technology (HIIT)
00232 and the authors. All rights reserved.
00233 
00234 Authors: Tero Hasu <tero.hasu@hut.fi>
00235 
00236 Permission is hereby granted, free of charge, to any person
00237 obtaining a copy of this software and associated documentation files
00238 (the "Software"), to deal in the Software without restriction,
00239 including without limitation the rights to use, copy, modify, merge,
00240 publish, distribute, sublicense, and/or sell copies of the Software,
00241 and to permit persons to whom the Software is furnished to do so,
00242 subject to the following conditions:
00243 
00244 The above copyright notice and this permission notice shall be
00245 included in all copies or substantial portions of the Software.
00246 
00247 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00248 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00249 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00250 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
00251 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00252 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00253 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00254 SOFTWARE.
00255 
00256  **/

ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:54 2011 by Doxygen 1.6.1