blob: 1146c317be110b3ba37806aeb4f86e457d5de987 [file]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "tools/base/deploy/installer/dump.h"
#include <iostream>
#include <dirent.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "tools/base/deploy/common/event.h"
#include "tools/base/deploy/common/log.h"
#include "tools/base/deploy/common/utils.h"
#include "tools/base/deploy/installer/apk_archive.h"
#include "tools/base/deploy/installer/command_cmd.h"
#include "tools/base/deploy/installer/executor/executor.h"
#include "tools/base/deploy/installer/executor/runas_executor.h"
#include "tools/base/deploy/proto/deploy.pb.h"
namespace deploy {
void DumpCommand::ParseParameters(int argc, char** argv) {
if (argc < 1) {
return;
}
for (int i = 0; i < argc; ++i) {
package_names_.emplace_back(argv[i]);
}
ready_to_run_ = true;
}
void DumpCommand::Run(proto::InstallerResponse* response) {
Phase p("Command Dump");
auto dump_response = response->mutable_dump_response();
for (const std::string& package_name : package_names_) {
proto::PackageDump* package_dump = dump_response->add_packages();
package_dump->set_name(package_name);
GetProcessIds(package_name, package_dump);
// TODO: Since dump performs multiple operations, this should not
// terminate the command. Processing should continue here.
if (!GetApks(package_name, package_dump)) {
dump_response->set_status(proto::DumpResponse::ERROR_PACKAGE_NOT_FOUND);
dump_response->set_failed_package(package_name);
return;
}
}
dump_response->set_status(proto::DumpResponse::OK);
}
bool DumpCommand::GetApks(const std::string& package_name,
proto::PackageDump* package_dump) {
// Retrieve apks for this package.
std::vector<std::string> apks_path;
std::string error_message;
CmdCommand cmd(workspace_);
if (!cmd.GetApks(package_name, &apks_path, &error_message)) {
ErrEvent("Could not find apks for this package: " + package_name);
ErrEvent("Error: " + error_message);
return false;
}
if (apks_path.size() == 0) {
ErrEvent("Could not find apks for package: " + package_name);
return false;
}
// Extract all apks.
for (std::string& apk_path : apks_path) {
Phase p2("processing APK");
ApkArchive archive(workspace_.GetRoot() + apk_path);
Dump dump = archive.ExtractMetadata();
proto::ApkDump* apk_dump = package_dump->add_apks();
apk_dump->set_absolute_path(apk_path);
if (dump.cd != nullptr || dump.signature != nullptr) {
std::string apk_file_name =
std::string(strrchr(apk_path.c_str(), '/') + 1);
apk_dump->set_name(apk_file_name);
}
if (dump.cd != nullptr) {
apk_dump->set_allocated_cd(dump.cd.release());
}
if (dump.signature != nullptr) {
apk_dump->set_allocated_signature(dump.signature.release());
}
}
return true;
}
bool DumpCommand::GetProcessIds(const std::string& package_name,
proto::PackageDump* package_dump) {
Phase p("get process ids");
std::string output;
std::string error;
RunasExecutor run_as(package_name, workspace_.GetExecutor());
if (!run_as.Run("id", {"-u"}, &output, &error)) {
ErrEvent("Could not get package user id: " + error);
return false;
}
int package_uid = strtol(output.c_str(), nullptr, 10);
if (package_uid < 0) {
ErrEvent("Could not parse package user id: " + output);
return false;
}
std::string proc_path = workspace_.GetRoot() + "/proc";
DIR* proc_dir = opendir(proc_path.c_str());
if (proc_dir == nullptr) {
ErrEvent("Could not open system proc directory: " + proc_path);
return false;
}
int zygote_pid = 0;
int zygote64_pid = 0;
std::vector<ProcStats> stats_list;
// Search the /proc directory for processes with a uid equal to the package
// uid, as well as for the zygote and zygote64 processes.
dirent* proc_entry;
while ((proc_entry = readdir(proc_dir)) != nullptr) {
// Skip entries that aren't integers.
int pid = strtol(proc_entry->d_name, nullptr, 10);
if (pid <= 0) {
continue;
}
// Try to parse this process. If we fail, just continue to the next one.
// Default constructor handles proper intialization of the stats struct.
ProcStats stats;
if (!ParseProc(proc_entry, &stats)) {
continue;
}
// We obtain the zygote pids to allow us to filter out any non-ART
// processes, as well as to determine whether each process is 32 or 64
// bit.
if (strcmp(stats.name, "zygote") == 0) {
zygote_pid = stats.pid;
} else if (strcmp(stats.name, "zygote64") == 0) {
zygote64_pid = stats.pid;
} else if (stats.uid == package_uid) {
stats_list.emplace_back(stats);
}
}
closedir(proc_dir);
// If we haven't found any zygote processes, we can't tell what is an ART
// process and what isn't, so we should exit early.
if (zygote_pid == 0 && zygote64_pid == 0) {
ErrEvent("Could not find a zygote process");
return false;
}
for (auto& stats : stats_list) {
// We assume an app can't mix 32-bit and 64-bit ART processes, so we just
// set this to the last architecture of the last zygote child we find.
if (stats.ppid == zygote_pid) {
package_dump->set_arch(proto::ARCH_32_BIT);
} else if (stats.ppid == zygote64_pid) {
package_dump->set_arch(proto::ARCH_64_BIT);
} else {
continue;
}
package_dump->add_processes(stats.pid);
}
return true;
}
bool DumpCommand::ParseProc(dirent* proc_entry, ProcStats* stats) {
std::string proc_path = workspace_.GetRoot() + "/proc/";
proc_path += proc_entry->d_name;
struct stat proc_dir_stat;
if (stat(proc_path.c_str(), &proc_dir_stat) < 0) {
return false;
}
// When testing the installer we do not use the real
// userid's on files, we mostly run all the same code
// but in this case, if the root file system is not the
// real one, we use a fake uid for the file.
if (workspace_.GetRoot().empty()) {
stats->uid = proc_dir_stat.st_uid;
} else {
FILE* uid = fopen((proc_path + "/.uid").c_str(), "r");
if (uid == nullptr) {
Log::E("Cannot fake-stat %s", proc_path.c_str());
return false;
}
fscanf(uid, "%d", &stats->uid);
fclose(uid);
}
std::string cmdline_path = proc_path + "/cmdline";
FILE* proc_cmdline = fopen(cmdline_path.c_str(), "r");
if (proc_cmdline == nullptr) {
return false;
}
// Read up to 15 characters + the null terminator. Processes may have an
// empty command line, so we don't need to check that this succeeds.
fscanf(proc_cmdline, "%15s", stats->name);
fclose(proc_cmdline);
std::string stat_path = proc_path + "/stat";
FILE* proc_stat = fopen(stat_path.c_str(), "r");
if (proc_stat == nullptr) {
return false;
}
// The format of this string is well-specified by man proc(5).
int parsed = fscanf(proc_stat, "%d %*s %*c %d", &stats->pid, &stats->ppid);
fclose(proc_stat);
if (parsed != 2) {
return false;
}
return true;
}
} // namespace deploy