Refactoring dotnet-launcher
[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 "path_manager.h"
35 #include "log.h"
36
37 static pthread_t loggingThread;
38
39 static bool iCompare(const std::string& a, int aOffset, const std::string& b, int bOffset, int length)
40 {
41         return static_cast<int>(a.length()) - length >= aOffset &&
42                 static_cast<int>(b.length()) - length >= bOffset &&
43                 std::equal(b.begin() + bOffset, b.begin() + bOffset + length, a.begin() + aOffset,
44                         [](unsigned char a, unsigned char b)
45                         { return std::tolower(a) == std::tolower(b); });
46 }
47
48 bool isManagedAssembly(const std::string& fileName)
49 {
50         return (iCompare(fileName, fileName.size()-4, ".dll", 0, 4) ||
51                         iCompare(fileName, fileName.size()-4, ".exe", 0, 4)) &&
52                         !isNativeImage(fileName);
53 }
54
55 bool isNativeImage(const std::string& fileName)
56 {
57         return iCompare(fileName, fileName.size()-7, ".ni", 0, 3);
58 }
59
60 std::string readSelfPath()
61 {
62         char buff[PATH_MAX];
63         ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff)-1);
64         if (len != -1) {
65                 buff[len] = '\0';
66                 return std::string(buff);
67         }
68
69         return "";
70 }
71
72 std::string concatPath(const std::string& path1, const std::string& path2)
73 {
74         std::string path(path1);
75         if (path.back() == PATH_SEPARATOR) {
76                 path.append(path2);
77         } else {
78                 path += PATH_SEPARATOR;
79                 path.append(path2);
80         }
81
82         return path;
83 }
84
85 void splitPath(const std::string& path, std::vector<std::string>& out)
86 {
87         std::istringstream ss(path);
88         std::string token;
89
90         while (std::getline(ss, token, ':')) {
91                 out.push_back(token);
92         }
93 }
94
95 std::string absolutePath(const std::string& path)
96 {
97         std::string absPath;
98         char realPath[PATH_MAX];
99         if (realpath(path.c_str(), realPath) != nullptr && realPath[0] != '\0')
100                 absPath.assign(realPath);
101
102         return absPath;
103 }
104
105 std::string baseName(const std::string& path)
106 {
107         auto pos = path.find_last_of(PATH_SEPARATOR);
108         if (pos != std::string::npos)
109                 return path.substr(0, pos);
110         else
111                 return std::string(".");
112         return path;
113 }
114
115 bool isFileExist(const std::string& path)
116 {
117         struct stat sb;
118         return stat(path.c_str(), &sb) == 0;
119 }
120
121 std::string stripNiDLL(const std::string& path)
122 {
123         std::string niPath(path);
124         if (path.size() < 5) return niPath;
125         if (!strncasecmp(path.c_str() + path.size() - 4, ".dll", 4))
126                 niPath = path.substr(0, path.size()-4);
127         else if (!strncasecmp(path.c_str() + path.size() - 4, ".exe", 4))
128                 niPath = path.substr(0, path.size()-4);
129
130         if (!strncasecmp(niPath.c_str() + niPath.size() - 3, ".ni", 3))
131                 return niPath.substr(0, niPath.size()-3);
132
133         return niPath;
134 }
135
136 void assembliesInDirectory(const std::vector<std::string>& directories, std::string& tpaList)
137 {
138         std::map<std::string, std::string> assemblyList;
139         std::map<std::string, std::string> tmpList;
140
141         auto reader = [&assemblyList, &tmpList] (const std::string& path, const char* name) {
142                 if (isManagedAssembly(path) || isNativeImage(path)) {
143                         std::string dllName = stripNiDLL(name);
144                         std::pair<std::map<std::string, std::string>::iterator, bool> ret;
145                         ret = tmpList.insert(std::pair<std::string, std::string>(dllName, path));
146                         if (ret.second == false) {
147                                 if (isNativeImage(path))
148                                         tmpList[dllName] = path;
149                         }
150                 }
151         };
152
153         for (auto directory : directories) {
154                 scanFilesInDir(directory.c_str(), reader, 1);
155                 // merge scaned dll list to tpa list.
156                 // if the dll is already exist in the list, that is skipped.
157                 assemblyList.insert(tmpList.begin(), tmpList.end());
158         }
159
160         std::map<std::string, std::string>::iterator it;
161         for (it = assemblyList.begin(); it != assemblyList.end(); it++)
162                 tpaList += it->second + ':';
163
164         if (tpaList.back() == ':')
165                 tpaList.pop_back();
166 }
167
168 void scanFilesInDir(const std::string& directory, FileReader reader, unsigned int depth)
169 {
170         DIR *dir;
171         struct dirent* entry;
172         bool isDir;
173
174         dir = opendir(directory.c_str());
175
176         if (dir == nullptr)
177                 return;
178
179         std::vector<std::string> innerDirectories;
180
181         while ((entry = readdir(dir)) != nullptr) {
182                 isDir = false;
183                 std::string path = concatPath(directory, entry->d_name);
184                 switch (entry->d_type) {
185                         case DT_REG: break;
186                         case DT_DIR:
187                                 isDir = true;
188                                 break;
189                         case DT_LNK:
190                         case DT_UNKNOWN:
191                                 struct stat sb;
192                                 if (stat(path.c_str(), &sb) == -1)
193                                         continue;
194
195                                 if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
196                                         break;
197                         default:
198                                 continue;
199                 }
200                 if (!isDir)
201                         reader(path, entry->d_name);
202                 else if (depth > 1 && strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
203                         innerDirectories.push_back(path);
204         }
205
206         if (depth != 0)
207                 for (auto& d : innerDirectories)
208                         scanFilesInDir(d.c_str(), reader, depth-1);
209
210         closedir(dir);
211 }
212
213 static int __pfd[2];
214
215 static void *stdlog(void*)
216 {
217     ssize_t readSize;
218     char buf[1024];
219
220     while ((readSize = read(__pfd[0], buf, sizeof buf - 1)) > 0) {
221         if (buf[readSize - 1] == '\n') {
222             --readSize;
223         }
224
225         buf[readSize] = 0;
226
227         _LOGX("%s", buf);
228     }
229
230         close(__pfd[0]);
231
232     return 0;
233 }
234
235 int runLoggingThread()
236 {
237     if (setvbuf(stdout, NULL, _IOLBF, 0) < 0) {
238                 _DBG("fail to make stdout line-buffered");
239                 return 0;
240     }
241
242     if (setvbuf(stderr, NULL, _IONBF, 0) < 0) {
243                 _DBG("make stderr unbuffered");
244                 return 0;
245         }
246
247     /* create the pipe and redirect stdout and stderr */
248     if (pipe(__pfd) < 0) {
249                 _DBG("fail to create pipe for logging");
250                 return 0;
251     }
252
253     // stdout
254     if (dup2(__pfd[1], 1) == -1) {
255                 _DBG("fail to duplicate fd to stdout");
256                 return 0;
257     }
258
259     // stderr
260     if (dup2(__pfd[1], 2) == -1) {
261                 _DBG("fail to duplicate fd to stderr");
262                 return 0;
263         }
264
265         close(__pfd[1]);
266
267     /* spawn the logging thread */
268     if (pthread_create(&loggingThread, 0, stdlog, 0) != 0) {
269                 _DBG("fail to create pthread");
270         return -1;
271     }
272
273     if (pthread_detach(loggingThread) != 0) {
274                 _DBG("fail to detach pthread");
275                 return -1;
276         }
277
278     return 0;
279 }
280