blob: 8018cbdbff013e997673da742260574db791299e [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 "command_handler.h"
#include <ctype.h> // isdigit()
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include "general_installer.h"
#include "diskimage_installer.h"
using std::cerr;
using std::endl;
using std::string;
using std::stringstream;
namespace chromeos_memento_updater {
const string CommandHandler::kOptionList[] = {
"reset_device",
"partition",
"factory",
"release",
"stateful",
"oem",
"efi",
"firmware",
"activate"
};
const int CommandHandler::kOptions = 9;
CommandHandler::CommandHandler(int argc, char** argv) {
installer_ptr_ = NULL;
have_release_ = false;
have_factory_ = false;
SetRootDev();
ParseCommandLine(argc, argv);
}
CommandHandler::~CommandHandler() {
if (installer_ptr_ != NULL) {
delete installer_ptr_;
}
}
void CommandHandler::Usage() {
cerr << "memento_update --to <dest-device>" << endl <<
" --from <type>:<location>" << endl <<
" --contents <content:option, ...>" << endl <<
" --board <board>" << endl;
cerr << endl;
cerr << "Please check README for more information about Usage." << endl;
exit(0);
}
void CommandHandler::SetRootDev() {
FILE* fp = popen("rootdev -s", "r");
if (fp == NULL) {
cerr << "ERROR: Cannot run rootdev." << endl;
exit(1);
}
int kDevLength = 100;
char buffer[kDevLength];
buffer[0] = '\0';
fgets(buffer, kDevLength, fp);
if (buffer[0] == '\0') {
// Empty string
cerr << "ERROR: Run rootdev failed" << endl;
exit(1);
}
root_device_.assign(buffer);
}
string CommandHandler::GetOutputDevicePartition(PartitionNum partition) {
stringstream device;
device << output_device_;
// Ends with digit, add 'p'
if (isdigit(output_device_[output_device_.size() - 1])) {
device << 'p';
}
// Check if boot and install on the same device
if (root_device_.find(output_device_) != string::npos) {
// It is not possible to boot and install both image on the same device.
if (have_release_ && have_factory_ ) {
cerr << "ERROR: Not able to install both release and factory when boot"
"on output device." << endl;
exit(1);
}
switch (partition) {
case kFactoryPartition:
case kReleasePartition:
if (root_device_.find("3") != string::npos) {
device << '5';
} else {
device << '3';
}
break;
case kFactoryKernelPartition:
case kReleaseKernelPartition:
if (root_device_.find("3") != string::npos) {
device << '4';
} else {
device << '2';
}
break;
default:
device << partition;
}
} else {
device << partition;
}
return device.str();
}
void CommandHandler::ParseInputDevice(const string& input_string,
const string& running_dir) {
// TODO(chunyen): find a solution to download from http://chromeos-image
// TODO(chunyen): Fill in the code blocks after the corresponding handler
// are written.
if (input_string.find("image:") == 0) {
input_device_ = input_string.substr(strlen("image:"));
DiskImageInstallFormat format = kImage;
const string kZipSuffix = ".zip";
// Check if path ends with .zip
if (input_device_.find(kZipSuffix) ==
input_device_.length() - kZipSuffix.length()) {
format = kZip;
}
installer_ptr_ = new DiskImageInstaller(running_dir, board_,
input_device_, format);
// TODO(chunyen):
// } else if (input_string.find("shopfloor:") == 0) {
// } else if (input_string.find("miniomaha:") == 0) {
// } else if (input_string.find("recovery:") == 0){
} else {
cerr << "Unrecognized source media." << endl;
Usage();
}
}
void CommandHandler::ParseContent(const string& content_string) {
std::istringstream content(content_string);
string token;
// Create a map that map option string to enum
std::map<string, InstallOption> string_to_enum;
for (int i = 0; i < kOptions; i++) {
string_to_enum[kOptionList[i]] = static_cast<InstallOption>(i);
}
while (std::getline(content, token, ',')) {
InstallOption option_id;
string option_string;
string key;
size_t colon;
if ((colon = token.find(":")) != string::npos) {
key = token.substr(0, colon);
option_string.assign(token.substr(colon + 1));
} else {
key = token;
}
std::map<string, InstallOption>::iterator it = string_to_enum.find(key);
if (it == string_to_enum.end()) {
cerr << "Cannot find matching option: " << token << endl;
Usage();
} else {
option_id = it->second;
}
if (option_id == kRelease) {
have_release_ = true;
}
if (option_id == kFactory) {
have_factory_ = true;
}
option_list_.push_back(make_pair(option_id, option_string));
}
}
void CommandHandler::ParseCommandLine(int argc, char** argv) {
string input_string;
string content_string;
int option_index = 0;
char c;
static struct option long_options[] = {
{"to", required_argument, 0, 't'},
{"from", required_argument, 0, 'f'},
{"contents", required_argument, 0, 'c'},
{"board", required_argument, 0, 'b'}
};
while ((c = getopt_long(argc, argv, "t:f:c:b:",
long_options, &option_index)) != -1) {
switch(c) {
case 't':
output_device_.assign(optarg);
break;
case 'f':
input_string.assign(optarg);
break;
case 'c':
content_string.assign(optarg);
break;
case 'b':
board_.assign(optarg);
break;
case '?':
Usage();
break;
default:
cerr << "Unexpected return value " << c << " from getopt." << endl;
exit(1);
break;
}
}
if (output_device_.empty() || input_string.empty() ||
content_string.empty()) {
Usage();
}
// TODO(chunyen): Auto detect board?
if (board_.empty()) {
cerr << "Auto detect board is not supported yet.";
Usage();
}
// Find the running directory from argv[0]
string program_name(argv[0]);
string home_dir(".");
size_t pos;
if ((pos = program_name.find_last_of('/')) != string::npos) {
home_dir.assign(program_name.substr(0, program_name.length() - pos));
}
cerr << home_dir << endl;
ParseInputDevice(input_string, home_dir);
ParseContent(content_string);
}
} // namespace chromeos_memento_updater