diff --git a/README.md b/README.md index 802cae74a..f89f9081a 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ Congratulations, you have just compiled and run your own Hello world application ### AppKit -AppKit is still highly expiramental and incomplete, but to work on it you need to configure CMake with `-DFRAMEWORK_APPKIT=1` and install some additional packages. +AppKit is still highly expiramental and incomplete, but to work on it you need to configure CMake with `-DFRAMEWORK_APPKIT=1` and install some additional packages. Ubuntu 16.04: ``` diff --git a/src/dyld/darling.c b/src/dyld/darling.c index c58ac4832..6593b02ca 100644 --- a/src/dyld/darling.c +++ b/src/dyld/darling.c @@ -38,94 +38,230 @@ along with Darling. If not, see . #include "darling-config.h" const char* DARLING_INIT_COMM = "darling-init"; -uid_t g_originalUid; +char *prefix; +uid_t g_originalUid, g_originalGid; int main(int argc, const char** argv) { - const char* dprefix; - int pidInit; - + pid_t pidInit, pidChild; + char path[4096]; + int wstatus; + if (argc <= 1) { showHelp(argv[0]); return 1; } - - /*if (geteuid() != 0) + + if (geteuid() != 0) { missingSetuidRoot(); return 1; - }*/ - + } + if (loadKernelModule()) return 1; - // Temporarily drop root privileges g_originalUid = getuid(); - seteuid(getuid()); - setegid(getgid()); - - dprefix = getenv("DPREFIX"); - if (!dprefix) - dprefix = defaultPrefixPath(); - if (!dprefix) + g_originalGid = getgid(); + + prefix = getenv("DPREFIX"); + if (!prefix) + prefix = defaultPrefixPath(); + if (!prefix) return 1; - checkPrefixOwner(dprefix); - - pidInit = getInitProcess(dprefix); - + setenv("DPREFIX", prefix, 0); + + if (!checkPrefixDir()) + setupPrefix(); + checkPrefixOwner(); + + pidInit = getInitProcess(); + // If prefix's init is not running, start it up if (pidInit == 0) { - char* opts; - - createDir(dprefix); - setupWorkdir(dprefix); - - // Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace - // and do the mount while we can be root - //seteuid(0); - if (unshare(CLONE_NEWNS) != 0) - { - fprintf(stderr, "Cannot unshare(CLONE_NEWNS): %s\n", strerror(errno)); - return 1; - } - - // Because IDIOTIC systemd marks / as MS_SHARED and we would inherit this into the overlay mount, - // causing it not to be unmounted once the init process dies. - if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) - { - fprintf(stderr, "Cannot remount / as private: %s\n", strerror(errno)); - return 1; - } - - opts = (char*) malloc(strlen(dprefix)*2 + sizeof(LIBEXEC_PATH) + 50); - sprintf(opts, "lowerdir=%s,upperdir=%s,workdir=%s.workdir", LIBEXEC_PATH, dprefix, dprefix); - - // Mount overlay onto our prefix - if (mount("overlay", dprefix, "overlay", 0, opts) != 0) - { - fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno)); - return 1; - } - - free(opts); - //seteuid(getuid()); - - pidInit = spawnInitProcess(dprefix); + setupWorkdir(); + pidInit = spawnInitProcess(); + putInitPid(pidInit); } - - // TODO: Spawn the executable in the namespace, wait for it and return its exit code - unloadKernelModule(); + + if (strcmp(argv[1], "shell") != 0) + { + const char *argv_child[argc + 1]; + + argv_child[0] = "dyld"; + for (int i = 1; i < argc; i++) + argv_child[i] = argv[i]; + argv_child[argc] = NULL; + + pidChild = spawnChild(pidInit, "/usr/local/bin/dyld", argv_child); + } + else + { + // Spawn the shell + snprintf(path, sizeof(path), "%s/bin/bash", prefix); + if (argc > 2) + { + size_t total_len = 0; + for (int i = 2; i < argc; i++) + total_len += strlen(argv[i]); + + char buffer[total_len + argc]; + + char *to = buffer; + for (int i = 2; i < argc; i++) + to = stpcpy(stpcpy(to, argv[i]), " "); + // Overwrite the last whitespace + *(to - 1) = '\0'; + + pidChild = spawnChild(pidInit, "/usr/local/bin/dyld", + (const char *[5]) {"dyld", path, "-c", buffer, NULL}); + } + else + pidChild = spawnChild(pidInit, "/usr/local/bin/dyld", + (const char *[3]) {"dyld", path, NULL}); + } + + // Drop the privileges so that we can be killed, etc by the user + seteuid(g_originalUid); + + waitpid(pidChild, &wstatus, 0); + + // Should we unloadKernelModule() here? Others may be still using it + if (WIFEXITED(wstatus)) + return WEXITSTATUS(wstatus); + if (WIFSIGNALED(wstatus)) + return WTERMSIG(wstatus); return 0; } +pid_t spawnChild(int pidInit, const char *path, const char *const argv[]) +{ + int fdNS; + pid_t pidChild; + char pathNS[4096], curPath[4096]; + + if (getcwd(curPath, sizeof(curPath)) == NULL) + { + fprintf(stderr, "Cannot get current directory: %s\n", strerror(errno)); + exit(1); + } + + snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/pid", pidInit); + + fdNS = open(pathNS, O_RDONLY); + + if (fdNS < 0) + { + fprintf(stderr, "Cannot open PID namespace file: %s\n", strerror(errno)); + exit(1); + } + + // Calling setns() with a PID namespace doesn't move our process into it, + // but our child process will be spawned inside the namespace + if (setns(fdNS, CLONE_NEWPID) != 0) + { + fprintf(stderr, "Cannot join PID namespace: %s\n", strerror(errno)); + exit(1); + } + close(fdNS); + + pidChild = fork(); + if (pidChild < 0) + { + fprintf(stderr, "Cannot spawn a child process: %s\n", strerror(errno)); + exit(1); + } + + if (pidChild == 0) + { + // This is the child process + + // We still have the outside PIDs in /proc + snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/mnt", pidInit); + fdNS = open(pathNS, O_RDONLY); + if (fdNS < 0) + { + fprintf(stderr, "Cannot open mount namespace file: %s\n", strerror(errno)); + exit(1); + } + + if (setns(fdNS, CLONE_NEWNS) != 0) + { + fprintf(stderr, "Cannot join mount namespace: %s\n", strerror(errno)); + exit(1); + } + close(fdNS); + + snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/user", pidInit); + fdNS = open(pathNS, O_RDONLY); + if (fdNS < 0) + { + fprintf(stderr, "Cannot open user namespace file: %s\n", strerror(errno)); + exit(1); + } + + setresuid(g_originalUid, g_originalUid, g_originalUid); + setresgid(g_originalGid, g_originalGid, g_originalGid); + + if (setns(fdNS, CLONE_NEWUSER) != 0) + { + fprintf(stderr, "Cannot join user namespace: %s\n", strerror(errno)); + exit(1); + } + close(fdNS); + + setupChild(curPath); + + execv(path, (char * const *) argv); + + fprintf(stderr, "Cannot exec the target program: %s\n", strerror(errno)); + exit(1); + } + + return pidChild; +} + +void setupChild(const char *curPath) +{ + char buffer1[4096]; + char buffer2[4096]; + + setenv("PATH", "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin", 1); + + sscanf(getenv("HOME"), "/home/%4096s", buffer1); + snprintf(buffer2, sizeof(buffer2), "/Users/%s", buffer1); + setenv("HOME", buffer2, 1); + + if (sscanf(curPath, "/home/%4096s", buffer1) == 1) + { + // We're currently inside our home directory + snprintf(buffer2, sizeof(buffer2), "/Users/%s", buffer1); + setenv("PWD", buffer2, 1); + + snprintf(buffer2, sizeof(buffer2), "%s/Users/%s", prefix, buffer1); + chdir(buffer2); + } + else + { + snprintf(buffer2, sizeof(buffer2), "/system-root%s", curPath); + setenv("PWD", buffer2, 1); + + snprintf(buffer2, sizeof(buffer2), "%s/system-root%s", prefix, curPath); + chdir(buffer2); + } +} + void showHelp(const char* argv0) { fprintf(stderr, "This is Darling, translation layer for macOS software.\n\n"); fprintf(stderr, "Copyright (C) 2012-2016 Lubos Dolezel\n\n"); - fprintf(stderr, "Usage: %s program-path [arguments...]\n\n", argv0); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\t%s program-path [arguments...]\n", argv0); + fprintf(stderr, "\t%s shell [arguments...]\n", argv0); + fprintf(stderr, "\n"); fprintf(stderr, "Environment variables:\n" "DPREFIX - specifies the location of Darling prefix, defaults to ~/.darling\n"); } @@ -134,80 +270,184 @@ void missingSetuidRoot(void) { char path[4096]; int len; - + len = readlink("/proc/self/exe", path, sizeof(path)-1); if (len < 0) strcpy(path, "darling"); else path[len] = '\0'; - + fprintf(stderr, "Sorry, the `%s' binary is not setuid root, which is mandatory.\n", path); fprintf(stderr, "Darling needs this in order to create mount and PID namespaces and to perform mounts.\n"); } -int spawnInitProcess(const char* prefix) +pid_t spawnInitProcess(void) { - char* childStack; - int pid; - char uidmap[100]; - - childStack = (char*) malloc(16*1024); - childStack += 16*1024; - - //setuid(0); - pid = clone(darlingPreInit, childStack, /*CLONE_NEWPID |*/ CLONE_NEWUSER, (void*) prefix); - - if (pid <= 0) + pid_t pid; + int pipefd[2]; + char idmap[100]; + char buffer[1]; + FILE *file; + + if (pipe(pipefd) == -1) { - fprintf(stderr, "Cannot clone() to create darling-init: %s\n", strerror(errno)); + fprintf(stderr, "Cannot create a pipe for synchronization: %s\n", strerror(errno)); exit(1); } - - sprintf(uidmap, "/proc/%d/uid_map", pid); - - FILE* file = fopen(uidmap, "w"); + + if (unshare(CLONE_NEWPID) != 0) + { + fprintf(stderr, "Cannot unshare pid namespace to create darling-init: %s\n", strerror(errno)); + exit(1); + } + + pid = fork(); + + if (pid < 0) + { + fprintf(stderr, "Cannot fork() to create darling-init: %s\n", strerror(errno)); + exit(1); + } + + if (pid == 0) + { + // The child + + char *opts; + + // Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace + // and do the mount while we can be root + if (unshare(CLONE_NEWNS) != 0) + { + fprintf(stderr, "Cannot unshare mount namespace: %s\n", strerror(errno)); + exit(1); + } + + // Because systemd marks / as MS_SHARED and we would inherit this into the overlay mount, + // causing it not to be unmounted once the init process dies. + if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) != 0) + { + fprintf(stderr, "Cannot remount / as slave: %s\n", strerror(errno)); + exit(1); + } + + opts = (char*) malloc(strlen(prefix)*2 + sizeof(LIBEXEC_PATH) + 50); + sprintf(opts, "lowerdir=%s,upperdir=%s,workdir=%s.workdir", LIBEXEC_PATH, prefix, prefix); + + // Mount overlay onto our prefix + if (mount("overlay", prefix, "overlay", 0, opts) != 0) + { + fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno)); + exit(1); + } + + free(opts); + + // Drop the privileges + setresuid(g_originalUid, g_originalUid, g_originalUid); + setresgid(g_originalGid, g_originalGid, g_originalGid); + prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + + prctl(PR_SET_NAME, DARLING_INIT_COMM, 0, 0); + + if (unshare(CLONE_NEWUSER) != 0) + { + fprintf(stderr, "Cannot unshare user namespace: %s\n", strerror(errno)); + exit(1); + } + + // Tell the parent we're ready for it to set up UID/GID mappings + write(pipefd[1], buffer, 1); + close(pipefd[1]); + // And wait for it to do it + read(pipefd[0], buffer, 1); + close(pipefd[0]); + + darlingPreInit(); + // Never returns + } + + // Wait for the child to drop UID/GIDs and unshare stuff + read(pipefd[0], buffer, 1); + close(pipefd[0]); + + snprintf(idmap, sizeof(idmap), "/proc/%d/uid_map", pid); + + file = fopen(idmap, "w"); if (file != NULL) { - fprintf(file, "0 %d 1\n", getuid()); // all users map to our user on the outside + fprintf(file, "0 %d 1\n", g_originalUid); // all users map to our user on the outside fclose(file); } else { - fprintf(stderr, "Failure setting uid_map in init process\n"); + fprintf(stderr, "Cannot set uid_map for the init process: %s\n", strerror(errno)); } - - seteuid(g_originalUid); - setuid(g_originalUid); - + + snprintf(idmap, sizeof(idmap), "/proc/%d/gid_map", pid); + + file = fopen(idmap, "w"); + if (file != NULL) + { + fprintf(file, "0 %d 1\n", g_originalGid); // all groups map to our group on the outside + fclose(file); + } + else + { + fprintf(stderr, "Cannot set gid_map for the init process: %s\n", strerror(errno)); + } + + // Resume the child + write(pipefd[1], buffer, 1); + close(pipefd[1]); + return pid; } -int darlingPreInit(void* arg) +void putInitPid(pid_t pidInit) +{ + const char pidFile[] = "/.init.pid"; + char* pidPath; + FILE *fp; + + pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile)); + strcpy(pidPath, prefix); + strcat(pidPath, pidFile); + + seteuid(g_originalUid); + setegid(g_originalGid); + + fp = fopen(pidPath, "w"); + + seteuid(0); + setegid(0); + + if (fp == NULL) + { + fprintf(stderr, "Cannot write out PID of the init process: %s\n", strerror(errno)); + return; + } + fprintf(fp, "%d", (int) pidInit); + fclose(fp); +} + +void darlingPreInit(void) { - const char* prefix = (const char*) arg; - - prctl(PR_SET_NAME, DARLING_INIT_COMM, 0, 0); - - // Wait until the parent sets our uid_map - while (getuid() == 65534); - // TODO: Run /usr/libexec/makewhatis - + // TODO: this is where we will exec() launchd in future. // Instead, we just reap zombies. while (1) { int status, sig; sigset_t chld; - + sigemptyset(&chld); sigaddset(&chld, SIGCHLD); sigwait(&chld, &sig); - + while (waitpid(-1, &status, 0) != -1); } - - return 0; } char* defaultPrefixPath(void) @@ -215,24 +455,24 @@ char* defaultPrefixPath(void) const char defaultPath[] = "/.darling"; const char* home = getenv("HOME"); char* buf; - + if (!home) { fprintf(stderr, "Cannot detect your home directory!\n"); return NULL; } - + buf = (char*) malloc(strlen(home) + sizeof(defaultPath)); strcpy(buf, home); strcat(buf, defaultPath); - + return buf; } void createDir(const char* path) { struct stat st; - + if (stat(path, &st) == 0) { if (!S_ISDIR(st.st_mode)) @@ -253,56 +493,140 @@ void createDir(const char* path) } else { - fprintf(stderr, "Problem accessing %s: %s\n", path, strerror(errno)); + fprintf(stderr, "Cannot access %s: %s\n", path, strerror(errno)); exit(1); } } } -void setupWorkdir(const char* prefix) +void setupWorkdir() { char* workdir; const char suffix[] = ".workdir"; size_t len; - + len = strlen(prefix); workdir = (char*) alloca(len + sizeof(suffix)); strcpy(workdir, prefix); - + // Remove trailing / while (workdir[len-1] == '/') len--; workdir[len] = '\0'; - + strcat(workdir, suffix); - + createDir(workdir); } -int getInitProcess(const char* prefix) +int checkPrefixDir() +{ + struct stat st; + + if (stat(prefix, &st) == 0) + { + if (!S_ISDIR(st.st_mode)) + { + fprintf(stderr, "%s is a file. Remove the file.\n", prefix); + exit(1); + } + return 1; // OK + } + if (errno == ENOENT) + return 0; // not found + fprintf(stderr, "Cannot access %s: %s\n", prefix, strerror(errno)); + exit(1); +} + +void setupPrefix() +{ + char path[4096]; + + fprintf(stderr, "Setting up a new Darling prefix at %s\n", prefix); + + seteuid(g_originalUid); + setegid(g_originalGid); + + createDir(prefix); + + snprintf(path, sizeof(path), "%s/system-root", prefix); + if (symlink("/", path) != 0) + { + fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno)); + exit(1); + } + + snprintf(path, sizeof(path), "%s/dev", prefix); + if (symlink("system-root/dev", path) != 0) + { + fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno)); + exit(1); + } + + snprintf(path, sizeof(path), "%s/tmp", prefix); + if (symlink("system-root/tmp", path) != 0) + { + fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno)); + exit(1); + } + + snprintf(path, sizeof(path), "%s/Users", prefix); + if (symlink("system-root/home", path) != 0) + { + fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno)); + exit(1); + } + + snprintf(path, sizeof(path), "%s/Volumes", prefix); + createDir(path); + snprintf(path, sizeof(path), "%s/Applications", prefix); + createDir(path); + + snprintf(path, sizeof(path), "%s/var", prefix); + createDir(path); + snprintf(path, sizeof(path), "%s/var/root", prefix); + createDir(path); + snprintf(path, sizeof(path), "%s/var/run", prefix); + createDir(path); + + snprintf(path, sizeof(path), "%s/var/run/syslog", prefix); + if (symlink("system-root/dev/log", path) != 0) + { + fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno)); + exit(1); + } + + seteuid(0); + setegid(0); +} + +pid_t getInitProcess() { const char pidFile[] = "/.init.pid"; char* pidPath; - int fd, rd, pid; - char pidBuf[10]; - char exeBuf[17], procBuf[100]; - + pid_t pid; + int pid_i; + FILE *fp; + char procBuf[100]; + char *exeBuf, *statusBuf; + int uidMatch = 0, gidMatch = 0; + pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile)); strcpy(pidPath, prefix); strcat(pidPath, pidFile); - - fd = open(pidPath, O_RDONLY); - if (fd == -1) + + fp = fopen(pidPath, "r"); + if (fp == NULL) return 0; - - rd = read(fd, pidBuf, sizeof(pidBuf)-1); - close(fd); - - if (rd <= 0) + + if (fscanf(fp, "%d", &pid_i) != 1) + { + fclose(fp); + unlink(pidPath); return 0; - - pidBuf[rd-1] = '\0'; - pid = atoi(pidBuf); + } + fclose(fp); + pid = (pid_t) pid_i; // Does the process exist? if (kill(pid, 0) == -1) @@ -310,42 +634,86 @@ int getInitProcess(const char* prefix) unlink(pidPath); return 0; } - + // Is it actually an init process? - sprintf(procBuf, "/proc/%d/comm", pid); - fd = open(procBuf, O_RDONLY); - if (fd == -1) + snprintf(procBuf, sizeof(procBuf), "/proc/%d/comm", pid); + fp = fopen(procBuf, "r"); + if (fp == NULL) { unlink(pidPath); return 0; } - - rd = read(fd, exeBuf, sizeof(exeBuf)-1); - close(fd); - - if (rd <= 0) + + if (fscanf(fp, "%ms", &exeBuf) != 1) { + fclose(fp); unlink(pidPath); return 0; } - - exeBuf[rd] = '\0'; + fclose(fp); + if (strcmp(exeBuf, DARLING_INIT_COMM) != 0) { unlink(pidPath); return 0; } - + free(exeBuf); + + // Is it owned by the current user? + if (g_originalUid != 0) + { + snprintf(procBuf, sizeof(procBuf), "/proc/%d/status", pid); + fp = fopen(procBuf, "r"); + if (fp == NULL) + { + unlink(pidPath); + return 0; + } + + while (1) + { + statusBuf = NULL; + size_t len; + if (getline(&statusBuf, &len, fp) == -1) + break; + int rid, eid, sid, fid; + if (sscanf(statusBuf, "Uid: %d %d %d %d", &rid, &eid, &sid, &fid) == 4) + { + uidMatch = 1; + uidMatch &= rid == g_originalUid; + uidMatch &= eid == g_originalUid; + uidMatch &= sid == g_originalUid; + uidMatch &= fid == g_originalUid; + } + if (sscanf(statusBuf, "Gid: %d %d %d %d", &rid, &eid, &sid, &fid) == 4) + { + gidMatch = 1; + gidMatch &= rid == g_originalGid; + gidMatch &= eid == g_originalGid; + gidMatch &= sid == g_originalGid; + gidMatch &= fid == g_originalGid; + } + free(statusBuf); + } + fclose(fp); + + if (!uidMatch || !gidMatch) + { + unlink(pidPath); + return 0; + } + } + return pid; } -void checkPrefixOwner(const char* prefix) +void checkPrefixOwner() { struct stat st; - + if (stat(prefix, &st) == 0) { - if (st.st_uid != getuid()) + if (g_originalUid != 0 && st.st_uid != g_originalUid) { fprintf(stderr, "You do not own the prefix directory.\n"); exit(1); @@ -368,6 +736,7 @@ int isModuleLoaded() if ((fp = fopen("/proc/modules", "r")) == NULL) { fprintf(stderr, "Failure opening /proc/modules: %s\n", strerror(errno)); + fclose(fp); return 0; } @@ -375,9 +744,13 @@ int isModuleLoaded() { read = getline(&line, &len, fp); if (read > 0 && strstr(line, "darling_mach") != NULL) + { + fclose(fp); return 1; + } } + fclose(fp); return 0; } @@ -391,7 +764,7 @@ int loadKernelModule() return 0; uname(&name); - sprintf(path, "/lib/modules/%s/kernel/misc/darling-mach.ko", name.release); + snprintf(path, sizeof(path), "/lib/modules/%s/kernel/misc/darling-mach.ko", name.release); if (access(path, F_OK)) { fprintf(stderr, "Cannot find kernel module at %s: %s\n", path, strerror(errno)); diff --git a/src/dyld/darling.h b/src/dyld/darling.h index 0cc42abfc..74686a4c3 100644 --- a/src/dyld/darling.h +++ b/src/dyld/darling.h @@ -17,6 +17,8 @@ You should have received a copy of the GNU General Public License along with Darling. If not, see . */ +#include + #ifndef _DARLING_H_ #define _DARLING_H_ @@ -27,20 +29,37 @@ void missingSetuidRoot(void); // Returns ~/.darling with ~ expanded char* defaultPrefixPath(void); -void setupWorkdir(const char* prefix); + +void setupWorkdir(void); + +void setupPrefix(void); + +int checkPrefixDir(void); // Creates the given directory, exit()ing if not possible void createDir(const char* path); -// Return the PID of the init process in prefix (in our namespace) +// Spawn the specified proceess inside the namespaces that PID 1 is in +// Returns the PID of the child +// exit()s on error +pid_t spawnChild(int pidInit, const char *path, const char *const argv[]); + +// Set up some environment variables +// As well as the working directory +// Called in the child process +void setupChild(const char *curPath); + +// Returns the PID of the init process in prefix (in our namespace) // Returns 0 if no init is running -int getInitProcess(const char* prefix); +pid_t getInitProcess(void); -int spawnInitProcess(const char* prefix); +pid_t spawnInitProcess(void); -int darlingPreInit(void* arg); +void putInitPid(pid_t pidInit); -void checkPrefixOwner(const char* prefix); +void darlingPreInit(void); + +void checkPrefixOwner(void); int isModuleLoaded(void); diff --git a/src/dyld/dirstructure.cpp b/src/dyld/dirstructure.cpp index ce4636e54..92b338d2f 100644 --- a/src/dyld/dirstructure.cpp +++ b/src/dyld/dirstructure.cpp @@ -29,25 +29,26 @@ along with Darling. If not, see . static std::string GetUserLibrary() { - const char* home; + const char *home, *prefix; std::stringstream ss; std::string path; - + + prefix = getenv("DPREFIX"); home = getenv("HOME"); - if (!home) + if (!prefix || !home) return std::string(); // give up on this user - - ss << home << '/' << "Library" << '/'; + + ss << prefix << home << '/' << "Library" << '/'; return ss.str(); } bool HasUserDirectoryStructure() { std::string path = GetUserLibrary(); - + if (path.empty()) return true; // give up on this user - + if (::access(path.c_str(), F_OK) == -1) return false; else @@ -90,25 +91,24 @@ void SetupUserDirectoryStructure() "Spelling", "Voices", }; - + std::string path = GetUserLibrary(); - + if (path.empty()) return; - + std::cerr << "Darling: Creating Library structure at " << path << std::endl; - + if (::mkdir(path.c_str(), 0777) == -1) std::cerr << "Darling: Cannot mkdir(" << path << "): " << strerror(errno) << std::endl; - + for (const char* dir : dirs) { std::string s = path; - + s += dir; - + if (::mkdir(s.c_str(), 0777) == -1) std::cerr << "Darling: Cannot mkdir(" << s << "): " << strerror(errno) << std::endl; } } -