add nitool option to regenerate all app ni files
[platform/core/dotnet/launcher.git] / NativeLauncher / installer-plugin / 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
40 #include "common.h"
41
42 #ifdef  LOG_TAG
43 #undef  LOG_TAG
44 #endif
45 #define LOG_TAG "NETCORE_INSTALLER_PLUGIN"
46
47 #ifndef DEVICE_API_DIR
48 #error "DEVICE_API_DIR is missed"
49 #endif
50
51 #ifndef RUNTIME_DIR
52 #error "RUNTIME_DIR is missed"
53 #endif
54
55 #ifndef CROSSGEN_PATH
56 #error "CROSSGEN_PATH is missed"
57 #endif
58
59 #define __XSTR(x) #x
60 #define __STR(x) __XSTR(x)
61 static const char* __DEVICE_API_DIR = __STR(DEVICE_API_DIR);
62 static const char* __RUNTIME_DIR = __STR(RUNTIME_DIR);
63 static const char* __CROSSGEN_PATH = __STR(CROSSGEN_PATH);
64 static const char* __JIT_PATH = __STR(RUNTIME_DIR)"/libclrjit.so";
65 #undef __STR
66 #undef __XSTR
67
68 #if 0
69 static std::string replace(std::string &str, const std::string& from, const std::string& to)
70 {
71         size_t startPos = 0;
72         while ((startPos = str.find(from, startPos)) != std::string::npos) {
73                 str.replace(startPos, from.length(), to);
74                 startPos += to.length();
75         }
76         return str;
77 }
78 #endif
79
80 static void smack_(const char* dllPath, const char* label)
81 {
82         static const char* chsmack = "/usr/bin/chsmack";
83         pid_t pid = fork();
84         if (pid == -1)
85                 return;
86
87         if (pid > 0) {
88                 int status;
89                 waitpid(pid, &status, 0);
90                 if (WIFEXITED(status))
91                         return;
92         } else {
93                 const char* args[] = {
94                         chsmack,
95                         "-a", label,
96                         dllPath,
97                         nullptr
98                 };
99                 execv(chsmack, const_cast<char*const*>(args));
100
101                 exit(0);
102         }
103 }
104
105 static void crossgen(const char* dllPath, const char* appPath)
106 {
107         //pid_t parent = getpid();
108         pid_t pid = fork();
109         if (pid == -1)
110                 return;
111
112         if (pid > 0) {
113                 int status;
114                 waitpid(pid, &status, 0);
115                 if (WIFEXITED(status))
116                         return;
117         } else {
118                 // search dlls in the application directory first, to use application dlls
119                 // instead of system dlls when proceeding NI
120                 std::vector<std::string> tpaDir;
121                 if (appPath != NULL) {
122                         std::string path(appPath);
123                         std::string::size_type prevPos = 0, pos = 0;
124                         while ((pos = path.find(':', pos)) != std::string::npos) {
125                                 std::string substring(path.substr(prevPos, pos - prevPos));
126                                 tpaDir.push_back(substring);
127                                 prevPos = ++pos;
128                         }
129                         std::string substring(path.substr(prevPos, pos - prevPos));
130                         tpaDir.push_back(substring);
131                 }
132                 tpaDir.push_back(__RUNTIME_DIR);
133                 tpaDir.push_back(__DEVICE_API_DIR);
134
135                 // get reference API directory ([DEVICE_API_DIR]/ref)
136                 int len = strlen(__DEVICE_API_DIR);
137                 char* refAPIDir = (char*)calloc(len + 5, 1);
138                 if (!refAPIDir) {
139                         printf("fail to allocate memory for reference API directory\n");
140                         return;
141                 }
142                 snprintf(refAPIDir, len + 5, "%s%s", __DEVICE_API_DIR, "/ref");
143                 tpaDir.push_back(refAPIDir);
144
145                 std::string tpa;
146                 assembliesInDirectory(tpaDir, tpa);
147
148                 std::vector<const char*> argv = {
149                         __CROSSGEN_PATH,
150                         "/Trusted_Platform_Assemblies", tpa.c_str(),
151                         "/JITPath", __JIT_PATH,
152                         "/FragileNonVersionable"
153                 };
154                 if (appPath != nullptr) {
155                         argv.push_back("/App_Paths");
156                         argv.push_back(appPath);
157                 }
158                 argv.push_back(dllPath);
159                 argv.push_back(nullptr);
160
161                 printf("+ %s\n", dllPath);
162
163                 execv(__CROSSGEN_PATH, const_cast<char* const*>(argv.data()));
164                 exit(0);
165         }
166 }
167
168 static int getRootPath(const char *pkgId, std::string& rootPath)
169 {
170         int ret = 0;
171         char *path = 0;
172
173         uid_t uid = 0;
174
175         if (pkgmgr_installer_info_get_target_uid(&uid) < 0) {
176                 _ERR("Failed to get UID");
177                 return -1;
178         }
179
180         _INFO("user id is %d", uid);
181
182         pkgmgrinfo_pkginfo_h handle;
183         if (uid == 0) {
184                 ret = pkgmgrinfo_pkginfo_get_pkginfo(pkgId, &handle);
185                 if (ret != PMINFO_R_OK)
186                         return -1;
187         } else {
188                 ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgId, uid, &handle);
189                 if (ret != PMINFO_R_OK)
190                         return -1;
191         }
192
193         ret = pkgmgrinfo_pkginfo_get_root_path(handle, &path);
194         if (ret != PMINFO_R_OK) {
195                 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
196                 return -1;
197         }
198         rootPath = path;
199         pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
200
201         return 0;
202 }
203
204 static bool niExist(const std::string& path, std::string& ni)
205 {
206         // native image of System.Private.CoreLib.dll should have to overwrite
207         // original file to support new coreclr
208         if (path.find("System.Private.CoreLib.dll") != std::string::npos) {
209                 std::string coreLibBackup = path + ".Backup";
210                 if (!fileNotExist(coreLibBackup)) {
211                         ni = path;
212                         return true;
213                 }
214                 return false;
215         }
216
217         static const char* possibleExts[] = {
218                 ".ni.dll", ".NI.dll", ".NI.DLL", ".ni.DLL",
219                 ".ni.exe", ".NI.exe", ".NI.EXE", ".ni.EXE"
220         };
221         std::string fName = path.substr(0, path.size() - 4);
222
223         struct stat sb;
224
225         for (const char* ext : possibleExts) {
226                 std::string f = fName + ext;
227                 if (stat(f.c_str(), &sb) == 0) {
228                         ni = f;
229                         return true;
230                 }
231         }
232
233         return false;
234 }
235
236 static int appAotCb(pkgmgrinfo_appinfo_h handle, void *userData)
237 {
238         char *pkgId = NULL;
239         int ret = 0;
240
241         ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId);
242         if (ret != PMINFO_R_OK) {
243                 _DBG("Failed to get pkgid");
244                 return -1;
245         }
246
247         // When you create native image with pkgid, ni file is generated even though already ni file exist.
248         if (createNiUnderPkgRoot(pkgId) != 0) {
249                 _ERR("Failed to get root path from [%s]", pkgId);
250                 return -1;
251         } else {
252                 _DBG("Complete make application to native image");
253         }
254
255         return 0;
256 }
257
258 int regenerateAppNI()
259 {
260         int ret = 0;
261         pkgmgrinfo_appinfo_metadata_filter_h handle;
262
263         ret = pkgmgrinfo_appinfo_metadata_filter_create(&handle);
264         if (ret != PMINFO_R_OK)
265                 return -1;
266
267         ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, "http://tizen.org/metadata/prefer_dotnet_aot", "true");
268         if (ret != PMINFO_R_OK) {
269                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
270                 return -1;
271         }
272
273         ret = pkgmgrinfo_appinfo_metadata_filter_foreach(handle, appAotCb, NULL);
274         if (ret != PMINFO_R_OK) {
275                 _DBG("Failed pkgmgrinfo_appinfo_metadata_filter_foreach");
276                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
277                 return -1;
278         }
279
280         _DBG("Success pkgmgrinfo_appinfo_metadata_filter_foreach");
281
282         pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
283         return 0;
284 }
285
286 static void createCoreLibNI()
287 {
288         std::string coreLib = concatPath(__RUNTIME_DIR, "System.Private.CoreLib.dll");
289         std::string niCoreLib = concatPath(__RUNTIME_DIR, "System.Private.CoreLib.ni.dll");
290         std::string coreLibBackup = concatPath(__RUNTIME_DIR, "System.Private.CoreLib.dll.Backup");
291
292         if (!niExist(coreLib, niCoreLib)) {
293                 crossgen(coreLib.c_str(), nullptr);
294                 if (!fileNotExist(niCoreLib)) {
295                         // change owner and groups for generated ni file.
296                         struct stat info;
297                         if (!stat(coreLib.c_str(), &info)) {
298                                 if (chown(niCoreLib.c_str(), info.st_uid, info.st_gid) == -1)
299                                         _ERR("Failed to change owner and group name");
300                         }
301                         smack_(niCoreLib.c_str(), "_");
302                         rename(coreLib.c_str(), coreLibBackup.c_str());
303                         rename(niCoreLib.c_str(), coreLib.c_str());
304                 }
305         }
306 }
307
308 void removeNiUnderDirs(const char* rootPaths[], int count)
309 {
310         auto convert = [](const char* path, const char* name) {
311                 std::string ni;
312                 if (isNativeImage(path)) {
313                         remove(path);
314                 }
315         };
316
317         for (int i = 0; i < count; i++)
318                 scanFilesInDir(rootPaths[i], convert, -1);
319 }
320
321 void removeNiPlatform()
322 {
323         std::string coreLib = concatPath(__RUNTIME_DIR, "System.Private.CoreLib.dll");
324         std::string niCoreLib = concatPath(__RUNTIME_DIR, "System.Private.CoreLib.ni.dll");
325         std::string coreLibBackup = concatPath(__RUNTIME_DIR, "System.Private.CoreLib.dll.Backup");
326
327         if (fileNotExist(coreLibBackup)) {
328                 return;
329         }
330
331         if (remove(coreLib.c_str())) {
332                 _ERR("Failed to remove System.Private.CoreLib native image file");
333         }
334
335         rename(coreLibBackup.c_str(), coreLib.c_str());
336
337         const char* platformDirs[] = {__RUNTIME_DIR, __DEVICE_API_DIR, "/usr/bin"};
338
339         removeNiUnderDirs(platformDirs, 3);
340 }
341
342 void createNiPlatform()
343 {
344         createCoreLibNI();
345
346         const char* platformDirs[] = {__RUNTIME_DIR, __DEVICE_API_DIR, "/usr/bin"};
347
348         createNiUnderDirs(platformDirs, 3, [](const char* ni) {
349                 smack_(ni, "_");
350         }, false);
351 }
352
353 void createNiSelect(const char* dllPath)
354 {
355         createCoreLibNI();
356
357         std::string niPath;
358         if (!fileNotExist(dllPath)) {
359                 if (!niExist(dllPath, niPath)) {
360                         crossgen(dllPath, nullptr);
361                         if (niExist(dllPath, niPath)) {
362                                 // change owner and groups for generated ni file.
363                                 struct stat info;
364                                 if (!stat(dllPath, &info)) {
365                                         if (chown(niPath.c_str(), info.st_uid, info.st_gid) == -1)
366                                                 _ERR("Failed to change owner and group name");
367                                 }
368                                 smack_(niPath.c_str(), "_");
369                         }
370                 } else {
371                         printf("Already [%s] file is exist\n", niPath.c_str());
372                 }
373         } else {
374                 printf("Failed to find dll : %s", dllPath);
375         }
376 }
377
378 void createNiUnderDirs(const char* rootPaths[], int count, const char* ignores[], int igcount, afterCreate cb, bool update)
379 {
380         std::string appPaths;
381         for (int i = 0; i < count; i++) {
382                 appPaths += rootPaths[i];
383                 appPaths += ':';
384         }
385
386         if (appPaths.back() == ':')
387                 appPaths.pop_back();
388
389         auto convert = [&appPaths, ignores, igcount, &cb, update](const char* path, const char* name) {
390                 for (int i = 0; i < igcount; i++) {
391                         if (strcmp(path, ignores[i]) == 0)
392                                 return;
393                 }
394                 std::string niPath;
395                 if (isManagedAssembly(path) && !isNativeImage(path)) {
396                         if (niExist(path, niPath)) {
397                                 if (update && !niPath.empty()) {
398                                         _INFO("override [%s] file", niPath.c_str());
399                                 } else {
400                                         _INFO("Already [%s] file is exist", niPath.c_str());
401                                         return;
402                                 }
403                         }
404                         crossgen(path, appPaths.c_str());
405                         if (niExist(path, niPath)) {
406                                 // change owner and groups for generated ni file.
407                                 struct stat info;
408                                 if (!stat(path, &info)) {
409                                         if (chown(niPath.c_str(), info.st_uid, info.st_gid) == -1)
410                                                 _ERR("Failed to change owner and group name");
411                                 }
412
413                                 if (cb != nullptr)
414                                         cb(niPath.c_str());
415                         } else {
416                                 _INFO("Failed to create native image for %s", path);
417                         }
418                 }
419         };
420
421         for (int i = 0; i < count; i++)
422                 scanFilesInDir(rootPaths[i], convert, 1);
423 }
424 void createNiUnderDirs(const char* rootPaths[], int count, afterCreate cb, bool update)
425 {
426         createNiUnderDirs(rootPaths, count, nullptr, 0, cb, update);
427 }
428 void createNiUnderDirs(const char* rootPaths[], int count, bool update)
429 {
430         createNiUnderDirs(rootPaths, count, nullptr, update);
431 }
432
433 int removeNiUnderPkgRoot(const char* pkgName)
434 {
435         std::string pkgRoot;
436         if (getRootPath(pkgName, pkgRoot) < 0)
437                 return 1;
438
439         std::string binDir = concatPath(pkgRoot, "bin");
440         std::string libDir = concatPath(pkgRoot, "lib");
441         _INFO("bindir : %s", binDir.c_str());
442         _INFO("libdir : %s", libDir.c_str());
443
444         const char* paths[] = {
445                 binDir.c_str(),
446                 libDir.c_str()
447         };
448
449         removeNiUnderDirs(paths, 2);
450
451         return 0;
452 }
453
454
455 int createNiUnderPkgRoot(const char* pkgName)
456 {
457         std::string pkgRoot;
458         if (getRootPath(pkgName, pkgRoot) < 0)
459                 return 1;
460
461         // get interval value
462         const char* intervalFile = "/usr/share/dotnet.tizen/lib/crossgen_interval.txt";
463         int interval = 0;
464         std::ifstream inFile(intervalFile);
465         if (inFile) {
466                 _INFO("crossgen_interval.txt is found");
467                 inFile >> interval;
468         } else {
469                 _INFO("fail to read crossgen_interval.txt file");
470         }
471
472         std::string binDir = concatPath(pkgRoot, "bin");
473         std::string libDir = concatPath(pkgRoot, "lib");
474         _INFO("bindir : %s", binDir.c_str());
475         _INFO("libdir : %s", libDir.c_str());
476
477         const char* paths[] = {
478                 binDir.c_str(),
479                 libDir.c_str()
480         };
481
482         // change smack label for generated ni file.
483         std::string label = "User::Pkg::" + std::string(pkgName) + "::RO";
484         createNiUnderDirs(paths, 2, [label, interval](const char* ni) {
485                         smack_(ni, label.c_str());
486                         if (interval != 0) {
487                                 _INFO("sleep %d usec", interval);
488                                 usleep(interval);
489                         }
490         }, true);
491
492         return 0;
493 }