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