summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--minecctl/misc-commands.c9
-rw-r--r--minecctl/misc.c69
-rw-r--r--minecctl/misc.h14
-rw-r--r--minecctl/server.c46
-rw-r--r--minecproxy/main.c96
-rw-r--r--minecproxy/main.h30
-rw-r--r--minecproxy/server-config.c13
-rw-r--r--shared/config-parser.c1
-rw-r--r--shared/systemd.c1
-rw-r--r--shared/utils.c176
-rw-r--r--shared/utils.h44
11 files changed, 291 insertions, 208 deletions
diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c
index db9b937..3ad9792 100644
--- a/minecctl/misc-commands.c
+++ b/minecctl/misc-commands.c
@@ -69,6 +69,7 @@ static bool write_cfg_file(int dfd, const char *name,
static bool find_user_service(int xfd, const char *service)
{
_cleanup_close_ int dfd = -1;
+ _cleanup_free_ char *dpath = NULL;
/* FIXME: Make this a macro, make paths #defines */
char sub_path[STRLEN("systemd/user/") + strlen(service) + 1];
char etc_path[STRLEN("/etc/systemd/user/") + strlen(service) + 1];
@@ -98,11 +99,11 @@ static bool find_user_service(int xfd, const char *service)
return true;
}
- dfd = open_xdg_data_dir(false);
+ dfd = open_xdg_data_dir(false, &dpath);
if (dfd >= 0) {
if (faccessat(dfd, sub_path, R_OK, 0) == 0) {
info("User service %s already installed in "
- "$XDG_DATA_HOME/systemd/user/", service);
+ "%s/systemd/user/", service, dpath);
return true;
}
}
@@ -187,7 +188,7 @@ bool do_init(_unused_ struct cfg *cfg)
_cleanup_close_ int xdfd = -1;
_cleanup_close_ int mdfd = -1;
- xcfd = open_xdg_cfg_dir(true);
+ xcfd = open_xdg_cfg_dir(true, NULL);
if (xcfd < 0)
return false;
@@ -215,7 +216,7 @@ bool do_init(_unused_ struct cfg *cfg)
___examples_minecproxy_service_len))
return false;
- xdfd = open_xdg_data_dir(true);
+ xdfd = open_xdg_data_dir(true, NULL);
if (xdfd < 0)
return false;
diff --git a/minecctl/misc.c b/minecctl/misc.c
index 90189dc..72c711a 100644
--- a/minecctl/misc.c
+++ b/minecctl/misc.c
@@ -8,81 +8,12 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include <pwd.h>
#include <errno.h>
#include "shared/utils.h"
#include "misc.h"
#include "minecctl.h"
-static const char *get_homedir()
-{
- const char *e;
- struct passwd *passwd;
- uid_t uid;
-
- e = getenv("HOME");
- if (e && e[0] == '/')
- return e;
-
- uid = getuid();
- if (uid == 0)
- return "/root";
-
- passwd = getpwuid(uid);
- if (passwd && passwd->pw_dir[0] == '/')
- return passwd->pw_dir;
-
- return NULL;
-}
-
-int open_subdir(int dfd, const char *subdir, bool nofail)
-{
- int sfd;
-
- if (nofail && mkdirat(dfd, subdir, 0777) < 0 && errno != EEXIST) {
- error("Unable to create subdirectory %s: %m", subdir);
- return -1;
- }
-
- sfd = openat(dfd, subdir, O_PATH | O_CLOEXEC | O_DIRECTORY);
- if (sfd < 0 && nofail)
- error("Unable to open subdirectory %s: %m", subdir);
-
- return sfd;
-}
-
-int open_xdg_dir(const char *envname, const char *altpath, bool nofail)
-{
- const char *e;
- const char *h;
- int dfd, hfd;
-
- e = getenv(envname);
- if (e && e[0] == '/') {
- dfd = open(e, O_PATH | O_CLOEXEC | O_DIRECTORY);
- if (dfd < 0)
- error("Unable to open $%s(%s): %m", envname, e);
- return dfd;
- }
-
- h = get_homedir();
- if (!h) {
- error("Unable to determine home directory");
- return -1;
- }
-
- hfd = open(h, O_PATH | O_CLOEXEC | O_DIRECTORY);
- if (hfd < 0) {
- error("Unable to open $HOME(%s): %m", h);
- return -1;
- }
-
- dfd = open_subdir(hfd, altpath, nofail);
- close(hfd);
- return dfd;
-}
-
/* FIXME: Can be shared */
void set_use_colors()
{
diff --git a/minecctl/misc.h b/minecctl/misc.h
index 02c01ea..3182ef8 100644
--- a/minecctl/misc.h
+++ b/minecctl/misc.h
@@ -2,20 +2,6 @@
#ifndef foomischfoo
#define foomischfoo
-int open_subdir(int dfd, const char *subdir, bool nofail);
-
-int open_xdg_dir(const char *envname, const char *altpath, bool nofail);
-
-static inline int open_xdg_data_dir(bool nofail)
-{
- return open_xdg_dir("XDG_DATA_HOME", ".local/share", nofail);
-}
-
-static inline int open_xdg_cfg_dir(bool nofail)
-{
- return open_xdg_dir("XDG_CONFIG_HOME", ".config", nofail);
-}
-
void set_use_colors();
char **strv_copy(char *const *strv);
diff --git a/minecctl/server.c b/minecctl/server.c
index 3acc537..e50e10c 100644
--- a/minecctl/server.c
+++ b/minecctl/server.c
@@ -9,63 +9,29 @@
#include "minecctl.h"
#include "server.h"
#include "misc.h"
-#include "config.h"
#define INVALID(msg) do { *error = (msg); return false; } while(0)
-static DIR *__open_dir(int (*base_dir)(bool nofail), const char *fallback)
-{
- _cleanup_close_ int xfd = -1;
- _cleanup_close_ int mfd = -1;
- DIR *dir;
-
- /* First, attempt per-user config dir... */
- xfd = base_dir(false);
- if (xfd < 0)
- goto fallback;
-
- mfd = openat(xfd, "minecproxy", O_CLOEXEC | O_DIRECTORY | O_RDONLY);
- if (mfd < 0)
- goto fallback;
-
- dir = fdopendir(mfd);
- if (dir)
- mfd = -1;
- return dir;
-
- /* ...and fallback on the system dir */
-fallback:
- return opendir(fallback);
-}
-
-static inline DIR *open_cfg_dir()
-{
- return __open_dir(open_xdg_cfg_dir, DEFAULT_CFG_DIR);
-}
-
-static inline DIR *open_data_dir()
-{
- return __open_dir(open_xdg_data_dir, DEFAULT_DATA_DIR);
-}
-
void server_load_all_known(struct cfg *cfg)
{
struct dirent *dent;
+ _cleanup_free_ char *cpath = NULL;
+ _cleanup_free_ char *dpath = NULL;
if (cfg->server_list_loaded)
return;
cfg->server_list_loaded = true;
- cfg->cfg_dir = open_cfg_dir();
+ cfg->cfg_dir = open_cfg_dir(NULL, &cpath);
if (!cfg->cfg_dir) {
- error("Failed to open config directory");
+ error("Failed to open config directory %s", cpath);
return;
}
- cfg->data_dir = open_data_dir();
+ cfg->data_dir = open_data_dir(NULL, &dpath);
if (!cfg->data_dir) {
- error("Failed to open server directory");
+ error("Failed to open server directory %s", dpath);
return;
}
diff --git a/minecproxy/main.c b/minecproxy/main.c
index 0406976..48bb1fa 100644
--- a/minecproxy/main.c
+++ b/minecproxy/main.c
@@ -179,6 +179,8 @@ static void cfg_free(struct uring_task *task)
debug(DBG_SIG, "called");
systemd_delete();
xfree(cfg->igmp_iface);
+ xfree(cfg->data_real_path);
+ xfree(cfg->cfg_real_path);
cfg->igmp_iface = NULL;
exiting = true;
/* The cfg struct is free:d in main() */
@@ -258,54 +260,45 @@ struct cfg_key_value_map mcfg_key_map[] = {
static void cfg_read()
{
- FILE *cfgfile;
- const char *path;
char buf[4096];
- char *pos = buf;
- size_t rd = 0;
+ char *pos;
+ size_t off;
size_t r;
unsigned lineno;
+ _cleanup_close_ int fd = -1;
+ _cleanup_fclose_ FILE *cfgfile = NULL;
assert_return(cfg);
- if (cfg->cfg_file)
- path = cfg->cfg_file;
- else
- path = DEFAULT_MAIN_CFG_FILE;
-
- cfgfile = fopen(path, "re");
- if (!cfgfile) {
- /* ENOENT is only an error with an explicitly set path */
- if (errno == ENOENT && !cfg->cfg_file)
- return;
- else if (errno == ENOENT)
- die("main config file (%s) missing", path);
- else
- die("fopen(%s): %m", path);
- }
+ fd = openat(dirfd(cfg->cfg_dir), DEFAULT_MAIN_CFG_FILE,
+ O_RDONLY | O_CLOEXEC | O_NOCTTY);
+ if (fd < 0)
+ return;
+
+ cfgfile = fdopen(fd, "re");
+ if (!cfgfile)
+ return;
- debug(DBG_CFG, "opened main config file (%s)", path);
+ fd = -1;
+ debug(DBG_CFG, "opened main config file %s/%s", cfg->cfg_real_path,
+ DEFAULT_MAIN_CFG_FILE);
- while (rd < sizeof(buf)) {
- r = fread(pos, 1, sizeof(buf) - rd - 1, cfgfile);
+ for (off = 0; off < sizeof(buf); off += r) {
+ r = fread(buf + off, 1, sizeof(buf) - off, cfgfile);
if (r == 0)
break;
- rd += r;
- pos += r;
}
- if (rd == 0)
- die("main config file (%s) zero size", path);
+ if (off >= sizeof(buf) - 1)
+ die("main config file %s/%s too large", cfg->cfg_real_path,
+ DEFAULT_MAIN_CFG_FILE);
- if (rd >= sizeof(buf))
- die("main config file (%s) too large", path);
-
- fclose(cfgfile);
- *pos = '\0';
+ buf[off] = '\0';
pos = buf;
if (!config_parse_header("mcproxy", &pos, &lineno))
- die("main config file (%s) missing/invalid header", path);
+ die("main config file %s/%s missing/invalid header",
+ cfg->cfg_real_path, DEFAULT_MAIN_CFG_FILE);
while (true) {
int key;
@@ -313,13 +306,15 @@ static void cfg_read()
struct cfg_value value;
const char *error;
- if (!config_parse_line(path, &pos, mcfg_key_map, &key, &keyname,
+ if (!config_parse_line(DEFAULT_MAIN_CFG_FILE, &pos,
+ mcfg_key_map, &key, &keyname,
&value, false, &lineno, &error))
break;
if (key == MCFG_KEY_INVALID)
- die("main config file (%s) invalid: line %u: %s", path,
- lineno, error);
+ die("main config file %s/%s invalid: line %u: %s",
+ cfg->cfg_real_path, DEFAULT_MAIN_CFG_FILE, lineno,
+ error);
debug(DBG_CFG, "main cfg: key %s", keyname);
@@ -369,7 +364,7 @@ static void cfg_read()
case MCFG_KEY_INVALID:
default:
- die("main config file (%s) invalid", path);
+ die("main config file invalid");
}
}
}
@@ -436,8 +431,8 @@ _noreturn_ static void usage(bool invalid)
info("Usage: %s [OPTIONS]\n"
"\n"
"Valid options:\n"
- " -c, --cfgdir=DIR\tlook for server configuration files in DIR\n"
- " -C, --cfgfile=PATH\tuse PATH as the main configuration file\n"
+ " -c, --cfgdir=DIR\tuse DIR for configuration files\n"
+ " -C, --datadir=DIR\tuse DIR for server data\n"
" -u, --user=USER\trun as USER\n"
" -D, --daemonize\trun in daemon mode (disables stderr output)\n"
" -l, --logfile=FILE\tlog to FILE instead of stderr\n"
@@ -465,7 +460,12 @@ static void cfg_init(int argc, char **argv)
uring_task_init(&cfg->task, "main", NULL, cfg_free);
INIT_LIST_HEAD(&cfg->servers);
- cfg->cfg_dir = DEFAULT_CFG_DIR;
+ cfg->cfg_path = NULL;
+ cfg->cfg_real_path = NULL;
+ cfg->cfg_dir = NULL;
+ cfg->data_path = NULL;
+ cfg->data_real_path = NULL;
+ cfg->data_dir = NULL;
cfg->announce_interval = DEFAULT_ANNOUNCE_INTERVAL;
cfg->proxy_connection_interval = DEFAULT_PROXY_CONN_INTERVAL;
cfg->proxy_connection_attempts = DEFAULT_PROXY_CONN_ATTEMPTS;
@@ -482,7 +482,7 @@ static void cfg_init(int argc, char **argv)
/* clang-format off */
static struct option long_options[] = {
{ "cfgdir", required_argument, 0, 'c' },
- { "cfgfile", required_argument, 0, 'C' },
+ { "datadir", required_argument, 0, 'C' },
{ "user", required_argument, 0, 'u' },
{ "daemonize", no_argument, 0, 'D' },
{ "logfile", required_argument, 0, 'l' },
@@ -500,11 +500,11 @@ static void cfg_init(int argc, char **argv)
switch (c) {
case 'c':
- cfg->cfg_dir = optarg;
+ cfg->cfg_path = optarg;
break;
case 'C':
- cfg->cfg_file = optarg;
+ cfg->data_path = optarg;
break;
case 'v':
@@ -618,8 +618,16 @@ static void cfg_apply()
* Do this after caps have been dropped to make sure we're not
* accessing a directory we should have permissions to.
*/
- if (chdir(cfg->cfg_dir))
- die("chdir(%s): %m", cfg->cfg_dir);
+ cfg->cfg_dir = open_cfg_dir(cfg->cfg_path, &cfg->cfg_real_path);
+ if (!cfg->cfg_dir)
+ die("Unable to open configuration directory");
+
+ cfg->data_dir = open_data_dir(cfg->data_path, &cfg->data_real_path);
+ if (!cfg->data_dir)
+ die("Unable to open server directory");
+
+ if (fchdir(dirfd(cfg->data_dir)))
+ die("Unable to chdir to server directory: %m");
if (debug_enabled(DBG_VERBOSE)) {
char *wd;
diff --git a/minecproxy/main.h b/minecproxy/main.h
index 2c7be83..f7dd547 100644
--- a/minecproxy/main.h
+++ b/minecproxy/main.h
@@ -14,28 +14,6 @@ struct uring_task;
extern struct cfg *cfg;
extern bool exiting;
-/*
-enum debug_lvl {
- DBG_ERROR = (0x1 << 1),
- DBG_INFO = (0x1 << 2),
- DBG_VERBOSE = (0x1 << 3),
- DBG_CFG = (0x1 << 4),
- DBG_REF = (0x1 << 5),
- DBG_MALLOC = (0x1 << 6),
- DBG_ANN = (0x1 << 7),
- DBG_SIG = (0x1 << 8),
- DBG_UR = (0x1 << 9),
- DBG_SRV = (0x1 << 10),
- DBG_PROXY = (0x1 << 11),
- DBG_RCON = (0x1 << 12),
- DBG_IDLE = (0x1 << 13),
- DBG_IGMP = (0x1 << 14),
- DBG_SYSD = (0x1 << 15),
- DBG_DNS = (0x1 << 16),
- DBG_TIMER = (0x1 << 17),
-};
-*/
-
void dump_tree();
/* To save typing in all the function definitions below */
@@ -75,10 +53,14 @@ struct uring_task {
struct cfg {
/* Options */
+ const char *cfg_path;
+ char *cfg_real_path;
+ DIR *cfg_dir;
+ const char *data_path;
+ char *data_real_path;
+ DIR *data_dir;
uid_t uid;
gid_t gid;
- const char *cfg_dir;
- const char *cfg_file;
bool do_igmp;
char *igmp_iface;
bool splice_supported;
diff --git a/minecproxy/server-config.c b/minecproxy/server-config.c
index 959f1d0..2e7f277 100644
--- a/minecproxy/server-config.c
+++ b/minecproxy/server-config.c
@@ -215,11 +215,10 @@ void server_cfg_monitor_init()
int ifd;
int iwd;
struct server_cfg_monitor *scfgm;
- DIR *dir;
struct dirent *dent;
struct server *server;
- assert_return(!cfg->server_cfg_monitor);
+ assert_return(!cfg->server_cfg_monitor && cfg->cfg_dir);
scfgm = zmalloc(sizeof(*scfgm));
if (!scfgm)
@@ -231,7 +230,7 @@ void server_cfg_monitor_init()
/* ln = IN_CREATE, cp/vi/mv = IN_CREATE, IN_OPEN, IN_CLOSE_WRITE */
iwd = inotify_add_watch(
- ifd, ".",
+ ifd, cfg->cfg_real_path,
IN_CLOSE_WRITE | IN_DELETE | IN_CREATE | IN_DELETE_SELF |
IN_MOVE_SELF | IN_MOVED_TO | IN_MOVED_FROM |
IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_ONLYDIR);
@@ -244,11 +243,7 @@ void server_cfg_monitor_init()
cfg->server_cfg_monitor = scfgm;
uring_read(&scfgm->task, scfgm->buf, sizeof(scfgm->buf), inotify_cb);
- dir = opendir(".");
- if (!dir)
- die("opendir(%s): %m", cfg->cfg_dir);
-
- while ((dent = readdir(dir)) != NULL) {
+ while ((dent = readdir(cfg->cfg_dir)) != NULL) {
if (!config_valid_server_filename(dent, NULL))
continue;
@@ -257,6 +252,4 @@ void server_cfg_monitor_init()
uring_openat(&server->task, server->scfg.filename,
server_cfg_open_cb);
}
-
- closedir(dir);
}
diff --git a/shared/config-parser.c b/shared/config-parser.c
index 4dc446c..909c558 100644
--- a/shared/config-parser.c
+++ b/shared/config-parser.c
@@ -14,7 +14,6 @@
#include "config-parser.h"
#include "server-config-options.h"
#include "server-properties-options.h"
-#include "config.h"
static bool handle_addrinfo_results(struct addrinfo *results,
struct list_head *target_list,
diff --git a/shared/systemd.c b/shared/systemd.c
index c45c7c5..4edc055 100644
--- a/shared/systemd.c
+++ b/shared/systemd.c
@@ -7,7 +7,6 @@
#include "utils.h"
#include "config-parser.h"
#include "systemd.h"
-#include "config.h"
static sd_bus *bus = NULL;
static bool bus_failed = false;
diff --git a/shared/utils.c b/shared/utils.c
index 99fcaa3..f41a8a1 100644
--- a/shared/utils.c
+++ b/shared/utils.c
@@ -5,6 +5,7 @@
#include <limits.h>
#include <arpa/inet.h>
#include <string.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
@@ -14,11 +15,156 @@
#include <netinet/tcp.h>
#include <inttypes.h>
#include <stdarg.h>
+#include <pwd.h>
#include "utils.h"
unsigned debug_mask = 0;
+static const char *get_homedir()
+{
+ const char *e;
+ struct passwd *passwd;
+ uid_t uid;
+
+ e = getenv("HOME");
+ if (e && e[0] == '/')
+ return e;
+
+ uid = getuid();
+ if (uid == 0)
+ return "/root";
+
+ passwd = getpwuid(uid);
+ if (passwd && passwd->pw_dir[0] == '/')
+ return passwd->pw_dir;
+
+ return NULL;
+}
+
+int open_subdir(int dfd, const char *subdir, bool nofail)
+{
+ int sfd;
+
+ if (nofail && mkdirat(dfd, subdir, 0777) < 0 && errno != EEXIST) {
+ error("Unable to create subdirectory %s: %m", subdir);
+ return -1;
+ }
+
+ sfd = openat(dfd, subdir, O_PATH | O_CLOEXEC | O_DIRECTORY);
+ if (sfd < 0 && nofail)
+ error("Unable to open subdirectory %s: %m", subdir);
+
+ return sfd;
+}
+
+int open_xdg_dir(const char *envname, const char *altpath, bool nofail,
+ char **rpath)
+{
+ const char *e;
+ const char *h;
+ int dfd, hfd;
+
+ e = getenv(envname);
+ if (e && e[0] == '/') {
+ dfd = open(e, O_PATH | O_CLOEXEC | O_DIRECTORY);
+ if (dfd < 0)
+ error("Unable to open $%s(%s): %m", envname, e);
+ else if (rpath) {
+ *rpath = xstrdup(e);
+ if (!rpath) {
+ error("xstrdup: %m");
+ close(dfd);
+ return -1;
+ }
+ }
+ return dfd;
+ }
+
+ h = get_homedir();
+ if (!h) {
+ error("Unable to determine home directory");
+ return -1;
+ }
+
+ hfd = open(h, O_PATH | O_CLOEXEC | O_DIRECTORY);
+ if (hfd < 0) {
+ error("Unable to open $HOME(%s): %m", h);
+ return -1;
+ }
+
+ dfd = open_subdir(hfd, altpath, nofail);
+ close(hfd);
+
+ if (dfd >= 0 && rpath) {
+ *rpath = xstrcat(h, "/", altpath, NULL);
+ if (!*rpath) {
+ error("xstrcat: %m");
+ close(dfd);
+ return -1;
+ }
+ }
+ return dfd;
+}
+
+DIR *__open_dir(const char *user_override,
+ int (*base_dir)(bool nofail, char **rpath),
+ const char *fallback, char **rpath)
+{
+ _cleanup_close_ int xfd = -1;
+ _cleanup_close_ int mfd = -1;
+ _cleanup_free_ char *tmp = NULL;
+ DIR *dir;
+
+ /* First, use explicitly set path... */
+ if (user_override) {
+ dir = opendir(user_override);
+ if (dir && rpath) {
+ *rpath = xstrdup(user_override);
+ if (!*rpath) {
+ closedir(dir);
+ return NULL;
+ }
+ }
+ return dir;
+ }
+
+ /* ...second, attempt per-user config dir... */
+ xfd = base_dir(false, &tmp);
+ if (xfd < 0)
+ goto fallback;
+
+ mfd = openat(xfd, "minecproxy", O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+ if (mfd < 0)
+ goto fallback;
+
+ dir = fdopendir(mfd);
+ if (dir)
+ mfd = -1;
+
+ if (rpath) {
+ *rpath = xstrcat(tmp, "/minecproxy", NULL);
+ if (!*rpath) {
+ closedir(dir);
+ return NULL;
+ }
+ }
+
+ return dir;
+
+ /* ...third, fallback on the system dir */
+fallback:
+ dir = opendir(fallback);
+ if (dir && rpath) {
+ *rpath = xstrdup(fallback);
+ if (!*rpath) {
+ closedir(dir);
+ return NULL;
+ }
+ }
+ return dir;
+}
+
const char *ansi_red = "";
const char *ansi_green = "";
const char *ansi_yellow = "";
@@ -252,3 +398,33 @@ char *xsprintf(size_t *rlen, const char *fmt, ...)
return str;
}
+
+char *xstrcat(const char *a, ...) {
+ size_t len;
+ const char *b;
+ char *str;
+ char *to;
+ va_list ap;
+
+ if (!a)
+ return NULL;
+
+ len = strlen(a) + 1;
+
+ va_start(ap, a);
+ while ((b = va_arg(ap, const char *)))
+ len += strlen(b);
+ va_end(ap);
+
+ str = zmalloc(len);
+ if (!str)
+ return NULL;
+
+ to = stpcpy(str, a);
+ va_start(ap, a);
+ while ((b = va_arg(ap, const char *)))
+ to = stpcpy(to, b);
+ va_end(ap);
+
+ return str;
+}
diff --git a/shared/utils.h b/shared/utils.h
index 1291d21..b6bf51c 100644
--- a/shared/utils.h
+++ b/shared/utils.h
@@ -8,9 +8,11 @@
#include <stdlib.h>
#include <linux/if_packet.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
+#include <dirent.h>
extern unsigned debug_mask;
@@ -42,6 +44,7 @@ extern unsigned debug_mask;
#include "list.h"
#include "debug.h"
#include "external.h"
+#include "config.h"
#include "ansi-colors.h"
/* Length of longest DNS name = 253 + trailing dot */
@@ -68,6 +71,37 @@ struct saddr {
struct list_head list;
};
+int open_subdir(int dfd, const char *subdir, bool nofail);
+
+int open_xdg_dir(const char *envname, const char *altpath, bool nofail,
+ char **rpath);
+
+static inline int open_xdg_data_dir(bool nofail, char **rpath)
+{
+ return open_xdg_dir("XDG_DATA_HOME", ".local/share", nofail, rpath);
+}
+
+static inline int open_xdg_cfg_dir(bool nofail, char **rpath)
+{
+ return open_xdg_dir("XDG_CONFIG_HOME", ".config", nofail, rpath);
+}
+
+DIR *__open_dir(const char *user_override,
+ int (*base_dir)(bool nofail, char **rpath),
+ const char *fallback, char **rpath);
+
+static inline DIR *open_cfg_dir(const char *user_override, char **rpath)
+{
+ return __open_dir(user_override, open_xdg_cfg_dir, DEFAULT_CFG_DIR,
+ rpath);
+}
+
+static inline DIR *open_data_dir(const char *user_override, char **rpath)
+{
+ return __open_dir(user_override, open_xdg_data_dir, DEFAULT_DATA_DIR,
+ rpath);
+}
+
void enable_colors();
void free_password(char **password);
@@ -91,6 +125,8 @@ int strtou16_strict(const char *str, uint16_t *result);
char *xsprintf(size_t *rlen, const char *fmt, ...) _printf_(2, 3);
+char *xstrcat(const char *a, ...);
+
static inline bool empty_str(const char *str)
{
if (!str || str[0] == '\0')
@@ -136,7 +172,7 @@ static inline bool strcaseeq(const char *a, const char *b)
static inline void closep(int *fd)
{
- if (*fd && *fd >= 0)
+ if (fd && *fd >= 0)
close(*fd);
}
#define _cleanup_close_ _cleanup_(closep)
@@ -146,4 +182,10 @@ static inline void freep(void *p) {
}
#define _cleanup_free_ _cleanup_(freep)
+static inline void fclosep(FILE **f) {
+ if (f && *f)
+ fclose(*f);
+}
+#define _cleanup_fclose_ _cleanup_(fclosep)
+
#endif