Merge "Add option to enable clang Asan build." into tizen
[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 int pfd[2];
37 static pthread_t loggingThread;
38
39 bool iCompare(const std::string& a, const std::string& b)
40 {
41         return a.length() == b.length() &&
42                 std::equal(b.begin(), b.end(), a.begin(),
43                         [](unsigned char a, unsigned char b)
44                         { return std::tolower(a) == std::tolower(b); });
45 }
46
47 bool iCompare(const std::string& a, int aOffset, const std::string& b, int bOffset, int length)
48 {
49         return static_cast<int>(a.length()) - length >= aOffset &&
50                 static_cast<int>(b.length()) - length >= bOffset &&
51                 std::equal(b.begin() + bOffset, b.begin() + bOffset + length, a.begin() + aOffset,
52                         [](unsigned char a, unsigned char b)
53                         { return std::tolower(a) == std::tolower(b); });
54 }
55
56 bool isManagedAssembly(const std::string& fileName)
57 {
58         return iCompare(fileName, fileName.size()-4, ".dll", 0, 4) ||
59                 iCompare(fileName, fileName.size()-4, ".exe", 0, 4);
60 }
61
62 bool isNativeImage(const std::string& fileName)
63 {
64         return iCompare(fileName, fileName.size()-7, ".ni", 0, 3);
65 }
66
67 std::string readSelfPath()
68 {
69         char buff[PATH_MAX];
70         ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff)-1);
71         if (len != -1) {
72                 buff[len] = '\0';
73                 return std::string(buff);
74         }
75
76         return "";
77 }
78
79 std::string concatPath(const std::string& path1, const std::string& path2)
80 {
81         std::string path(path1);
82         if (path.back() == PATH_SEPARATOR) {
83                 path.append(path2);
84         } else {
85                 path += PATH_SEPARATOR;
86                 path.append(path2);
87         }
88
89         return path;
90 }
91
92 void appendPath(std::string& path1, const std::string& path2)
93 {
94         if (path1.back() == PATH_SEPARATOR) {
95                 path1.append(path2);
96         } else {
97                 path1 += PATH_SEPARATOR;
98                 path1.append(path2);
99         }
100 }
101
102 std::string absolutePath(const std::string& path)
103 {
104         std::string absPath;
105         char realPath[PATH_MAX];
106         if (realpath(path.c_str(), realPath) != nullptr && realPath[0] != '\0')
107                 absPath.assign(realPath);
108
109         return absPath;
110 }
111
112 std::string baseName(const std::string& path)
113 {
114         auto pos = path.find_last_of(PATH_SEPARATOR);
115         if (pos != std::string::npos)
116                 return path.substr(0, pos);
117         else
118                 return std::string(".");
119         return path;
120 }
121
122 bool endWithIgnoreCase(const std::string& str1, const std::string& str2, std::string& fileName)
123 {
124         std::string::size_type len1 = str1.length();
125         std::string::size_type len2 = str2.length();
126         if (len2 > len1)
127                 return false;
128
129         int i = 0;
130         bool result = std::all_of(str1.cend() - len2, str1.end(),
131                                 [&i, &str2] (char x) {
132                                         return std::tolower(x) == std::tolower(str2[i++]);
133                                 });
134         if (result)
135                 fileName = str1.substr(0, len1 - len2);
136
137         return result;
138 }
139
140 bool fileNotExist(const std::string& path)
141 {
142         struct stat sb;
143         return stat(path.c_str(), &sb) != 0;
144 }
145
146 #ifdef NOT_USE_FUNCTION
147 static bool extCheckAndGetFileNameIfExist(const std::string& dir, const std::string& ext, struct dirent* entry, std::string& fileName)
148 {
149         std::string fName(entry->d_name);
150         if (fName.length() < ext.length() ||
151                         fHame.compare(fName.length() - ext.length(), ext.length(), ext) != 0) {
152                 return false;
153         }
154
155         std::string fullName = concatPath(dir, entry->d_name);
156         switch (entry->d_type) {
157                 case DT_REG: break;
158                 case DT_LNK:
159                 case DT_UNKNOWN:
160                         if (fileNotExist(fullName))
161                                 return false;
162                 default:
163                         return false;
164         }
165
166         fileName = fullName;
167
168         return true;
169 }
170 #endif
171
172 std::string stripNiDLL(const std::string& path)
173 {
174         std::string niPath(path);
175         if (path.size() < 5) return niPath;
176         if (!strncasecmp(path.c_str() + path.size() - 4, ".dll", 4))
177                 niPath = path.substr(0, path.size()-4);
178         else if (!strncasecmp(path.c_str() + path.size() - 4, ".exe", 4))
179                 niPath = path.substr(0, path.size()-4);
180
181         if (!strncasecmp(niPath.c_str() + niPath.size() - 3, ".ni", 3))
182                 return niPath.substr(0, niPath.size()-3);
183
184         return niPath;
185 }
186
187 std::string joinStrings(const std::vector<std::string>& strings, const char* const delimeter)
188 {
189         switch (strings.size()) {
190                 case 0:
191                         return "";
192                 case 1:
193                         return strings[0];
194                 default:
195                         std::ostringstream os;
196                         std::copy(strings.begin(), strings.end()-1, std::ostream_iterator<std::string>(os, delimeter));
197                         os << *strings.rbegin();
198                         return os.str();
199         }
200 }
201
202 struct AssemblyFile {
203         std::string noExt;
204         std::string ext;
205 };
206
207 bool operator == (const AssemblyFile& lhs, const AssemblyFile& rhs)
208 {
209         return lhs.noExt == rhs.noExt && lhs.ext == rhs.ext;
210 }
211
212 namespace std {
213         template<>
214         struct hash<AssemblyFile> {
215                 std::size_t operator () (const AssemblyFile& f) const {
216                         const std::size_t h1 = std::hash<std::string>{}(f.noExt);
217                         const std::size_t h2 = std::hash<std::string>{}(f.ext);
218
219                         return h1 ^ (h2 << 1);
220                 }
221         };
222 }
223
224 void assembliesInDirectory(const std::vector<std::string>& directories, std::string& tpaList)
225 {
226         std::map<std::string, std::string> assemblyList;
227         std::map<std::string, std::string> tmpList;
228
229         auto reader = [&assemblyList, &tmpList] (const char* path, const char* name) {
230                 std::string pathStr(path);
231                 if (isManagedAssembly(pathStr)) {
232                         std::string dllName = stripNiDLL(name);
233                         std::pair<std::map<std::string, std::string>::iterator, bool> ret;
234                         ret = tmpList.insert(std::pair<std::string, std::string>(dllName, pathStr));
235                         if (ret.second == false) {
236                                 if (isNativeImage(pathStr))
237                                         tmpList[dllName] = pathStr;
238                         }
239                 }
240         };
241
242         for (auto directory : directories) {
243                 scanFilesInDir(directory.c_str(), reader, 1);
244                 // merge scaned dll list to tpa list.
245                 // if the dll is already exist in the list, that is skipped.
246                 assemblyList.insert(tmpList.begin(), tmpList.end());
247         }
248
249         std::map<std::string, std::string>::iterator it;
250         for (it = assemblyList.begin(); it != assemblyList.end(); it++)
251                 tpaList += it->second + ':';
252
253         if (tpaList.back() == ':')
254                 tpaList.pop_back();
255 }
256
257 void scanFilesInDir(const char* directory, FileReader reader, unsigned int depth)
258 {
259         DIR *dir;
260         struct dirent* entry;
261         bool isDir;
262
263         dir = opendir(directory);
264
265         if (dir == nullptr)
266                 return;
267
268         std::vector<std::string> innerDirectories;
269
270         while ((entry = readdir(dir)) != nullptr) {
271                 isDir = false;
272                 std::string path = concatPath(directory, entry->d_name);
273                 switch (entry->d_type) {
274                         case DT_REG: break;
275                         case DT_DIR:
276                                 isDir = true;
277                                 break;
278                         case DT_LNK:
279                         case DT_UNKNOWN:
280                                 struct stat sb;
281                                 if (stat(path.c_str(), &sb) == -1)
282                                         continue;
283
284                                 if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
285                                         break;
286                         default:
287                                 continue;
288                 }
289                 if (!isDir)
290                         reader(path.c_str(), entry->d_name);
291                 else if (depth > 1 && strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
292                         innerDirectories.push_back(path);
293         }
294
295         if (depth != 0)
296                 for (auto& d : innerDirectories)
297                         scanFilesInDir(d.c_str(), reader, depth-1);
298
299         closedir(dir);
300 }
301
302 static void *stdlog(void*)
303 {
304     ssize_t readSize;
305     char buf[1024];
306
307     while ((readSize = read(pfd[0], buf, sizeof buf - 1)) > 0) {
308         if (buf[readSize - 1] == '\n') {
309             --readSize;
310         }
311
312         buf[readSize] = 0;
313
314         _ERRX("%s", buf);
315     }
316
317     return 0;
318 }
319
320 int runLoggingThread() { // run this function to redirect your output to android log
321     if (setvbuf(stdout, NULL, _IOLBF, 0) < 0) {
322                 _DBG("fail to make stdout line-buffered");
323                 return -1;
324     }
325
326     if (setvbuf(stderr, NULL, _IONBF, 0) < 0) {
327                 _DBG("make stderr unbuffered");
328                 return -1;
329         }
330
331     /* create the pipe and redirect stdout and stderr */
332     if (pipe(pfd) < 0) {
333                 _DBG("fail to create pipe for logging");
334                 return -1;
335     }
336
337     if (dup2(pfd[1], fileno(stdout)) == -1) {
338                 _DBG("fail to duplicate fd to stdout");
339                 return -1;
340     }
341
342     if (dup2(pfd[1], fileno(stderr)) == -1) {
343                 _DBG("fail to duplicate fd to stderr");
344                 return -1;
345         }
346
347     /* spawn the logging thread */
348     if (pthread_create(&loggingThread, 0, stdlog, 0) != 0) {
349                 _DBG("fail to create pthread");
350         return -1;
351     }
352
353     if (pthread_detach(loggingThread) != 0) {
354                 _DBG("fail to detach pthread");
355                 return -1;
356         }
357
358     return 0;
359 }
360