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