source: branches/gsoc11-statistics/base/src/cregistry/registry.c @ 140222

Last change on this file since 140222 was 105085, checked in by snc@…, 7 years ago

merge from trunk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.5 KB
Line 
1/*
2 * registry.c
3 * $Id: registry.c 105085 2013-04-09 18:46:31Z snc@macports.org $
4 * vim:expandtab:tw=80
5 *
6 * Copyright (c) 2007 Chris Pickel <sfiera@macports.org>
7 * Copyright (c) 2012 The MacPorts Project
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#if HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35#include "entry.h"
36#include "file.h"
37#include "sql.h"
38
39#include <stdio.h>
40#include <unistd.h>
41#include <stdlib.h>
42#include <libgen.h>
43#include <string.h>
44#include <stdarg.h>
45#include <sqlite3.h>
46#include <sys/stat.h>
47#include <errno.h>
48
49/*
50 * TODO: maybe all the errPtrs could be made a property of `reg_registry`
51 *       instead, and be destroyed with it? That would make memory-management
52 *       much easier for errors, and there's no need to have more than one error
53 *       alive at any given time.
54 */
55
56/**
57 * Destroys a `reg_error` object. This should be called on any reg_error when a
58 * registry function returns a failure condition; depending on the function,
59 * failure could be false, negative, or null.
60 *
61 * @param [in] errPtr the error to destroy
62 */
63void reg_error_destruct(reg_error* errPtr) {
64    if (errPtr->free) {
65        errPtr->free(errPtr->description);
66    }
67}
68
69/**
70 * Sets `errPtr` according to the last error in `db`. Convenience function for
71 * internal use only. Sets the error code to REG_SQLITE_ERROR and sets an
72 * appropriate error description (including a query if non-NULL).
73 *
74 * @param [in] db      sqlite3 database connection which had the error
75 * @param [out] errPtr an error to write to
76 * @param [in] query   the query that this error occurred during
77 */
78void reg_sqlite_error(sqlite3* db, reg_error* errPtr, char* query) {
79    errPtr->code = REG_SQLITE_ERROR;
80    errPtr->free = (reg_error_destructor*)sqlite3_free;
81    if (query == NULL) {
82        errPtr->description = sqlite3_mprintf("sqlite error: %s (%d)",
83                sqlite3_errmsg(db), sqlite3_errcode(db));
84    } else {
85        errPtr->description = sqlite3_mprintf("sqlite error: %s (%d) while "
86                "executing query: %s", sqlite3_errmsg(db), sqlite3_errcode(db),
87                query);
88    }
89}
90
91void reg_throw(reg_error* errPtr, char* code, char* fmt, ...) {
92    va_list list;
93    va_start(list, fmt);
94    errPtr->description = sqlite3_vmprintf(fmt, list);
95    va_end(list);
96
97    errPtr->code = code;
98    errPtr->free = (reg_error_destructor*)sqlite3_free;
99}
100
101/**
102 * Creates a new registry object. To start using a registry, one must first be
103 * attached with `reg_attach`.
104 *
105 * @param [out] regPtr address of the allocated registry
106 * @param [out] errPtr on error, a description of the error that occurred
107 * @return             true if success; false if failure
108 */
109int reg_open(reg_registry** regPtr, reg_error* errPtr) {
110    reg_registry* reg = malloc(sizeof(reg_registry));
111    if (!reg) {
112        return 0;
113    }
114    if (sqlite3_open(NULL, &reg->db) == SQLITE_OK) {
115        /* Enable extended result codes, requires SQLite >= 3.3.8
116         * Check added for compatibility with Tiger. */
117#if SQLITE_VERSION_NUMBER >= 3003008
118        sqlite3_extended_result_codes(reg->db, 1);
119#endif
120
121        sqlite3_busy_timeout(reg->db, 25);
122
123        if (init_db(reg->db, errPtr)) {
124            reg->status = reg_none;
125            *regPtr = reg;
126            return 1;
127        }
128    } else {
129        reg_sqlite_error(reg->db, errPtr, NULL);
130    }
131    sqlite3_close(reg->db);
132    free(reg);
133    return 0;
134}
135
136/**
137 * Closes a registry object. Will detach if necessary.
138 *
139 * @param [in] reg     the registry to close
140 * @param [out] errPtr on error, a description of the error that occurred
141 * @return             true if success; false if failure
142 */
143int reg_close(reg_registry* reg, reg_error* errPtr) {
144    if ((reg->status & reg_attached) && !reg_detach(reg, errPtr)) {
145        return 0;
146    }
147    if (sqlite3_close(reg->db) == SQLITE_OK) {
148        free(reg);
149        return 1;
150    } else {
151        reg_throw(errPtr, REG_SQLITE_ERROR, "registry db not closed correctly "
152                "(%s)\n", sqlite3_errmsg(reg->db));
153        return 0;
154    }
155}
156
157/**
158 * Attaches a registry database to the registry object. Prior to calling this,
159 * the registry object is not actually connected to the registry. This function
160 * attaches it so it can be queried and manipulated.
161 *
162 * @param [in] reg     the registry to attach to
163 * @param [in] path    path to the registry db on disk
164 * @param [out] errPtr on error, a description of the error that occurred
165 * @return             true if success; false if failure
166 */
167int reg_attach(reg_registry* reg, const char* path, reg_error* errPtr) {
168    struct stat sb;
169    int initialized = 1; /* registry already exists */
170    int can_write = 1; /* can write to this location */
171    int result = 0;
172    if (reg->status & reg_attached) {
173        reg_throw(errPtr, REG_MISUSE, "a database is already attached to this "
174                "registry");
175        return 0;
176    }
177    if (stat(path, &sb) != 0) {
178        initialized = 0;
179        if (errno == ENOENT) {
180            char *dirc, *dname;
181            dirc = strdup(path);
182            dname = dirname(dirc);
183            if (stat(dname, &sb) != 0) {
184                can_write = 0;
185            }
186            free(dirc);
187        } else {
188            can_write = 0;
189        }
190    }
191    /* can_write is still true if one of the stat calls succeeded */
192    if (can_write) {
193        if (sb.st_uid == getuid()) {
194            if (!(sb.st_mode & S_IWUSR)) {
195                can_write = 0;
196            }
197        } else if (sb.st_gid == getgid()) {
198            if (!(sb.st_mode & S_IWGRP)) {
199                can_write = 0;
200            }
201        } else if (!(sb.st_mode & S_IWOTH) && getuid() != 0) {
202            can_write = 0;
203        }
204    }
205    if (initialized || can_write) {
206        sqlite3_stmt* stmt = NULL;
207        char* query = sqlite3_mprintf("ATTACH DATABASE '%q' AS registry", path);
208        int r;
209        do {
210            r = sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL);
211        } while (r == SQLITE_BUSY);
212        if (r == SQLITE_OK) {
213            /* XXX: Busy waiting, consider using sqlite3_busy_handler/timeout */
214            do {
215                sqlite3_step(stmt);
216                r = sqlite3_reset(stmt);
217                switch (r) {
218                    case SQLITE_OK:
219                        if (initialized || (create_tables(reg->db, errPtr))) {
220                            Tcl_InitHashTable(&reg->open_entries,
221                                    sizeof(sqlite_int64)/sizeof(int));
222                            Tcl_InitHashTable(&reg->open_files,
223                                    TCL_STRING_KEYS);
224                            reg->status |= reg_attached;
225                            result = 1;
226                        }
227                        break;
228                    case SQLITE_BUSY:
229                        break;
230                    default:
231                        reg_sqlite_error(reg->db, errPtr, query);
232                }
233            } while (r == SQLITE_BUSY);
234
235            sqlite3_finalize(stmt);
236            stmt = NULL;
237
238            if (result) {
239                result &= update_db(reg->db, errPtr);
240            }
241        } else {
242            reg_sqlite_error(reg->db, errPtr, query);
243        }
244        if (stmt) {
245            sqlite3_finalize(stmt);
246        }
247        sqlite3_free(query);
248    } else {
249        reg_throw(errPtr, REG_CANNOT_INIT, "port registry doesn't exist at "
250                "\"%q\" and couldn't write to this location", path);
251    }
252    return result;
253}
254
255/**
256 * Detaches a registry database from the registry object. This does some cleanup
257 * for an attached registry, then detaches it. Allocated `reg_entry` objects are
258 * deleted here.
259 *
260 * @param [in] reg     registry to detach from
261 * @param [out] errPtr on error, a description of the error that occurred
262 * @return             true if success; false if failure
263 */
264int reg_detach(reg_registry* reg, reg_error* errPtr) {
265    sqlite3_stmt* stmt = NULL;
266    int result = 0;
267    char* query = "DETACH DATABASE registry";
268    if (!(reg->status & reg_attached)) {
269        reg_throw(errPtr,REG_MISUSE,"no database is attached to this registry");
270        return 0;
271    }
272    if (sqlite3_prepare_v2(reg->db, query, -1, &stmt, NULL) == SQLITE_OK) {
273        int r;
274        reg_entry* entry;
275        Tcl_HashEntry* curr;
276        Tcl_HashSearch search;
277        /* XXX: Busy waiting, consider using sqlite3_busy_handler/timeout */
278        do {
279            sqlite3_step(stmt);
280            r = sqlite3_reset(stmt);
281            switch (r) {
282                case SQLITE_OK:
283                    for (curr = Tcl_FirstHashEntry(&reg->open_entries, &search);
284                            curr != NULL; curr = Tcl_NextHashEntry(&search)) {
285                        entry = Tcl_GetHashValue(curr);
286                        if (entry->proc) {
287                            free(entry->proc);
288                        }
289                        free(entry);
290                    }
291                    Tcl_DeleteHashTable(&reg->open_entries);
292                    for (curr = Tcl_FirstHashEntry(&reg->open_files, &search);
293                            curr != NULL; curr = Tcl_NextHashEntry(&search)) {
294                        reg_file* file = Tcl_GetHashValue(curr);
295
296                        free(file->proc);
297                        free(file->key.path);
298                        free(file);
299                    }
300                    Tcl_DeleteHashTable(&reg->open_files);
301                    reg->status &= ~reg_attached;
302                    result = 1;
303                    break;
304                case SQLITE_BUSY:
305                    break;
306                default:
307                    reg_sqlite_error(reg->db, errPtr, query);
308                    break;
309            }
310        } while (r == SQLITE_BUSY);
311    } else {
312        reg_sqlite_error(reg->db, errPtr, query);
313    }
314    if (stmt) {
315        sqlite3_finalize(stmt);
316    }
317    return result;
318}
319
320/**
321 * Helper function for `reg_start_read` and `reg_start_write`.
322 */
323static int reg_start(reg_registry* reg, const char* query, reg_error* errPtr) {
324    if (reg->status & reg_transacting) {
325        reg_throw(errPtr, REG_MISUSE, "couldn't start transaction because a "
326                "transaction is already open");
327        errPtr->free = NULL;
328        return 0;
329    } else {
330        int r;
331        do {
332            r = sqlite3_exec(reg->db, query, NULL, NULL, NULL);
333            if (r == SQLITE_OK) {
334                return 1;
335            }
336        } while (r == SQLITE_BUSY);
337        reg_sqlite_error(reg->db, errPtr, NULL);
338        return 0;
339    }
340}
341
342/**
343 * Starts a read transaction on registry. This acquires a shared lock on the
344 * database. It must be released with `reg_commit` or `reg_rollback` (it doesn't
345 * actually matter which, since the transaction won't have changed any values).
346 *
347 * @param [in] reg     registry to start transaction on
348 * @param [out] errPtr on error, a description of the error that occurred
349 * @return             true if success; false if failure
350 */
351int reg_start_read(reg_registry* reg, reg_error* errPtr) {
352    if (reg_start(reg, "BEGIN", errPtr)) {
353        reg->status |= reg_transacting;
354        return 1;
355    } else {
356        return 0;
357    }
358}
359
360/**
361 * Starts a write transaction on registry. This acquires an exclusive lock on
362 * the database. It must be released with `reg_commit` or `reg_rollback`.
363 *
364 * @param [in] reg     registry to start transaction on
365 * @param [out] errPtr on error, a description of the error that occurred
366 * @return             true if success; false if failure
367 */
368int reg_start_write(reg_registry* reg, reg_error* errPtr) {
369    if (reg_start(reg, "BEGIN IMMEDIATE", errPtr)) {
370        reg->status |= reg_transacting | reg_can_write;
371        return 1;
372    } else {
373        return 0;
374    }
375}
376
377/**
378 * Helper function for `reg_commit` and `reg_rollback`.
379 */
380static int reg_end(reg_registry* reg, const char* query, reg_error* errPtr, int is_rollback) {
381    if (!(reg->status & reg_transacting)) {
382        reg_throw(errPtr, REG_MISUSE, "couldn't end transaction because no "
383                "transaction is open");
384        return 0;
385    } else {
386        int r;
387        do {
388            r = sqlite3_exec(reg->db, query, NULL, NULL, NULL);
389            if (r == SQLITE_OK) {
390                return 1;
391            }
392        } while (r == SQLITE_BUSY && !is_rollback);
393        reg_sqlite_error(reg->db, errPtr, NULL);
394        return 0;
395    }
396}
397
398/**
399 * Commits the current transaction. All values written since `reg_start_*` was
400 * called will be written to the database.
401 *
402 * @param [in] reg     registry to commit transaction to
403 * @param [out] errPtr on error, a description of the error that occurred
404 * @return             true if success; false if failure
405 */
406int reg_commit(reg_registry* reg, reg_error* errPtr) {
407    if (reg_end(reg, "COMMIT", errPtr, 0)) {
408        reg->status &= ~(reg_transacting | reg_can_write);
409        return 1;
410    } else {
411        return 0;
412    }
413}
414
415/**
416 * Rolls back the current transaction. All values written since `reg_start_*`
417 * was called will be reverted, and no changes will be written to the database.
418 *
419 * @param [in] reg     registry to roll back transaction from
420 * @param [out] errPtr on error, a description of the error that occurred
421 * @return             true if success; false if failure
422 */
423int reg_rollback(reg_registry* reg, reg_error* errPtr) {
424    if (reg_end(reg, "ROLLBACK", errPtr, 1)) {
425        reg->status &= ~(reg_transacting | reg_can_write);
426        return 1;
427    } else {
428        return 0;
429    }
430}
431
432/**
433 * Runs VACUUM (compact/defragment) on the given db file.
434 * Works on a path rather than an open db pointer because you can't vacuum an
435 * attached db, which is what the rest of the registry uses for some reason.
436 *
437 * @param [in] db_path path to db file to vacuum
438 * @return             true if success; false if failure
439 */
440int reg_vacuum(char *db_path) {
441    sqlite3* db;
442    sqlite3_stmt* stmt = NULL;
443    int result = 0;
444    reg_error err;
445
446    if (sqlite3_open(db_path, &db) == SQLITE_OK) {
447        if (!init_db(db, &err)) {
448            sqlite3_close(db);
449            return 0;
450        }
451    } else {
452        return 0;
453    }
454
455    if (sqlite3_prepare_v2(db, "VACUUM", -1, &stmt, NULL) == SQLITE_OK) {
456        int r;
457        /* XXX: Busy waiting, consider using sqlite3_busy_handler/timeout */
458        do {
459            sqlite3_step(stmt);
460            r = sqlite3_reset(stmt);
461            if (r == SQLITE_OK) {
462                result = 1;
463            }
464        } while (r == SQLITE_BUSY);
465    }
466    if (stmt) {
467        sqlite3_finalize(stmt);
468    }
469    sqlite3_close(db);
470    return result;
471}
Note: See TracBrowser for help on using the repository browser.