Merge branch 'tizen' into tac_env
[platform/core/dotnet/launcher.git] / NativeLauncher / tool / ni_common.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 <pkgmgr-info.h>
18 #include <pkgmgr_installer_info.h>
19 #include <aul.h>
20
21 #include "log.h"
22 #include "utils.h"
23 #include "pkgmgr_parser_plugin_interface.h"
24
25 #include <wait.h>
26 #include <dirent.h>
27 #include <sys/stat.h>
28
29 #include <algorithm>
30 #include <string>
31 #include <fstream>
32
33 #include <pwd.h>
34 #include <grp.h>
35 #include <unistd.h>
36 #include <string.h>
37
38 #include "ni_common.h"
39 #include "path_manager.h"
40 #include "plugin_manager.h"
41
42 #ifdef  LOG_TAG
43 #undef  LOG_TAG
44 #endif
45 #define LOG_TAG "DOTNET_INSTALLER_PLUGIN"
46
47 #ifndef CROSSGEN_PATH
48 #error "CROSSGEN_PATH is missed"
49 #endif
50
51 #define __XSTR(x) #x
52 #define __STR(x) __XSTR(x)
53 static const char* __CROSSGEN_PATH = __STR(CROSSGEN_PATH);
54 #undef __STR
55 #undef __XSTR
56
57 static int __interval = 0;
58 static std::string __tpa;
59
60 static void waitInterval()
61 {
62         // by the recommand, ignore small value for performance.
63         if (__interval > 10000) {
64                 fprintf(stderr, "sleep %d usec\n", __interval);
65                 usleep(__interval);
66         }
67 }
68
69 static std::string getNiFilePath(const std::string& dllPath)
70 {
71         size_t index = dllPath.find_last_of(".");
72         if (index == std::string::npos) {
73                 fprintf(stderr, "File doesnot contain extension. fail to get NI file name\n");
74                 return "";
75         }
76         std::string fName = dllPath.substr(0, index);
77         std::string fExt = dllPath.substr(index, dllPath.length());
78
79         // crossgen generate file with lower case extension only
80         std::transform(fExt.begin(), fExt.end(), fExt.begin(), ::tolower);
81         std::string niPath = fName + ".ni" + fExt;
82
83         return niPath;
84 }
85
86 static std::string getAppNIPath(const std::string& niPath)
87 {
88         std::string fileName;
89         std::string niDirPath;
90         std::string prevPath;
91
92         size_t index = niPath.find_last_of("/");
93         if (index != std::string::npos) {
94                 prevPath = niPath.substr(0, index);
95                 fileName = niPath.substr(index + 1, niPath.length());
96         } else {
97                 prevPath = ".";
98                 fileName = niPath;
99         }
100
101         niDirPath = concatPath(prevPath, APP_NI_SUB_DIR);
102
103         if (!isFileExist(niDirPath)) {
104                 if (mkdir(niDirPath.c_str(), 0755) == 0) {
105                         updateAssemblyInfo(prevPath, niDirPath);
106                 } else {
107                         fprintf(stderr, "Fail to create app ni directory (%s)\n", niDirPath.c_str());
108                 }
109         }
110
111         return concatPath(niDirPath, fileName);
112 }
113
114 static bool niExist(const std::string& path)
115 {
116         std::string f = getNiFilePath(path);
117         if (f.empty()) {
118                 return false;
119         }
120
121         if (isFileExist(f)) {
122                 return true;
123         }
124
125         // native image of System.Private.CoreLib.dll should have to overwrite
126         // original file to support new coreclr
127         if (path.find("System.Private.CoreLib.dll") != std::string::npos) {
128                 std::string coreLibBackup = path + ".Backup";
129                 if (isFileExist(coreLibBackup)) {
130                         return true;
131                 }
132         }
133
134         return false;
135 }
136
137 static ni_error_e crossgen(const std::string& dllPath, const std::string& appPath, bool enableR2R, bool isAppNI = false)
138 {
139         if (!isFileExist(dllPath)) {
140                 fprintf(stderr, "dll file is not exist : %s\n", dllPath.c_str());
141                 return NI_ERROR_NO_SUCH_FILE;
142         }
143
144         if (!isManagedAssembly(dllPath)) {
145                 fprintf(stderr, "Input file is not a dll file : %s\n", dllPath.c_str());
146                 return NI_ERROR_INVALID_PARAMETER;
147         }
148
149         if (niExist(dllPath)) {
150                 fprintf(stderr, "Already ni file is exist for %s\n", dllPath.c_str());
151                 return NI_ERROR_ALREADY_EXIST;
152         }
153
154         std::string absDllPath = absolutePath(dllPath);
155         std::string absNiPath = getNiFilePath(dllPath);
156         if (absNiPath.empty()) {
157                 fprintf(stderr, "Fail to get ni file name\n");
158                 return NI_ERROR_UNKNOWN;
159         }
160
161         if (isAppNI) {
162                 absNiPath = getAppNIPath(absNiPath);
163         }
164
165         pid_t pid = fork();
166         if (pid == -1)
167                 return NI_ERROR_UNKNOWN;
168
169         if (pid > 0) {
170                 int status;
171                 waitpid(pid, &status, 0);
172                 if (WIFEXITED(status)) {
173                         // Do not use niExist() function to check whether ni file created or not.
174                         // niEixst() return false for System.Private.Corelib.dll
175                         if (isFileExist(absNiPath)) {
176                                 updateAssemblyInfo(absDllPath, absNiPath);
177                                 return NI_ERROR_NONE;
178                         } else {
179                                 fprintf(stderr, "Fail to create native image for %s\n", dllPath.c_str());
180                                 return NI_ERROR_NO_SUCH_FILE;
181                         }
182                 }
183         } else {
184                 std::string jitPath = getRuntimeDir() + "/libclrjit.so";
185                 std::vector<const char*> argv = {
186                         __CROSSGEN_PATH,
187                         "/nologo",
188                         "/Trusted_Platform_Assemblies", __tpa.c_str(),
189                         "/JITPath", jitPath.c_str()
190                 };
191
192                 if (!enableR2R) {
193                         argv.push_back("/FragileNonVersionable");
194                 }
195
196                 argv.push_back("/App_Paths");
197                 std::string absAppPath;
198                 if (!appPath.empty()) {
199                         absAppPath = appPath;
200                 } else {
201                         absAppPath = baseName(absDllPath);
202                 }
203                 argv.push_back(absAppPath.c_str());
204
205                 argv.push_back("/out");
206                 argv.push_back(absNiPath.c_str());
207
208                 argv.push_back(absDllPath.c_str());
209                 argv.push_back(nullptr);
210
211                 fprintf(stderr, "+ %s (%s)\n", absDllPath.c_str(), enableR2R ? "R2R" : "FNV");
212
213                 execv(__CROSSGEN_PATH, const_cast<char* const*>(argv.data()));
214                 exit(0);
215         }
216
217         return NI_ERROR_NONE;
218 }
219
220 // callback function of "pkgmgrinfo_appinfo_metadata_filter_foreach"
221 static int appAotCb(pkgmgrinfo_appinfo_h handle, void *userData)
222 {
223         char *pkgId = NULL;
224         int ret = 0;
225         bool* enableR2R = (bool*)userData;
226
227         ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId);
228         if (ret != PMINFO_R_OK) {
229                 fprintf(stderr, "Failed to get pkgid\n");
230                 return -1;
231         }
232
233         if (removeNiUnderPkgRoot(pkgId) != 0) {
234                 fprintf(stderr, "Failed to remove previous dlls from [%s]\n", pkgId);
235                 return -1;
236         }
237
238         // Regenerate ni files with R2R mode forcibiliy. (there is no way to now which option is used)
239         if (createNiUnderPkgRoot(pkgId, *enableR2R) != 0) {
240                 fprintf(stderr, "Failed to get root path from [%s]\n", pkgId);
241                 return -1;
242         } else {
243                 fprintf(stderr, "Complete make application to native image\n");
244         }
245
246         return 0;
247 }
248
249 static void createCoreLibNI(bool enableR2R)
250 {
251         std::string coreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll");
252         std::string niCoreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.ni.dll");
253         std::string coreLibBackup = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll.Backup");
254
255         if (!isFileExist(coreLibBackup)) {
256                 if (!crossgen(coreLib, std::string(), enableR2R)) {
257                         if (rename(coreLib.c_str(), coreLibBackup.c_str())) {
258                                 fprintf(stderr, "Failed to rename System.Private.CoreLib.dll\n");
259                         }
260                         if (rename(niCoreLib.c_str(), coreLib.c_str())) {
261                                 fprintf(stderr, "Failed to rename System.Private.CoreLib.ni.dll\n");
262                         }
263                 } else {
264                         fprintf(stderr, "Failed to create native image for %s\n", coreLib.c_str());
265                 }
266         }
267 }
268
269 ni_error_e initNICommon(NiCommonOption* option)
270 {
271 #if defined(__arm__)
272         // get interval value
273         const char* intervalFile = "/usr/share/dotnet.tizen/lib/crossgen_interval.txt";
274         std::ifstream inFile(intervalFile);
275         if (inFile) {
276                 fprintf(stderr, "crossgen_interval.txt is found\n");
277                 inFile >> __interval;
278         }
279
280         if (initializePluginManager("normal")) {
281                 fprintf(stderr, "Fail to initialize plugin manager\n");
282                 return NI_ERROR_UNKNOWN;
283         }
284         if (initializePathManager(option->runtimeDir, option->tizenFXDir, option->extraDirs)) {
285                 fprintf(stderr, "Fail to initialize path manager\n");
286                 return NI_ERROR_UNKNOWN;
287         }
288
289         __tpa = getTPA();
290
291         return NI_ERROR_NONE;
292 #else
293         fprintf(stderr, "crossgen supports arm architecture only. skip ni file generation\n");
294         return NI_ERROR_NOT_SUPPORTED;
295 #endif
296 }
297
298 void finalizeNICommon()
299 {
300         __interval = 0;
301
302         finalizePluginManager();
303         finalizePathManager();
304
305         __tpa.clear();
306 }
307
308
309 void createNiPlatform(bool enableR2R)
310 {
311         const std::string platformDirs[] = {getRuntimeDir(), getTizenFXDir()};
312         createNiUnderDirs(platformDirs, 2, enableR2R);
313 }
314
315 ni_error_e createNiDll(const std::string& dllPath, bool enableR2R)
316 {
317         createCoreLibNI(enableR2R);
318         // System.Private.CoreLib.dll is generated in the createCoreLibNI function.
319         // Skip if input dll is System.Private.CoreLib.dll
320         if (dllPath.find("System.Private.CoreLib.dll") != std::string::npos) {
321                 return NI_ERROR_NONE;
322         }
323
324         return crossgen(dllPath, std::string(), enableR2R);
325 }
326
327 void createNiUnderDirs(const std::string rootPaths[], int count, bool enableR2R, bool isAppNI)
328 {
329         createCoreLibNI(enableR2R);
330
331         std::string appPaths;
332         for (int i = 0; i < count; i++) {
333                 appPaths += rootPaths[i];
334                 appPaths += ':';
335         }
336
337         if (appPaths.back() == ':')
338                 appPaths.pop_back();
339
340         auto convert = [&appPaths, enableR2R, isAppNI](const std::string& path, const char* name) {
341                 if (!crossgen(path, appPaths.c_str(), enableR2R, isAppNI)) {
342                         waitInterval();
343                 }
344         };
345
346         for (int i = 0; i < count; i++) {
347                 scanFilesInDir(rootPaths[i], convert, 1);
348         }
349 }
350
351 ni_error_e createNiUnderPkgRoot(const std::string& pkgId, bool enableR2R)
352 {
353         std::string pkgRoot;
354         if (getRootPath(pkgId, pkgRoot) != NI_ERROR_NONE) {
355                 fprintf(stderr, "Failed to get root path from [%s]\n", pkgId.c_str());
356                 return NI_ERROR_INVALID_PACKAGE;
357         }
358
359         std::string binDir = concatPath(pkgRoot, "bin");
360         std::string libDir = concatPath(pkgRoot, "lib");
361         std::string tacDir = concatPath(binDir, TAC_SYMLINK_SUB_DIR);
362         std::string paths[] = {binDir, libDir, tacDir};
363
364         createNiUnderDirs(paths, 3, enableR2R, true);
365
366         return NI_ERROR_NONE;
367 }
368
369 ni_error_e createNiDllUnderPkgRoot(const std::string& pkgId, const std::string& dllPath, bool enableR2R)
370 {
371         std::string pkgRoot;
372         if (getRootPath(pkgId, pkgRoot) < 0) {
373                 fprintf(stderr, "Failed to get root path from [%s]\n", pkgId.c_str());
374                 return NI_ERROR_INVALID_PACKAGE;
375         }
376
377         std::string binDir = concatPath(pkgRoot, "bin");
378         std::string libDir = concatPath(pkgRoot, "lib");
379         std::string appTAC = concatPath(binDir, ".TAC.Release");
380         std::string paths = binDir + ":" + libDir + ":" + appTAC;
381
382         return crossgen(dllPath, paths, enableR2R, true);
383 }
384
385 void removeNiPlatform()
386 {
387         std::string coreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll");
388         std::string coreLibBackup = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll.Backup");
389
390         if (!isFileExist(coreLibBackup)) {
391                 return;
392         }
393
394         if (remove(coreLib.c_str())) {
395                 fprintf(stderr, "Failed to remove System.Private.CoreLib native image file\n");
396         }
397
398         if (rename(coreLibBackup.c_str(), coreLib.c_str())) {
399                 fprintf(stderr, "Failed to rename System.Private.CoreLib.Backup to origin\n");
400         }
401
402         const std::string platformDirs[] = {getRuntimeDir(), getTizenFXDir()};
403
404         removeNiUnderDirs(platformDirs, 2);
405 }
406
407 void removeNiUnderDirs(const std::string rootPaths[], int count)
408 {
409         auto convert = [](const std::string& path, std::string name) {
410                 if (isNativeImage(path)) {
411                         if (remove(path.c_str())) {
412                                 fprintf(stderr, "Failed to remove %s\n", path.c_str());
413                         }
414                 }
415         };
416
417         for (int i = 0; i < count; i++)
418                 scanFilesInDir(rootPaths[i], convert, -1);
419 }
420
421 ni_error_e removeNiUnderPkgRoot(const std::string& pkgId)
422 {
423         std::string pkgRoot;
424         if (getRootPath(pkgId, pkgRoot) < 0) {
425                 fprintf(stderr, "Failed to get root path from [%s]\n", pkgId.c_str());
426                 return NI_ERROR_INVALID_PACKAGE;
427         }
428
429         std::string binDir = concatPath(pkgRoot, "bin");
430         std::string libDir = concatPath(pkgRoot, "lib");
431         std::string paths[] = {binDir, libDir};
432
433         removeNiUnderDirs(paths, 2);
434
435         std::string binNIDir = concatPath(binDir, APP_NI_SUB_DIR);
436         if (isFileExist(binNIDir)) {
437                 if (rmdir(binNIDir.c_str()) != 0) {
438                         fprintf(stderr, "Failed to remove app ni dir [%s]\n", binNIDir.c_str());
439                 }
440         }
441
442         std::string libNIDir = concatPath(libDir, APP_NI_SUB_DIR);
443         if (isFileExist(libNIDir)) {
444                 if (rmdir(libNIDir.c_str()) != 0) {
445                         fprintf(stderr, "Failed to remove app ni dir [%s]\n", libNIDir.c_str());
446                 }
447         }
448
449         return NI_ERROR_NONE;
450 }
451
452 ni_error_e regenerateAppNI(bool enableR2R)
453 {
454         int ret = 0;
455         pkgmgrinfo_appinfo_metadata_filter_h handle;
456
457         ret = pkgmgrinfo_appinfo_metadata_filter_create(&handle);
458         if (ret != PMINFO_R_OK)
459                 return NI_ERROR_UNKNOWN;
460
461         ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, AOT_METADATA_KEY, METADATA_VALUE);
462         if (ret != PMINFO_R_OK) {
463                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
464                 return NI_ERROR_UNKNOWN;
465         }
466
467         ret = pkgmgrinfo_appinfo_metadata_filter_foreach(handle, appAotCb, &enableR2R);
468         if (ret != PMINFO_R_OK) {
469                 fprintf(stderr, "Failed pkgmgrinfo_appinfo_metadata_filter_foreach\n");
470                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
471                 return NI_ERROR_UNKNOWN;
472         }
473
474         fprintf(stderr, "Success pkgmgrinfo_appinfo_metadata_filter_foreach\n");
475
476         pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
477         return NI_ERROR_NONE;
478 }