| // 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) { |
| chromeos::Error::AddTo(error, FROM_HERE, |
| chromeos::errors::dbus::kDomain, |
| kManagerServiceError, |
| "Unable to create temporary file."); |
| 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) { |
| chromeos::Error::AddTo(error, FROM_HERE, |
| chromeos::errors::dbus::kDomain, |
| kManagerServiceError, |
| "Unable to read scanner list output file"); |
| 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) { |
| chromeos::Error::AddTo(error, FROM_HERE, |
| chromeos::errors::dbus::kDomain, |
| kManagerServiceError, |
| "Unable to create process pipe"); |
| 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) { |
| chromeos::Error::AddToPrintf(error, FROM_HERE, |
| chromeos::errors::dbus::kDomain, |
| kManagerServiceError, |
| "Invalid mode parameter %s", |
| mode.c_str()); |
| 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 { |
| chromeos::Error::AddToPrintf( |
| error, FROM_HERE, |
| chromeos::errors::dbus::kDomain, kManagerServiceError, |
| "Invalid scan parameter %s of type %s", |
| property_name.c_str(), |
| chromeos::UndecorateTypeName( |
| property_value.GetType().name()).c_str()); |
| 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) { |
| chromeos::Error::AddToPrintf(error, FROM_HERE, |
| chromeos::errors::dbus::kDomain, |
| kManagerServiceError, |
| "Scan process exited with result %d", |
| scan_result); |
| // 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) { |
| chromeos::Error::AddToPrintf( |
| error, FROM_HERE, chromeos::errors::dbus::kDomain, kManagerServiceError, |
| "Image converter process failed with result %d", converter_result); |
| 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; |
| } |
| |
| } // namespace lorgnette |