1 /** 2 * Provides simple getters for XDG base directories and FHS 3 * Apropiate directoies a choosen based on whether the program is run as root or not. 4 * All directories are namespaced with tagion. 5 **/ 6 module tagion.basic.dir; 7 8 @safe: 9 10 import core.sys.posix.unistd; 11 12 import std.process : environment; 13 import std.path; 14 import std.conv; 15 16 import tagion.basic.basic : isinit; 17 18 struct Dir { 19 enum program_name = "tagion"; 20 21 /// The effective user permissions, so running with sudo or doas counts as well 22 const bool isRoot; 23 const uint euid; 24 this(uint _euid) nothrow { 25 euid = _euid; 26 isRoot = (_euid == 0); 27 } 28 29 string _home; 30 31 /// the home directory 32 string home() { 33 if (_home.isinit) { 34 _home = environment.get("HOME"); 35 // '/' is set if user is 'nobody' 36 assert(_home !is string.init && _home != "/", "This system is not for homeless users"); 37 } 38 return _home; 39 } 40 41 private string xdg_dir(const(string) XDG_SPEC, lazy string fallback) 42 out (dir; dir.isValidPath) 43 out (dir; dir.isRooted) { 44 const dir = environment.get(XDG_SPEC, buildPath(home, fallback)); 45 return buildPath(dir, program_name); 46 } 47 48 private string root_dir(lazy string name) 49 out (dir; dir.isValidPath) 50 out (dir; dir.isRooted) { 51 return buildPath("/", name, program_name); 52 } 53 54 private void set_val(ref string var, lazy string root_val, lazy string user_val) { 55 if (var.isinit) { 56 if (isRoot) { 57 var = root_val; 58 } 59 else { 60 var = user_val; 61 } 62 } 63 } 64 65 private string _data; 66 /// Site specific data 67 string data() { 68 set_val(_data, root_dir("srv"), xdg_dir("XDG_DATA_HOME", ".local/share")); 69 return _data; 70 } 71 72 private string _config; 73 /// static program config files 74 string config() { 75 set_val(_config, root_dir("etc"), xdg_dir("XDG_CONFIG_HOME", ".config/")); 76 return _config; 77 } 78 79 private string _cache; 80 /// Cached data, data that is a result expensive computation or I/O. 81 /// The cached files can be deleted without loss of data. 82 string cache() { 83 set_val(_cache, root_dir("/var/cache"), xdg_dir("XDG_CACHE_HOME", ".cache")); 84 return _cache; 85 } 86 87 private string _run; 88 /// This directory contains system information data describing the system since it was booted 89 string run() { 90 set_val(_run, 91 root_dir("run"), 92 environment.get("XDG_RUNTIME_DIR", buildPath("/run", "user", euid.to!string, program_name) 93 )); 94 return _run; 95 } 96 97 private string _log; 98 /// Log files 99 string log() { 100 set_val(_log, root_dir("/var/log"), xdg_dir("XDG_STATE_HOME", ".local/state")); 101 return _log; 102 } 103 } 104 105 static Dir base_dir; 106 static this() { 107 base_dir = Dir(geteuid); 108 } 109 110 unittest { 111 auto my_dirs = Dir(1000); // A normal user 112 enum homeless = "/home/less/"; 113 environment["HOME"] = homeless; 114 environment["XDG_CACHE_HOME"] = homeless ~ ".cache"; 115 assert(my_dirs.cache == "/home/less/.cache/tagion", my_dirs.cache); 116 117 environment.remove("XDG_RUNTIME_DIR"); 118 assert(my_dirs.run == "/run/user/1000/tagion", my_dirs.run); 119 120 environment.remove("XDG_DATA_HOME"); 121 assert(my_dirs.data == buildPath(homeless ~ ".local/share/tagion"), my_dirs.data); 122 123 auto root_dirs = Dir(0); 124 assert(root_dirs.run == "/run/tagion", root_dirs.run); 125 }