lib: add functions to search in configuration/data directories
The previous approach to loading config files was to ask WP for the directory and then search those for the config files. This patch changes the approach - a caller now asks WP to search for a specific config file or iterate over a config file directory. This allows us to implement a directory lookup order, i.e. "wireplumber.conf" may be in XDG_CONFIG_DIR, /etc/, /usr/share and the first one found is used. For configuration directories, the new method iterates over all matching entries (files + directories) and invokes a callback for each entry. This enables distributions to ship default files in /usr/share/wireplumber but have admins and users override them on a local basis. For lua scripts in particular, overriding a distribution-provided file with an empty file effectively disables it, adding a file adds it in the right sort order.
This commit is contained in:

committed by
George Kiagiadakis

parent
153e84c8b8
commit
d38c3fb4cc
@@ -120,18 +120,24 @@ and ignored. Possible commands are
|
|||||||
Location of configuration files
|
Location of configuration files
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
WirePlumber's default location of its configuration files is determined at
|
WirePlumber's default locations of its configuration files are determined at
|
||||||
compile time by the build system. Typically, it ends up being `/etc/wireplumber`.
|
compile time by the build system. Typically, those end up being
|
||||||
|
`XDG_CONFIG_DIR/wireplumber`, `/etc/wireplumber`, and
|
||||||
|
`/usr/share/wireplumber`, in that order of priority.
|
||||||
|
|
||||||
In more detail, this is controlled by the `--sysconfdir` meson option. When
|
In more detail, the latter two are controlled by the `--sysconfdir` and `--datadir`
|
||||||
this is set to an absolute path, such as `/etc`, the location of the
|
meson options. When those are set to an absolute path, such as `/etc`, the
|
||||||
configuration files is set to be `$sysconfdir/wireplumber`. When this is set
|
location of the configuration files is set to be `$sysconfdir/wireplumber`.
|
||||||
to a relative path, such as `etc`, then the installation prefix (`--prefix`)
|
When set to a relative path, such as `etc`, then the installation prefix (`--prefix`)
|
||||||
is prepended to the path: `$prefix/$sysconfdir/wireplumber`
|
is prepended to the path: `$prefix/$sysconfdir/wireplumber`
|
||||||
|
|
||||||
WirePlumber expects its `wireplumber.conf` to reside in that directory.
|
The three locations are intended for custom user configuration,
|
||||||
It is possible to override that at runtime by setting the
|
host-specific configuration and distribution-provided configuration,
|
||||||
`WIREPLUMBER_CONFIG_FILE` environment variable::
|
respectively. At runtime, WirePlumber will search the directories
|
||||||
|
for the highest-priority directory to contain the `wireplumber.conf`
|
||||||
|
configuration file. This allows a user or system administrator to easily
|
||||||
|
override the distribution provided configuration files by placing an equally
|
||||||
|
named file in the respective directory.
|
||||||
|
|
||||||
It is possible to override the lookup path at runtime by passing the
|
It is possible to override the lookup path at runtime by passing the
|
||||||
`--config-file` or `-c` option::
|
`--config-file` or `-c` option::
|
||||||
@@ -144,6 +150,8 @@ by setting the `WIREPLUMBER_CONFIG_DIR` environment variable::
|
|||||||
|
|
||||||
WIREPLUMBER_CONFIG_DIR=src/config wireplumber
|
WIREPLUMBER_CONFIG_DIR=src/config wireplumber
|
||||||
|
|
||||||
|
If `WIREPLUMBER_CONFIG_DIR` is set, the default locations are ignored.
|
||||||
|
|
||||||
Location of modules
|
Location of modules
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
202
lib/wp/wp.c
202
lib/wp/wp.c
@@ -153,4 +153,206 @@ wp_get_data_dir (void)
|
|||||||
return data_dir;
|
return data_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
check_path (const gchar *basedir, const gchar *subdir, const gchar *filename)
|
||||||
|
{
|
||||||
|
g_autofree gchar *path = g_build_filename (basedir,
|
||||||
|
subdir ? subdir : filename,
|
||||||
|
subdir ? filename : NULL,
|
||||||
|
NULL);
|
||||||
|
g_autofree gchar *abspath = g_canonicalize_filename (path, NULL);
|
||||||
|
wp_trace ("checking %s", abspath);
|
||||||
|
if (g_file_test (abspath, G_FILE_TEST_IS_REGULAR))
|
||||||
|
return g_steal_pointer (&abspath);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Flags to specify lookup directories
|
||||||
|
* Use one of the `WP_CDIR_SET` values instead of the values directly.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
/* config group */
|
||||||
|
WP_CDIR_ENV_CONFIG = (1 << 0), /**< $WIREPLUMBER_CONFIG_DIR */
|
||||||
|
WP_CDIR_XDG_HOME = (1 << 1), /**< XDG_CONFIG_HOME */
|
||||||
|
WP_CDIR_ETC = (1 << 2), /**< /etc/wireplumber */
|
||||||
|
|
||||||
|
/* data group */
|
||||||
|
WP_CDIR_ENV_DATA = (1 << 10), /**< $WIREPLUMBER_DATA_DIR */
|
||||||
|
WP_CDIR_USR = (1 << 11), /**< /usr/share/wireplumber */
|
||||||
|
|
||||||
|
/** The set for system-only files */
|
||||||
|
WP_CDIR_SET_SYSTEM = WP_CDIR_ETC | WP_CDIR_USR | WP_CDIR_ENV_DATA,
|
||||||
|
|
||||||
|
/** The set for user + system files */
|
||||||
|
WP_CDIR_SET_USER = WP_CDIR_ENV_CONFIG | WP_CDIR_XDG_HOME | WP_CDIR_SET_SYSTEM,
|
||||||
|
} WpConfigDir;
|
||||||
|
|
||||||
|
static GPtrArray *
|
||||||
|
lookup_dirs (guint flags)
|
||||||
|
{
|
||||||
|
g_autoptr(GPtrArray) dirs = g_ptr_array_new_with_free_func (g_free);
|
||||||
|
const gchar *dir;
|
||||||
|
|
||||||
|
/* Compile the list of lookup directories in priority order:
|
||||||
|
* - environment variables
|
||||||
|
* - XDG directories
|
||||||
|
* - etc
|
||||||
|
* - /usr/share/....
|
||||||
|
*
|
||||||
|
* Note that environment variables *replace* the equivalent config directory
|
||||||
|
* group.
|
||||||
|
*/
|
||||||
|
if ((flags & WP_CDIR_ENV_CONFIG) &&
|
||||||
|
(dir = g_getenv ("WIREPLUMBER_CONFIG_DIR"))) {
|
||||||
|
g_ptr_array_add (dirs, g_canonicalize_filename (dir, NULL));
|
||||||
|
} else {
|
||||||
|
if ((flags & WP_CDIR_XDG_HOME) && (dir = wp_get_xdg_config_dir ()))
|
||||||
|
g_ptr_array_add (dirs, (gpointer)g_strdup (dir));
|
||||||
|
if (flags & WP_CDIR_ETC)
|
||||||
|
g_ptr_array_add (dirs, (gpointer)g_strdup (WIREPLUMBER_DEFAULT_CONFIG_DIR));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* data dirs */
|
||||||
|
if ((flags & WP_CDIR_ENV_DATA) && (dir = g_getenv ("WIREPLUMBER_DATA_DIR"))) {
|
||||||
|
g_ptr_array_add (dirs, g_canonicalize_filename (dir, NULL));
|
||||||
|
} else if (flags & WP_CDIR_USR) {
|
||||||
|
g_ptr_array_add (dirs,
|
||||||
|
(gpointer)g_canonicalize_filename(WIREPLUMBER_DEFAULT_DATA_DIR, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_steal_pointer (&dirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the full path of \a filename as found in
|
||||||
|
* the hierarchy of configuration and data directories.
|
||||||
|
* \returns An allocated string with the configuration file path or NULL if
|
||||||
|
* the file was not found.
|
||||||
|
*/
|
||||||
|
gchar *
|
||||||
|
wp_find_config_file (const gchar *filename, const char *subdir)
|
||||||
|
{
|
||||||
|
g_autoptr(GPtrArray) dirs = lookup_dirs (WP_CDIR_SET_USER);
|
||||||
|
|
||||||
|
if (g_path_is_absolute (filename))
|
||||||
|
return g_strdup (filename);
|
||||||
|
|
||||||
|
for (guint i = 0; i < dirs->len; i++) {
|
||||||
|
gchar *path = check_path (g_ptr_array_index (dirs, i),
|
||||||
|
subdir, filename);
|
||||||
|
if (path)
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the full path of \a filename as found in
|
||||||
|
* the hierarchy of system-only configuration and data directories.
|
||||||
|
* \returns An allocated string with the configuration file path or NULL if
|
||||||
|
* the file was not found.
|
||||||
|
*/
|
||||||
|
gchar *
|
||||||
|
wp_find_sysconfig_file (const gchar *filename, const char *subdir)
|
||||||
|
{
|
||||||
|
g_autoptr(GPtrArray) dirs = lookup_dirs (WP_CDIR_SET_SYSTEM);
|
||||||
|
|
||||||
|
if (g_path_is_absolute (filename))
|
||||||
|
return g_strdup (filename);
|
||||||
|
|
||||||
|
for (guint i = 0; i < dirs->len; i++) {
|
||||||
|
gchar *path = check_path (g_ptr_array_index (dirs, i),
|
||||||
|
subdir, filename);
|
||||||
|
if (path)
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Iterates over configuration files in the \a subdir and calls the
|
||||||
|
* \a func for each file.
|
||||||
|
*
|
||||||
|
* Files are sorted across the hierarchy of configuration and data
|
||||||
|
* directories with files in higher-priority directories shadowing files in
|
||||||
|
* lower-priority directories. Files are only checked for existence, a
|
||||||
|
* caller must be able to handle read errors.
|
||||||
|
*
|
||||||
|
* If the \a func returns a negative errno the iteration stops and that
|
||||||
|
* errno is returned to the caller. The \a func should set \a error on
|
||||||
|
* failure.
|
||||||
|
*
|
||||||
|
* Note that \a func is called for directories too, it is the responsibility
|
||||||
|
* of the caller to ignore or recurse into those.
|
||||||
|
*
|
||||||
|
* \param subdir The name of the subdirectory to search for in the
|
||||||
|
* configuration directories
|
||||||
|
* \param suffix The filename suffix, NULL matches all entries
|
||||||
|
* \param func The callback to invoke for each file.
|
||||||
|
* \param user_data Passed through to \a func
|
||||||
|
* \param error Passed through to \a func
|
||||||
|
*
|
||||||
|
* \return the number of files on success or a negative errno on failure
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
wp_iter_config_files (const gchar *subdir, const gchar *suffix,
|
||||||
|
wp_file_iter_func func, gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GHashTable) ht = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||||
|
g_free, g_free);
|
||||||
|
g_autoptr(GPtrArray) dirs = NULL;
|
||||||
|
gint count = 0;
|
||||||
|
|
||||||
|
if (subdir == NULL)
|
||||||
|
subdir = ".";
|
||||||
|
|
||||||
|
/* Note: this list is highest-priority first */
|
||||||
|
dirs = lookup_dirs (WP_CDIR_SET_USER);
|
||||||
|
|
||||||
|
/* Store all filenames with their full path in the hashtable, overriding
|
||||||
|
* previous values. We need to run backwards through the list for that */
|
||||||
|
for (guint i = dirs->len; i > 0; i--) {
|
||||||
|
g_autofree gchar *dirpath = g_build_filename (g_ptr_array_index (dirs, i - 1),
|
||||||
|
subdir, NULL);
|
||||||
|
g_autoptr(GDir) dir = g_dir_open (dirpath, 0, NULL);
|
||||||
|
|
||||||
|
wp_trace ("searching config dir: %s", dirpath);
|
||||||
|
|
||||||
|
if (dir) {
|
||||||
|
const gchar *filename;
|
||||||
|
while ((filename = g_dir_read_name (dir))) {
|
||||||
|
if (suffix && !g_str_has_suffix (filename, suffix))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
g_hash_table_replace (ht, g_strdup (filename),
|
||||||
|
g_build_filename (dirpath, filename, NULL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_hash_table_size (ht) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Sort by filename */
|
||||||
|
g_autoptr(GList) keys = g_hash_table_get_keys (ht);
|
||||||
|
keys = g_list_sort (keys, (GCompareFunc)g_strcmp0);
|
||||||
|
|
||||||
|
/* Now we have our filenames in a sorted order so we can call the callback */
|
||||||
|
for (GList *elem = g_list_first (keys); elem; elem = g_list_next (elem)) {
|
||||||
|
const gchar *path = g_hash_table_lookup (ht, elem->data);
|
||||||
|
gint rc = func (path, user_data, error);
|
||||||
|
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/*! \} */
|
/*! \} */
|
||||||
|
14
lib/wp/wp.h
14
lib/wp/wp.h
@@ -77,6 +77,20 @@ const gchar * wp_get_config_dir (void);
|
|||||||
WP_API
|
WP_API
|
||||||
const gchar * wp_get_data_dir (void);
|
const gchar * wp_get_data_dir (void);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
gchar * wp_find_config_file (const gchar *filename, const char *subdir);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
gchar * wp_find_sysconfig_file (const gchar *filename, const char *subdir);
|
||||||
|
|
||||||
|
typedef gint (*wp_file_iter_func)(const gchar *filename, gpointer user_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
WP_API
|
||||||
|
gint wp_iter_config_files (const gchar *subdir, const gchar *suffix,
|
||||||
|
wp_file_iter_func func, gpointer user_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -131,39 +131,7 @@ find_script (const gchar * script, gboolean daemon)
|
|||||||
g_file_test (script, G_FILE_TEST_IS_REGULAR))
|
g_file_test (script, G_FILE_TEST_IS_REGULAR))
|
||||||
return g_strdup (script);
|
return g_strdup (script);
|
||||||
|
|
||||||
/* /etc/wireplumber/scripts */
|
return wp_find_sysconfig_file (script, "scripts");
|
||||||
{
|
|
||||||
g_autofree gchar * file = g_build_filename (
|
|
||||||
wp_get_config_dir (), "scripts", script, NULL);
|
|
||||||
|
|
||||||
wp_trace ("trying %s", file);
|
|
||||||
|
|
||||||
if (g_file_test (file, G_FILE_TEST_IS_REGULAR))
|
|
||||||
return g_steal_pointer (&file);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
g_autofree gchar * file = g_build_filename (
|
|
||||||
wp_get_data_dir (), "scripts", script, NULL);
|
|
||||||
|
|
||||||
wp_trace ("trying %s", file);
|
|
||||||
|
|
||||||
if (g_file_test (file, G_FILE_TEST_IS_REGULAR))
|
|
||||||
return g_steal_pointer (&file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* {XDG_DATA_DIRS,/usr/local/share,/usr/share}/wireplumber/scripts */
|
|
||||||
const gchar * const * data_dirs = g_get_system_data_dirs ();
|
|
||||||
for (; *data_dirs; data_dirs++) {
|
|
||||||
g_autofree gchar * file = g_build_filename (
|
|
||||||
*data_dirs, "wireplumber", "scripts", script, NULL);
|
|
||||||
|
|
||||||
wp_trace ("trying %s", file);
|
|
||||||
|
|
||||||
if (g_file_test (file, G_FILE_TEST_IS_REGULAR))
|
|
||||||
return g_steal_pointer (&file);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <wp/wp.h>
|
#include <wp/wp.h>
|
||||||
#include <wplua/wplua.h>
|
#include <wplua/wplua.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
load_components (lua_State *L, WpCore * core, GError ** error)
|
load_components (lua_State *L, WpCore * core, GError ** error)
|
||||||
@@ -78,9 +79,17 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
sort_filelist (gconstpointer a, gconstpointer b)
|
load_file (const gchar *path, gpointer data, GError **error)
|
||||||
{
|
{
|
||||||
return g_strcmp0 (*(const gchar **) a, *(const gchar **) b);
|
lua_State *L = data;
|
||||||
|
|
||||||
|
if (g_file_test (path, G_FILE_TEST_IS_DIR))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
wp_info ("loading config file: %s", path);
|
||||||
|
if (!wplua_load_path (L, path, 0, 0, error))
|
||||||
|
return -EINVAL;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
@@ -89,51 +98,24 @@ wp_lua_scripting_load_configuration (const gchar * conf_file,
|
|||||||
{
|
{
|
||||||
g_autofree gchar * path = NULL;
|
g_autofree gchar * path = NULL;
|
||||||
g_autoptr (lua_State) L = wplua_new ();
|
g_autoptr (lua_State) L = wplua_new ();
|
||||||
gboolean found = FALSE;
|
gint nfiles = 0;
|
||||||
|
|
||||||
wplua_enable_sandbox (L, WP_LUA_SANDBOX_MINIMAL_STD);
|
wplua_enable_sandbox (L, WP_LUA_SANDBOX_MINIMAL_STD);
|
||||||
|
|
||||||
/* load conf_file itself */
|
/* load conf_file itself */
|
||||||
path = g_build_filename (wp_get_config_dir (), conf_file, NULL);
|
path = wp_find_config_file (conf_file, NULL);
|
||||||
if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
|
if (path) {
|
||||||
wp_info ("loading config file: %s", path);
|
wp_info ("loading config file: %s", path);
|
||||||
if (!wplua_load_path (L, path, 0, 0, error))
|
if (!wplua_load_path (L, path, 0, 0, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
found = TRUE;
|
nfiles = 1;
|
||||||
}
|
}
|
||||||
g_clear_pointer (&path, g_free);
|
g_clear_pointer (&path, g_free);
|
||||||
|
|
||||||
/* aggregate split files from the ${conf_file}.d subdirectory */
|
path = g_strdup_printf ("%s.d", conf_file);
|
||||||
path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.d",
|
nfiles += wp_iter_config_files (path, ".lua", load_file, L, error);
|
||||||
wp_get_config_dir (), conf_file);
|
|
||||||
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
|
|
||||||
g_autoptr (GDir) conf_dir = g_dir_open (path, 0, error);
|
|
||||||
if (!conf_dir)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* sort files before loading them */
|
if (nfiles == 0) {
|
||||||
g_autoptr (GPtrArray) filenames = g_ptr_array_new ();
|
|
||||||
const gchar *filename = NULL;
|
|
||||||
while ((filename = g_dir_read_name (conf_dir))) {
|
|
||||||
/* Only parse files that have the proper extension */
|
|
||||||
if (g_str_has_suffix (filename, ".lua")) {
|
|
||||||
g_ptr_array_add (filenames, (gpointer) filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_ptr_array_sort (filenames, sort_filelist);
|
|
||||||
|
|
||||||
/* load */
|
|
||||||
for (guint i = 0; i < filenames->len; i++) {
|
|
||||||
g_autofree gchar * file = g_build_filename (path,
|
|
||||||
g_ptr_array_index (filenames, i), NULL);
|
|
||||||
wp_info ("loading config file: %s", file);
|
|
||||||
if (!wplua_load_path (L, file, 0, 0, error))
|
|
||||||
return FALSE;
|
|
||||||
found = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||||
"Could not locate configuration file '%s'", conf_file);
|
"Could not locate configuration file '%s'", conf_file);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
26
src/main.c
26
src/main.c
@@ -327,22 +327,6 @@ init_done (WpCore * core, GAsyncResult * res, WpDaemon * d)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gchar *
|
|
||||||
find_config (const gchar *config_file_name)
|
|
||||||
{
|
|
||||||
g_autofree gchar *path = NULL;
|
|
||||||
|
|
||||||
if (!config_file_name)
|
|
||||||
config_file_name = "wireplumber.conf";
|
|
||||||
|
|
||||||
if (g_path_is_absolute (config_file_name))
|
|
||||||
path = g_strdup (config_file_name);
|
|
||||||
else
|
|
||||||
path = g_build_filename (wp_get_config_dir (), config_file_name, NULL);
|
|
||||||
|
|
||||||
return g_canonicalize_filename (path, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
gint
|
gint
|
||||||
main (gint argc, gchar **argv)
|
main (gint argc, gchar **argv)
|
||||||
{
|
{
|
||||||
@@ -361,7 +345,15 @@ main (gint argc, gchar **argv)
|
|||||||
return WP_EXIT_USAGE;
|
return WP_EXIT_USAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
config_file_path = find_config (config_file);
|
if (!config_file)
|
||||||
|
config_file = "wireplumber.conf";
|
||||||
|
|
||||||
|
config_file_path = wp_find_config_file (config_file, NULL);
|
||||||
|
if (config_file_path == NULL) {
|
||||||
|
fprintf (stderr, "Unable to find the required configuration file %s\n",
|
||||||
|
config_file);
|
||||||
|
return WP_EXIT_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
properties = wp_properties_new (
|
properties = wp_properties_new (
|
||||||
PW_KEY_CONFIG_NAME, config_file_path,
|
PW_KEY_CONFIG_NAME, config_file_path,
|
||||||
|
Reference in New Issue
Block a user