support multiple dll path for plugin
[platform/core/dotnet/launcher.git] / NativeLauncher / util / utils.cc
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <dirent.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <limits.h>
22 #include <strings.h>
23 #include <pthread.h>
24
25 #include <cstdlib>
26 #include <cstring>
27 #include <algorithm>
28 #include <unordered_map>
29 #include <vector>
30 #include <iterator>
31 #include <sstream>
32
33 #include "utils.h"
34 #include "log.h"
35
36 static pthread_t loggingThread;
37
38 bool iCompare(const std::string& a, const std::string& b)
39 {
40         return a.length() == b.length() &&
41                 std::equal(b.begin(), b.end(), a.begin(),
42                         [](unsigned char a, unsigned char b)
43                         { return std::tolower(a) == std::tolower(b); });
44 }
45
46 bool iCompare(const std::string& a, int aOffset, const std::string& b, int bOffset, int length)
47 {
48         return static_cast<int>(a.length()) - length >= aOffset &&
49                 static_cast<int>(b.length()) - length >= bOffset &&
50                 std::equal(b.begin() + bOffset, b.begin() + bOffset + length, a.begin() + aOffset,
51                         [](unsigned char a, unsigned char b)
52                         { return std::tolower(a) == std::tolower(b); });
53 }
54
55 bool isManagedAssembly(const std::string& fileName)
56 {
57         return iCompare(fileName, fileName.size()-4, ".dll", 0, 4) ||
58                 iCompare(fileName, fileName.size()-4, ".exe", 0, 4);
59 }
60
61 bool isNativeImage(const std::string& fileName)
62 {
63         return iCompare(fileName, fileName.size()-7, ".ni", 0, 3);
64 }
65
66 std::string readSelfPath()
67 {
68         char buff[PATH_MAX];
69         ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff)-1);
70         if (len != -1) {
71                 buff[len] = '\0';
72                 return std::string(buff);
73         }
74
75         return "";
76 }
77
78 std::string concatPath(const std::string& path1, const std::string& path2)
79 {
80         std::string path(path1);
81         if (path.back() == PATH_SEPARATOR) {
82                 path.append(path2);
83         } else {
84                 path += PATH_SEPARATOR;
85                 path.append(path2);
86         }
87
88         return path;
89 }
90
91 void splitPath(const std::string& path, std::vector<std::string>& out)
92 {
93         std::istringstream ss(path);
94         std::string token;
95
96         while (std::getline(ss, token, ':')) {
97                 out.push_back(token);
98         }
99 }
100
101 void appendPath(std::string& path1, const std::string& path2)
102 {
103         if (path1.back() == PATH_SEPARATOR) {
104                 path1.append(path2);
105         } else {
106                 path1 += PATH_SEPARATOR;
107                 path1.append(path2);
108         }
109 }
110
111 std::string absolutePath(const std::string& path)
112 {
113         std::string absPath;
114         char realPath[PATH_MAX];
115         if (realpath(path.c_str(), realPath) != nullptr && realPath[0] != '\0')
116                 absPath.assign(realPath);
117
118         return absPath;
119 }
120
121 std::string baseName(const std::string& path)
122 {
123         auto pos = path.find_last_of(PATH_SEPARATOR);
124         if (pos != std::string::npos)
125                 return path.substr(0, pos);
126         else
127                 return std::string(".");
128         return path;
129 }
130
131 bool endWithIgnoreCase(const std::string& str1, const std::string& str2, std::string& fileName)
132 {
133         std::string::size_type len1 = str1.length();
134         std::string::size_type len2 = str2.length();
135         if (len2 > len1)
136                 return false;
137
138         int i = 0;
139         bool result = std::all_of(str1.cend() - len2, str1.end(),
140                                 [&i, &str2] (char x) {
141                                         return std::tolower(x) == std::tolower(str2[i++]);
142                                 });
143         if (result)
144                 fileName = str1.substr(0, len1 - len2);
145
146         return result;
147 }
148
149 bool fileNotExist(const std::string& path)
150 {
151         struct stat sb;
152         return stat(path.c_str(), &sb) != 0;
153 }
154
155 #ifdef NOT_USE_FUNCTION
156 static bool extCheckAndGetFileNameIfExist(const std::string& dir, const std::string& ext, struct dirent* entry, std::string& fileName)
157 {
158         std::string fName(entry->d_name);
159         if (fName.length() < ext.length() ||
160                         fHame.compare(fName.length() - ext.length(), ext.length(), ext) != 0) {
161                 return false;
162         }
163
164         std::string fullName = concatPath(dir, entry->d_name);
165         switch (entry->d_type) {
166                 case DT_REG: break;
167                 case DT_LNK:
168                 case DT_UNKNOWN:
169                         if (fileNotExist(fullName))
170                                 return false;
171                 default:
172                         return false;
173         }
174
175         fileName = fullName;
176
177         return true;
178 }
179 #endif
180
181 std::string stripNiDLL(const std::string& path)
182 {
183         std::string niPath(path);
184         if (path.size() < 5) return niPath;
185         if (!strncasecmp(path.c_str() + path.size() - 4, ".dll", 4))
186                 niPath = path.substr(0, path.size()-4);
187         else if (!strncasecmp(path.c_str() + path.size() - 4, ".exe", 4))
188                 niPath = path.substr(0, path.size()-4);
189
190         if (!strncasecmp(niPath.c_str() + niPath.size() - 3, ".ni", 3))
191                 return niPath.substr(0, niPath.size()-3);
192
193         return niPath;
194 }
195
196 std::string joinStrings(const std::vector<std::string>& strings, const char* const delimeter)
197 {
198         switch (strings.size()) {
199                 case 0:
200                         return "";
201                 case 1:
202                         return strings[0];
203                 default:
204                         std::ostringstream os;
205                         std::copy(strings.begin(), strings.end()-1, std::ostream_iterator<std::string>(os, delimeter));
206                         os << *strings.rbegin();
207                         return os.str();
208         }
209 }
210
211 struct AssemblyFile {
212         std::string noExt;
213         std::string ext;
214 };
215
216 bool operator == (const AssemblyFile& lhs, const AssemblyFile& rhs)
217 {
218         return lhs.noExt == rhs.noExt && lhs.ext == rhs.ext;
219 }
220
221 namespace std {
222         template<>
223         struct hash<AssemblyFile> {
224                 std::size_t operator () (const AssemblyFile& f) const {
225                         const std::size_t h1 = std::hash<std::string>{}(f.noExt);
226                         const std::size_t h2 = std::hash<std::string>{}(f.ext);
227
228                         return h1 ^ (h2 << 1);
229                 }
230         };
231 }
232
233 void assembliesInDirectory(const std::vector<std::string>& directories, std::string& tpaList)
234 {
235         std::map<std::string, std::string> assemblyList;
236         std::map<std::string, std::string> tmpList;
237
238         auto reader = [&assemblyList, &tmpList] (const char* path, const char* name) {
239                 std::string pathStr(path);
240                 if (isManagedAssembly(pathStr)) {
241                         std::string dllName = stripNiDLL(name);
242                         std::pair<std::map<std::string, std::string>::iterator, bool> ret;
243                         ret = tmpList.insert(std::pair<std::string, std::string>(dllName, pathStr));
244                         if (ret.second == false) {
245                                 if (isNativeImage(pathStr))
246                                         tmpList[dllName] = pathStr;
247                         }
248                 }
249         };
250
251         for (auto directory : directories) {
252                 scanFilesInDir(directory.c_str(), reader, 1);
253                 // merge scaned dll list to tpa list.
254                 // if the dll is already exist in the list, that is skipped.
255                 assemblyList.insert(tmpList.begin(), tmpList.end());
256         }
257
258         std::map<std::string, std::string>::iterator it;
259         for (it = assemblyList.begin(); it != assemblyList.end(); it++)
260                 tpaList += it->second + ':';
261
262         if (tpaList.back() == ':')
263                 tpaList.pop_back();
264 }
265
266 void scanFilesInDir(const char* directory, FileReader reader, unsigned int depth)
267 {
268         DIR *dir;
269         struct dirent* entry;
270         bool isDir;
271
272         dir = opendir(directory);
273
274         if (dir == nullptr)
275                 return;
276
277         std::vector<std::string> innerDirectories;
278
279         while ((entry = readdir(dir)) != nullptr) {
280                 isDir = false;
281                 std::string path = concatPath(directory, entry->d_name);
282                 switch (entry->d_type) {
283                         case DT_REG: break;
284                         case DT_DIR:
285                                 isDir = true;
286                                 break;
287                         case DT_LNK:
288                         case DT_UNKNOWN:
289                                 struct stat sb;
290                                 if (stat(path.c_str(), &sb) == -1)
291                                         continue;
292
293                                 if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
294                                         break;
295                         default:
296                                 continue;
297                 }
298                 if (!isDir)
299                         reader(path.c_str(), entry->d_name);
300                 else if (depth > 1 && strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
301                         innerDirectories.push_back(path);
302         }
303
304         if (depth != 0)
305                 for (auto& d : innerDirectories)
306                         scanFilesInDir(d.c_str(), reader, depth-1);
307
308         closedir(dir);
309 }
310
311 static void *stdlog(void*)
312 {
313         int pfd[2];
314     ssize_t readSize;
315     char buf[1024];
316
317     if (setvbuf(stdout, NULL, _IOLBF, 0) < 0) {
318                 _DBG("fail to make stdout line-buffered");
319                 return 0;
320     }
321
322     if (setvbuf(stderr, NULL, _IONBF, 0) < 0) {
323                 _DBG("make stderr unbuffered");
324                 return 0;
325         }
326
327     /* create the pipe and redirect stdout and stderr */
328     if (pipe(pfd) < 0) {
329                 _DBG("fail to create pipe for logging");
330                 return 0;
331     }
332
333     if (dup2(pfd[1], fileno(stdout)) == -1) {
334                 _DBG("fail to duplicate fd to stdout");
335                 return 0;
336     }
337
338     if (dup2(pfd[1], fileno(stderr)) == -1) {
339                 _DBG("fail to duplicate fd to stderr");
340                 return 0;
341         }
342
343         close(pfd[1]);
344
345     while ((readSize = read(pfd[0], buf, sizeof buf - 1)) > 0) {
346         if (buf[readSize - 1] == '\n') {
347             --readSize;
348         }
349
350         buf[readSize] = 0;
351
352         _LOGX("%s", buf);
353     }
354
355         close(pfd[0]);
356
357     return 0;
358 }
359
360 int runLoggingThread() {
361     /* spawn the logging thread */
362     if (pthread_create(&loggingThread, 0, stdlog, 0) != 0) {
363                 _DBG("fail to create pthread");
364         return -1;
365     }
366
367     if (pthread_detach(loggingThread) != 0) {
368                 _DBG("fail to detach pthread");
369                 return -1;
370         }
371
372     return 0;
373 }
374