blob: e173114ad74621c97b929d801cbb46871d59dbf0 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lorgnette/manager.h"
#include <signal.h>
#include <stdint.h>
#include <unistd.h>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/stl_util.h>
#include <base/strings/string_split.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
#include <chromeos/process.h>
#include <chromeos/type_name_undecorate.h>
#include <chromeos/variant_dictionary.h>
#include "lorgnette/daemon.h"
using base::ScopedFD;
using base::StringPrintf;
using std::map;
using std::string;
using std::vector;
namespace lorgnette {
// static
const char Manager::kScanConverterPath[] = "/usr/bin/pnm2png";
const char Manager::kScanImageFormattedDeviceListCmd[] =
"--formatted-device-list=%d%%%v%%%m%%%t%n";
const char Manager::kScanImagePath[] = "/usr/bin/scanimage";
const int Manager::kTimeoutAfterKillSeconds = 1;
Manager::Manager(base::Callback<void()> activity_callback)
: org::chromium::lorgnette::ManagerAdaptor(this),
activity_callback_(activity_callback) {}
Manager::~Manager() {}
void Manager::RegisterAsync(
chromeos::dbus_utils::ExportedObjectManager* object_manager,
chromeos::dbus_utils::AsyncEventSequencer* sequencer) {
CHECK(!dbus_object_) << "Already registered";
dbus_object_.reset(new chromeos::dbus_utils::DBusObject(
object_manager,
object_manager ? object_manager->GetBus() : nullptr,
dbus::ObjectPath(kManagerServicePath)));
RegisterWithDBusObject(dbus_object_.get());
dbus_object_->RegisterAsync(
sequencer->GetHandler("Manager.RegisterAsync() failed.", true));
}
bool Manager::ListScanners(chromeos::ErrorPtr *error,
Manager::ScannerInfo* scanner_list) {
base::FilePath output_path;
FILE *output_file_handle;
output_file_handle = base::CreateAndOpenTemporaryFile(&output_path);
if (!output_file_handle) {
SetError(__func__, "Unable to create temporary file.", error);
return false;
}
chromeos::ProcessImpl process;
RunListScannersProcess(fileno(output_file_handle), &process);
fclose(output_file_handle);
string scanner_output_string;
bool read_status = base::ReadFileToString(output_path,
&scanner_output_string);
const bool recursive_delete = false;
base::DeleteFile(output_path, recursive_delete);
if (!read_status) {
SetError(__func__, "Unable to read scanner list output file", error);
return false;
}
activity_callback_.Run();
*scanner_list = ScannerInfoFromString(scanner_output_string);
return true;
}
bool Manager::ScanImage(
chromeos::ErrorPtr *error,
const string &device_name,
const dbus::FileDescriptor &outfd,
const chromeos::VariantDictionary &scan_properties) {
int pipe_fds[2];
if (pipe(pipe_fds) != 0) {
SetError(__func__, "Unable to create process pipe", error);
return false;
}
ScopedFD pipe_fd_input(pipe_fds[0]);
ScopedFD pipe_fd_output(pipe_fds[1]);
chromeos::ProcessImpl scan_process;
chromeos::ProcessImpl convert_process;
// Since the FileDescriptor object retains ownership of this file descriptor,
// make a local copy.
int out_fd = dup(outfd.value());
RunScanImageProcess(device_name,
out_fd,
&pipe_fd_input,
&pipe_fd_output,
scan_properties,
&scan_process,
&convert_process,
error);
activity_callback_.Run();
return true;
}
// static
void Manager::RunListScannersProcess(int fd, chromeos::Process *process) {
process->AddArg(kScanImagePath);
process->AddArg(kScanImageFormattedDeviceListCmd);
process->BindFd(fd, STDOUT_FILENO);
process->Run();
}
// static
void Manager::RunScanImageProcess(
const string &device_name,
int out_fd,
ScopedFD *pipe_fd_input,
ScopedFD *pipe_fd_output,
const chromeos::VariantDictionary &scan_properties,
chromeos::Process *scan_process,
chromeos::Process *convert_process,
chromeos::ErrorPtr *error) {
scan_process->AddArg(kScanImagePath);
scan_process->AddArg("-d");
scan_process->AddArg(device_name);
for (const auto &property : scan_properties) {
const string &property_name = property.first;
const auto &property_value = property.second;
if (property_name == kScanPropertyMode &&
property_value.IsTypeCompatible<string>()) {
string mode = property_value.Get<string>();
if (mode != kScanPropertyModeColor &&
mode != kScanPropertyModeGray &&
mode != kScanPropertyModeLineart) {
SetError(__func__,
StringPrintf("Invalid mode parameter %s", mode.c_str()),
error);
return;
}
scan_process->AddArg("--mode");
scan_process->AddArg(mode);
} else if (property_name == kScanPropertyResolution &&
property_value.IsTypeCompatible<uint32_t>()) {
scan_process->AddArg("--resolution");
scan_process->AddArg(base::UintToString(
property_value.Get<unsigned int>()));
} else {
SetError(__func__,
StringPrintf("Invalid scan parameter %s of type %s",
property_name.c_str(),
chromeos::UndecorateTypeName(
property_value.GetType().name()).c_str()),
error);
return;
}
}
scan_process->BindFd(pipe_fd_output->release(), STDOUT_FILENO);
convert_process->AddArg(kScanConverterPath);
convert_process->BindFd(pipe_fd_input->release(), STDIN_FILENO);
convert_process->BindFd(out_fd, STDOUT_FILENO);
convert_process->Start();
scan_process->Start();
int scan_result = scan_process->Wait();
if (scan_result != 0) {
SetError(__func__,
StringPrintf("Scan process exited with result %d", scan_result),
error);
// Explicitly kill and reap the converter since we may fail to successfully
// reap the processes as it exits this scope.
convert_process->Kill(SIGKILL, kTimeoutAfterKillSeconds);
return;
}
int converter_result = convert_process->Wait();
if (converter_result != 0) {
SetError(__func__,
StringPrintf("Image converter process failed with result %d",
converter_result),
error);
return;
}
LOG(INFO) << __func__ << ": completed image scan and conversion.";
}
// static
Manager::ScannerInfo Manager::ScannerInfoFromString(
const string &scanner_info_string) {
vector<string> scanner_output_lines;
base::SplitString(scanner_info_string, '\n', &scanner_output_lines);
ScannerInfo scanners;
for (const auto &line : scanner_output_lines) {
vector<string> scanner_info_parts;
base::SplitString(line, '%', &scanner_info_parts);
if (scanner_info_parts.size() != 4) {
continue;
}
map<string, string> scanner_info;
scanner_info[kScannerPropertyManufacturer] = scanner_info_parts[1];
scanner_info[kScannerPropertyModel] = scanner_info_parts[2];
scanner_info[kScannerPropertyType] = scanner_info_parts[3];
scanners[scanner_info_parts[0]] = scanner_info;
}
return scanners;
}
// static
void Manager::SetError(const string &method,
const string &message,
chromeos::ErrorPtr *error) {
chromeos::Error::AddTo(
error, chromeos::errors::dbus::kDomain, kManagerServiceError, message);
LOG(ERROR) << method << ": " << message;
}
} // namespace lorgnette