7ced0a3f91cfb817e405fa239cb7bc8f709f9beb
[platform/core/dotnet/launcher.git] / NativeLauncher / hydra / hydra_main.cc
1 /*
2  * Copyright (c) 2019 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 <launchpad_hydra.h>
18
19 #include <stdio.h>
20 #include <dlfcn.h>
21 #include <cstring>
22 #include <fstream>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <set>
26
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30
31 #include "log.h"
32 #include "launcher_env.h"
33
34 static const char* __coreclr_lib = "/usr/share/dotnet.tizen/netcoreapp/libcoreclr.so";
35 static const char* __dotnet_loader = "/usr/bin/dotnet-loader";
36
37 typedef int (*coreclr_preload_assembly_ptr)(const char* assemblyPath);
38 typedef int (*launcher_real_main_ptr)(int argc, char *argv[]);
39
40 static std::string getAbsolutePath(const std::string& path)
41 {
42         std::string absPath;
43         char *realPath = realpath(path.c_str(), NULL);
44         if (realPath) {
45                 absPath.assign(realPath);
46                 free(realPath);
47         }
48
49         return absPath;
50 }
51
52 static bool isFile(const std::string& path)
53 {
54         struct stat sb;
55         return lstat(path.c_str(), &sb) == 0;
56 }
57
58 static std::string replaceAll(const std::string& str, const std::string& pattern, const std::string& replace)
59 {
60         std::string result = str;
61         std::string::size_type pos = 0;
62         std::string::size_type offset = 0;
63
64         while ((pos = result.find(pattern, offset)) != std::string::npos) {
65                 result.replace(result.begin() + pos, result.begin() + pos + pattern.size(), replace);
66                 offset = pos + replace.size();
67         }
68
69         return result;
70 }
71
72 static std::string findDllPath(const std::string& filename)
73 {
74         const std::string netcoreappDir = "/usr/share/dotnet.tizen/netcoreapp/";
75         const std::string frameworkDir = "/usr/share/dotnet.tizen/framework/";
76
77         std::string result;
78
79         // check whether the target file exist under netcoreapp directory
80         result = netcoreappDir + filename;
81         if (isFile(result)) {
82                 return result;
83         }
84
85         // check whether the target file exist under framework directory
86         result = frameworkDir + filename;
87         if (isFile(result)) {
88                 return result;
89         }
90
91         // dll file is not found. return empty string
92         result.clear();
93
94         return result;
95 }
96
97 static void preloadAssemblies()
98 {
99 #ifdef USE_DEFAULT_BASE_ADDR
100         putenv(const_cast<char *>("COMPlus_UseDefaultBaseAddr=1"));
101 #endif // USE_DEFAULT_BASE_ADDR
102
103         void* coreclrLib = dlopen(__coreclr_lib, RTLD_NOW | RTLD_GLOBAL);
104         if (coreclrLib == nullptr) {
105                 _DBG("dlopen failed to open libcoreclr.so");
106                 return;
107         }
108
109         coreclr_preload_assembly_ptr preloadAssembly;
110         preloadAssembly = (coreclr_preload_assembly_ptr)dlsym(coreclrLib, "coreclr_preload_assembly");
111         if (preloadAssembly == nullptr) {
112                 _DBG("coreclr_preload_assembly is not found in the libcoreclr.so");
113                 return;
114         }
115
116         const std::string preloadDir = "/usr/share/dotnet.tizen/preload/";
117
118         // get file list from preload directory
119         // file is sorted by std::set
120         std::set<std::string> preloadFiles;
121         DIR* dirp = opendir(preloadDir.c_str());
122         struct dirent * dp;
123         while ((dp = readdir(dirp)) != NULL) {
124                 if (dp->d_type != DT_DIR) {
125                         // Make sure that the file name follows naming conventions.
126                         if (dp->d_name &&
127                                 isdigit(dp->d_name[0]) &&
128                                 isdigit(dp->d_name[1]) &&
129                                 (dp->d_name[2] == '.')) {
130                                 preloadFiles.insert(preloadDir + dp->d_name);
131                         }
132                 }
133         }
134         closedir(dirp);
135
136         // get dll list from each preload file, and preload dll.
137         std::set<std::string> dllList;
138         std::ifstream ifs;
139         std::string in_line;
140         for (const auto& pf: preloadFiles) {
141                 ifs.open(pf);
142                 if (!ifs.is_open()) {
143                         _ERR("failed to open preload file (%s)", pf.c_str());
144                         continue;
145                 }
146
147                 while (std::getline(ifs, in_line)) {
148                         in_line = in_line.substr(0, in_line.find_first_of(" ", 0));
149
150                         // select dll file case
151                         if (in_line[0] == '#' ||
152                                 in_line[0] == ' ' ||
153                                 in_line.empty() ||
154                                 (in_line.find(".dll") == std::string::npos)) {
155                                 continue;
156                         }
157
158                         // only native image should be passed as a parameter of coreclr_preload_assembly.
159                         if (in_line.find(".ni.dll") == std::string::npos &&
160                                 in_line.compare("System.Private.CoreLib.dll")) {
161                                         in_line = replaceAll(in_line, ".dll", ".ni.dll");
162                         }
163
164                         // coreclr_preload_assembly cannot ignore duplicate loading.
165                         // Therefore, only one dll should be preloaded.
166                         // dllList is used to ignore duplicated loading request
167                         if (dllList.insert(in_line).second) {
168                                 // check whether the target file exist under netcoreapp directory
169                                 std::string path = findDllPath(in_line);
170                                 if (!path.empty()) {
171                                         int st = preloadAssembly(getAbsolutePath(path).c_str());
172                                         if (st != 0) {
173                                                 _ERR("preload of %s failed! (0x%08x)", path.c_str(), st);
174                                         } else {
175                                                 _INFO("preload of %s succeeded", path.c_str());
176                                         }
177                                 } else {
178                                         _ERR("preload failed : file (%s) does not eixst", in_line.c_str());
179                                 }
180                         }
181                 }
182
183                 ifs.close();
184         }
185 }
186
187 int main(int argc, char** argv)
188 {
189         hydra_lifecycle_callback_s hydra_callback;
190
191         hydra_callback.precreate = [](void* user_data) {
192                 _INFO("hydra : precreate");
193                 preloadAssemblies();
194         };
195
196         hydra_callback.create = [](void* user_data) {
197                 _INFO("hydra : create");
198         };
199
200         hydra_callback.fork = [](int argc, char **argv, void* user_data) -> int {
201                 _INFO("hydra : fork");
202                 void* launcher_h = dlopen(__dotnet_loader, RTLD_NOW | RTLD_GLOBAL);
203                 if (launcher_h == nullptr) {
204                         _DBG("dlopen failed to open dotnet-launcher");
205                         return -1;
206                 }
207
208                 launcher_real_main_ptr realMain = (launcher_real_main_ptr)dlsym(launcher_h, "realMain");
209                 if (realMain == nullptr) {
210                         _DBG("realMain is not found in the dotnet-launcher");
211                         dlclose(launcher_h);
212                         return -1;
213                 }
214
215                 return realMain(argc, argv);
216         };
217
218         hydra_callback.terminate = [](void* user_data)-> int {
219                 _INFO("hydra : terminate");
220                 return 0;
221         };
222
223         return launchpad_hydra_main(argc, argv, &hydra_callback, nullptr);
224 }