Exemple #1
0
/**
 * 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;
}
Exemple #2
0
/**
 * 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;
}
Exemple #3
0
/**
 * 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);
}
Exemple #4
0
/**
 * 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;
}
Exemple #5
0
/**
 * 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;
}
Exemple #6
0
/**
 * 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;
}
Exemple #7
0
/**
 * 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;
}
Exemple #8
0
/**
 * 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 */
}
Exemple #9
0
/**
 * 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;
}
Exemple #10
0
/**
 * 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;
}
Exemple #11
0
/**
 * 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));
			}
		}
	}
}
Exemple #12
0
/**
 * 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;
}
Exemple #13
0
/**
 * 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;
}
Exemple #14
0
/**
 * 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;
}
Exemple #15
0
/**
 * 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;
}
Exemple #17
0
/**
 * 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;
}