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