From ddf0edbf8e5d0213396401da4becfc4680779617 Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Tue, 28 Mar 2017 10:50:40 +1100 Subject: mboxctl: Implement mboxctl to facilitate dbus control of the daemon The daemon can now be controlled by issuing commands over dbus. Implement a mailbox control program to facilitate communication with the daemon over dbus. Signed-off-by: Suraj Jitindar Singh Change-Id: I2a11f187268d28ce92d3c2592e0021e1ba5588b4 --- mboxctl.c | 485 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 485 insertions(+) create mode 100644 mboxctl.c (limited to 'mboxctl.c') diff --git a/mboxctl.c b/mboxctl.c new file mode 100644 index 0000000..ca6d9ad --- /dev/null +++ b/mboxctl.c @@ -0,0 +1,485 @@ +/* + * Mailbox Control Implementation + * + * Copyright 2017 IBM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dbus.h" + +#define USAGE \ +"\nUsage: %s [--silent | -s] [args]\n\n" \ +"\t\t--silent\t\t- no output on the command line\n\n" \ +"\tCommands: (num args)\n" \ +"\t\t--ping\t\t\t- ping the daemon (0)\n" \ +"\t\t--daemon-state\t\t- check state of the daemon (0)\n" \ +"\t\t--lpc-state\t\t- check the state of the lpc mapping (0)\n" \ +"\t\t--kill\t\t\t- stop the daemon [no flush] (0)\n" \ +"\t\t--reset\t\t\t- hard reset the daemon state (0)\n" \ +"\t\t--point-to-flash\t- point the lpc mapping back to flash (0)\n" \ +"\t\t--suspend\t\t- suspend the daemon to inhibit flash accesses (0)\n" \ +"\t\t--resume\t\t- resume the daemon (1)\n" \ +"\t\t\targ[0]: < \"clean\" | \"modified\" >\n" \ +"\t\t--clear-cache\t- tell the daemon to discard any caches (0)\n" + +#define NAME "MBOX Control" +#define VERSION 1 +#define SUBVERSION 0 + +static bool silent; + +#define MSG_OUT(...) do { if (!silent) { \ + fprintf(stdout, __VA_ARGS__); } \ + } while (0) +#define MSG_ERR(...) do { if (!silent) { \ + fprintf(stderr, __VA_ARGS__); } \ + } while (0) + +struct mboxctl_context { + sd_bus *bus; +}; + +static void usage(char *name) +{ + MSG_OUT(USAGE, name); + exit(0); +} + +static const char *dbus_err_str[] = { + "Success", + "Failed - Internal Error", + "Failed - Invalid Command or Request", + "Failed - Request Rejected by Daemon", + "Failed - BMC Hardware Error", + "Failed - Insufficient Memory for Allocation Request" +}; + +static int init_mboxctl_dbus(struct mboxctl_context *context) +{ + int rc; + + rc = sd_bus_default_system(&context->bus); + if (rc < 0) { + MSG_ERR("Failed to connect to the system bus: %s\n", + strerror(-rc)); + } + + return rc; +} + +static int send_dbus_msg(struct mboxctl_context *context, + struct mbox_dbus_msg *msg, + struct mbox_dbus_msg *resp) +{ + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *m = NULL, *n = NULL; + uint8_t *buf; + size_t sz; + int rc; + + /* Generate the bus message */ + rc = sd_bus_message_new_method_call(context->bus, &m, DBUS_NAME, + DOBJ_NAME, DBUS_NAME, "cmd"); + if (rc < 0) { + MSG_ERR("Failed to init method call: %s\n", + strerror(-rc)); + rc = -E_DBUS_INTERNAL; + goto out; + } + + /* Add the command */ + rc = sd_bus_message_append(m, "y", msg->cmd); + if (rc < 0) { + MSG_ERR("Failed to add cmd to message: %s\n", + strerror(-rc)); + rc = -E_DBUS_INTERNAL; + goto out; + } + + /* Add the args */ + rc = sd_bus_message_append_array(m, 'y', msg->args, msg->num_args); + if (rc < 0) { + MSG_ERR("Failed to add args to message: %s\n", + strerror(-rc)); + rc = -E_DBUS_INTERNAL; + goto out; + } + + /* Send the message */ + rc = sd_bus_call(context->bus, m, 0, &error, &n); + if (rc < 0) { + MSG_ERR("Failed to post message: %s\n", strerror(-rc)); + rc = -E_DBUS_INTERNAL; + goto out; + } + + /* Read response code */ + rc = sd_bus_message_read(n, "y", &resp->cmd); + if (rc < 0) { + MSG_ERR("Failed to read response code: %s\n", + strerror(-rc)); + rc = -E_DBUS_INTERNAL; + goto out; + } + + /* Read response args */ + rc = sd_bus_message_read_array(n, 'y', (const void **) &buf, &sz); + if (rc < 0) { + MSG_ERR("Failed to read response args: %s\n", + strerror(-rc)); + rc = -E_DBUS_INTERNAL; + goto out; + } + + if (sz < resp->num_args) { + MSG_ERR("Command returned insufficient response args\n"); + rc = -E_DBUS_INTERNAL; + goto out; + } + + memcpy(resp->args, buf, resp->num_args); + rc = 0; + +out: + sd_bus_error_free(&error); + sd_bus_message_unref(m); + sd_bus_message_unref(n); + + return rc; +} + +static int handle_cmd_ping(struct mboxctl_context *context) +{ + struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; + int rc; + + msg.cmd = DBUS_C_PING; + + rc = send_dbus_msg(context, &msg, &resp); + if (rc < 0) { + MSG_ERR("Failed to send ping command\n"); + return rc; + } + + rc = -resp.cmd; + MSG_OUT("Ping: %s\n", dbus_err_str[-rc]); + + return rc; +} + +static int handle_cmd_daemon_state(struct mboxctl_context *context) +{ + struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; + int rc; + + msg.cmd = DBUS_C_DAEMON_STATE; + resp.num_args = DAEMON_STATE_NUM_ARGS; + resp.args = calloc(resp.num_args, sizeof(*resp.args)); + if (!resp.args) { + MSG_ERR("Memory allocation failed\n"); + return -E_DBUS_NO_MEM; + } + + rc = send_dbus_msg(context, &msg, &resp); + if (rc < 0) { + MSG_ERR("Failed to send daemon state command\n"); + goto out; + } + + rc = -resp.cmd; + if (resp.cmd != DBUS_SUCCESS) { + MSG_ERR("Daemon state command failed\n"); + goto out; + } + + MSG_OUT("Daemon State: %s\n", resp.args[0] == DAEMON_STATE_ACTIVE ? + "Active" : "Suspended"); + +out: + free(resp.args); + return rc; +} + +static int handle_cmd_lpc_state(struct mboxctl_context *context) +{ + struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; + int rc; + + msg.cmd = DBUS_C_LPC_STATE; + resp.num_args = LPC_STATE_NUM_ARGS; + resp.args = calloc(resp.num_args, sizeof(*resp.args)); + if (!resp.args) { + MSG_ERR("Memory allocation failed\n"); + return -E_DBUS_NO_MEM; + } + + rc = send_dbus_msg(context, &msg, &resp); + if (rc < 0) { + MSG_ERR("Failed to send lpc state command\n"); + goto out; + } + + rc = -resp.cmd; + if (resp.cmd != DBUS_SUCCESS) { + MSG_ERR("LPC state command failed\n"); + goto out; + } + + MSG_OUT("LPC Bus Maps: %s\n", resp.args[0] == LPC_STATE_MEM ? + "BMC Memory" : + (resp.args[0] == LPC_STATE_FLASH ? + "Flash Device" : + "Invalid System State")); + +out: + free(resp.args); + return rc; +} + +static int handle_cmd_kill(struct mboxctl_context *context) +{ + struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; + int rc; + + msg.cmd = DBUS_C_KILL; + + rc = send_dbus_msg(context, &msg, &resp); + if (rc < 0) { + MSG_ERR("Failed to send kill command\n"); + return rc; + } + + rc = -resp.cmd; + MSG_OUT("Kill: %s\n", dbus_err_str[-rc]); + + return rc; +} + +static int handle_cmd_reset(struct mboxctl_context *context) +{ + struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; + int rc; + + msg.cmd = DBUS_C_RESET; + + rc = send_dbus_msg(context, &msg, &resp); + if (rc < 0) { + MSG_ERR("Failed to send reset command\n"); + return rc; + } + + rc = -resp.cmd; + MSG_OUT("Reset: %s\n", dbus_err_str[-rc]); + + return rc; +} + +static int handle_cmd_suspend(struct mboxctl_context *context) +{ + struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; + int rc; + + msg.cmd = DBUS_C_SUSPEND; + + rc = send_dbus_msg(context, &msg, &resp); + if (rc < 0) { + MSG_ERR("Failed to send suspend command\n"); + return rc; + } + + rc = -resp.cmd; + MSG_OUT("Suspend: %s\n", dbus_err_str[-rc]); + + return rc; +} + +static int handle_cmd_resume(struct mboxctl_context *context, char *arg) +{ + struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; + int rc; + + if (!arg) { + MSG_ERR("Resume command takes an argument\n"); + return -E_DBUS_INVAL; + } + + msg.cmd = DBUS_C_RESUME; + msg.num_args = RESUME_NUM_ARGS; + msg.args = calloc(msg.num_args, sizeof(*msg.args)); + if (!msg.args) { + MSG_ERR("Memory allocation failed\n"); + return -E_DBUS_NO_MEM; + } + + if (!strncmp(arg, "clean", strlen("clean"))) { + msg.args[0] = RESUME_NOT_MODIFIED; + } else if (!strncmp(arg, "modified", strlen("modified"))) { + msg.args[0] = RESUME_FLASH_MODIFIED; + } else { + MSG_ERR("Resume command takes argument < \"clean\" | " + "\"modified\" >\n"); + rc = -E_DBUS_INVAL; + goto out; + } + + rc = send_dbus_msg(context, &msg, &resp); + if (rc < 0) { + MSG_ERR("Failed to send resume command\n"); + goto out; + } + + rc = -resp.cmd; + MSG_OUT("Resume: %s\n", dbus_err_str[-rc]); + +out: + free(msg.args); + return rc; +} + +static int handle_cmd_modified(struct mboxctl_context *context) +{ + struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; + int rc; + + msg.cmd = DBUS_C_MODIFIED; + + rc = send_dbus_msg(context, &msg, &resp); + if (rc < 0) { + MSG_ERR("Failed to send flash modified command\n"); + return rc; + } + + rc = -resp.cmd; + MSG_OUT("Clear Cache: %s\n", dbus_err_str[-rc]); + + return rc; +} + +static int parse_cmdline(struct mboxctl_context *context, int argc, char **argv) +{ + int opt, rc = -1; + + static const struct option long_options[] = { + { "silent", no_argument, 0, 's' }, + { "ping", no_argument, 0, 'p' }, + { "daemon-state", no_argument, 0, 'd' }, + { "lpc-state", no_argument, 0, 'l' }, + { "kill", no_argument, 0, 'k' }, + { "reset", no_argument, 0, 'r' }, + { "point-to-flash", no_argument, 0, 'f' }, + { "suspend", no_argument, 0, 'u' }, + { "resume", required_argument, 0, 'e' }, + { "clear-cache", no_argument, 0, 'c' }, + { "version", no_argument, 0, 'v' }, + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + if (argc <= 1) { + usage(argv[0]); + return -E_DBUS_INVAL; + } + + while ((opt = getopt_long(argc, argv, "spdlkrfue:cvh", long_options, + NULL)) != -1) { + switch (opt) { + case 's': + silent = true; + continue; + case 'p': + rc = handle_cmd_ping(context); + break; + case 'd': + rc = handle_cmd_daemon_state(context); + break; + case 'l': + rc = handle_cmd_lpc_state(context); + break; + case 'k': + rc = handle_cmd_kill(context); + break; + case 'r': /* These are the same for now (reset may change) */ + case 'f': + rc = handle_cmd_reset(context); + break; + case 'u': + rc = handle_cmd_suspend(context); + break; + case 'e': + rc = handle_cmd_resume(context, optarg); + break; + case 'c': + rc = handle_cmd_modified(context); + break; + case 'v': + MSG_OUT("%s V%d.%.2d\n", NAME, VERSION, SUBVERSION); + rc = 0; + break; + case 'h': + usage(argv[0]); + rc = 0; + break; + default: + usage(argv[0]); + rc = -E_DBUS_INVAL; + break; + } + } + + return rc; +} + +int main(int argc, char **argv) +{ + struct mboxctl_context context; + int rc; + + silent = false; + + rc = init_mboxctl_dbus(&context); + if (rc < 0) { + MSG_ERR("Failed to init dbus\n"); + return rc; + } + + rc = parse_cmdline(&context, argc, argv); + + return rc; +} -- cgit v1.2.1