* Copyright (c) 2022 Huawei Technologies Co.,Ltd.
*
* CBB is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* cm_iofence.c
*
*
* IDENTIFICATION
* src/cm_utils/cm_iofence.c
*
* -------------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef WIN32
#include <sys/wait.h>
#include <unistd.h>
#define CM_IOF_EXEC_FAILED_EXIT_CODE 127
#endif
#include "cm_file.h"
#include "cm_log.h"
#include "cm_iofence.h"
static bool32 cm_iof_validate_path(const char *path, const char *name)
{
if (path == NULL || path[0] == '\0') {
LOG_RUN_ERR("iofence %s is NULL or empty.", name);
return CM_FALSE;
}
if (cm_check_exist_special_char(path, (uint32)strlen(path))) {
LOG_RUN_ERR("iofence %s contains special characters, rejected.", name);
return CM_FALSE;
}
return CM_TRUE;
}
#ifndef WIN32
static void cm_iof_close_pipe(int pipefd[2])
{
(void)close(pipefd[0]);
(void)close(pipefd[1]);
}
static void cm_iof_exec_child(char *const argv[], int write_fd)
{
(void)dup2(write_fd, STDOUT_FILENO);
(void)dup2(write_fd, STDERR_FILENO);
(void)close(write_fd);
(void)execvp(argv[0], argv);
_exit(CM_IOF_EXEC_FAILED_EXIT_CODE);
}
static status_t cm_iof_spawn_cmd(const char *cmd, char *const argv[], int pipefd[2], pid_t *pid)
{
if (pipe(pipefd) != 0) {
LOG_RUN_ERR("multibus iofence pipe failed, cmd: %s.", cmd);
return CM_ERROR;
}
*pid = fork();
if (*pid < 0) {
LOG_RUN_ERR("multibus iofence fork failed, cmd: %s.", cmd);
cm_iof_close_pipe(pipefd);
return CM_ERROR;
}
if (*pid == 0) {
(void)close(pipefd[0]);
cm_iof_exec_child(argv, pipefd[1]);
}
(void)close(pipefd[1]);
return CM_SUCCESS;
}
static void cm_iof_read_cmd_result(int read_fd, char *buf, uint32 buf_len)
{
char drain[128] = {0};
uint32 offset = 0;
while (CM_TRUE) {
char *dst = (offset < buf_len - 1) ? (buf + offset) : drain;
size_t dst_len = (offset < buf_len - 1) ? (buf_len - 1 - offset) : sizeof(drain);
ssize_t read_len = read(read_fd, dst, dst_len);
if (read_len > 0) {
if (offset < buf_len - 1) {
offset += (uint32)read_len;
}
continue;
}
if (read_len == 0) {
break;
}
if (errno != EINTR && errno != EAGAIN) {
break;
}
}
buf[offset] = '\0';
}
static status_t cm_iof_wait_cmd(const char *cmd, pid_t pid, int *status)
{
pid_t wait_ret;
do {
wait_ret = waitpid(pid, status, 0);
} while (wait_ret < 0 && errno == EINTR);
if (wait_ret < 0) {
LOG_RUN_ERR("multibus iofence waitpid failed, cmd: %s.", cmd);
return CM_ERROR;
}
return CM_SUCCESS;
}
static int32 cm_iof_parse_cmd_result(const char *buf)
{
if (strstr(buf, "Reservation Conflict") != NULL) {
LOG_RUN_INF("multibus iofence, execute succ. Reservation Conflict");
return CM_SCSI_ERR_CONFLICT;
}
if (strstr(buf, "a password is required") != NULL || strstr(buf, "error opening file") != NULL ||
strstr(buf, "reservation key doesn't match") != NULL || strstr(buf, "command failed") != NULL ||
strstr(buf, "command not found") != NULL) {
LOG_RUN_INF("multibus iofence, execute faild.");
return (int32)CM_ERROR;
}
LOG_RUN_INF("multibus iofence, execute succ.");
return (int32)CM_SUCCESS;
}
#endif
static int32 cm_iof_exec_cmd(const char *cmd, char *const argv[])
{
#ifdef WIN32
(void)cmd;
(void)argv;
return CM_SUCCESS;
#else
int pipefd[2] = {-1, -1};
int status = 0;
char buf[512] = {0};
pid_t pid;
if (cm_iof_spawn_cmd(cmd, argv, pipefd, &pid) != CM_SUCCESS) {
return (int32)CM_ERROR;
}
cm_iof_read_cmd_result(pipefd[0], buf, (uint32)sizeof(buf));
(void)close(pipefd[0]);
if (cm_iof_wait_cmd(cmd, pid, &status) != CM_SUCCESS) {
return (int32)CM_ERROR;
}
LOG_RUN_INF("multibus iofence, cmd: %s,result: %s.", cmd, buf);
if ((!WIFEXITED(status) || WEXITSTATUS(status) != 0) && buf[0] == '\0') {
LOG_RUN_ERR("multibus iofence exec failed, cmd: %s.", cmd);
return (int32)CM_ERROR;
}
return cm_iof_parse_cmd_result(buf);
#endif
}
int32 cm_iof_multibus_register(const char *mpathpersist_path, const char *log_path, const char *iof_dev, int64 sark)
{
if (!cm_iof_validate_path(mpathpersist_path, "mpathpersist_path") ||
!cm_iof_validate_path(log_path, "log_path") ||
!cm_iof_validate_path(iof_dev, "iof_dev")) {
return (int32)CM_ERROR;
}
int32 ret;
char cmd[MULTIBUS_MAX_CMD_LEN];
char device_arg[MULTIBUS_MAX_CMD_LEN] = {0};
char sark_arg[64] = {0};
ret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1,
"sudo -n %s %s --out --register --device=%s --param-sark=%lld 2>&1",
mpathpersist_path, log_path, iof_dev, sark);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(device_arg, sizeof(device_arg), sizeof(device_arg) - 1, "--device=%s", iof_dev);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(sark_arg, sizeof(sark_arg), sizeof(sark_arg) - 1, "--param-sark=%lld", sark);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
char *argv[] = {"sudo", "-n", (char *)mpathpersist_path, (char *)log_path,
"--out", "--register", device_arg, sark_arg, NULL};
return cm_iof_exec_cmd(cmd, argv);
}
int32 cm_iof_multibus_reserve(const char *mpathpersist_path, const char *log_path, const char *iof_dev,
int64 rk, scsi_reserv_type_e type)
{
if (!cm_iof_validate_path(mpathpersist_path, "mpathpersist_path") ||
!cm_iof_validate_path(log_path, "log_path") ||
!cm_iof_validate_path(iof_dev, "iof_dev")) {
return (int32)CM_ERROR;
}
int32 ret;
char cmd[MULTIBUS_MAX_CMD_LEN];
char device_arg[MULTIBUS_MAX_CMD_LEN] = {0};
char rk_arg[64] = {0};
char type_arg[64] = {0};
ret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1,
"sudo -n %s %s --out --reserve --device=%s --param-rk=%lld --prout-type=%d 2>&1",
mpathpersist_path, log_path, iof_dev, rk, type);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(device_arg, sizeof(device_arg), sizeof(device_arg) - 1, "--device=%s", iof_dev);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(rk_arg, sizeof(rk_arg), sizeof(rk_arg) - 1, "--param-rk=%lld", rk);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(type_arg, sizeof(type_arg), sizeof(type_arg) - 1, "--prout-type=%d", type);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
char *argv[] = {"sudo", "-n", (char *)mpathpersist_path, (char *)log_path,
"--out", "--reserve", device_arg, rk_arg, type_arg, NULL};
return cm_iof_exec_cmd(cmd, argv);
}
int32 cm_iof_multibus_preempt(const char *mpathpersist_path, const char *log_path, const char *iof_dev, int64 rk,
int64 sark, scsi_reserv_type_e type)
{
if (!cm_iof_validate_path(mpathpersist_path, "mpathpersist_path") ||
!cm_iof_validate_path(log_path, "log_path") ||
!cm_iof_validate_path(iof_dev, "iof_dev")) {
return (int32)CM_ERROR;
}
int32 ret;
char cmd[MULTIBUS_MAX_CMD_LEN];
char device_arg[MULTIBUS_MAX_CMD_LEN] = {0};
char rk_arg[64] = {0};
char sark_arg[64] = {0};
char type_arg[64] = {0};
ret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1,
"sudo -n %s %s --out --preempt --device=%s --param-rk=%lld --param-sark=%lld --prout-type=%d 2>&1",
mpathpersist_path, log_path, iof_dev, rk, sark, type);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(device_arg, sizeof(device_arg), sizeof(device_arg) - 1, "--device=%s", iof_dev);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(rk_arg, sizeof(rk_arg), sizeof(rk_arg) - 1, "--param-rk=%lld", rk);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(sark_arg, sizeof(sark_arg), sizeof(sark_arg) - 1, "--param-sark=%lld", sark);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(type_arg, sizeof(type_arg), sizeof(type_arg) - 1, "--prout-type=%d", type);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
char *argv[] = {"sudo", "-n", (char *)mpathpersist_path, (char *)log_path,
"--out", "--preempt", device_arg, rk_arg, sark_arg, type_arg, NULL};
return cm_iof_exec_cmd(cmd, argv);
}
int32 cm_iof_multibus_unregister(const char *mpathpersist_path, const char *log_path, const char *iof_dev, int64 rk)
{
if (!cm_iof_validate_path(mpathpersist_path, "mpathpersist_path") ||
!cm_iof_validate_path(log_path, "log_path") ||
!cm_iof_validate_path(iof_dev, "iof_dev")) {
return (int32)CM_ERROR;
}
int32 ret;
char cmd[MULTIBUS_MAX_CMD_LEN];
char device_arg[MULTIBUS_MAX_CMD_LEN] = {0};
char rk_arg[64] = {0};
ret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1,
"sudo -n %s %s --out --register --device=%s --param-rk=%lld 2>&1",
mpathpersist_path, log_path, iof_dev, rk);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(device_arg, sizeof(device_arg), sizeof(device_arg) - 1, "--device=%s", iof_dev);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
ret = snprintf_s(rk_arg, sizeof(rk_arg), sizeof(rk_arg) - 1, "--param-rk=%lld", rk);
if (SECUREC_UNLIKELY(ret == -1)) {
CM_THROW_ERROR(ERR_SYSTEM_CALL, ret);
return (int32)CM_ERROR;
}
char *argv[] = {"sudo", "-n", (char *)mpathpersist_path, (char *)log_path,
"--out", "--register", device_arg, rk_arg, NULL};
return cm_iof_exec_cmd(cmd, argv);
}
int32 cm_iof_register(iof_reg_out_t *iof_out)
{
#ifdef WIN32
#else
int32 ret;
if (iof_out == NULL) {
return CM_ERROR;
}
if (iof_out->linux_multibus) {
ret = cm_iof_multibus_register(iof_out->mpathpersist_dss_path, iof_out->log_path,
iof_out->dev, CM_OUT_SCSI_RK(iof_out));
} else {
ret = perctrl_scsi3_register(iof_out->dev, CM_OUT_SCSI_RK(iof_out));
}
if (CM_SUCCESS != ret) {
if (CM_SCSI_ERR_CONFLICT == ret) {
LOG_RUN_INF(
"Scsi3 register conflict, rk %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out), iof_out->dev, errno);
} else {
LOG_RUN_ERR(
"Scsi3 register failed, rk %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out), iof_out->dev, errno);
return CM_ERROR;
}
}
if (iof_out->linux_multibus) {
ret = cm_iof_multibus_reserve(iof_out->mpathpersist_dss_path, iof_out->log_path, iof_out->dev,
CM_OUT_SCSI_RK(iof_out), iof_out->type);
} else {
ret = perctrl_scsi3_reserve(iof_out->dev, CM_OUT_SCSI_RK(iof_out), iof_out->type);
}
if (CM_SUCCESS != ret) {
if (CM_SCSI_ERR_CONFLICT == ret) {
LOG_RUN_INF(
"Scsi3 register conflict, rk %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out), iof_out->dev, errno);
} else {
LOG_RUN_ERR(
"Scsi3 reserve failed, rk %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out), iof_out->dev, errno);
return CM_ERROR;
}
}
LOG_RUN_INF("IOfence register succ, rk %lld, dev %s.", CM_OUT_SCSI_RK(iof_out), iof_out->dev);
#endif
return CM_SUCCESS;
}
int32 cm_iof_unregister(iof_reg_out_t *iof_out)
{
#ifdef WIN32
#else
int32 ret;
if (iof_out == NULL) {
return CM_ERROR;
}
if (iof_out->linux_multibus) {
ret = cm_iof_multibus_unregister(iof_out->mpathpersist_dss_path, iof_out->log_path,
iof_out->dev, CM_OUT_SCSI_RK(iof_out));
} else {
ret = perctrl_scsi3_unregister(iof_out->dev, CM_OUT_SCSI_RK(iof_out));
}
if (CM_SUCCESS != ret) {
if (CM_SCSI_ERR_CONFLICT == ret) {
LOG_RUN_INF(
"Scsi3 unregister conflict, rk %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out), iof_out->dev, errno);
} else {
LOG_RUN_ERR(
"Scsi3 unregister failed, rk %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out), iof_out->dev, errno);
return CM_ERROR;
}
}
LOG_RUN_INF("IOfence unregister succ, rk %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out), iof_out->dev, errno);
#endif
return CM_SUCCESS;
}
status_t cm_iof_kick(iof_reg_out_t *iof_out)
{
#ifdef WIN32
#else
status_t status;
if (iof_out == NULL) {
return CM_ERROR;
}
if (iof_out->linux_multibus) {
status = cm_iof_multibus_preempt(iof_out->mpathpersist_dss_path, iof_out->log_path,
iof_out->dev, CM_OUT_SCSI_RK(iof_out), CM_OUT_SCSI_SARK(iof_out), iof_out->type);
} else {
status = perctrl_scsi3_preempt(iof_out->dev, CM_OUT_SCSI_RK(iof_out), CM_OUT_SCSI_SARK(iof_out), iof_out->type);
}
if (CM_SUCCESS != status) {
LOG_RUN_ERR("Scsi3 preempt failed, rk %lld, rk_kick %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out),
CM_OUT_SCSI_SARK(iof_out), iof_out->dev, errno);
return CM_ERROR;
}
LOG_RUN_INF("IOfence kick succ, rk %lld, rk_kick %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out),
CM_OUT_SCSI_SARK(iof_out), iof_out->dev, errno);
#endif
return CM_SUCCESS;
}
status_t cm_iof_clear(iof_reg_out_t *iof_out)
{
#ifdef WIN32
#else
status_t status;
if (iof_out == NULL) {
return CM_ERROR;
}
status = perctrl_scsi3_clear(iof_out->dev, CM_OUT_SCSI_RK(iof_out));
if (CM_SUCCESS != status) {
LOG_RUN_ERR("Scsi3 clear failed, rk %lld, dev %s, errno %d.", CM_OUT_SCSI_RK(iof_out), iof_out->dev, errno);
return CM_ERROR;
}
LOG_RUN_INF("IOfence clear succ, rk %lld, dev %s.", CM_OUT_SCSI_RK(iof_out), iof_out->dev);
#endif
return CM_SUCCESS;
}
status_t cm_iof_inql(iof_reg_in_t *iof_in)
{
#ifdef WIN32
#else
status_t status;
if (iof_in == NULL) {
return CM_ERROR;
}
iof_in->key_count = CM_MAX_RKEY_COUNT;
status = perctrl_scsi3_rkeys(iof_in->dev, iof_in->reg_keys, &iof_in->key_count, &iof_in->generation);
if (CM_SUCCESS != status) {
LOG_RUN_ERR("Scsi3 inql rkeys failed, dev %s, errno %d.", iof_in->dev, errno);
return CM_ERROR;
}
status = perctrl_scsi3_rres(iof_in->dev, &iof_in->resk, &iof_in->generation);
if (CM_SUCCESS != status) {
LOG_RUN_ERR("Scsi3 inql rres failed, dev %s, errno %d.", iof_in->dev, errno);
return CM_ERROR;
}
LOG_RUN_INF("IOfence inql succ, dev %s.", iof_in->dev);
#endif
return CM_SUCCESS;
}