Support for architecture-specific assemblies in a folder named runtimes (#276)
[platform/core/dotnet/launcher.git] / NativeLauncher / installer-plugin / prefer_nuget_cache_plugin.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 "log.h"
18 #include "utils.h"
19 #include "db_manager.h"
20 #include "tac_common.h"
21
22 #include <cstring>
23 #include <fstream>
24 #include <regex>
25 #include <sstream>
26 #include <vector>
27 #include <boost/filesystem.hpp>
28 #include <glib.h>
29 #include <json/json.h>
30 #include <pkgmgr-info.h>
31
32 #ifdef  LOG_TAG
33 #undef  LOG_TAG
34 #endif
35 #define LOG_TAG "DOTNET_INSTALLER_PLUGIN"
36
37 #define __XSTR(x) #x
38 #define __STR(x) __XSTR(x)
39 static const char* __DOTNET_DIR = __STR(DOTNET_DIR);
40 #undef __STR
41 #undef __XSTR
42
43 std::vector<std::string> nugetPackagesAssembliesSha;
44 std::vector<std::string> tacDB;
45 std::vector<std::string> createDirectories;
46 std::vector<std::string> createLibraries;
47 std::vector<std::string> updateTac;
48 std::string status = "";
49 static sqlite3 *tac_db = NULL;
50 static sqlite3 *tlc_db = NULL;
51 bool tacPluginInstalled = false;
52 bool tacPluginFinished = false;
53
54 static void createSHA256Info(std::string sha256Info, std::string nugetPackage)
55 {
56         std::ofstream ofs(sha256Info, std::ios::app);
57         int assembly_count = 0;
58         for (auto& npAssemblySha : nugetPackagesAssembliesSha) {
59                 std::string nuget_package_assembly = npAssemblySha.substr(0, npAssemblySha.rfind(':'));
60                 std::string nuget_package = nuget_package_assembly.substr(0, nuget_package_assembly.rfind(':'));
61                 std::string assembly = nuget_package_assembly.substr(nuget_package_assembly.rfind(':') + 1);
62                 std::string sha = npAssemblySha.substr(npAssemblySha.rfind(':') + 1);
63                 if (!strcmp(nuget_package.c_str(), nugetPackage.c_str())) {
64                         ofs << assembly << ":" << sha << std::endl;
65                         assembly_count++;
66                 }
67         }
68         ofs << assembly_count << std::endl;
69         ofs.close();
70 }
71
72 static bool compareSHA256Info(std::string sha256Info, std::string nugetPackage)
73 {
74         int compare_count = 0;
75         int assembly_count = 0;
76         std::string sha256_count = "0";
77         for (auto& npAssemblySha : nugetPackagesAssembliesSha) {
78                 std::string nuget_package_assembly = npAssemblySha.substr(0, npAssemblySha.rfind(':'));
79                 std::string nuget_package = nuget_package_assembly.substr(0, nuget_package_assembly.rfind(':'));
80                 std::string assembly = nuget_package_assembly.substr(nuget_package_assembly.rfind(':') + 1);
81                 std::string sha = npAssemblySha.substr(npAssemblySha.rfind(':') + 1);
82                 if (!strcmp(nuget_package.c_str(), nugetPackage.c_str())) {
83                         assembly_count++;
84                         std::ifstream ifs(sha256Info);
85                         std::string get_str;
86                         if (ifs.is_open()) {
87                                 while (getline(ifs, get_str)) {
88                                         if (!strcmp(get_str.c_str(), (assembly + ":" + sha).c_str())) {
89                                                 compare_count++;
90                                         }
91                                         sha256_count = get_str;
92                                 }
93                                 ifs.close();
94                         }
95                 }
96         }
97         if (!strcmp(std::to_string(assembly_count).c_str(), std::to_string(compare_count).c_str()) &&
98                 !strcmp(std::to_string(assembly_count).c_str(), sha256_count.c_str())) {
99                 _INFO("Same nuget : %s", nugetPackage.c_str());
100                 return true;
101         }
102         return false;
103 }
104
105 static bool copyAssemblyCreateSymlink(std::string binPath, std::string tacDir, std::string nugetPackage, bool isCreateTacDir)
106 {
107         std::string tac_version_dir = concatPath(__DOTNET_DIR, nugetPackage);
108         bool nuget_restoration = false;
109         for (auto& npAssemblySha : nugetPackagesAssembliesSha) {
110                 std::string nuget_package_assembly = npAssemblySha.substr(0, npAssemblySha.rfind(':'));
111                 std::string nuget_package = nuget_package_assembly.substr(0, nuget_package_assembly.rfind(':'));
112                 std::string assembly = nuget_package_assembly.substr(nuget_package_assembly.rfind(':') + 1);
113                 if (!strcmp(nuget_package.c_str(), nugetPackage.c_str())) {
114                         if (exist(concatPath(binPath, assembly))) {
115                                 if (isCreateTacDir) {
116                                         if (!copyFile(concatPath(binPath, assembly), concatPath(tac_version_dir, assembly))) {
117                                                 _ERR("Failed to copy of %s", assembly.c_str());
118                                                 nuget_restoration = true;
119                                                 break;
120                                         }
121                                 }
122                                 bf::create_symlink(concatPath(tac_version_dir, assembly), concatPath(tacDir, assembly));
123
124                                 if (!removeFile(concatPath(binPath, assembly))) {
125                                         _ERR("Failed to remove of %s", assembly.c_str());
126                                         nuget_restoration = true;
127                                         break;
128                                 }
129                         }
130                 }
131         }
132
133         if (nuget_restoration) {
134                 for (auto& npAssemblySha : nugetPackagesAssembliesSha) {
135                         std::string nuget_package_assembly = npAssemblySha.substr(0, npAssemblySha.rfind(':'));
136                         std::string nuget_package = nuget_package_assembly.substr(0, nuget_package_assembly.rfind(':'));
137                         std::string assembly = nuget_package_assembly.substr(nuget_package_assembly.rfind(':') + 1);
138                         if (!strcmp(nuget_package.c_str(), nugetPackage.c_str())) {
139                                 copyFile(concatPath(tac_version_dir, assembly), concatPath(binPath, assembly));
140                                 removeFile(concatPath(tacDir, assembly));
141                         }
142                 }
143         }
144
145         return nuget_restoration;
146 }
147
148 static void copyLibraryCreateSymlink(const char* pkgId, std::vector<std::string> LibrariesInfo, std::string tlcDir)
149 {
150         if (LibrariesInfo.empty()) {
151                 _INFO("Not exist data for TLC in %s", pkgId);
152                 return;
153         }
154
155         for (auto& librarySha : LibrariesInfo) {
156                 std::string library = librarySha.substr(0, librarySha.find(':'));
157                 std::string filename = library.substr(library.rfind('/') + 1);
158                 std::string fileSha = filename + ".." + librarySha.substr(librarySha.find(':') + 1);
159                 bool fileCopied = false;
160                 if (!exist(concatPath(tlcDir, fileSha))) {
161                         if (!copyFile(library, concatPath(tlcDir, fileSha))) {
162                                 _ERR("Failed to copy of %s", filename.c_str());
163                                 continue;
164                         }
165                         fileCopied = true;
166                         createLibraries.push_back(concatPath(tlcDir, fileSha));
167                 }
168                 if (!removeFile(library)) {
169                         _ERR("Failed to remove of %s", library.c_str());
170                         if (fileCopied) {
171                                 removeFile(concatPath(tlcDir, fileSha));
172                         }
173                         continue;
174                 }
175                 bf::create_symlink(concatPath(tlcDir, fileSha), library);
176
177                 char *sql = sqlite3_mprintf("INSERT INTO TLC (PKGID, LIBRARY) VALUES (%Q, %Q);", pkgId, fileSha.c_str());
178                 if (!insertDB(tlc_db, sql)) {
179                         _ERR("Sqlite insert error");
180                         sqlite3_free(sql);
181                         copyFile(concatPath(tlcDir, fileSha), library);
182                         if (fileCopied) {
183                                 removeFile(concatPath(tlcDir, fileSha));
184                         }
185                         continue;
186                 }
187                 sqlite3_free(sql);
188         }
189 }
190
191 static void checkDepsJson(std::string rootPath, std::string binPath, std::string execName)
192 {
193         for (auto& npAssembly : depsJsonParser(rootPath, execName)) {
194                 std::string nuget_package = npAssembly.substr(0, npAssembly.rfind(':'));
195                 std::string assembly_name = npAssembly.substr(npAssembly.rfind(':') + 1);
196                 tacDB.push_back(nuget_package);
197                 std::string buffer = SHA256(concatPath(binPath, assembly_name));
198                 nugetPackagesAssembliesSha.push_back(nuget_package + ":" + assembly_name + ":" + buffer);
199                 _INFO("Assembly : [%s] / SHA256 : [%s]", assembly_name.c_str(), buffer.c_str());
200         }
201         std::sort(tacDB.begin(), tacDB.end());
202         tacDB.erase(unique(tacDB.begin(), tacDB.end()), tacDB.end());
203 }
204
205 extern "C" int PKGMGR_MDPARSER_PLUGIN_INSTALL(const char *pkgId, const char *appId, GList *list)
206 {
207         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_INSTALL =====]");
208         _INFO("PackageID : %s", pkgId);
209
210         // Can be multiple apps in one package
211         if (tacPluginInstalled) {
212                 _INFO("TAC plugin already installed");
213                 return 0;
214         }
215         tacPluginInstalled = true;
216
217         std::string appType = getAppType(std::string(pkgId));
218         if (strstr(appType.c_str(), "dotnet") == NULL) {
219                 _ERR("App type is not dotnet");
220                 return 0;
221         }
222         std::string execName = getExecName(std::string(pkgId));
223         std::string rootPath = getRootPath(std::string(pkgId));
224         if (execName.empty() || rootPath.empty()) {
225                 return 0;
226         }
227         std::string binPath = concatPath(rootPath, "bin");
228         std::string metaValue = getMetadataValue(std::string(pkgId), TAC_METADATA_KEY);
229         if (metaValue.empty()) {
230                 return 0;
231         }
232         if (metaValue == METADATA_VALUE) {
233                 checkDepsJson(rootPath, binPath, execName);
234         }
235
236         status = "install";
237         if (tacDB.empty()) {
238                 _INFO("Not exist data for TAC in %s", pkgId);
239                 return 0;
240         }
241
242         tac_db = createDB(TAC_APP_LIST_DB, CREATE_TAC_DB_TABLE);
243         if (!tac_db) {
244                 _ERR("Sqlite create error. So restore the database.");
245                 if (tac_restoreDB() != TAC_ERROR_NONE) {
246                         _ERR("Sqlite create error");
247                         return -1;
248                 }
249                 tac_db = createDB(TAC_APP_LIST_DB, CREATE_TAC_DB_TABLE);
250                 if (!tac_db) {
251                         _ERR("Sqlite create error");
252                         return -1;
253                 }
254         }
255         sqlite3_exec(tac_db, "BEGIN;", NULL, NULL, NULL);
256
257         std::string tac_dir = concatPath(binPath, TAC_SYMLINK_SUB_DIR);
258         if (!createDir(tac_dir)) {
259                 _ERR("Cannot create directory: %s", tac_dir.c_str());
260                 return 0;
261         }
262         copySmackAndOwnership(binPath.c_str(), tac_dir.c_str());
263
264         for (auto& np : tacDB) {
265                 std::string tac_name = np.substr(0, np.find('/'));
266                 std::string tac_version = np.substr(np.rfind('/') + 1);
267                 _INFO("TAC name : %s", tac_name.c_str());
268                 _INFO("TAC version : %s", tac_version.c_str());
269
270                 std::string tac_version_dir = concatPath(__DOTNET_DIR, np);
271                 std::string sha256_info = concatPath(tac_version_dir, TAC_SHA_256_INFO);
272                 if (!exist(tac_version_dir)) {
273                         _INFO("Create tac_version_dir [%s]", tac_version_dir.c_str());
274                         if (!createDir(tac_version_dir)) {
275                                 _ERR("Cannot create directory: %s", tac_version_dir.c_str());
276                                 status = "restore";
277                                 return -1;
278                         }
279                         createDirectories.push_back(tac_version_dir);
280                         if (!bf::is_symlink(sha256_info)) {
281                                 createSHA256Info(sha256_info, np);
282                         } else {
283                                 _ERR("Failed to create sha256_info. Symbolic link is detected");
284                                 status = "restore";
285                                 return -1;
286                         }
287
288                         if (!exist(sha256_info)) {
289                                 status = "restore";
290                                 return -1;
291                         }
292
293                         if (copyAssemblyCreateSymlink(binPath, tac_dir, np, true)) {
294                                 _ERR("Failed to create symlink");
295                                 status = "restore";
296                                 return -1;
297                         }
298
299                         char *sql = sqlite3_mprintf(
300                                 "INSERT INTO TAC (PKGID, NUGET, NAME, VERSION) " \
301                                 "VALUES (%Q, %Q, %Q, %Q);", pkgId, np.c_str(), tac_name.c_str(), tac_version.c_str());
302                         if (!insertDB(tac_db, sql)) {
303                                 _ERR("Sqlite insert error");
304                                 sqlite3_free(sql);
305                                 status = "restore";
306                                 return -1;
307                         }
308                         sqlite3_free(sql);
309                 } else {
310                         _INFO("Exists tac_version_dir [%s]", tac_version_dir.c_str());
311                         if (!bf::is_symlink(sha256_info)) {
312                                 if (compareSHA256Info(sha256_info, np)) {
313                                         if (copyAssemblyCreateSymlink(binPath, tac_dir, np, false)) {
314                                                 _ERR("Failed to create symlink");
315                                                 status = "restore";
316                                                 return -1;
317                                         }
318
319                                         char *sql = sqlite3_mprintf(
320                                                 "INSERT INTO TAC (PKGID, NUGET, NAME, VERSION) " \
321                                                 "VALUES (%Q, %Q, %Q, %Q);", pkgId, np.c_str(), tac_name.c_str(), tac_version.c_str());
322                                         if (!insertDB(tac_db, sql)) {
323                                                 _ERR("Sqlite insert error");
324                                                 sqlite3_free(sql);
325                                                 status = "restore";
326                                                 return -1;
327                                         }
328                                         sqlite3_free(sql);
329                                 } else {
330                                         _INFO("Different nuget : %s", np.c_str());
331                                         continue;
332                                 }
333                         } else {
334                                 _ERR("Failed to create sha256_info. Symbolic link is detected");
335                                 status = "restore";
336                                 return -1;
337                         }
338                 }
339         }
340
341         ///// TLC /////
342         std::string tlcDir = concatPath(__DOTNET_DIR, TLC_LIBRARIES_DIR);
343         if (!createDir(tlcDir)) {
344                 _ERR("Cannot create directory: %s", tlcDir.c_str());
345                 return 0;
346         }
347
348         tlc_db = createDB(TLC_APP_LIST_DB, CREATE_TLC_DB_TABLE);
349         if (!tlc_db) {
350                 _ERR("Sqlite create error. So restore the database.");
351                 if (tlc_restoreDB() != TAC_ERROR_NONE) {
352                         _ERR("Sqlite create error");
353                         return 0;
354                 }
355                 tlc_db = createDB(TLC_APP_LIST_DB, CREATE_TLC_DB_TABLE);
356                 if (!tlc_db) {
357                         _ERR("Sqlite create error");
358                         return 0;
359                 }
360         }
361         sqlite3_exec(tlc_db, "BEGIN;", NULL, NULL, NULL);
362
363         copyLibraryCreateSymlink(pkgId, getLibrariesInfo(rootPath), tlcDir);
364
365         return 0;
366 }
367
368 static int sqliteCb(void *count, int argc, char **argv, char **colName)
369 {
370         int *c = (int*)count;
371         *c = atoi(argv[0]);
372         return 0;
373 }
374
375 static void tac_updateDB(sqlite3 *sqlite)
376 {
377         for (auto& unp : updateTac) {
378                 int count = -1;
379                 char *sql = sqlite3_mprintf("SELECT COUNT(NUGET) FROM TAC WHERE NUGET = %Q;", unp.c_str());
380                 int ret = sqlite3_exec(sqlite, sql, sqliteCb, &count, NULL);
381                 if (ret != SQLITE_OK) {
382                         _ERR("Sqlite select error");
383                         sqlite3_free(sql);
384                         continue;
385                 }
386                 if (count == 0) {
387                         std::string tac_version_dir_prev = concatPath(__DOTNET_DIR, unp);
388                         std::string tac_version_dir_backup = tac_version_dir_prev + ".bck";
389                         if (!copyDir(tac_version_dir_prev, tac_version_dir_backup)) {
390                                 _ERR("Failed to copy of %s to %s", tac_version_dir_prev.c_str(), tac_version_dir_backup.c_str());
391                                 sqlite3_free(sql);
392                                 continue;
393                         }
394                         if (!removeAll(tac_version_dir_prev)) {
395                                 _ERR("Failed to remove of %s", tac_version_dir_prev.c_str());
396                                 sqlite3_free(sql);
397                                 continue;
398                         }
399                 }
400                 sqlite3_free(sql);
401         }
402 }
403
404 static void tlc_updateDB(sqlite3 *sqlite, std::vector<std::string> updateTlc, std::string tlcDir)
405 {
406         for (auto& ulp : updateTlc) {
407                 int count = -1;
408                 char *sql = sqlite3_mprintf("SELECT COUNT(LIBRARY) FROM TLC WHERE LIBRARY = %Q;", ulp.c_str());
409                 int ret = sqlite3_exec(sqlite, sql, sqliteCb, &count, NULL);
410                 if (ret != SQLITE_OK) {
411                         _ERR("Sqlite select error");
412                         sqlite3_free(sql);
413                         continue;
414                 }
415                 if (count == 0) {
416                         std::string library_prev = concatPath(tlcDir, ulp);
417                         std::string library_backup = library_prev + ".bck";
418                         if (!copyFile(library_prev, library_backup)) {
419                                 _ERR("Failed to copy of %s", library_prev.c_str());
420                                 sqlite3_free(sql);
421                                 continue;
422                         }
423                         if (!removeFile(library_prev)) {
424                                 _ERR("Failed to remove of %s", library_prev.c_str());
425                                 sqlite3_free(sql);
426                                 continue;
427                         }
428                 }
429                 sqlite3_free(sql);
430         }
431 }
432
433 extern "C" int PKGMGR_MDPARSER_PLUGIN_UPGRADE(const char *pkgId, const char *appId, GList *list)
434 {
435         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_UPGRADE =====]");
436         _INFO("PackageID : %s", pkgId);
437
438         // Can be multiple apps in one package
439         if (tacPluginInstalled) {
440                 _INFO("TAC plugin already upgraded");
441                 return 0;
442         }
443         tacPluginInstalled = true;
444
445         std::string appType = getAppType(std::string(pkgId));
446         if (strstr(appType.c_str(), "dotnet") == NULL) {
447                 _ERR("App type is not dotnet");
448                 return 0;
449         }
450         std::string execName = getExecName(std::string(pkgId));
451         std::string rootPath = getRootPath(std::string(pkgId));
452         if (execName.empty() || rootPath.empty()) {
453                 return 0;
454         }
455         std::string binPath = concatPath(rootPath, "bin");
456
457         if (!strcmp("removed", status.c_str())) {
458                 _INFO("Skipped to parse of deps.json");
459         } else {
460                 std::string metaValue = getMetadataValue(std::string(pkgId), TAC_METADATA_KEY);
461                 if (metaValue.empty()) {
462                         return 0;
463                 }
464                 if (metaValue == METADATA_VALUE) {
465                         checkDepsJson(rootPath, binPath, execName);
466                 }
467         }
468
469         status = "update";
470         tac_db = createDB(TAC_APP_LIST_DB, CREATE_TAC_DB_TABLE);
471         if (!tac_db) {
472                 _ERR("Sqlite create error. So restore the database.");
473                 if (tac_restoreDB() != TAC_ERROR_NONE) {
474                         _ERR("Sqlite create error");
475                         return -1;
476                 }
477                 tac_db = createDB(TAC_APP_LIST_DB, CREATE_TAC_DB_TABLE);
478                 if (!tac_db) {
479                         _ERR("Sqlite create error");
480                         return -1;
481                 }
482         }
483         sqlite3_exec(tac_db, "BEGIN;", NULL, NULL, NULL);
484
485         char *sql = sqlite3_mprintf("SELECT * FROM TAC WHERE PKGID = %Q;", pkgId);
486         updateTac = selectDB(tac_db, sql);
487         sqlite3_free(sql);
488
489         bool skipTLC = false;
490         if (tacDB.empty()) {
491                 sql = sqlite3_mprintf("DELETE FROM TAC WHERE PKGID = %Q;", pkgId);
492                 if (!deleteDB(tac_db, sql)) {
493                         _ERR("Sqlite delete error");
494                         sqlite3_free(sql);
495                         return -1;
496                 }
497                 sqlite3_free(sql);
498
499                 tac_updateDB(tac_db);
500
501                 skipTLC = true;
502                 _INFO("Not exist data for TAC in %s", pkgId);
503         } else {
504                 std::string tac_dir = concatPath(binPath, TAC_SYMLINK_SUB_DIR);
505                 if (!createDir(tac_dir)) {
506                         _ERR("Cannot create directory: %s", tac_dir.c_str());
507                         return 0;
508                 }
509                 copySmackAndOwnership(binPath.c_str(), tac_dir.c_str());
510
511                 for (auto& np : tacDB) {
512                         std::string tac_name = np.substr(0, np.find('/'));
513                         std::string tac_version = np.substr(np.rfind('/') + 1);
514                         _INFO("TAC name : %s", tac_name.c_str());
515                         _INFO("TAC version : %s", tac_version.c_str());
516
517                         std::string tac_version_dir = concatPath(__DOTNET_DIR, np);
518                         std::string sha256_info = concatPath(tac_version_dir, TAC_SHA_256_INFO);
519                         if (!exist(tac_version_dir)) {
520                                 _INFO("Create tac_version_dir [%s]", tac_version_dir.c_str());
521                                 if (!createDir(tac_version_dir)) {
522                                         _ERR("Cannot create directory: %s", tac_version_dir.c_str());
523                                         status = "restore";
524                                         return -1;
525                                 }
526                                 createDirectories.push_back(tac_version_dir);
527                                 if (!bf::is_symlink(sha256_info)) {
528                                         createSHA256Info(sha256_info, np);
529                                 } else {
530                                         _ERR("Failed to create sha256_info. Symbolic link is detected");
531                                         status = "restore";
532                                         return -1;
533                                 }
534
535                                 if (!exist(sha256_info)) {
536                                         status = "restore";
537                                         return -1;
538                                 }
539
540                                 if (copyAssemblyCreateSymlink(binPath, tac_dir, np, true)) {
541                                         _ERR("Failed to create symlink");
542                                         status = "restore";
543                                         return -1;
544                                 }
545
546                                 int count = -1;
547                                 sql = sqlite3_mprintf(
548                                                 "SELECT COUNT(NUGET) FROM TAC WHERE PKGID = %Q AND NAME = %Q;", pkgId, tac_name.c_str());
549                                 int ret = sqlite3_exec(tac_db, sql, sqliteCb, &count, NULL);
550                                 if (ret != SQLITE_OK) {
551                                         _ERR("Sqlite select error");
552                                         sqlite3_free(sql);
553                                         status = "restore";
554                                         return -1;
555                                 }
556                                 sqlite3_free(sql);
557                                 if (count == 1) {
558                                         sql = sqlite3_mprintf(
559                                                 "UPDATE TAC SET NAME = %Q, VERSION = %Q, NUGET = %Q WHERE PKGID = %Q AND NAME = %Q;",
560                                                 tac_name.c_str(), tac_version.c_str(), np.c_str(), pkgId, tac_name.c_str());
561                                         if (!updateDB(tac_db, sql)) {
562                                                 _ERR("Sqlite update error");
563                                                 sqlite3_free(sql);
564                                                 status = "restore";
565                                                 return -1;
566                                         }
567                                         sqlite3_free(sql);
568                                 } else if (count == 0) {
569                                         sql = sqlite3_mprintf(
570                                                 "INSERT INTO TAC (PKGID, NUGET, NAME, VERSION) " \
571                                                 "VALUES (%Q, %Q, %Q, %Q);", pkgId, np.c_str(), tac_name.c_str(), tac_version.c_str());
572                                         if (!insertDB(tac_db, sql)) {
573                                                 _ERR("Sqlite insert error");
574                                                 sqlite3_free(sql);
575                                                 status = "restore";
576                                                 return -1;
577                                         }
578                                         sqlite3_free(sql);
579                                 }
580                         } else {
581                                 _INFO("Exists tac_version_dir [%s]", tac_version_dir.c_str());
582                                 if (!bf::is_symlink(sha256_info)) {
583                                         if (compareSHA256Info(sha256_info, np)) {
584                                                 if (copyAssemblyCreateSymlink(binPath, tac_dir, np, false)) {
585                                                         _ERR("Failed to create symlink");
586                                                         status = "restore";
587                                                         return -1;
588                                                 }
589
590                                                 int count = -1;
591                                                 char *sql = sqlite3_mprintf(
592                                                         "SELECT COUNT(NUGET) FROM TAC WHERE PKGID = %Q AND NAME = %Q;", pkgId, tac_name.c_str());
593                                                 int ret = sqlite3_exec(tac_db, sql, sqliteCb, &count, NULL);
594                                                 if (ret != SQLITE_OK) {
595                                                         _ERR("Sqlite select error");
596                                                         sqlite3_free(sql);
597                                                         status = "restore";
598                                                         return -1;
599                                                 }
600                                                 sqlite3_free(sql);
601                                                 if (count == 1) {
602                                                         sql = sqlite3_mprintf(
603                                                                 "UPDATE TAC SET NAME = %Q, VERSION = %Q, NUGET = %Q WHERE PKGID = %Q AND NAME = %Q;",
604                                                                 tac_name.c_str(), tac_version.c_str(), np.c_str(), pkgId, tac_name.c_str());
605                                                         if (!updateDB(tac_db, sql)) {
606                                                                 _ERR("Sqlite update error");
607                                                                 sqlite3_free(sql);
608                                                                 status = "restore";
609                                                                 return -1;
610                                                         }
611                                                         sqlite3_free(sql);
612                                                 } else if (count == 0) {
613                                                         sql = sqlite3_mprintf(
614                                                                 "INSERT INTO TAC (PKGID, NUGET, NAME, VERSION) " \
615                                                                 "VALUES (%Q, %Q, %Q, %Q);", pkgId, np.c_str(), tac_name.c_str(), tac_version.c_str());
616                                                         if (!insertDB(tac_db, sql)) {
617                                                                 _ERR("Sqlite insert error");
618                                                                 sqlite3_free(sql);
619                                                                 status = "restore";
620                                                                 return -1;
621                                                         }
622                                                         sqlite3_free(sql);
623                                                 }
624                                         } else {
625                                                 _INFO("Different nuget : %s", np.c_str());
626                                                 continue;
627                                         }
628                                 } else {
629                                         _ERR("Failed to create sha256_info. Symbolic link is detected");
630                                         status = "restore";
631                                         return -1;
632                                 }
633                         }
634                 }
635                 for (auto& unp : updateTac) {
636                         bool isExits = false;
637                         for (auto& np : tacDB) {
638                                 if (!strcmp(unp.c_str(), np.c_str())) {
639                                         isExits = true;
640                                         break;
641                                 }
642                         }
643                         if (!isExits) {
644                                 char *sql = sqlite3_mprintf("DELETE FROM TAC WHERE PKGID = %Q AND NUGET = %Q;", pkgId, unp.c_str());
645                                 if (!deleteDB(tac_db, sql)) {
646                                         _ERR("Sqlite delete error");
647                                         sqlite3_free(sql);
648                                         status = "restore";
649                                         return -1;
650                                 }
651                                 sqlite3_free(sql);
652                         }
653                 }
654                 tac_updateDB(tac_db);
655         }
656
657         ///// TLC /////
658         std::string tlcDir = concatPath(__DOTNET_DIR, TLC_LIBRARIES_DIR);
659         if (!createDir(tlcDir)) {
660                 _ERR("Cannot create directory: %s", tlcDir.c_str());
661                 return 0;
662         }
663
664         tlc_db = createDB(TLC_APP_LIST_DB, CREATE_TLC_DB_TABLE);
665         if (!tlc_db) {
666                 _ERR("Sqlite create error. So restore the database.");
667                 if (tlc_restoreDB() != TAC_ERROR_NONE) {
668                         _ERR("Sqlite create error");
669                         return 0;
670                 }
671                 tlc_db = createDB(TLC_APP_LIST_DB, CREATE_TLC_DB_TABLE);
672                 if (!tlc_db) {
673                         _ERR("Sqlite create error");
674                         return 0;
675                 }
676         }
677         sqlite3_exec(tlc_db, "BEGIN;", NULL, NULL, NULL);
678
679         sql = sqlite3_mprintf("SELECT * FROM TLC WHERE PKGID = %Q;", pkgId);
680         std::vector<std::string> updateTlc = selectDB(tlc_db, sql);
681         sqlite3_free(sql);
682
683         sql = sqlite3_mprintf("DELETE FROM TLC WHERE PKGID = %Q;", pkgId);
684         if (!deleteDB(tlc_db, sql)) {
685                 _ERR("Sqlite delete error");
686                 sqlite3_free(sql);
687                 return 0;
688         }
689         sqlite3_free(sql);
690
691         std::vector<std::string> librariesInfo;
692         if (!skipTLC) {
693                 librariesInfo = getLibrariesInfo(rootPath);
694         }
695
696         copyLibraryCreateSymlink(pkgId, librariesInfo, tlcDir);
697
698         tlc_updateDB(tlc_db, updateTlc, tlcDir);
699
700         return 0;
701 }
702
703 extern "C" int PKGMGR_MDPARSER_PLUGIN_UNINSTALL(const char *pkgId, const char *appId, GList *list)
704 {
705         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_UNINSTALL =====]");
706         _INFO("PackageID : %s", pkgId);
707
708         // Can be multiple apps in one package
709         if (tacPluginInstalled) {
710                 _INFO("TAC plugin already uninstalled");
711                 return 0;
712         }
713         tacPluginInstalled = true;
714
715         status = "uninstall";
716         tac_db = openDB(TAC_APP_LIST_DB);
717         if (!tac_db) {
718                 _ERR("Sqlite open error. So restore the database.");
719                 if (tac_restoreDB() != TAC_ERROR_NONE) {
720                         _ERR("Sqlite open error");
721                         return -1;
722                 }
723                 tac_db = openDB(TAC_APP_LIST_DB);
724                 if (!tac_db) {
725                         _ERR("Sqlite open error");
726                         return -1;
727                 }
728         }
729         sqlite3_exec(tac_db, "BEGIN;", NULL, NULL, NULL);
730
731         char *sql = sqlite3_mprintf("SELECT * FROM TAC WHERE PKGID = %Q;", pkgId);
732         updateTac = selectDB(tac_db, sql);
733         sqlite3_free(sql);
734
735         sql = sqlite3_mprintf("DELETE FROM TAC WHERE PKGID = %Q;", pkgId);
736
737         if (!deleteDB(tac_db, sql)) {
738                 _ERR("Sqlite delete error");
739                 sqlite3_free(sql);
740                 status = "restore";
741                 return -1;
742         }
743         sqlite3_free(sql);
744
745         tac_updateDB(tac_db);
746
747         ///// TLC /////
748         tlc_db = openDB(TLC_APP_LIST_DB);
749         if (!tlc_db) {
750                 _ERR("Sqlite open error. So restore the database.");
751                 if (tlc_restoreDB() != TAC_ERROR_NONE) {
752                         _ERR("Sqlite open error");
753                         return 0;
754                 }
755                 tlc_db = openDB(TLC_APP_LIST_DB);
756                 if (!tlc_db) {
757                         _ERR("Sqlite open error");
758                         return 0;
759                 }
760         }
761         sqlite3_exec(tlc_db, "BEGIN;", NULL, NULL, NULL);
762
763         sql = sqlite3_mprintf("SELECT * FROM TLC WHERE PKGID = %Q;", pkgId);
764         std::vector<std::string> updateTlc = selectDB(tlc_db, sql);
765         sqlite3_free(sql);
766
767         sql = sqlite3_mprintf("DELETE FROM TLC WHERE PKGID = %Q;", pkgId);
768         if (!deleteDB(tlc_db, sql)) {
769                 _ERR("Sqlite delete error");
770                 sqlite3_free(sql);
771                 return 0;
772         }
773         sqlite3_free(sql);
774
775         tlc_updateDB(tlc_db, updateTlc, concatPath(__DOTNET_DIR, TLC_LIBRARIES_DIR));
776
777         return 0;
778 }
779
780 extern "C" int PKGMGR_MDPARSER_PLUGIN_REMOVED(const char *pkgId, const char *appId, GList *list)
781 {
782         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_REMOVED =====]");
783         _INFO("PackageID : %s", pkgId);
784
785         status = "removed";
786
787         return PKGMGR_MDPARSER_PLUGIN_UPGRADE(pkgId, appId, list);
788 }
789
790 void undoStep(std::string tac)
791 {
792         std::string current_tac = concatPath(__DOTNET_DIR, tac.substr(0, tac.find('/')));
793         try {
794                 for (auto& bck : bf::recursive_directory_iterator(current_tac)) {
795                         std::string bck_path = bck.path().string();
796                         if (bf::is_directory(bck_path) && strstr(bck_path.c_str(), ".bck") != NULL) {
797                                 if (!moveFile(bck_path, bck_path.substr(0, bck_path.rfind(".bck")))) {
798                                         _ERR("Failed to move %s", bck_path.c_str());
799                                 }
800                                 break;
801                         }
802                 }
803         } catch (const bf::filesystem_error& error) {
804                 _ERR("Failed to recursive directory: %s", error.what());
805                 return;
806         }
807
808         ///// TLC /////
809         auto convert = [](const std::string& path, const std::string& filename) {
810                 if (filename.rfind(".bck") != std::string::npos) {
811                         if (!moveFile(path, replaceAll(path, ".bck", ""))) {
812                                 _ERR("Failed to move %s", path.c_str());
813                         }
814                 }
815         };
816
817         scanFilesInDirectory(concatPath(__DOTNET_DIR, TLC_LIBRARIES_DIR), convert, 0);
818 }
819
820 void install_Undo()
821 {
822         for (auto& cd : createDirectories) {
823                 if (!removeAll(cd)) {
824                         _ERR("Failed to remove of %s", cd.c_str());
825                 }
826         }
827
828         for (auto& cl : createLibraries) {
829                 if (!removeFile(cl)) {
830                         _ERR("Failed to remove of %s", cl.c_str());
831                 }
832         }
833 }
834
835 void unInstall_Undo()
836 {
837         for (auto& unp : updateTac) {
838                 undoStep(unp);
839         }
840 }
841
842 void update_Undo()
843 {
844         install_Undo();
845         if (!tacDB.empty()) {
846                 for (auto& np : tacDB) {
847                         undoStep(np);
848                 }
849         }
850         unInstall_Undo();
851 }
852
853 extern "C" int PKGMGR_MDPARSER_PLUGIN_UNDO(const char *pkgId, const char *appId, GList *list)
854 {
855         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_UNDO =====]");
856         _INFO("PackageID : %s", pkgId);
857
858         // Can be multiple apps in one package
859         if (tacPluginFinished) {
860                 _INFO("TAC plugin already finished(UNDO)");
861                 return 0;
862         }
863         tacPluginFinished = true;
864
865         if (!strcmp("install", status.c_str())) {
866                 install_Undo();
867         } else if (!strcmp("update", status.c_str())) {
868                 update_Undo();
869         } else if (!strcmp("uninstall", status.c_str())) {
870                 unInstall_Undo();
871         } else if (!strcmp("restore", status.c_str())) {
872                 update_Undo();
873         }
874
875         if (tac_db) {
876                 rollbackDB(tac_db);
877                 tac_db = NULL;
878         }
879
880         if (tlc_db) {
881                 rollbackDB(tlc_db);
882                 tlc_db = NULL;
883         }
884
885         return 0;
886 }
887
888 void cleanStep(std::string tac)
889 {
890         std::string current_tac = concatPath(__DOTNET_DIR, tac.substr(0, tac.find('/')));
891         try {
892                 for (auto& bck : bf::recursive_directory_iterator(current_tac)) {
893                         std::string bck_path = bck.path().string();
894                         if (bf::is_directory(bck_path) && strstr(bck_path.c_str(), ".bck") != NULL) {
895                                 if (!removeAll(bck_path)) {
896                                         _ERR("Failed to remove of %s", bck_path.c_str());
897                                 }
898                                 break;
899                         }
900                 }
901
902                 bool isExist = false;
903                 for (auto& bck : bf::recursive_directory_iterator(current_tac)) {
904                         std::string bck_path = bck.path().string();
905                         if (exist(bck_path) && bf::is_directory(bck_path) && strstr(bck_path.c_str(), ".bck") == NULL) {
906                                 isExist = true;
907                                 break;
908                         }
909                 }
910                 if (!isExist) {
911                         if (!removeAll(current_tac)) {
912                                 _ERR("Failed to remove of %s", current_tac.c_str());
913                         }
914                 }
915         } catch (const bf::filesystem_error& error) {
916                 _ERR("Failed to recursive directory: %s", error.what());
917                 return;
918         }
919
920         ///// TLC /////
921         auto convert = [](const std::string& path, const std::string& filename) {
922                 if (filename.rfind(".bck") != std::string::npos) {
923                         if (!removeFile(path)) {
924                                 _ERR("Failed to remove of %s", path.c_str());
925                         }
926                 }
927         };
928
929         scanFilesInDirectory(concatPath(__DOTNET_DIR, TLC_LIBRARIES_DIR), convert, 0);
930 }
931
932 void install_Clean()
933 {
934         return;
935 }
936
937 void unInstall_Clean()
938 {
939         for (auto& unp : updateTac) {
940                 cleanStep(unp);
941         }
942 }
943
944 void update_Clean()
945 {
946         if (!tacDB.empty()) {
947                 for (auto& np : tacDB) {
948                         cleanStep(np);
949                 }
950         }
951         unInstall_Clean();
952 }
953
954 extern "C" int PKGMGR_MDPARSER_PLUGIN_CLEAN(const char *pkgId, const char *appId, GList *list)
955 {
956         if (!strcmp("restore", status.c_str())) {
957                 disableTACPackage(pkgId);
958
959                 std::string rootPath = getRootPath(std::string(pkgId));
960                 if (!rootPath.empty()) {
961                         std::string binPath = concatPath(rootPath, "bin");
962                         removeAll(concatPath(binPath, TAC_SYMLINK_SUB_DIR));
963                 }
964
965                 std::string runtimesDir = concatPath(rootPath, "bin/runtimes");
966                 if (exist(runtimesDir)) {
967                         char buffer[128];
968                         sprintf(buffer, "(tizen|linux|unix|base|any)(.\\d.\\d.\\d)?(-%s)?", ARCHITECTURE_IDENTIFIER);
969                         std::regex pattern(buffer);
970
971                         try {
972                                 for (auto& path : bf::recursive_directory_iterator(runtimesDir)) {
973                                         std::string symPath = path.path().string();
974                                         if (isDirectory(symPath) || !bf::is_symlink(symPath))
975                                                 continue;
976                                         std::string targetDir = symPath.substr(symPath.rfind("/runtimes/") + 10);
977                                         if (!std::regex_match(targetDir.substr(0, targetDir.find('/')), pattern))
978                                                 continue;
979                                         if (symPath.rfind(".so") == std::string::npos)
980                                                 continue;
981                                         copyFile(bf::read_symlink(symPath).string(), symPath);
982                                 }
983                         } catch (const bf::filesystem_error& error) {
984                                 _ERR("Failed to recursive directory: %s", error.what());
985                         }
986                 }
987
988                 return PKGMGR_MDPARSER_PLUGIN_UNDO(pkgId, appId, list);
989         }
990
991         _DBG("[===== PKGMGR_MDPARSER_PLUGIN_CLEAN =====]");
992         _INFO("PackageID : %s", pkgId);
993
994         // Can be multiple apps in one package
995         if (tacPluginFinished) {
996                 _INFO("TAC plugin already finished(CLEAN)");
997                 return 0;
998         }
999         tacPluginFinished = true;
1000
1001         if (!strcmp("install", status.c_str())) {
1002                 install_Clean();
1003         } else if (!strcmp("update", status.c_str())) {
1004                 update_Clean();
1005         } else if (!strcmp("uninstall", status.c_str())) {
1006                 unInstall_Clean();
1007         }
1008
1009         if (tac_db) {
1010                 closeDB(tac_db);
1011                 tac_db = NULL;
1012         }
1013
1014         if (tlc_db) {
1015                 closeDB(tlc_db);
1016                 tlc_db = NULL;
1017         }
1018
1019         return 0;
1020 }