1 /**
2 @file SystemResources.h
3 @author Lime Microsystems
4 @brief APIs for locating system resources.
5 */
6 
7 #include "SystemResources.h"
8 #include "Logger.h"
9 
10 #include <cstdlib> //getenv, system
11 #include <vector>
12 #include <sstream>
13 #include <iostream>
14 
15 #ifdef _MSC_VER
16 #include <windows.h>
17 #include <shlobj.h>
18 #include <io.h>
19 
20 //access mode constants
21 #define F_OK 0
22 #define R_OK 2
23 #define W_OK 4
24 #endif
25 
26 #ifdef __unix__
27 #include <pwd.h>
28 #include <unistd.h>
29 #endif
30 
31 #include <sys/types.h>
32 #include <sys/stat.h> //stat
33 
getLimeSuiteRoot(void)34 std::string lime::getLimeSuiteRoot(void)
35 {
36     //first check the environment variable
37     const char *limeSuiteRoot = std::getenv("LIME_SUITE_ROOT");
38     if (limeSuiteRoot != nullptr) return limeSuiteRoot;
39 
40     // Get the path to the current dynamic linked library.
41     // The path to this library can be used to determine
42     // the installation root without prior knowledge.
43     #if defined(_MSC_VER) && defined(LIME_DLL)
44     char path[MAX_PATH];
45     HMODULE hm = NULL;
46     if (GetModuleHandleExA(
47         GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
48         GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
49         (LPCSTR) &lime::getLimeSuiteRoot, &hm))
50     {
51         const DWORD size = GetModuleFileNameA(hm, path, sizeof(path));
52         if (size != 0)
53         {
54             const std::string libPath(path, size);
55             const size_t slash0Pos = libPath.find_last_of("/\\");
56             const size_t slash1Pos = libPath.substr(0, slash0Pos).find_last_of("/\\");
57             if (slash0Pos != std::string::npos && slash1Pos != std::string::npos)
58                 return libPath.substr(0, slash1Pos);
59         }
60     }
61     #endif //_MSC_VER && LIME_DLL
62 
63     return "@LIME_SUITE_ROOT@";
64 }
65 
getHomeDirectory(void)66 std::string lime::getHomeDirectory(void)
67 {
68     //first check the HOME environment variable
69     const char *userHome = std::getenv("HOME");
70     if (userHome != nullptr) return userHome;
71 
72     //use unix user id lookup to get the home directory
73     #ifdef __unix__
74     const char *pwDir = getpwuid(getuid())->pw_dir;
75     if (pwDir != nullptr) return pwDir;
76     #endif
77 
78     return "";
79 }
80 
81 /*!
82  * The generic location for data storage with user permission level.
83  */
getBareAppDataDirectory(void)84 static std::string getBareAppDataDirectory(void)
85 {
86     //always check APPDATA (usually windows, but can be set for linux)
87     const char *appDataDir = std::getenv("APPDATA");
88     if (appDataDir != nullptr) return appDataDir;
89 
90     //use windows API to query for roaming app data directory
91     #ifdef _MSC_VER
92     char csidlAppDataDir[MAX_PATH];
93     if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, csidlAppDataDir)))
94     {
95         return csidlAppDataDir;
96     }
97     #endif
98 
99     //xdg freedesktop standard location environment variable
100     #ifdef __unix__
101     const char *xdgDataHome = std::getenv("XDG_DATA_HOME");
102     if (xdgDataHome != nullptr) return xdgDataHome;
103     #endif
104 
105     //xdg freedesktop standard location for data in home directory
106     return lime::getHomeDirectory() + "/.local/share";
107 }
108 
getAppDataDirectory(void)109 std::string lime::getAppDataDirectory(void)
110 {
111     return getBareAppDataDirectory() + "/LimeSuite";
112 }
113 
getConfigDirectory(void)114 std::string lime::getConfigDirectory(void)
115 {
116     //xdg standard is XDG_CONFIG_HOME or $HOME/.config
117     //but historically we have used $HOME/.limesuite
118     return lime::getHomeDirectory() + "/.limesuite";
119 }
120 
listImageSearchPaths(void)121 std::vector<std::string> lime::listImageSearchPaths(void)
122 {
123     std::vector<std::string> imageSearchPaths;
124 
125     //separator for search paths in the environment variable
126     #ifdef _MSC_VER
127     static const char sep = ';';
128     #else
129     static const char sep = ':';
130     #endif
131 
132     //check the environment's search path
133     const char *imagePathEnv = std::getenv("LIME_IMAGE_PATH");
134     if (imagePathEnv != nullptr)
135     {
136         std::stringstream imagePaths(imagePathEnv);
137         std::string imagePath;
138         while (std::getline(imagePaths, imagePath, sep))
139         {
140             if (imagePath.empty()) continue;
141             imageSearchPaths.push_back(imagePath);
142         }
143     }
144 
145     //search directories in the user's home directory
146     imageSearchPaths.push_back(lime::getAppDataDirectory() + "/images");
147 
148     //search global installation directories
149     imageSearchPaths.push_back(lime::getLimeSuiteRoot() + "/share/LimeSuite/images");
150 
151     return imageSearchPaths;
152 }
153 
locateImageResource(const std::string & name)154 std::string lime::locateImageResource(const std::string &name)
155 {
156     for (const auto &searchPath : lime::listImageSearchPaths())
157     {
158         const std::string fullPath(searchPath + "/@VERSION_MAJOR@.@VERSION_MINOR@/" + name);
159         if (access(fullPath.c_str(), R_OK) == 0) return fullPath;
160     }
161     return "";
162 }
163 
downloadImageResource(const std::string & name)164 int lime::downloadImageResource(const std::string &name)
165 {
166     const std::string destDir(lime::getAppDataDirectory() + "/images/@VERSION_MAJOR@.@VERSION_MINOR@");
167     const std::string destFile(destDir + "/" + name);
168     const std::string sourceUrl("https://downloads.myriadrf.org/project/limesuite/@VERSION_MAJOR@.@VERSION_MINOR@/" + name);
169 
170     //check if the directory already exists
171     struct stat s;
172     if (stat(destDir.c_str(), &s) == 0)
173     {
174         if ((s.st_mode & S_IFDIR) == 0)
175         {
176             return lime::ReportError("Not a directory: %s", destDir.c_str());
177         }
178     }
179 
180     //create images directory
181     else
182     {
183         #ifdef __unix__
184         const std::string mkdirCmd("mkdir -p \""+destDir+"\"");
185         #else
186         const std::string mkdirCmd("md.exe \""+destDir+"\"");
187         #endif
188         std::system(mkdirCmd.c_str());
189     }
190 
191     //check for write access
192     if (access(destDir.c_str(), W_OK) != 0) lime::ReportError("Cannot write: %s", destDir.c_str());
193 
194     //download the file
195     #ifdef __unix__
196     const std::string dnloadCmd("wget --output-document=\""+destFile+"\" \""+sourceUrl+"\"");
197     #else
198     const std::string dnloadCmd("powershell.exe -Command \"(new-object System.Net.WebClient).DownloadFile('"+sourceUrl+"', '"+destFile+"')\"");
199     #endif
200     int result = std::system(dnloadCmd.c_str());
201     if (result != 0) return lime::ReportError(result, "Failed: %s", dnloadCmd.c_str());
202 
203     return 0;
204 }
205