/** * Search executable within the user's PATH. * * @return full path if found, NULL otherwise. * The returned string is allocated with halloc(). */ char * file_locate_from_path(const char *argv0) { static bool already_done; char *path; char *tok; char filepath[MAX_PATH_LEN + 1]; char *result = NULL; char *ext = ""; if (is_running_on_mingw() && !is_strsuffix(argv0, (size_t) -1, ".exe")) { ext = ".exe"; } if (filepath_basename(argv0) != argv0) { if (!already_done) { s_warning("can't locate \"%s\" in PATH: name contains '%c' already", argv0, strchr(argv0, G_DIR_SEPARATOR) != NULL ? G_DIR_SEPARATOR : '/'); } goto done; } path = getenv("PATH"); if (NULL == path) { if (!already_done) { s_warning("can't locate \"%s\" in PATH: " "no such environment variable", argv0); } goto done; } path = h_strdup(path); tok = strtok(path, G_SEARCHPATH_SEPARATOR_S); while (NULL != tok) { const char *dir = tok; filestat_t buf; if ('\0' == *dir) dir = "."; concat_strings(filepath, sizeof filepath, dir, G_DIR_SEPARATOR_S, argv0, ext, NULL); if (-1 != stat(filepath, &buf)) { if (S_ISREG(buf.st_mode) && -1 != access(filepath, X_OK)) { result = h_strdup(filepath); break; } } tok = strtok(NULL, G_SEARCHPATH_SEPARATOR_S); } hfree(path); done: already_done = TRUE; /* No warning on subsequent invocation */ return result; }
/** * Get special folder path. * * @return pointer to static string, NULL if folder does not exist. */ static const char * get_folder_basepath(enum special_folder which_folder) { char *special_path = NULL; switch (which_folder) { case PRIVLIB_PATH: { static char *pathname; static bool fetched_xdg_data_dirs; if (NULL == pathname && !fetched_xdg_data_dirs) { special_path = getenv("XDG_DATA_DIRS"); fetched_xdg_data_dirs = TRUE; if (special_path != NULL) { if (is_absolute_path(special_path)) { pathname = omalloc(MAX_PATH_LEN); concat_strings(pathname, MAX_PATH_LEN, special_path, G_DIR_SEPARATOR_S, PACKAGE, (void *) 0); } else { s_warning("ignoring environment XDG_DATA_DIRS: " "holds non-absolute path \"%s\"", special_path); } } } special_path = pathname; } break; case NLS_PATH: { static char *pathname; static bool fetched_nlspath; if (NULL == pathname && !fetched_nlspath) { pathname = getenv("NLSPATH"); fetched_nlspath = TRUE; if (pathname != NULL && !is_absolute_path(pathname)) { s_warning("ignoring environment NLSPATH: " "holds non-absolute path \"%s\"", pathname); pathname = NULL; } else { pathname = ostrdup(pathname); } } if (NULL == pathname) pathname = LOCALE_EXP; special_path = pathname; } break; } return special_path; }
/** * Get the full program path. * * @return a newly allocated string (through halloc()) that points to the * path of the program being run, NULL if we can't compute a suitable path. */ char * file_program_path(const char *argv0) { filestat_t buf; char *file = deconstify_char(argv0); char filepath[MAX_PATH_LEN + 1]; if (is_running_on_mingw() && !is_strsuffix(argv0, (size_t) -1, ".exe")) { concat_strings(filepath, sizeof filepath, argv0, ".exe", NULL_PTR); } else { clamp_strcpy(filepath, sizeof filepath, argv0); } if (-1 == stat(filepath, &buf)) { int saved_errno = errno; file = file_locate_from_path(argv0); if (NULL == file) { errno = saved_errno; s_warning("%s(): could not stat() \"%s\": %m", G_STRFUNC, filepath); return NULL; } } if (file != NULL && file != argv0) return file; /* Allocated by file_locate_from_path() */ return h_strdup(filepath); }
/** * Open file, returning FILE pointer if success or NULL on error. * Errors are logged as a warning, unless error is ENOENT and `missing' * is TRUE. */ static FILE * do_fopen(const char *path, const char *mode, bool missing) { char m; FILE *f; const char *what; if (!is_absolute_path(path)) { errno = EPERM; return NULL; } f = fopen(path, mode); if (f != NULL) return f; m = *mode; if (m == 'r') what = "read"; else if (m == 'w') what = "write into"; else if (m == 'a') what = "append to"; else what = "open"; /* * If we ran out of file descriptors, try to reclaim one from the * banning pool and retry. */ if ( (errno == EMFILE || errno == ENFILE) && reclaim_fd != NULL && (*reclaim_fd)() ) { f = fopen(path, mode); if (f != NULL) { s_warning("had to close a banned fd to %s file", what); return f; } } if (!missing || errno != ENOENT) s_warning("can't %s file \"%s\": %m", what, path); return NULL; }
/** * Close configuration file opened for writing, and rename it. * * @returns TRUE on success. */ bool file_config_close(FILE *out, const file_path_t *fv) { char *path = NULL; char *path_new = NULL; bool success = FALSE; /* * Be extra careful when operating on filesystems with delayed disk block * allocation, such as "ext4". If there is a crash and the data was not * properly allocated to disk, we could lose both the old and new file data! * * To fight against that, make sure we sync the new data to disk before * renaming the new file into the old, as the rename operation is likely * to be stored on disk before allocation of the new data blocks is made. * --RAM, 2013-08-12 */ if (0 != file_sync_fclose(out)) { s_warning("could not flush \"%s\": %m", fv->name); goto failed; } path = make_pathname(fv->dir, fv->name); g_return_val_if_fail(NULL != path, FALSE); path_new = h_strdup_printf("%s.%s", path, new_ext); if (NULL == path_new) goto failed; if (-1 == rename(path_new, path)) { s_warning("could not rename \"%s\" as \"%s\": %m", path_new, path); goto failed; } success = TRUE; failed: HFREE_NULL(path_new); HFREE_NULL(path); return success; }
/** * Creates a disk database with an SDBM or memory map back-end. * * If we can't create the SDBM files on disk, we'll transparently use * an in-core version. * * @param name the name of the storage created, for logs * @param dir the directory where SDBM files will be put * @param base the base name of SDBM files * @param flags the sdbm_open() flags * @param kv key/value description * @param packing key/value serialization description * @param cache_size Amount of items to cache (0 = no cache, 1 = default) * @param hash_func Key hash function * @param eq_func Key equality test function * @param incore If TRUE, use a RAM-only database * * @return the DBMW wrapping object. */ static dbmw_t * dbstore_create_internal(const char *name, const char *dir, const char *base, int flags, dbstore_kv_t kv, dbstore_packing_t packing, size_t cache_size, hash_fn_t hash_func, eq_fn_t eq_func, bool incore) { dbmap_t *dm; dbmw_t *dw; size_t adjusted_cache_size = cache_size; if (!incore) { char *path; g_assert(base != NULL); path = make_pathname(dir, base); dm = dbmap_create_sdbm(kv.key_size, kv.key_len, name, path, flags, STORAGE_FILE_MODE); /* * For performance reasons, always use deferred writes. Maps which * are going to persist from session to session are synchronized on * a regular basis. */ if (dm != NULL) { dbmap_set_deferred_writes(dm, TRUE); } else { s_warning("DBSTORE cannot open SDBM at %s for %s: %m", path, name); } HFREE_NULL(path); } else { dm = NULL; } if (!dm) { dm = dbmap_create_hash(kv.key_size, kv.key_len, hash_func, eq_func); adjusted_cache_size = 0; } /* * Wrap map access in a DB map wrapper to have transparent serialization * support and caching of (deserialized) values. */ dw = dbmw_create(dm, name, kv.value_size, kv.value_data_size, packing.pack, packing.unpack, packing.valfree, adjusted_cache_size, hash_func, eq_func); return dw; }
/** * Hash list iterator callback to free and remove waiting events. */ static bool wq_free_waiting(void *key, void *unused_data) { wq_event_t *we = key; wq_event_check(we); (void) unused_data; s_warning("leaked waiting event %s() on %p", stacktrace_function_name(we->cb), we->key); wq_event_free(we); return TRUE; }
/** * Close a stream, flushing data to disk. * * This is needed on "ext4" filesystems or any other filesystem which delays * the allocation of data blocks, in case there is a crash between the close * and the moment the data is written physically to the disk. * * @return the fclose() status. */ int file_sync_fclose(FILE *f) { int fd, ret; g_assert(f != NULL); ret = fflush(f); /* Send all buffered data to kernel first */ fd = fileno(f); if (-1 == fd_fdatasync(fd)) s_warning("cannot flush data blocks to disk for fd=%d: %m", fd); return fclose(f) | ret; /* Report error if fflush() failed */ }
/** * Open configuration file for writing. We don't clobber the existing file * yet and open a ".new" instead. Renaming will occur afterwards, when * file_config_close() is called. * * @returns opened FILE if success, NULL on error. */ static FILE * file_config_open(const char *what, const file_path_t *fv) { FILE *out = NULL; char *path; path = h_strconcat(fv->dir, G_DIR_SEPARATOR_S, fv->name, ".", new_ext, (void *) 0); g_return_val_if_fail(NULL != path, NULL); if (is_absolute_path(path)) { out = file_fopen(path, "w"); if (out == NULL) s_warning("unable to persist %s", what); } HFREE_NULL(path); return out; }
/** * Open file, returning file descriptor or -1 on error with errno set. * Errors are logged as a warning, unless `missing' is true, in which * case no error is logged for ENOENT. */ static int do_open(const char *path, int flags, int mode, bool missing, bool absolute) { const char *what; int fd; if (absolute && !is_absolute_path(path)) { s_warning("%s(): can't open absolute \"%s\": relative path", G_STRFUNC, path); errno = EPERM; return -1; } #ifdef O_NOCTTY flags |= O_NOCTTY; #endif /* O_NOCTTY */ fd = open(path, flags, mode); if (fd < 0) { if (flags & O_CREAT) what = "create"; else if (flags & O_RDONLY) what = "read"; else if (flags & O_WRONLY) what = "write into"; else what = "open"; /* * If we ran out of file descriptors, try to reclaim one from the * banning pool and retry. */ if ( (errno == EMFILE || errno == ENFILE) && reclaim_fd != NULL && (*reclaim_fd)() ) { fd = open(path, flags, mode); if (fd >= 0) { s_warning("%s(): had to close a banned fd to %s file", G_STRFUNC, what); } } } if (fd >= 0) { fd = get_non_stdio_fd(fd); set_close_on_exec(fd); /* Just in case */ return fd; } /* * Hack for broken libc, which can return -1 with errno = 0! * This happens when compiling with gcc-3.x and linking with -lpthread * on a Debian linux system. * --RAM, 15/02/2004 */ if (errno == 0) { s_warning("%s(): open() returned -1 with errno = 0, assuming ENOENT", G_STRFUNC); errno = ENOENT; } if (!missing || errno != ENOENT) { s_warning("%s(): can't %s file \"%s\": %m", G_STRFUNC, what, path); } return -1; }
/** * Delete key from database. */ void dbmw_delete(dbmw_t *dw, const void *key) { struct cached *entry; dbmw_check(dw); g_assert(key); dw->w_access++; entry = map_lookup(dw->values, key); if (entry) { if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING | DBG_DSF_DELETE)) { dbg_ds_log(dw->dbg, dw, "%s: %s key=%s%s", G_STRFUNC, entry->dirty ? "dirty" : "clean", dbg_ds_keystr(dw->dbg, key, (size_t) -1), entry->absent ? " (was absent)" : ""); } if (entry->dirty) dw->w_hits++; if (!entry->absent) { /* * Entry was present but is now deleted. * * If it was clean, then it was flushed to the database and we now * know that there is one less entry in the database than there is * physically present in the map. * * If it was dirty, then we do not know whether it exists in the * database or not, and therefore we cannot adjust the amount * of cached entries down. */ if (entry->dirty) dw->count_needs_sync = TRUE; /* Deferred delete */ else dw->cached--; /* One less entry in database */ fill_entry(dw, entry, NULL, 0); entry->absent = TRUE; } hash_list_moveto_tail(dw->keys, key); } else { if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_DELETE)) { dbg_ds_log(dw->dbg, dw, "%s: removing key=%s", G_STRFUNC, dbg_ds_keystr(dw->dbg, key, (size_t) -1)); } dw->ioerr = FALSE; dbmap_remove(dw->dm, key); if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; s_warning("DBMW \"%s\" I/O error whilst deleting key: %s", dw->name, dbmap_strerror(dw->dm)); } /* * If the maximum value length of the DB is 0, then it is used as a * "search table" only, meaning there will be no read to get values, * only existence checks. * * Therefore, it makes sense to cache that the key is no longer valid. * Otherwise, possibly pushing a value out of the cache to record * a deletion is not worth it. */ if (0 == dw->value_size) { WALLOC0(entry); entry->absent = TRUE; (void) allocate_entry(dw, key, entry); if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING)) { dbg_ds_log(dw->dbg, dw, "%s: cached absent key=%s", G_STRFUNC, dbg_ds_keystr(dw->dbg, key, (size_t) -1)); } } } }
/** * Open configuration file, renaming it as ".orig" when `renaming' is TRUE. * If configuration file cannot be found, try opening the ".orig" variant * if already present and `renaming' is TRUE. * If not found, try with successive alternatives, if supplied. * * @attention * NB: the supplied `fv' argument is a vector of `fvcnt' elements. Items * with a NULL `dir' field are ignored, but fv[0].dir cannot be NULL. * * @param what is what is being opened, for logging purposes. * @param fv is a vector of files to try to open, in sequence * @param fvcnt is the size of the vector * @param renaming indicates whether the opened file should be renamed .orig. * @param chosen is filled with the index of the chosen path in the vector, * unless NULL is given. * * @return opened FILE, or NULL if we were unable to open any. `chosen' is * only filled if the file is opened. */ static FILE * open_read( const char *what, const file_path_t *fv, int fvcnt, bool renaming, int *chosen) { FILE *in; char *path; char *path_orig; const char *instead = empty_str; int idx = 0; g_assert(fv != NULL); g_assert(fvcnt >= 1); g_assert(fv->dir != NULL); path = make_pathname(fv->dir, fv->name); if (!is_absolute_path(path)) { HFREE_NULL(path); return NULL; } path_orig = h_strdup_printf("%s.%s", path, orig_ext); in = fopen(path, "r"); if (in) { if (is_running_on_mingw()) { /* Windows can't rename an open file */ fclose(in); in = NULL; } if (renaming && -1 == rename(path, path_orig)) { s_warning("[%s] could not rename \"%s\" as \"%s\": %m", what, path, path_orig); } if (NULL == in) { in = fopen(path_orig, "r"); } goto out; } else { if (ENOENT == errno) { if (common_dbg > 0) { g_debug("[%s] cannot load non-existent \"%s\"", what, path); } } else { instead = instead_str; /* Regular file was present */ s_warning("[%s] failed to retrieve from \"%s\": %m", what, path); } if (fvcnt > 1 && common_dbg > 0) g_debug("[%s] trying to load from alternate locations...", what); } /* * Maybe we crashed after having retrieved the file in a previous run * but before being able to write it again correctly? Try to open the * ".orig" file instead. */ g_assert(in == NULL); if (renaming) in = fopen(path_orig, "r"); /* The ".orig", in case of a crash */ if (in != NULL) { instead = instead_str; HFREE_NULL(path); path = path_orig; path_orig = NULL; } /* * Try with alternatives, if supplied. */ if (in == NULL && fvcnt > 1) { const file_path_t *xfv; int xfvcnt; instead = instead_str; for (xfv = fv + 1, xfvcnt = fvcnt - 1; xfvcnt; xfv++, xfvcnt--) { HFREE_NULL(path); if (NULL == xfv->dir) /* In alternatives, dir may be NULL */ continue; path = make_pathname(xfv->dir, xfv->name); idx++; if (NULL != path && NULL != (in = fopen(path, "r"))) break; if (path != NULL && common_dbg > 0) { g_debug("[%s] cannot load non-existent \"%s\" either", what, path); } } } if (common_dbg > 0) { if (in) { g_debug("[%s] retrieving from \"%s\"%s", what, path, instead); } else if (instead == instead_str) { g_debug("[%s] unable to retrieve: tried %d alternate location%s", what, fvcnt, fvcnt == 1 ? "" : "s"); } else { g_debug("[%s] unable to retrieve: no alternate locations known", what); } } out: HFREE_NULL(path); HFREE_NULL(path_orig); if (in != NULL && chosen != NULL) *chosen = idx; return in; }
/** * Search executable within the user's PATH. * * @return full path if found, NULL otherwise. * The returned string is allocated with halloc(). */ char * file_locate_from_path(const char *argv0) { static bool already_done; char *path; char *tok; char filepath[MAX_PATH_LEN + 1]; char *result = NULL; char *ext = ""; if (is_running_on_mingw() && !is_strsuffix(argv0, (size_t) -1, ".exe")) { ext = ".exe"; } if (filepath_basename(argv0) != argv0) { if (!already_done) { s_warning("can't locate \"%s\" in PATH: name contains '%c' already", argv0, strchr(argv0, G_DIR_SEPARATOR) != NULL ? G_DIR_SEPARATOR : '/'); } result = h_strdup(argv0); goto done; } path = getenv("PATH"); if (NULL == path) { if (!already_done) { s_warning("can't locate \"%s\" in PATH: " "no such environment variable", argv0); } goto done; } /* * On Windows, we need to implicitly add "." to the path if not already * present -- this is done by appending a separator and a dot, not by * checking whether "." is already part of the path. * * The reason is that "." is implied, and also because one may omit the * ".exe" extension when launching a program. This means checks done * in crash_init() for instance to see whether the file listed in * argv[0] exists and which do not account for a missing ".exe" will * attempt to locate the program in the PATH to get a full name and will * fail if we do not add ".". * * On UNIX this cannot happen because there is no hidden extension and * the "." is never made part of the PATH implictly. * * --RAM, 2015-12-06 */ if (is_running_on_mingw()) path = h_strdup_printf("%s%c.", path, *G_SEARCHPATH_SEPARATOR_S); else path = h_strdup(path); path = h_strdup(path); /* FIXME: strtok() is not thread-safe --RAM, 2015-12-06 */ tok = strtok(path, G_SEARCHPATH_SEPARATOR_S); while (NULL != tok) { const char *dir = tok; filestat_t buf; if ('\0' == *dir) dir = "."; concat_strings(filepath, sizeof filepath, dir, G_DIR_SEPARATOR_S, argv0, ext, NULL_PTR); if (-1 != stat(filepath, &buf)) { if (S_ISREG(buf.st_mode) && -1 != access(filepath, X_OK)) { result = h_strdup(filepath); break; } } tok = strtok(NULL, G_SEARCHPATH_SEPARATOR_S); } hfree(path); done: already_done = TRUE; /* No warning on subsequent invocation */ return result; }
/** * Write back cached value to disk. * @return TRUE on success */ static bool write_back(dbmw_t *dw, const void *key, struct cached *value) { dbmap_datum_t dval; bool ok; g_assert(value->dirty); if (value->absent) { /* Key not present, value is null item */ dval.data = NULL; dval.len = 0; } else { /* * Serialize value into our reused message block if a * serialization routine was provided. */ if (dw->pack) { pmsg_reset(dw->mb); (*dw->pack)(dw->mb, value->data); dval.data = pmsg_start(dw->mb); dval.len = pmsg_size(dw->mb); /* * We allocated the message block one byte larger than the * maximum size, in order to detect unexpected serialization * overflows. */ if (dval.len > dw->value_data_size) { /* Don't s_carp() as this is asynchronous wrt data change */ s_critical("DBMW \"%s\" serialization overflow in %s() " "whilst flushing dirty entry", dw->name, stacktrace_function_name(dw->pack)); return FALSE; } } else { dval.data = value->data; dval.len = value->len; } } /* * If cached entry is absent, delete the key. * Otherwise store the serialized value. * * Dirty bit is cleared on success. */ if ( dbg_ds_debugging(dw->dbg, 1, DBG_DSF_CACHING | DBG_DSF_UPDATE | DBG_DSF_INSERT | DBG_DSF_DELETE) ) { dbg_ds_log(dw->dbg, dw, "%s: %s dirty value (%zu byte%s) key=%s", G_STRFUNC, value->absent ? "deleting" : "flushing", dval.len, plural(dval.len), dbg_ds_keystr(dw->dbg, key, (size_t) -1)); } dw->ioerr = FALSE; ok = value->absent ? dbmap_remove(dw->dm, key) : dbmap_insert(dw->dm, key, dval); if (ok) { value->dirty = FALSE; } else if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; s_warning("DBMW \"%s\" I/O error whilst %s dirty entry: %s", dw->name, value->absent ? "deleting" : "flushing", dbmap_strerror(dw->dm)); } else { s_warning("DBMW \"%s\" error whilst %s dirty entry: %s", dw->name, value->absent ? "deleting" : "flushing", dbmap_strerror(dw->dm)); } return ok; }
/** * Dump the links sorted by decreasing leak size. */ void G_COLD leak_dump(const leak_set_t *ls) { int count; struct filler filler; int i; leak_set_check(ls); count = htable_count(ls->stacks); if (count == 0) goto leaks_by_place; /* * Linearize hash table into an array before sorting it by * decreasing leak size. */ XMALLOC_ARRAY(filler.leaks, count); filler.count = count; filler.idx = 0; filler.kt = LEAK_KEY_STACK; htable_foreach(ls->stacks, fill_array, &filler); xqsort(filler.leaks, count, sizeof(struct leak), leak_size_cmp); /* * Dump the leaks by allocation place. */ s_warning("leak summary by stackframe and total decreasing size:"); s_warning("distinct calling stacks found: %d", count); for (i = 0; i < count; i++) { struct leak *l = &filler.leaks[i]; size_t avg = l->lr->size / (0 == l->lr->count ? 1 : l->lr->count); s_warning("%zu bytes (%zu block%s, average %zu byte%s) from:", l->lr->size, l->lr->count, plural(l->lr->count), avg, plural(avg)); stacktrace_atom_decorate(stderr, l->u.sa, STACKTRACE_F_ORIGIN | STACKTRACE_F_SOURCE); } xfree(filler.leaks); leaks_by_place: count = htable_count(ls->places); if (count == 0) return; /* * Linearize hash table into an array before sorting it by * decreasing leak size. */ XMALLOC_ARRAY(filler.leaks, count); filler.count = count; filler.idx = 0; filler.kt = LEAK_KEY_PLACE; htable_foreach(ls->places, fill_array, &filler); xqsort(filler.leaks, count, sizeof(struct leak), leak_size_cmp); /* * Dump the leaks by allocation place. */ s_warning("leak summary by origin and total decreasing size:"); s_warning("distinct allocation points found: %d", count); for (i = 0; i < count; i++) { struct leak *l = &filler.leaks[i]; size_t avg = l->lr->size / (0 == l->lr->count ? 1 : l->lr->count); s_warning("%zu bytes (%zu block%s, average %zu byte%s) from \"%s\"", l->lr->size, l->lr->count, plural(l->lr->count), avg, plural(avg), l->u.place); } xfree(filler.leaks); }
static inline int emulate_poll_with_select(struct pollfd *fds, unsigned int n, int timeout) { struct timeval tv; unsigned i; fd_set rfds, wfds, efds; int ret, max_fd = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); for (i = 0; i < n; i++) { int fd = cast_to_fd(fds[i].fd); safety_assert(!is_valid_fd(fd) || is_a_socket(fd) || is_a_fifo(fd)); if (!is_okay_for_select(fd) || i >= FD_SETSIZE) { fds[i].revents = POLLERR; continue; } max_fd = MAX(fd, max_fd); fds[i].revents = 0; if (POLLIN & fds[i].events) { FD_SET(socket_fd(fd), &rfds); } if (POLLOUT & fds[i].events) { FD_SET(socket_fd(fd), &wfds); } FD_SET(socket_fd(fd), &efds); } if (timeout >= 0) { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000UL; } ret = select(max_fd + 1, &rfds, &wfds, &efds, timeout < 0 ? NULL : &tv); if (ret > 0) { n = MIN(n, FD_SETSIZE); /* POLLERR is already set above */ for (i = 0; i < n; i++) { int fd = cast_to_fd(fds[i].fd); if (!is_okay_for_select(fd)) continue; if (FD_ISSET(fd, &rfds)) { fds[i].revents |= POLLIN; } if (FD_ISSET(fd, &wfds)) { fds[i].revents |= POLLOUT; } if (FD_ISSET(fd, &efds)) { fds[i].revents |= POLLERR; } } } else if (ret < 0) { s_warning("error during select(): %m"); } return ret; }
/** * Is key present in the database? */ bool dbmw_exists(dbmw_t *dw, const void *key) { struct cached *entry; bool ret; dbmw_check(dw); g_assert(key); dw->r_access++; entry = map_lookup(dw->values, key); if (entry) { if (dbg_ds_debugging(dw->dbg, 5, DBG_DSF_CACHING | DBG_DSF_ACCESS)) { dbg_ds_log(dw->dbg, dw, "%s: read cache hit on %s key=%s%s", G_STRFUNC, entry->dirty ? "dirty" : "clean", dbg_ds_keystr(dw->dbg, key, (size_t) -1), entry->absent ? " (absent)" : ""); } dw->r_hits++; return !entry->absent; } dw->ioerr = FALSE; ret = dbmap_contains(dw->dm, key); if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; s_warning("DBMW \"%s\" I/O error whilst checking key existence: %s", dw->name, dbmap_strerror(dw->dm)); return FALSE; } /* * If the maximum value length of the DB is 0, then it is used as a * "search table" only, meaning there will be no read to get values, * only existence checks. * * Therefore, it makes sense to cache existence checks. A data read * will also correctly return a null item from the cache. * * If the value length is not 0, we only cache negative lookups (i.e. * the value was not found) because we did not get any value so it is * possible to record an absent cache entry. */ if (0 == dw->value_size || !ret) { WALLOC0(entry); entry->absent = !ret; (void) allocate_entry(dw, key, entry); if (dbg_ds_debugging(dw->dbg, 2, DBG_DSF_CACHING)) { dbg_ds_log(dw->dbg, dw, "%s: cached %s key=%s", G_STRFUNC, entry->absent ? "absent" : "present", dbg_ds_keystr(dw->dbg, key, (size_t) -1)); } } return ret; }