Remove boost dependency
[platform/core/appfw/app-installers.git] / src / pkg_recovery / pkg_recovery.cc
1 // Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
4
5 #include <glib.h>
6 #include <common/utils/file_util.h>
7 #include <common/utils/request.h>
8 #include <common/utils/subprocess.h>
9 #include <common/utils/user_util.h>
10 #include <manifest_parser/utils/logging.h>
11 #include <sys/types.h>
12 #include <tzplatform_config.h>
13
14 #include <array>
15 #include <exception>
16 #include <filesystem>
17 #include <regex>
18 #include <string>
19 #include <vector>
20
21 namespace ci = common_installer;
22 namespace fs = std::filesystem;
23
24 namespace {
25
26 typedef std::pair<std::string, std::string> RecoverEntry;
27
28 const char kRecoveryFilePattern[] = "^(.*)-recovery-(.){6}$";
29 const char kBackupFilePattern[] = "^(.*)-recovery-(.){6}\\.bck$";
30 const uid_t kGlobalUserUid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
31
32 std::string TruncateNewLine(const char* data) {
33   int length = strlen(data);
34   if (data[length - 1] == '\n')
35       --length;
36   return std::string(data, length);
37 }
38
39 std::vector<std::string> ParseRecoveryFile(const char* file) {
40   FILE* handle = fopen(file, "r");
41   if (!handle) {
42     LOG(ERROR) << "Failed to open recovery file :" << file;
43     return {};
44   }
45
46   std::vector<std::string> arguments;
47   std::array<char, 200> data;
48   data[0] = '\0';
49   while (fgets(data.data(), data.size(), handle)) {
50     std::string line_data = TruncateNewLine(data.data());
51     if (line_data == "cleanup") {
52       arguments.emplace_back("--recovery-cleanup");
53       break;
54     }
55
56     if (!std::filesystem::exists(line_data))
57       continue;
58
59     arguments.emplace_back(line_data);
60   }
61   fclose(handle);
62
63   return arguments;
64 }
65
66 class PkgRecoveryService {
67  public:
68   PkgRecoveryService();
69   ~PkgRecoveryService();
70   void Run();
71
72  private:
73   void SearchBackupFiles(uid_t uid);
74   std::vector<RecoverEntry> SearchRecoveryFiles(uid_t uid);
75   void ProcessRecovery(uid_t uid, const std::vector<RecoverEntry>& entries);
76   bool RunBackend(uid_t uid, const char* type, const char* file);
77 };
78
79 PkgRecoveryService::PkgRecoveryService() {
80 }
81
82 PkgRecoveryService::~PkgRecoveryService() {
83 }
84
85 bool PkgRecoveryService::RunBackend(uid_t uid, const char* type,
86     const char* file) {
87   std::string backend_cmd = "/usr/bin/" + std::string(type) + "-backend";
88   ci::Subprocess backend(backend_cmd);
89   std::string str_uid = std::to_string(uid);
90   if (std::string(type) == "unified") {
91     auto arguments = ParseRecoveryFile(file);
92     if (!arguments.size())
93       return ci::Remove(fs::path(file));
94
95     arguments.emplace(arguments.begin(), "-b");
96     arguments.emplace_back("-u");
97     arguments.emplace_back(str_uid.c_str());
98
99     backend.RunWithArgs(arguments);
100   } else {
101     backend.Run("-b", file, "-u", str_uid.c_str());
102   }
103   int status = backend.Wait();
104   if (WIFSIGNALED(status) || WEXITSTATUS(status))
105     return false;
106
107   ci::Remove(fs::path(file));
108
109   return true;
110 }
111
112 void PkgRecoveryService::Run() {
113   // recover global packages
114   SearchBackupFiles(kGlobalUserUid);
115   LOG(INFO) << "Searching recovery files for user " << kGlobalUserUid;
116   std::vector<RecoverEntry> globalentries = SearchRecoveryFiles(kGlobalUserUid);
117   ProcessRecovery(kGlobalUserUid, globalentries);
118
119   // recover normal user packages
120   ci::UserList list = ci::GetUserList();
121   for (const auto& userinfo : list) {
122     uid_t uid = std::get<0>(userinfo);
123     LOG(INFO) << "Searching recovery files for user " << std::get<0>(userinfo);
124     SearchBackupFiles(uid);
125     std::vector<RecoverEntry> entries = SearchRecoveryFiles(uid);
126     ProcessRecovery(uid, entries);
127   }
128 }
129
130 void PkgRecoveryService::SearchBackupFiles(uid_t uid) {
131   const fs::path recovery_dir = ci::GetRootAppPath(false, uid);
132   try {
133     for (fs::directory_iterator iter(recovery_dir);
134         iter != fs::directory_iterator();
135         ++iter) {
136       std::string file = iter->path().filename().string();
137       std::regex backup_regex(kBackupFilePattern);
138       std::smatch match;
139       if (std::regex_search(file, match, backup_regex)) {
140         fs::path orig_file(iter->path().parent_path() / iter->path().stem());
141         if (fs::exists(orig_file))
142           fs::remove(orig_file);
143         fs::rename(iter->path(), orig_file);
144       }
145     }
146   } catch (const std::exception& e) {
147     LOG(WARNING) << "Exception occurred: "
148         << typeid(e).name() << ", " << e.what();
149   }
150 }
151
152 std::vector<RecoverEntry> PkgRecoveryService::SearchRecoveryFiles(uid_t uid) {
153   std::vector<RecoverEntry> list;
154   const fs::path recovery_dir = ci::GetRootAppPath(false, uid);
155   LOG(INFO) << "RootAppPath: " << recovery_dir;
156   for (fs::directory_iterator iter(recovery_dir);
157       iter != fs::directory_iterator();
158       ++iter) {
159     try {
160       std::string file = iter->path().filename().string();
161       std::regex recovery_regex(kRecoveryFilePattern);
162       std::smatch match;
163       if (std::regex_search(file, match, recovery_regex)) {
164         LOG(INFO) << "Found recovery file: " << file;
165         std::string type(match[1]);
166         if (type == "unified")
167           list.emplace(list.begin(), type, iter->path().string());
168         else
169           list.emplace_back(type, iter->path().string());
170       }
171     } catch (const std::exception& e) {
172       LOG(WARNING) << "Exception occurred: "
173           << typeid(e).name() << ", " << e.what();
174       continue;
175     }
176   }
177
178   return list;
179 }
180
181 void PkgRecoveryService::ProcessRecovery(uid_t uid,
182     const std::vector<RecoverEntry>& entries) {
183   LOG(INFO) << "Process recovery for user " << uid;
184   for (const auto& entry : entries) {
185     const char* type = entry.first.c_str();
186     const char* file = entry.second.c_str();
187     if (!fs::exists(file))
188       continue;
189
190     if (!RunBackend(uid, type, file))
191       LOG(ERROR) << "Recovery process for " << file << " failed";
192   }
193 }
194
195 }  // namespace
196
197 int main() {
198   try {
199     PkgRecoveryService service;
200     service.Run();
201     return 0;
202   } catch(...) {
203     LOG(ERROR) << "Exception occured";
204     return -1;
205   }
206 }