8384fea3d72931341022cf3faafcbb44a77956a2
[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         bool* enableR2R = (bool*)userData;
212
213         ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId);
214         if (ret != PMINFO_R_OK) {
215                 fprintf(stderr, "Failed to get pkgid\n");
216                 return -1;
217         }
218
219         if (removeNiUnderPkgRoot(pkgId) != 0) {
220                 fprintf(stderr, "Failed to remove previous dlls from [%s]\n", pkgId);
221                 return -1;
222         }
223
224         // Regenerate ni files with R2R mode forcibiliy. (there is no way to now which option is used)
225         if (createNiUnderPkgRoot(pkgId, *enableR2R) != 0) {
226                 fprintf(stderr, "Failed to get root path from [%s]\n", pkgId);
227                 return -1;
228         } else {
229                 fprintf(stderr, "Complete make application to native image\n");
230         }
231
232         return 0;
233 }
234
235 static void createCoreLibNI(bool enableR2R)
236 {
237         std::string coreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll");
238         std::string niCoreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.ni.dll");
239         std::string coreLibBackup = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll.Backup");
240
241         if (!niExist(coreLib, niCoreLib)) {
242                 crossgen(coreLib, std::string(), enableR2R);
243                 if (isFileExist(niCoreLib)) {
244                         if (rename(coreLib.c_str(), coreLibBackup.c_str())) {
245                                 fprintf(stderr, "Failed to rename System.Private.CoreLib.dll\n");
246                         }
247                         if (rename(niCoreLib.c_str(), coreLib.c_str())) {
248                                 fprintf(stderr, "Failed to rename System.Private.CoreLib.ni.dll\n");
249                         }
250                 } else {
251                         fprintf(stderr, "Failed to create native image for %s\n", coreLib.c_str());
252                 }
253         }
254 }
255
256 int initNICommon(NiCommonOption* option)
257 {
258 #if defined(__arm__)
259         // get interval value
260         const char* intervalFile = "/usr/share/dotnet.tizen/lib/crossgen_interval.txt";
261         std::ifstream inFile(intervalFile);
262         if (inFile) {
263                 fprintf(stderr, "crossgen_interval.txt is found\n");
264                 inFile >> __interval;
265         }
266
267         if (initializePluginManager("normal")) {
268                 fprintf(stderr, "Fail to initialize plugin manager\n");
269                 return -1;
270         }
271         if (initializePathManager(option->runtimeDir, option->tizenFXDir, option->extraDirs)) {
272                 fprintf(stderr, "Fail to initialize path manager\n");
273                 return -1;
274         }
275
276         __tpa = getTPA();
277
278         return 0;
279 #else
280         fprintf(stderr, "crossgen supports arm architecture only. skip ni file generation\n");
281         return -1;
282 #endif
283 }
284
285 void finalizeNICommon()
286 {
287         __interval = 0;
288
289         finalizePluginManager();
290         finalizePathManager();
291
292         __tpa.clear();
293 }
294
295
296 void createNiPlatform(bool enableR2R)
297 {
298         const std::string platformDirs[] = {getRuntimeDir(), getTizenFXDir()};
299
300         createNiUnderDirs(platformDirs, 2, enableR2R);
301 }
302
303 void createNiDll(const std::string& dllPath, bool enableR2R)
304 {
305         createCoreLibNI(enableR2R);
306
307         if (!isFileExist(dllPath)) {
308                 fprintf(stderr, "Failed to find dll : %s\n", dllPath.c_str());
309                 return;
310         }
311
312         std::string niPath;
313         if (niExist(dllPath, niPath)) {
314                 fprintf(stderr, "Already [%s] file is exist\n", niPath.c_str());
315                 return;
316         }
317
318         crossgen(dllPath, std::string(), enableR2R);
319         if (!niExist(dllPath, niPath)) {
320                 fprintf(stderr, "Failed to create native image for %s\n", dllPath.c_str());
321         }
322 }
323
324 void createNiUnderDirs(const std::string rootPaths[], int count, const std::string ignores[], int igcount, afterCreate cb, bool enableR2R)
325 {
326         createCoreLibNI(enableR2R);
327
328         std::string appPaths;
329         for (int i = 0; i < count; i++) {
330                 appPaths += rootPaths[i];
331                 appPaths += ':';
332         }
333
334         if (appPaths.back() == ':')
335                 appPaths.pop_back();
336
337         auto convert = [&appPaths, ignores, igcount, &cb, enableR2R](const std::string& path, const char* name) {
338                 for (int i = 0; i < igcount; i++) {
339                         if (path == ignores[i])
340                                 return;
341                 }
342                 std::string niPath;
343                 if (isManagedAssembly(path)) {
344                         if (niExist(path, niPath)) {
345                                 fprintf(stderr, "Already [%s] file is exist\n", niPath.c_str());
346                                 return;
347                         }
348                         crossgen(path, appPaths.c_str(), enableR2R);
349                         if (niExist(path, niPath)) {
350                                 if (cb != nullptr)
351                                         cb(niPath.c_str());
352                         } else {
353                                 fprintf(stderr, "Failed to create native image for %s\n", path.c_str());
354                         }
355                         waitInterval();
356                 }
357         };
358
359         for (int i = 0; i < count; i++)
360                 scanFilesInDir(rootPaths[i], convert, 1);
361 }
362 void createNiUnderDirs(const std::string rootPaths[], int count, afterCreate cb, bool enableR2R)
363 {
364         createNiUnderDirs(rootPaths, count, nullptr, 0, cb, enableR2R);
365 }
366 void createNiUnderDirs(const std::string rootPaths[], int count, bool enableR2R)
367 {
368         createNiUnderDirs(rootPaths, count, nullptr, enableR2R);
369 }
370
371 int createNiUnderPkgRoot(const std::string& pkgName, bool enableR2R)
372 {
373         std::string pkgRoot;
374         if (getRootPath(pkgName, pkgRoot) < 0)
375                 return -1;
376
377         std::string binDir = concatPath(pkgRoot, "bin");
378         std::string libDir = concatPath(pkgRoot, "lib");
379         std::string paths[] = {binDir, libDir};
380
381         createNiUnderDirs(paths, 2, enableR2R);
382
383         return 0;
384 }
385
386 int createNiDllUnderPkgRoot(const std::string& pkgName, const std::string& dllPath, bool enableR2R)
387 {
388         std::string pkgRoot;
389         if (getRootPath(pkgName, pkgRoot) < 0)
390                 return -1;
391
392         std::string binDir = concatPath(pkgRoot, "bin");
393         std::string libDir = concatPath(pkgRoot, "lib");
394         std::string appPaths = binDir + ":" + libDir;
395
396         if (!isFileExist(dllPath)) {
397                 fprintf(stderr, "Failed to find dll : %s\n", dllPath.c_str());
398                 return -1;
399         }
400
401         std::string niPath;
402         if (niExist(dllPath, niPath)) {
403                 fprintf(stderr, "Already [%s] file is exist\n", niPath.c_str());
404                 return -1;
405         }
406
407         crossgen(dllPath, appPaths, enableR2R);
408         if (!niExist(dllPath, niPath)) {
409                 fprintf(stderr, "Failed to create native image for %s\n", dllPath.c_str());
410                 return -1;
411         }
412
413         return 0;
414 }
415
416 void removeNiPlatform()
417 {
418         std::string coreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll");
419         std::string coreLibBackup = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll.Backup");
420
421         if (!isFileExist(coreLibBackup)) {
422                 return;
423         }
424
425         if (remove(coreLib.c_str())) {
426                 fprintf(stderr, "Failed to remove System.Private.CoreLib native image file\n");
427         }
428
429         if (rename(coreLibBackup.c_str(), coreLib.c_str())) {
430                 fprintf(stderr, "Failed to rename System.Private.CoreLib.Backup to origin\n");
431         }
432
433         const std::string platformDirs[] = {getRuntimeDir(), getTizenFXDir()};
434
435         removeNiUnderDirs(platformDirs, 2);
436 }
437
438 void removeNiUnderDirs(const std::string rootPaths[], int count)
439 {
440         auto convert = [](const std::string& path, std::string name) {
441                 std::string ni;
442                 if (isNativeImage(path)) {
443                         if (remove(path.c_str())) {
444                                 fprintf(stderr, "Failed to remove %s\n", path.c_str());
445                         }
446                 }
447         };
448
449         for (int i = 0; i < count; i++)
450                 scanFilesInDir(rootPaths[i], convert, -1);
451 }
452
453 int removeNiUnderPkgRoot(const std::string& pkgName)
454 {
455         std::string pkgRoot;
456         if (getRootPath(pkgName, pkgRoot) < 0)
457                 return -1;
458
459         std::string binDir = concatPath(pkgRoot, "bin");
460         std::string libDir = concatPath(pkgRoot, "lib");
461         std::string paths[] = {binDir, libDir};
462
463         removeNiUnderDirs(paths, 2);
464
465         return 0;
466 }
467
468 int regenerateAppNI(bool enableR2R)
469 {
470         int ret = 0;
471         pkgmgrinfo_appinfo_metadata_filter_h handle;
472
473         ret = pkgmgrinfo_appinfo_metadata_filter_create(&handle);
474         if (ret != PMINFO_R_OK)
475                 return -1;
476
477         ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, "http://tizen.org/metadata/prefer_dotnet_aot", "true");
478         if (ret != PMINFO_R_OK) {
479                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
480                 return -1;
481         }
482
483         ret = pkgmgrinfo_appinfo_metadata_filter_foreach(handle, appAotCb, &enableR2R);
484         if (ret != PMINFO_R_OK) {
485                 fprintf(stderr, "Failed pkgmgrinfo_appinfo_metadata_filter_foreach\n");
486                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
487                 return -1;
488         }
489
490         fprintf(stderr, "Success pkgmgrinfo_appinfo_metadata_filter_foreach\n");
491
492         pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
493         return 0;
494 }
495