common/recipes-core/power-util/files/power-util.c (583 lines of code) (raw):
/*
* power-util
*
* Copyright 2015-present Facebook. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <stdint.h>
#include <string.h>
#include <getopt.h>
#include <stdbool.h>
#include <fcntl.h>
#include <openbmc/kv.h>
#include <openbmc/pal.h>
#define POWER_ON_STR "on"
#define POWER_OFF_STR "off"
#define MAX_RETRIES 10
#ifndef PWR_OPTION_LIST
#define PWR_OPTION_LIST "status, graceful-shutdown, off, on, reset, cycle, " \
"12V-off, 12V-on, 12V-cycle"
#endif
#ifndef PWR_DEV_OPTION_LIST
#define PWR_DEV_OPTION_LIST "status, off, on, reset, cycle"
#endif
#define PWR_UTL_LOCK "/var/run/power-util_%d.lock"
#ifdef FRU_DEVICE_LIST
static const char * pal_dev_list_power = pal_dev_pwr_list;
static const char * dev_pwr_option_list = pal_dev_pwr_option_list;
#else
static const char * pal_dev_list_power = NULL;
static const char * dev_pwr_option_list = NULL;
#endif
const char *pwr_option_list = PWR_OPTION_LIST;
#ifdef ENABLE_FORCE_POWER_CMD
const char *force_pwr_option_list = "on, 12V-on";
#endif
enum {
PWR_STATUS = 1,
PWR_GRACEFUL_SHUTDOWN,
PWR_OFF,
PWR_ON,
PWR_RESET,
PWR_CYCLE,
PWR_12V_OFF,
PWR_12V_ON,
PWR_12V_CYCLE,
PWR_SLED_CYCLE
};
static const char *option_list[] = {
"NA", // place holder so the rest of the list matches enum above
"STATUS",
"GRACEFUL_SHUTDOWN",
"PWR_OFF",
"PWR_ON",
"POWER_RESET",
"POWER_CYCLE",
"12V_OFF",
"12V_ON",
"12V_CYCLE",
"SLED_CYCLE",
};
static void
print_usage() {
char fru_list[256] = {0};
pal_get_fru_list_by_caps(FRU_CAPABILITY_POWER_STATUS, fru_list, 256);
printf("Usage: power-util [ %s ] [ %s ]\nUsage: power-util sled-cycle\n",
fru_list, pwr_option_list);
// TODO Form list like fruid-util.
if (pal_dev_list_power != NULL && dev_pwr_option_list != NULL) {
if (!strncmp(pal_dev_list_power, "all, ", strlen("all, "))) {
pal_dev_list_power = pal_dev_list_power + strlen("all, ");
}
printf("Usage: power-util [ %s ] [ %s ] [ %s ]\n",
fru_list, pal_dev_list_power, dev_pwr_option_list);
}
#ifdef ENABLE_FORCE_POWER_CMD
printf("Usage: power-util [ %s ] --force [ %s ]\n",
fru_list, force_pwr_option_list);
#endif
}
static bool
is_power_cmd_valid(char *option)
{
char option_list[256];
char *pch;
/* All platforms support sled-cycle */
if (!strcmp(option, "sled-cycle")) {
return true;
}
/* strtok modifies the passed in string. We cannot
* pass a const string to it. So make a copy */
strcpy(option_list, pwr_option_list);
pch = strtok(option_list, ", ");
while (pch != NULL) {
if (!strcmp(pch, option)) {
return true;
}
pch = strtok(NULL, ", ");
}
return false;
}
static int
get_power_opt(char *option, uint8_t *opt) {
if (!is_power_cmd_valid(option)) {
return -1;
}
if (!strcmp(option, "status")) {
*opt = PWR_STATUS;
} else if (!strcmp(option, "graceful-shutdown")) {
*opt = PWR_GRACEFUL_SHUTDOWN;
} else if (!strcmp(option, "off")) {
*opt = PWR_OFF;
} else if (!strcmp(option, "on")) {
*opt = PWR_ON;
} else if (!strcmp(option, "reset")) {
*opt = PWR_RESET;
} else if (!strcmp(option, "cycle")) {
*opt = PWR_CYCLE;
} else if (!strcmp(option, "12V-off")) {
*opt = PWR_12V_OFF;
} else if (!strcmp(option, "12V-on")) {
*opt = PWR_12V_ON;
} else if (!strcmp(option, "12V-cycle")) {
*opt = PWR_12V_CYCLE;
} else if (!strcmp(option, "sled-cycle")) {
*opt = PWR_SLED_CYCLE;
} else {
return -1;
}
return 0;
}
//check power policy and power state to power on/off server after AC power restore
void
power_policy_control(uint8_t fru, char *last_ps, bool force) {
uint8_t chassis_status[5] = {0};
uint8_t chassis_status_length;
uint8_t power_policy = POWER_CFG_UKNOWN;
char pwr_state[MAX_VALUE_LEN] = {0};
if (pal_is_slot_server(fru) == 0) {
return;
}
if (force) { // ignore power policy, force power on
pal_set_server_power(fru, SERVER_FORCE_POWER_ON);
return;
}
//get power restore policy
//defined by IPMI Spec/Section 28.2.
pal_get_chassis_status(fru, NULL, chassis_status, &chassis_status_length);
//byte[1], bit[6:5]: power restore policy
power_policy = (*chassis_status >> 5);
//Check power policy and last power state
if(power_policy == POWER_CFG_LPS) {
if (!last_ps) {
pal_get_last_pwr_state(fru, pwr_state);
last_ps = pwr_state;
}
if (!(strcmp(last_ps, "on"))) {
sleep(3);
pal_set_server_power(fru, SERVER_POWER_ON);
}
}
else if(power_policy == POWER_CFG_ON) {
sleep(3);
pal_set_server_power(fru, SERVER_POWER_ON);
}
}
static int
dev_power_util(uint8_t fru, char *dev_name, uint8_t dev_id ,uint8_t opt) {
int ret = 0;
uint8_t status, type;
int retries;
uint8_t root = 0;
if (opt != PWR_STATUS) {
if (!pal_can_change_power(pal_get_root_fru(fru, &root) == PAL_EOK ? root : fru)) {
return -1;
}
}
switch(opt) {
case PWR_STATUS:
ret = pal_get_device_power(fru, dev_id, &status, &type);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_get_device_power failed for fru %u dev %s\n", fru, dev_name);
return ret;
}
printf("Power status for fru %u dev %s : %s\n", fru, dev_name, status?"ON":"OFF");
break;
case PWR_OFF:
printf("Powering fru %u dev %s to OFF state...\n", fru, dev_name);
ret = pal_set_device_power(fru, dev_id, SERVER_POWER_OFF);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_device_power failed for"
" fru %u", fru);
return ret;
} else if (ret == 1) {
printf("fru %u dev %s is already powered OFF...\n", fru, dev_name);
return 0;
} else {
syslog(LOG_CRIT, "SERVER_POWER_OFF successful for FRU: %d DEV: %s", fru, dev_name);
}
break;
case PWR_ON:
printf("Powering fru %u dev %s to ON state...\n", fru, dev_name);
ret = pal_set_device_power(fru, dev_id, SERVER_POWER_ON);
if (ret == 1) {
printf("fru %u dev %s is already powered ON...\n", fru, dev_name);
return 0;
} else if (ret == -2) { //check if fru is not ready
syslog(LOG_WARNING, "power_util: pal_set_device_power failed for"
" fru %u", fru);
return ret;
}
for (retries = 0; retries < MAX_RETRIES; retries++) {
sleep(3);
ret = pal_get_device_power(fru, dev_id, &status, &type);
if ((ret >= 0) && (status == SERVER_POWER_ON)) {
syslog(LOG_CRIT, "SERVER_POWER_ON successful for FRU: %u DEV: %s", fru, dev_name);
break;
}
ret = pal_set_device_power(fru, dev_id, SERVER_POWER_ON);
}
if (ret < 0 || status != SERVER_POWER_ON) {
syslog(LOG_WARNING, "power_util: pal_set_device_power failed for"
" for fru %u dev %s with status=%u", fru, dev_name, status);
return ret;
}
break;
case PWR_CYCLE:
printf("Power cycling fru %u dev %s...\n", fru, dev_name);
ret = pal_set_device_power(fru, dev_id, SERVER_POWER_CYCLE);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" fru %u", fru);
return ret;
} else {
syslog(LOG_CRIT, "SERVER_POWER_CYCLE successful for FRU: %d DEV: %s", fru, dev_name);
}
break;
default:
syslog(LOG_WARNING, "power_util: wrong option");
}
return ret;
}
static int
power_util(uint8_t fru, uint8_t opt, bool force) {
int ret = 0;
uint8_t status;
int retries;
char pwr_state[MAX_VALUE_LEN] = {0};
uint8_t root = 0;
if (opt == PWR_SLED_CYCLE) {
for(fru = 1; fru <= MAX_NUM_FRUS; fru++) {
if (!pal_can_change_power(pal_get_root_fru(fru, &root) == PAL_EOK ? root : fru)) {
return -1;
}
}
} else if (opt != PWR_STATUS) {
if (!pal_can_change_power(pal_get_root_fru(fru, &root) == PAL_EOK ? root : fru)) {
return -1;
}
}
switch(opt) {
case PWR_STATUS:
ret = pal_get_server_power(fru, &status);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_get_server_power failed for fru %u\n", fru);
return ret;
}
//printf("Power status for fru %u : %s\n", fru, status?"ON":"OFF");
printf("Power status for fru %u : ", fru);
switch(status) {
case SERVER_POWER_ON:
printf("ON\n");
break;
case SERVER_POWER_OFF:
printf("OFF\n");
break;
case SERVER_12V_OFF:
printf("OFF (12V-OFF)\n");
break;
case SERVER_12V_ON:
printf("ON (12V-ON)\n");
break;
}
break;
case PWR_GRACEFUL_SHUTDOWN:
printf("Shutting down fru %u gracefully...\n", fru);
ret = pal_set_server_power(fru, SERVER_GRACEFUL_SHUTDOWN);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" fru %u", fru);
return ret;
} else if (ret == 1) {
printf("fru %u is already powered OFF...\n", fru);
return 0;
} else {
syslog(LOG_CRIT, "SERVER_GRACEFUL_SHUTDOWN successful for FRU: %d", fru);
}
ret = pal_set_last_pwr_state(fru, POWER_OFF_STR);
if (ret < 0) {
return ret;
}
ret = pal_set_led(fru, LED_OFF);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_led failed for fru %u", fru);
return ret;
}
break;
case PWR_OFF:
printf("Powering fru %u to OFF state...\n", fru);
ret = pal_set_server_power(fru, SERVER_POWER_OFF);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" fru %u", fru);
return ret;
} else if (ret == 1) {
printf("fru %u is already powered OFF...\n", fru);
return 0;
} else {
syslog(LOG_CRIT, "SERVER_POWER_OFF successful for FRU: %d", fru);
}
ret = pal_set_last_pwr_state(fru, POWER_OFF_STR);
if (ret < 0) {
return ret;
}
ret = pal_set_led(fru, LED_OFF);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_led failed for fru %u", fru);
return ret;
}
break;
case PWR_ON:
printf("Powering fru %u to ON state...\n", fru);
if (force) {
ret = pal_set_server_power(fru, SERVER_FORCE_POWER_ON);
} else {
ret = pal_set_server_power(fru, SERVER_POWER_ON);
}
if (ret == 1) {
printf("fru %u is already powered ON...\n", fru);
return 0;
} else if (ret == -2) { //check if fru is not ready
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" fru %u", fru);
return ret;
} else if (ret == -3) { //block power on due to fan fail, do not retry power on
return ret;
}
for (retries = 0; retries < MAX_RETRIES; retries++) {
sleep(3);
ret = pal_get_server_power(fru, &status);
if ((ret >= 0) && (status == SERVER_POWER_ON)) {
syslog(LOG_CRIT, "SERVER_POWER_ON successful for FRU: %d", fru);
break;
}
if (force) {
ret = pal_set_server_power(fru, SERVER_FORCE_POWER_ON);
} else {
ret = pal_set_server_power(fru, SERVER_POWER_ON);
}
}
if (ret < 0 || status != SERVER_POWER_ON) {
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" for fru %u with status=%u", fru, status);
return ret;
}
ret = pal_set_last_pwr_state(fru, POWER_ON_STR);
if (ret < 0) {
return ret;
}
ret = pal_set_led(fru, LED_ON);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_led failed for fru %u", fru);
return ret;
}
pal_set_restart_cause(fru, RESTART_CAUSE_IPMI_CHASSIS_CMD);
break;
case PWR_RESET:
printf("Power reset fru %u...\n", fru);
ret = pal_set_server_power(fru, SERVER_POWER_RESET);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_rst_btn failed for"
" fru %u", fru);
printf("Power reset fail for fru %u\n", fru);
return ret;
}
syslog(LOG_CRIT, "SERVER_POWER_RESET successful for FRU: %d", fru);
pal_set_restart_cause(fru, RESTART_CAUSE_IPMI_CHASSIS_CMD);
break;
case PWR_CYCLE:
printf("Power cycling fru %u...\n", fru);
ret = pal_set_server_power(fru, SERVER_POWER_CYCLE);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" fru %u", fru);
return ret;
} else {
syslog(LOG_CRIT, "SERVER_POWER_CYCLE successful for FRU: %d", fru);
}
ret = pal_set_last_pwr_state(fru, POWER_ON_STR);
if (ret < 0) {
return ret;
}
ret = pal_set_led(fru, LED_ON);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_led failed for fru %u", fru);
return ret;
}
pal_set_restart_cause(fru, RESTART_CAUSE_IPMI_CHASSIS_CMD);
break;
case PWR_12V_OFF:
printf("12V Powering fru %u to OFF state...\n", fru);
ret = pal_set_server_power(fru, SERVER_12V_OFF);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" fru %u", fru);
return ret;
} else if (ret == 1) {
printf("fru %u is already powered 12V-OFF...\n", fru);
return 0;
} else {
syslog(LOG_CRIT, "SERVER_12V_OFF successful for FRU: %d", fru);
}
ret = pal_set_led(fru, LED_OFF);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_led failed for fru %u", fru);
return ret;
}
break;
case PWR_12V_ON:
printf("12V Powering fru %u to ON state...\n", fru);
if (force) {
ret = pal_set_server_power(fru, SERVER_FORCE_12V_ON);
} else {
ret = pal_set_server_power(fru, SERVER_12V_ON);
}
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" fru %u", fru);
return ret;
} else if (ret == 1) {
printf("fru %u is already powered 12V-ON...\n", fru);
return 0;
} else {
syslog(LOG_CRIT, "SERVER_12V_ON successful for FRU: %d", fru);
power_policy_control(fru, NULL,force);
}
break;
case PWR_12V_CYCLE:
printf("12V Power cycling fru %u...\n", fru);
pal_get_last_pwr_state(fru, pwr_state);
ret = pal_set_server_power(fru, SERVER_12V_CYCLE);
if (ret < 0) {
syslog(LOG_WARNING, "power_util: pal_set_server_power failed for"
" fru %u", fru);
return ret;
} else {
syslog(LOG_CRIT, "SERVER_12V_CYCLE successful for FRU: %d", fru);
power_policy_control(fru, pwr_state,force);
}
break;
case PWR_SLED_CYCLE:
syslog(LOG_CRIT, "SLED_CYCLE starting...");
pal_update_ts_sled();
sync();
sleep(2);
ret = pal_sled_cycle();
break;
default:
syslog(LOG_WARNING, "power_util: wrong option");
ret = 2;
}
return ret;
}
static int
add_process_running_flag(uint8_t slot_id, uint8_t opt) {
int pid_file;
char path[128];
if (opt == PWR_STATUS) {
return opt;
} else {
sprintf(path, PWR_UTL_LOCK, slot_id);
pid_file = open(path, O_CREAT | O_RDWR, 0666);
if (flock(pid_file, LOCK_EX | LOCK_NB) && (errno == EWOULDBLOCK)) {
return (-opt);
}
}
return opt;
}
static void
rm_process_running_flag(uint8_t slot_id, uint8_t opt) {
char path[128];
if (opt != PWR_STATUS) {
sprintf(path, PWR_UTL_LOCK, slot_id);
remove(path);
}
}
int parse_args(int argc, char *argv[], bool *force) {
int ret;
int index;
static struct option opts[] = {
{"force", no_argument, 0, 'f'},
{0,0,0,0},
};
/* Set defaults */
*force = false;
while(-1 != (ret = getopt_long(argc, argv, "f", opts, &index))) {
switch(ret) {
#ifdef ENABLE_FORCE_POWER_CMD
case 'f':
*force = true;
break;
#endif
default:
return -1;
}
}
return 0;
}
int
main(int argc, char **argv) {
int ret;
uint8_t fru, status, opt;
char *option;
bool force;
uint8_t num_devs = 0;
uint8_t dev_id = DEV_NONE;
uint8_t root = 0;
if (parse_args(argc, argv, &force)) {
print_usage();
exit(-1);
}
if (argc > optind + 1) {
ret = pal_get_fru_id(argv[optind], &fru);
if (ret < 0) {
printf("Wrong fru: %s\n", argv[optind]);
print_usage();
exit(-1);
}
ret = pal_is_fru_prsnt(fru, &status);
if (ret < 0) {
printf("pal_is_fru_prsnt failed for fru: %d\n", fru);
print_usage();
exit(-1);
}
if (status == 0) {
printf("%s is empty!\n", argv[optind]);
print_usage();
exit(-1);
}
} else {
fru = -1;
}
pal_get_num_devs(fru,&num_devs);
/* Check for sled-cycle */
if (argc < optind+1 || argc > optind +2) {
if ( argc != optind + 3 || num_devs == 0) {
print_usage();
exit (-1);
}
}
if (argc == optind + 3) {
ret = pal_get_dev_id(argv[optind+1], &dev_id);
if (ret < 0 || dev_id == DEV_ALL) {
printf("pal_get_dev_id failed for %s %s\n", argv[1], argv[2]);
print_usage();
exit(-1);
}
}
option = argc == optind+1 ? argv[optind] : argc == optind+2 ? argv [optind+1] : argv[optind+2];
ret = get_power_opt(option, &opt);
/* If argc is 2, the option is sled-cycle; we should ignore power-util fru sled-cycle*/
if ((ret < 0) || (argc == optind+1 && opt != PWR_SLED_CYCLE) || (argc != optind+1 && opt == PWR_SLED_CYCLE)) {
printf("Wrong option: %s\n", option);
print_usage();
exit(-1);
}
if (argc == optind+3 && opt != PWR_ON && opt != PWR_OFF && opt != PWR_STATUS && opt != PWR_CYCLE) {
printf("Wrong option for %s: %s\n",argv [2] ,option);
print_usage();
exit(-1);
}
// Check if another instance is running
if (add_process_running_flag(pal_get_root_fru(fru, &root) == PAL_EOK ? root : fru, opt) < 0) {
printf("power_util: another instance is running for FRU:%d...\n",fru);
//Make power-util exit code to "-2" when another instance is running
exit(-2);
}
if (dev_id == DEV_NONE) {
ret = power_util(fru, opt, force);
if (ret < 0) {
printf("ERROR: power-util fru[%d] [%s] failed\n", fru, option_list[opt]);
}
} else if (dev_id != DEV_ALL) {
ret = dev_power_util(fru, argv[optind+1], dev_id, opt);
if (ret < 0) {
printf("ERROR: power-util fru[%d] dev %s [%s] failed\n", fru, argv[optind+1], option_list[opt]);
}
}
rm_process_running_flag(pal_get_root_fru(fru, &root) == PAL_EOK ? root : fru, opt);
return ret;
}