summaryrefslogtreecommitdiff
path: root/shared/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/utils.c')
-rw-r--r--shared/utils.c176
1 files changed, 176 insertions, 0 deletions
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;
+}