blob: 5de82706048c21e11b5f5060af2282f95b3b9c3c [file] [log] [blame]
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#if USE_SYSLOG
#include <pwd.h>
#include <sys/types.h>
#include <syslog.h>
#endif
#include <sysutils/SocketClient.h>
#include <bootloader.h>
#include "command_listener.h"
#include "sync_fts_prop.h"
#include "watchdog_service.h"
// Reboot command format
// "reboot now"
// "reboot set FDR"
// "reboot set OTA"
// "reboot clear OTA"
//
// Reboot response format
// success: "000 Success"
// failure: "xxx Failure yyyy"
// xxx is 3-digit error code (255 = -1)
// yyyy is error string generated by strerror().
namespace {
class RebootServiceCommand : public FrameworkCommand {
public:
explicit RebootServiceCommand(const char *cmd);
virtual ~RebootServiceCommand() {}
virtual int runCommand(SocketClient *c, int argc, char ** argv);
private:
int sendResponse(SocketClient *cli, int ret) const;
#if USE_SYSLOG
void printCommand(SocketClient *cli, int argc, char *argv[]) const;
#endif
int rebootNow(SocketClient *cli);
int doRecovery(const char *cmd1, const char *cmd2);
int writeRecoveryParameter() const;
bool rebooting_;
bool set_fdr_for_next_reboot_;
bool set_ota_for_next_reboot_;
};
// command format and command file for /cache/recovery
const char OTA_PARAMETER[] = "--update_package=/cache/ota.zip";
const char FDR_PARAMETER[] = "--wipe_data";
const char OTA_FDR_PARAMETER[] = "--update_package=/cache/ota.zip\n--wipe_data";
}
// stub function to suppress compilation error. It should never be called.
// librecovery calls set_bootloader_message_default(), which is defined in
// bootable/recovery/bootlaoder.c. It's a fallback implementation when fts
// is not available.
extern "C" int set_bootloader_message_default(
const struct bootloader_message* /* in */) {
#if USE_SYSLOG
syslog(LOG_ERR, "should use fts and never come here");
#endif
// Some system doens't support fts, but that is OK.
return 0;
}
// commandlistener to socket /dev/socket/watchdog
CommandListener::CommandListener() :
FrameworkListener("watchdog") {
registerCmd(new RebootServiceCommand("reboot"));
}
RebootServiceCommand::RebootServiceCommand(const char *cmd)
: FrameworkCommand(cmd),
rebooting_(false),
set_fdr_for_next_reboot_(false),
set_ota_for_next_reboot_(false) {
}
int RebootServiceCommand::runCommand(SocketClient *cli,
int argc, char **argv) {
int ret;
#if USE_SYSLOG
printCommand(cli, argc, argv);
#endif
if (argc < 2 || argc > 3) {
sendResponse(cli, -EINVAL);
return 0;
}
if (rebooting_) {
syslog(LOG_INFO, "already rebooting");
sendResponse(cli, 0);
return 0;
}
if (argc == 2) {
// "reboot now"
if (!strcmp("now", argv[1])) {
return rebootNow(cli);
} else {
return sendResponse(cli, -EINVAL);
}
} else if (argc == 3) {
// "reboot set FDR"
// "reboot set OTA"
// "reboot clear OTA"
if ((!strcmp("set", argv[1]) &&
(!strcmp("FDR", argv[2]) || !strcmp("OTA", argv[2]))) ||
(!strcmp("clear", argv[1]) && !strcmp("OTA", argv[2]))) {
ret = doRecovery(argv[1], argv[2]);
return sendResponse(cli, ret);
} else {
return sendResponse(cli, -EINVAL);
}
} else {
return sendResponse(cli, -EINVAL);
}
}
int RebootServiceCommand::sendResponse(SocketClient *cli, int ret) const {
if (ret) {
if (cli->sendMsg(ret, "Failure", true)) {
return -1;
}
} else {
if (cli->sendMsg(ret, "Success", false)) {
return -1;
}
}
return 0;
}
#if USE_SYSLOG
void RebootServiceCommand::printCommand(SocketClient *cli, int argc,
char *argv[]) const {
int i;
pid_t pid = cli->getPid();
uid_t uid = cli->getUid();
struct passwd *pwd = getpwuid(uid);
syslog(LOG_INFO, "received command from process %d, uid %d %s.",
pid, uid, pwd ? pwd->pw_name : "unknown");
for (i = 0; i < argc; ++i) {
syslog(LOG_INFO, " %s", argv[i]);
}
}
#endif
int RebootServiceCommand::rebootNow(SocketClient *cli) {
// reboot is guaranteed to succeed.
// send response first.
int ret = sendResponse(cli, 0);
ret = reboot_service_request();
rebooting_ = true;
return ret;
}
// handler for "reboot set FDR" and "reboot set OTA" command.
// both boots in to recovery mode next time.
int RebootServiceCommand::doRecovery(const char *cmd1, const char *cmd2) {
if (!cmd2 || !cmd1) {
#if USE_SYSLOG
syslog(LOG_ERR, "doRecovery cmd is NULL");
#endif
return -1;
}
#if USE_SYSLOG
syslog(LOG_INFO, "reboot service: recovery cmd is %s %s", cmd1, cmd2);
#endif
if (!strcmp("set", cmd1)) {
if (!strcmp("FDR", cmd2)) {
set_fdr_for_next_reboot_ = true;
increment_fdr_count();
} else if (!strcmp("OTA", cmd2)) {
set_ota_for_next_reboot_ = true;
} else {
return -1;
}
} else if (!strcmp("clear", cmd1) && !strcmp("OTA", cmd2)) {
set_ota_for_next_reboot_ = false;
}
if (writeRecoveryParameter()) {
return -1;
}
return 0;
}
// write boot reocvery parameters to bootloader
int RebootServiceCommand::writeRecoveryParameter() const {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
const char* param;
#ifdef AB_OTA_UPDATER
if (set_fdr_for_next_reboot_) {
param = FDR_PARAMETER;
} else if (set_ota_for_next_reboot_) {
// We dont need to set anything for AB based products
return 0;
} else {
return -1;
}
#else
if (set_fdr_for_next_reboot_ && set_ota_for_next_reboot_) {
param = OTA_FDR_PARAMETER;
} else if (set_fdr_for_next_reboot_ && !set_ota_for_next_reboot_) {
param = FDR_PARAMETER;
} else if (set_ota_for_next_reboot_ && !set_fdr_for_next_reboot_) {
param = OTA_PARAMETER;
} else {
param = "";
}
#endif
if (strcmp(param, "")) {
strncpy(boot.command, "boot-recovery", sizeof(boot.command) - 1);
snprintf(boot.recovery, sizeof(boot.recovery) - 1, "recovery\n%s\n", param);
}
return set_bootloader_message(&boot);
}