diff options
Diffstat (limited to 'mps/code')
| -rw-r--r-- | mps/code/eventsql.c | 405 |
1 files changed, 346 insertions, 59 deletions
diff --git a/mps/code/eventsql.c b/mps/code/eventsql.c index 23a50f8063f..f1905231e3a 100644 --- a/mps/code/eventsql.c +++ b/mps/code/eventsql.c | |||
| @@ -5,9 +5,26 @@ | |||
| 5 | 5 | ||
| 6 | #include <stdio.h> | 6 | #include <stdio.h> |
| 7 | #include <stdlib.h> | 7 | #include <stdlib.h> |
| 8 | #include <string.h> | ||
| 8 | #include <sqlite3.h> | 9 | #include <sqlite3.h> |
| 9 | 10 | ||
| 10 | unsigned int verbosity = 4; | 11 | #define DATABASE_NAME_ENVAR "MPS_EVENT_DATABASE" |
| 12 | #define DEFAULT_DATABASE_NAME "mpsevent.db" | ||
| 13 | |||
| 14 | #define TELEMETRY_FILENAME_ENVAR "MPS_TELEMETRY_FILENAME" | ||
| 15 | #define DEFAULT_TELEMETRY_FILENAME "mpsio.log" | ||
| 16 | |||
| 17 | /* we output rows of dots. One dot per SMALL_TICK events, | ||
| 18 | * BIG_TICK dots per row. */ | ||
| 19 | |||
| 20 | #define SMALL_TICK 1000 | ||
| 21 | #define BIG_TICK 50 | ||
| 22 | |||
| 23 | /* Utility code for logging to stderr with multiple log levels, | ||
| 24 | * and for reporting errors. | ||
| 25 | */ | ||
| 26 | |||
| 27 | unsigned int verbosity = 4; /* TODO command-line -v switches */ | ||
| 11 | 28 | ||
| 12 | #define LOG_ALWAYS 0 | 29 | #define LOG_ALWAYS 0 |
| 13 | #define LOG_OFTEN 1 | 30 | #define LOG_OFTEN 1 |
| @@ -33,8 +50,6 @@ static void log(unsigned int level, const char *format, ...) | |||
| 33 | va_end(args); | 50 | va_end(args); |
| 34 | } | 51 | } |
| 35 | 52 | ||
| 36 | #if 0 /* UNUSED */ | ||
| 37 | |||
| 38 | static void error(const char *format, ...) | 53 | static void error(const char *format, ...) |
| 39 | { | 54 | { |
| 40 | va_list args; | 55 | va_list args; |
| @@ -44,7 +59,6 @@ static void error(const char *format, ...) | |||
| 44 | va_end(args); | 59 | va_end(args); |
| 45 | exit(1); | 60 | exit(1); |
| 46 | } | 61 | } |
| 47 | #endif /* UNUSED */ | ||
| 48 | 62 | ||
| 49 | static void sqlite_error(int res, sqlite3 *db, const char *format, ...) | 63 | static void sqlite_error(int res, sqlite3 *db, const char *format, ...) |
| 50 | { | 64 | { |
| @@ -57,15 +71,18 @@ static void sqlite_error(int res, sqlite3 *db, const char *format, ...) | |||
| 57 | exit(1); | 71 | exit(1); |
| 58 | } | 72 | } |
| 59 | 73 | ||
| 60 | static void openDatabase(sqlite3 **dbReturn) | 74 | /* openDatabase(p) opens the database file and returns a SQLite 3 |
| 75 | * database connection object. */ | ||
| 76 | |||
| 77 | static sqlite3 *openDatabase(void) | ||
| 61 | { | 78 | { |
| 62 | sqlite3 *db; | 79 | sqlite3 *db; |
| 63 | int res; | 80 | int res; |
| 64 | 81 | ||
| 65 | const char *filename = getenv("MPS_EVENT_DATABASE"); | 82 | const char *filename = getenv(DATABASE_NAME_ENVAR); |
| 66 | if(filename == NULL) | 83 | if(filename == NULL) |
| 67 | filename = "mpsevent.db"; | 84 | filename = DEFAULT_DATABASE_NAME; |
| 68 | log(LOG_OFTEN, "Opening %s.", filename); | 85 | log(LOG_ALWAYS, "Opening %s.", filename); |
| 69 | 86 | ||
| 70 | res = sqlite3_open_v2(filename, | 87 | res = sqlite3_open_v2(filename, |
| 71 | &db, | 88 | &db, |
| @@ -74,8 +91,99 @@ static void openDatabase(sqlite3 **dbReturn) | |||
| 74 | 91 | ||
| 75 | if (res != SQLITE_OK) | 92 | if (res != SQLITE_OK) |
| 76 | sqlite_error(res, db, "Opening %s failed", filename); | 93 | sqlite_error(res, db, "Opening %s failed", filename); |
| 77 | *dbReturn = db; | 94 | return db; |
| 78 | return; | 95 | } |
| 96 | |||
| 97 | /* closeDatabase(db) closes the database opened by openDatabase(). */ | ||
| 98 | |||
| 99 | static void closeDatabase(sqlite3 *db) | ||
| 100 | { | ||
| 101 | int res = sqlite3_close(db); | ||
| 102 | if (res != SQLITE_OK) | ||
| 103 | sqlite_error(res, db, "Closing database failed"); | ||
| 104 | log(LOG_ALWAYS, "Closed database."); | ||
| 105 | } | ||
| 106 | |||
| 107 | /* We need to be able to test for the existence of a table. The | ||
| 108 | * SQLite3 API seems to have no way to explore metadata like this, | ||
| 109 | * unless it is compiled in a particular way (in which case the | ||
| 110 | * function sqlite3_table_column_metadata could be used). Without | ||
| 111 | * that assistance, we can use a simple SQL trick (which could also | ||
| 112 | * tell us the number of rows in the table if we cared): */ | ||
| 113 | |||
| 114 | static int tableExists(sqlite3* db, const char *tableName) | ||
| 115 | { | ||
| 116 | const char *format = "SELECT SUM(1) FROM %s"; | ||
| 117 | char *sql; | ||
| 118 | int res; | ||
| 119 | |||
| 120 | sql = malloc(strlen(format) + strlen(tableName)); | ||
| 121 | if (!sql) | ||
| 122 | error("Out of memory."); | ||
| 123 | sprintf(sql, format, tableName); | ||
| 124 | res = sqlite3_exec(db, | ||
| 125 | sql, | ||
| 126 | NULL, /* put in a callback here if we really want to know the number of rows */ | ||
| 127 | NULL, /* callback closure */ | ||
| 128 | NULL); /* error messages handled by sqlite_error */ | ||
| 129 | switch(res) { | ||
| 130 | case SQLITE_OK: | ||
| 131 | return 1; /* table exists */ | ||
| 132 | break; | ||
| 133 | case SQLITE_ERROR: | ||
| 134 | return 0; /* table does not exist; we can | ||
| 135 | probably do a better test for this case. */ | ||
| 136 | break; | ||
| 137 | default: | ||
| 138 | sqlite_error(res, db, "Table test failed: %s", tableName); | ||
| 139 | } | ||
| 140 | /* UNREACHED */ | ||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Unit test for tableExists() */ | ||
| 145 | |||
| 146 | static const char *tableTests[] = { | ||
| 147 | "event_kind", | ||
| 148 | "spong", | ||
| 149 | "EVENT_SegSplit", | ||
| 150 | }; | ||
| 151 | |||
| 152 | static void testTableExists(sqlite3 *db) | ||
| 153 | { | ||
| 154 | int i; | ||
| 155 | for (i=0; i < (sizeof(tableTests)/sizeof(tableTests[0])); ++i) { | ||
| 156 | if (tableExists(db, tableTests[i])) | ||
| 157 | printf("Table exists: %s\n", tableTests[i]); | ||
| 158 | else | ||
| 159 | printf("Table does not exist: %s\n", tableTests[i]); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | /* Utility functions for SQLite statements. */ | ||
| 164 | |||
| 165 | static sqlite3_stmt *prepareStatement(sqlite3 *db, | ||
| 166 | const char *sql) | ||
| 167 | { | ||
| 168 | int res; | ||
| 169 | sqlite3_stmt *statement; | ||
| 170 | log(LOG_SELDOM, "Preparing statement %s", sql); | ||
| 171 | res = sqlite3_prepare_v2(db, sql, | ||
| 172 | -1, /* prepare whole string as statement */ | ||
| 173 | &statement, | ||
| 174 | NULL); | ||
| 175 | if (res != SQLITE_OK) | ||
| 176 | sqlite_error(res, db, "statementpreparation failed: %s", sql); | ||
| 177 | return statement; | ||
| 178 | } | ||
| 179 | |||
| 180 | static void finalizeStatement(sqlite3 *db, | ||
| 181 | sqlite3_stmt *statement) | ||
| 182 | { | ||
| 183 | int res; | ||
| 184 | res = sqlite3_finalize(statement); | ||
| 185 | if (res != SQLITE_OK) | ||
| 186 | sqlite_error(res, db, "event_type finalize failed"); | ||
| 79 | } | 187 | } |
| 80 | 188 | ||
| 81 | /* Macro magic to make a CREATE TABLE statement for each event type. */ | 189 | /* Macro magic to make a CREATE TABLE statement for each event type. */ |
| @@ -94,7 +202,10 @@ static void openDatabase(sqlite3 **dbReturn) | |||
| 94 | #define EVENT_TABLE_CREATE(X, name, code, always, kind) \ | 202 | #define EVENT_TABLE_CREATE(X, name, code, always, kind) \ |
| 95 | "CREATE TABLE IF NOT EXISTS EVENT_" #name " ( " \ | 203 | "CREATE TABLE IF NOT EXISTS EVENT_" #name " ( " \ |
| 96 | EVENT_##name##_PARAMS(EVENT_PARAM_SQL_COLUMN, X) \ | 204 | EVENT_##name##_PARAMS(EVENT_PARAM_SQL_COLUMN, X) \ |
| 97 | "time INTEGER ) ", | 205 | "time INTEGER, " \ |
| 206 | "log_serial INTEGER)", | ||
| 207 | |||
| 208 | /* An array of table-creation statement strings. */ | ||
| 98 | 209 | ||
| 99 | const char *createStatements[] = { | 210 | const char *createStatements[] = { |
| 100 | "CREATE TABLE IF NOT EXISTS event_kind (name TEXT," | 211 | "CREATE TABLE IF NOT EXISTS event_kind (name TEXT," |
| @@ -107,11 +218,37 @@ const char *createStatements[] = { | |||
| 107 | " kind INTEGER," | 218 | " kind INTEGER," |
| 108 | " FOREIGN KEY (kind) REFERENCES event_kind(enum));", | 219 | " FOREIGN KEY (kind) REFERENCES event_kind(enum));", |
| 109 | 220 | ||
| 221 | "CREATE TABLE IF NOT EXISTS event_log (name TEXT," | ||
| 222 | " file_id INTEGER," | ||
| 223 | " size INTEGER," | ||
| 224 | " time_sec INTEGER," | ||
| 225 | " time_nsec INTEGER," | ||
| 226 | " completed INTEGER," | ||
| 227 | " serial INTEGER PRIMARY KEY AUTOINCREMENT)", | ||
| 228 | |||
| 110 | EVENT_LIST(EVENT_TABLE_CREATE, X) | 229 | EVENT_LIST(EVENT_TABLE_CREATE, X) |
| 111 | }; | 230 | }; |
| 112 | 231 | ||
| 113 | const char *populateStatements[] = { | 232 | /* makeTables makes all the tables. */ |
| 114 | }; | 233 | |
| 234 | static void makeTables(sqlite3 *db) | ||
| 235 | { | ||
| 236 | int i; | ||
| 237 | int res; | ||
| 238 | |||
| 239 | for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) { | ||
| 240 | log(LOG_SOMETIMES, "Creating tables. SQL command: %s", createStatements[i]); | ||
| 241 | res = sqlite3_exec(db, | ||
| 242 | createStatements[i], | ||
| 243 | NULL, /* No callback */ | ||
| 244 | NULL, /* No callback closure */ | ||
| 245 | NULL); /* error messages handled by sqlite_error */ | ||
| 246 | if (res != SQLITE_OK) | ||
| 247 | sqlite_error(res, db, "Table creation failed: %s", createStatements[i]); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | /* Populate the metadata "glue" tables event_kind and event_type. */ | ||
| 115 | 252 | ||
| 116 | #define EVENT_KIND_DO_INSERT(X, name, description) \ | 253 | #define EVENT_KIND_DO_INSERT(X, name, description) \ |
| 117 | res = sqlite3_bind_text(statement, 1, #name, -1, SQLITE_STATIC); \ | 254 | res = sqlite3_bind_text(statement, 1, #name, -1, SQLITE_STATIC); \ |
| @@ -155,77 +292,227 @@ const char *populateStatements[] = { | |||
| 155 | if (res != SQLITE_OK) \ | 292 | if (res != SQLITE_OK) \ |
| 156 | sqlite_error(res, db, "Couldn't reset event_type insert statement."); | 293 | sqlite_error(res, db, "Couldn't reset event_type insert statement."); |
| 157 | 294 | ||
| 158 | static void fillTables(sqlite3 *db) | 295 | static void fillGlueTables(sqlite3 *db) |
| 159 | { | 296 | { |
| 160 | int i; | 297 | int i; |
| 298 | Res res; | ||
| 161 | sqlite3_stmt *statement; | 299 | sqlite3_stmt *statement; |
| 162 | int res; | 300 | |
| 163 | 301 | statement = prepareStatement(db, | |
| 164 | res = sqlite3_prepare_v2(db, | 302 | "INSERT OR IGNORE INTO event_kind (name, description, enum)" |
| 165 | "INSERT OR IGNORE INTO event_kind (name, description, enum)" | 303 | "VALUES (?, ?, ?)"); |
| 166 | "VALUES (?, ?, ?)", | ||
| 167 | -1, /* prepare whole string as statement */ | ||
| 168 | &statement, | ||
| 169 | NULL); | ||
| 170 | if (res != SQLITE_OK) | ||
| 171 | sqlite_error(res, db, "event_kind preparation failed"); | ||
| 172 | 304 | ||
| 173 | i = 0; | 305 | i = 0; |
| 174 | EventKindENUM(EVENT_KIND_DO_INSERT, X); | 306 | EventKindENUM(EVENT_KIND_DO_INSERT, X); |
| 175 | 307 | ||
| 176 | res = sqlite3_finalize(statement); | 308 | finalizeStatement(db, statement); |
| 177 | if (res != SQLITE_OK) | ||
| 178 | sqlite_error(res, db, "event_kind finalize failed"); | ||
| 179 | |||
| 180 | res = sqlite3_prepare_v2(db, | ||
| 181 | "INSERT OR IGNORE INTO event_type (name, code, always, kind)" | ||
| 182 | "VALUES (?, ?, ?, ?)", | ||
| 183 | -1, /* prepare whole string as statement */ | ||
| 184 | &statement, | ||
| 185 | NULL); | ||
| 186 | if (res != SQLITE_OK) | ||
| 187 | sqlite_error(res, db, "event_type preparation failed"); | ||
| 188 | 309 | ||
| 310 | statement = prepareStatement(db, | ||
| 311 | "INSERT OR IGNORE INTO event_type (name, code, always, kind)" | ||
| 312 | "VALUES (?, ?, ?, ?)"); | ||
| 189 | EVENT_LIST(EVENT_TYPE_DO_INSERT, X); | 313 | EVENT_LIST(EVENT_TYPE_DO_INSERT, X); |
| 190 | 314 | ||
| 191 | res = sqlite3_finalize(statement); | 315 | finalizeStatement(db, statement); |
| 192 | if (res != SQLITE_OK) | ||
| 193 | sqlite_error(res, db, "event_type finalize failed"); | ||
| 194 | } | 316 | } |
| 195 | 317 | ||
| 196 | static void makeTables(sqlite3 *db) | 318 | /* Populate the actual event tables. */ |
| 319 | |||
| 320 | #define EVENT_TYPE_DECLARE_STATEMENT(X, name, code, always, kind) \ | ||
| 321 | sqlite3_stmt *stmt_##name; | ||
| 322 | |||
| 323 | #define EVENT_PARAM_PREPARE_IDENT(X, index, sort, ident) "\"" #ident "\", " | ||
| 324 | |||
| 325 | #define EVENT_PARAM_PREPARE_PLACE(X, index, sort, ident) "?, " | ||
| 326 | |||
| 327 | #define EVENT_TYPE_PREPARE_STATEMENT(X, name, code, always, kind) \ | ||
| 328 | stmt_##name = \ | ||
| 329 | prepareStatement(db, \ | ||
| 330 | "INSERT INTO EVENT_" #name " (" \ | ||
| 331 | EVENT_##name##_PARAMS(EVENT_PARAM_PREPARE_IDENT, X) \ | ||
| 332 | "log_serial, time) VALUES (" \ | ||
| 333 | EVENT_##name##_PARAMS(EVENT_PARAM_PREPARE_PLACE,X) \ | ||
| 334 | "?, ?)"); | ||
| 335 | |||
| 336 | #define EVENT_TYPE_FINALIZE_STATEMENT(X, name, code, always, kind) \ | ||
| 337 | finalizeStatement(db, stmt_##name); | ||
| 338 | |||
| 339 | #define EVENT_PARAM_BIND_INTEGER(name, index, sort, ident) \ | ||
| 340 | res = sqlite3_bind_int64(statement, index+1, (unsigned long) event->name.f##index); | ||
| 341 | |||
| 342 | #define EVENT_PARAM_BIND_REAL(name, index, sort, ident) \ | ||
| 343 | res = sqlite3_bind_double(statement, index+1, event->name.f##index); | ||
| 344 | |||
| 345 | #define EVENT_PARAM_BIND_TEXT(name, index, sort, ident) \ | ||
| 346 | res = sqlite3_bind_text(statement, index+1, event->name.f##index, -1, SQLITE_STATIC); | ||
| 347 | |||
| 348 | #define EVENT_PARAM_BIND_A EVENT_PARAM_BIND_INTEGER | ||
| 349 | #define EVENT_PARAM_BIND_P EVENT_PARAM_BIND_INTEGER | ||
| 350 | #define EVENT_PARAM_BIND_U EVENT_PARAM_BIND_INTEGER | ||
| 351 | #define EVENT_PARAM_BIND_W EVENT_PARAM_BIND_INTEGER | ||
| 352 | #define EVENT_PARAM_BIND_D EVENT_PARAM_BIND_REAL | ||
| 353 | #define EVENT_PARAM_BIND_S EVENT_PARAM_BIND_TEXT | ||
| 354 | #define EVENT_PARAM_BIND_B EVENT_PARAM_BIND_INTEGER | ||
| 355 | |||
| 356 | #define EVENT_PARAM_BIND(name, index, sort, ident) \ | ||
| 357 | EVENT_PARAM_BIND_##sort (name, index, sort, ident) \ | ||
| 358 | if (res != SQLITE_OK) \ | ||
| 359 | sqlite_error(res, db, "Event " #name " bind of ident " #ident "failed."); \ | ||
| 360 | last_index = index+1; | ||
| 361 | |||
| 362 | |||
| 363 | #define EVENT_TYPE_WRITE_SQL(X, name, code, always, kind) \ | ||
| 364 | case code: \ | ||
| 365 | { \ | ||
| 366 | sqlite3_stmt *statement = stmt_##name; \ | ||
| 367 | int last_index = 0; \ | ||
| 368 | int res; \ | ||
| 369 | /* bind all the parameters of this particular event with macro magic. */ \ | ||
| 370 | EVENT_##name##_PARAMS(EVENT_PARAM_BIND, name) \ | ||
| 371 | /* bind the fields we store for every event */ \ | ||
| 372 | res = sqlite3_bind_int64(statement, last_index+1, log_serial); \ | ||
| 373 | if (res != SQLITE_OK) \ | ||
| 374 | sqlite_error(res, db, "Event " #name " bind of log_serial failed."); \ | ||
| 375 | res = sqlite3_bind_int64(statement, last_index+2, clock); \ | ||
| 376 | if (res != SQLITE_OK) \ | ||
| 377 | sqlite_error(res, db, "Event " #name " bind of clock failed."); \ | ||
| 378 | res = sqlite3_step(statement); \ | ||
| 379 | if (res != SQLITE_DONE) \ | ||
| 380 | sqlite_error(res, db, "insert of event \"" #name "\" failed."); \ | ||
| 381 | res = sqlite3_reset(statement); \ | ||
| 382 | if (res != SQLITE_OK) \ | ||
| 383 | sqlite_error(res, db, "Couldn't reset insert statement for \"" #name "\"."); \ | ||
| 384 | } \ | ||
| 385 | break; | ||
| 386 | |||
| 387 | /* readLog -- read and parse log | ||
| 388 | */ | ||
| 389 | |||
| 390 | static void readLog(EventProc proc, | ||
| 391 | sqlite3 *db) | ||
| 197 | { | 392 | { |
| 198 | int i; | 393 | int log_serial = 0; /* TODO get this from event_log table */ |
| 199 | int res; | 394 | size_t eventCount = 0; |
| 200 | 395 | /* declare statements for every event type */ | |
| 201 | for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) { | 396 | EVENT_LIST(EVENT_TYPE_DECLARE_STATEMENT, X); |
| 202 | log(LOG_SOMETIMES, "Creating tables. SQL command: %s", createStatements[i]); | 397 | |
| 203 | res = sqlite3_exec(db, | 398 | /* prepare statements for every event type */ |
| 204 | createStatements[i], | 399 | EVENT_LIST(EVENT_TYPE_PREPARE_STATEMENT, X); |
| 205 | NULL, /* No callback */ | 400 | |
| 206 | NULL, /* No callback closure */ | 401 | while (TRUE) { /* loop for each event */ |
| 207 | NULL); /* error messages handled by sqlite_error */ | 402 | Event event; |
| 208 | if (res != SQLITE_OK) | 403 | EventCode code; |
| 209 | sqlite_error(res, db, "Table creation failed: %s", createStatements[i]); | 404 | EventClock clock; |
| 405 | Res res; | ||
| 406 | |||
| 407 | /* Read and parse event. */ | ||
| 408 | res = EventRead(&event, proc); | ||
| 409 | if (res == ResFAIL) break; /* eof */ | ||
| 410 | if (res != ResOK) error("Truncated log"); | ||
| 411 | clock = event->any.clock; | ||
| 412 | code = event->any.code; | ||
| 413 | |||
| 414 | /* Write event to SQLite. */ | ||
| 415 | switch (code) { | ||
| 416 | EVENT_LIST(EVENT_TYPE_WRITE_SQL, X); | ||
| 417 | } | ||
| 418 | EventDestroy(proc, event); | ||
| 419 | eventCount++; | ||
| 420 | if ((eventCount % SMALL_TICK) == 0) { | ||
| 421 | fprintf(stdout, "."); | ||
| 422 | fflush(stdout); | ||
| 423 | if (((eventCount / SMALL_TICK) % BIG_TICK) == 0) { | ||
| 424 | log(LOG_OFTEN, "%lu events.", (unsigned long)eventCount); | ||
| 425 | } | ||
| 426 | } | ||
| 210 | } | 427 | } |
| 428 | log(LOG_OFTEN, "Wrote %lu events to SQL.", (unsigned long)eventCount); | ||
| 429 | /* finalize all the statements */ | ||
| 430 | EVENT_LIST(EVENT_TYPE_FINALIZE_STATEMENT, X); | ||
| 211 | } | 431 | } |
| 212 | 432 | ||
| 213 | void closeDatabase(sqlite3 *db) | 433 | |
| 434 | |||
| 435 | static Res logReader(void *file, void *p, size_t len) | ||
| 214 | { | 436 | { |
| 215 | int res = sqlite3_close(db); | 437 | size_t n; |
| 216 | if (res != SQLITE_OK) | 438 | |
| 217 | sqlite_error(res, db, "Closing database failed"); | 439 | n = fread(p, 1, len, (FILE *)file); |
| 218 | log(LOG_ALWAYS, "Closed database."); | 440 | return (n < len) ? (feof((FILE *)file) ? ResFAIL : ResIO) : ResOK; |
| 441 | } | ||
| 442 | |||
| 443 | #if 0 | ||
| 444 | /* TODO. Find out whether the database already contains this log file. | ||
| 445 | * Should probably also fopen it, and use fileno() and fstat() */ | ||
| 446 | |||
| 447 | static int logFileSerial(sqlite3 *db, const char *filename) | ||
| 448 | { | ||
| 449 | struct stat st; | ||
| 450 | sqlite3_stmt *statement; | ||
| 451 | |||
| 452 | stat(filename, &st); | ||
| 453 | statement = prepareStatement(db, | ||
| 454 | "SELECT serial, completed FROM event_log WHERE" | ||
| 455 | "file_id = ? AND size = ? AND time_sec = ? AND time_nsec = ?"); | ||
| 456 | /* TODO: Get stat results, look up in event_log, return accordingly */ | ||
| 457 | } | ||
| 458 | #endif | ||
| 459 | |||
| 460 | static FILE *openLog(void) | ||
| 461 | { | ||
| 462 | const char *filename; | ||
| 463 | FILE *input; | ||
| 464 | |||
| 465 | filename = getenv(TELEMETRY_FILENAME_ENVAR); | ||
| 466 | if(filename == NULL) | ||
| 467 | filename = DEFAULT_TELEMETRY_FILENAME; | ||
| 468 | input = fopen(filename, "rb"); | ||
| 469 | |||
| 470 | if (input == NULL) | ||
| 471 | error("unable to open %s", filename); | ||
| 472 | |||
| 473 | return input; | ||
| 474 | } | ||
| 475 | |||
| 476 | static void writeEventsToSQL(sqlite3 *db) | ||
| 477 | { | ||
| 478 | Res res; | ||
| 479 | EventProc proc; | ||
| 480 | FILE *input; | ||
| 481 | |||
| 482 | input = openLog(); | ||
| 483 | res = EventProcCreate(&proc, logReader, (void *)input); | ||
| 484 | if (res != ResOK) | ||
| 485 | error("Can't init EventProc module: error %d.", res); | ||
| 486 | |||
| 487 | readLog(proc, db); | ||
| 488 | |||
| 489 | EventProcDestroy(proc); | ||
| 490 | (void)fclose(input); | ||
| 219 | } | 491 | } |
| 220 | 492 | ||
| 221 | 493 | ||
| 222 | int main(void) | 494 | int main(void) |
| 223 | { | 495 | { |
| 224 | sqlite3 *db; | 496 | sqlite3 *db; |
| 497 | |||
| 498 | /* TODO: command line args | ||
| 499 | * -v verbosity; | ||
| 500 | * -f log filename; | ||
| 501 | * -d database filename; | ||
| 502 | * -r rebuild glue tables (by dropping them); | ||
| 503 | * -t unit tests? | ||
| 504 | */ | ||
| 225 | 505 | ||
| 226 | openDatabase(&db); | 506 | db = openDatabase(); |
| 227 | makeTables(db); | 507 | makeTables(db); |
| 228 | fillTables(db); | 508 | fillGlueTables(db); |
| 509 | |||
| 510 | writeEventsToSQL(db); | ||
| 511 | |||
| 512 | if (0) { | ||
| 513 | testTableExists(db); | ||
| 514 | } | ||
| 515 | |||
| 229 | closeDatabase(db); | 516 | closeDatabase(db); |
| 230 | return 0; | 517 | return 0; |
| 231 | } | 518 | } |