1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright (C) 2018 IBM Corp. 3 #include "config.h" 4 5 #include <getopt.h> 6 #include <stdbool.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <systemd/sd-bus.h> 10 11 #include "dbus.h" 12 13 #define USAGE \ 14 "\nUsage: %s [--silent | -s] <command> [args]\n\n" \ 15 "\t\t--silent\t\t- no output on the command line\n\n" \ 16 "\tCommands: (num args)\n" \ 17 "\t\t--ping\t\t\t- ping the daemon (0)\n" \ 18 "\t\t--daemon-state\t\t- check state of the daemon (0)\n" \ 19 "\t\t--lpc-state\t\t- check the state of the lpc mapping (0)\n" \ 20 "\t\t--kill\t\t\t- stop the daemon [no flush] (0)\n" \ 21 "\t\t--reset\t\t\t- hard reset the daemon state (0)\n" \ 22 "\t\t--point-to-flash\t- point the lpc mapping back to flash (0)\n" \ 23 "\t\t--suspend\t\t- suspend the daemon to inhibit flash accesses (0)\n" \ 24 "\t\t--resume\t\t- resume the daemon (1)\n" \ 25 "\t\t\targ[0]: < \"clean\" | \"modified\" >\n" \ 26 "\t\t--clear-cache\t- tell the daemon to discard any caches (0)\n" 27 28 #define NAME "Mailbox Control" 29 30 static bool silent; 31 32 #define MSG_OUT(...) do { if (!silent) { \ 33 fprintf(stdout, __VA_ARGS__); } \ 34 } while (0) 35 #define MSG_ERR(...) do { if (!silent) { \ 36 fprintf(stderr, __VA_ARGS__); } \ 37 } while (0) 38 39 struct mboxctl_context { 40 sd_bus *bus; 41 }; 42 43 static void usage(char *name) 44 { 45 MSG_OUT(USAGE, name); 46 exit(0); 47 } 48 49 static const char *dbus_err_str[] = { 50 "Success", 51 "Failed - Internal Error", 52 "Failed - Invalid Command or Request", 53 "Failed - Request Rejected by Daemon", 54 "Failed - BMC Hardware Error", 55 "Failed - Insufficient Memory for Allocation Request" 56 }; 57 58 static int init_mboxctl_dbus(struct mboxctl_context *context) 59 { 60 int rc; 61 62 rc = sd_bus_default_system(&context->bus); 63 if (rc < 0) { 64 MSG_ERR("Failed to connect to the system bus: %s\n", 65 strerror(-rc)); 66 } 67 68 return rc; 69 } 70 71 static int send_dbus_msg(struct mboxctl_context *context, 72 struct mbox_dbus_msg *msg, 73 struct mbox_dbus_msg *resp) 74 { 75 sd_bus_error error = SD_BUS_ERROR_NULL; 76 sd_bus_message *m = NULL, *n = NULL; 77 uint8_t *buf; 78 size_t sz; 79 int rc; 80 81 /* Generate the bus message */ 82 rc = sd_bus_message_new_method_call(context->bus, &m, DBUS_NAME, 83 DOBJ_NAME, DBUS_NAME, "cmd"); 84 if (rc < 0) { 85 MSG_ERR("Failed to init method call: %s\n", 86 strerror(-rc)); 87 rc = -E_DBUS_INTERNAL; 88 goto out; 89 } 90 91 /* Add the command */ 92 rc = sd_bus_message_append(m, "y", msg->cmd); 93 if (rc < 0) { 94 MSG_ERR("Failed to add cmd to message: %s\n", 95 strerror(-rc)); 96 rc = -E_DBUS_INTERNAL; 97 goto out; 98 } 99 100 /* Add the args */ 101 rc = sd_bus_message_append_array(m, 'y', msg->args, msg->num_args); 102 if (rc < 0) { 103 MSG_ERR("Failed to add args to message: %s\n", 104 strerror(-rc)); 105 rc = -E_DBUS_INTERNAL; 106 goto out; 107 } 108 109 /* Send the message */ 110 rc = sd_bus_call(context->bus, m, 0, &error, &n); 111 if (rc < 0) { 112 MSG_ERR("Failed to post message: %s\n", strerror(-rc)); 113 rc = -E_DBUS_INTERNAL; 114 goto out; 115 } 116 117 /* Read response code */ 118 rc = sd_bus_message_read(n, "y", &resp->cmd); 119 if (rc < 0) { 120 MSG_ERR("Failed to read response code: %s\n", 121 strerror(-rc)); 122 rc = -E_DBUS_INTERNAL; 123 goto out; 124 } 125 126 /* Read response args */ 127 rc = sd_bus_message_read_array(n, 'y', (const void **) &buf, &sz); 128 if (rc < 0) { 129 MSG_ERR("Failed to read response args: %s\n", 130 strerror(-rc)); 131 rc = -E_DBUS_INTERNAL; 132 goto out; 133 } 134 135 if (sz < resp->num_args) { 136 MSG_ERR("Command returned insufficient response args\n"); 137 rc = -E_DBUS_INTERNAL; 138 goto out; 139 } 140 141 memcpy(resp->args, buf, resp->num_args); 142 rc = 0; 143 144 out: 145 sd_bus_error_free(&error); 146 sd_bus_message_unref(m); 147 sd_bus_message_unref(n); 148 149 return rc; 150 } 151 152 static int handle_cmd_ping(struct mboxctl_context *context) 153 { 154 struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; 155 int rc; 156 157 msg.cmd = DBUS_C_PING; 158 159 rc = send_dbus_msg(context, &msg, &resp); 160 if (rc < 0) { 161 MSG_ERR("Failed to send ping command\n"); 162 return rc; 163 } 164 165 rc = -resp.cmd; 166 MSG_OUT("Ping: %s\n", dbus_err_str[-rc]); 167 168 return rc; 169 } 170 171 static int handle_cmd_daemon_state(struct mboxctl_context *context) 172 { 173 struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; 174 int rc; 175 176 msg.cmd = DBUS_C_DAEMON_STATE; 177 resp.num_args = DAEMON_STATE_NUM_ARGS; 178 resp.args = calloc(resp.num_args, sizeof(*resp.args)); 179 if (!resp.args) { 180 MSG_ERR("Memory allocation failed\n"); 181 return -E_DBUS_NO_MEM; 182 } 183 184 rc = send_dbus_msg(context, &msg, &resp); 185 if (rc < 0) { 186 MSG_ERR("Failed to send daemon state command\n"); 187 goto out; 188 } 189 190 rc = -resp.cmd; 191 if (resp.cmd != DBUS_SUCCESS) { 192 MSG_ERR("Daemon state command failed\n"); 193 goto out; 194 } 195 196 MSG_OUT("Daemon State: %s\n", resp.args[0] == DAEMON_STATE_ACTIVE ? 197 "Active" : "Suspended"); 198 199 out: 200 free(resp.args); 201 return rc; 202 } 203 204 static int handle_cmd_lpc_state(struct mboxctl_context *context) 205 { 206 struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; 207 int rc; 208 209 msg.cmd = DBUS_C_LPC_STATE; 210 resp.num_args = LPC_STATE_NUM_ARGS; 211 resp.args = calloc(resp.num_args, sizeof(*resp.args)); 212 if (!resp.args) { 213 MSG_ERR("Memory allocation failed\n"); 214 return -E_DBUS_NO_MEM; 215 } 216 217 rc = send_dbus_msg(context, &msg, &resp); 218 if (rc < 0) { 219 MSG_ERR("Failed to send lpc state command\n"); 220 goto out; 221 } 222 223 rc = -resp.cmd; 224 if (resp.cmd != DBUS_SUCCESS) { 225 MSG_ERR("LPC state command failed\n"); 226 goto out; 227 } 228 229 MSG_OUT("LPC Bus Maps: %s\n", resp.args[0] == LPC_STATE_MEM ? 230 "BMC Memory" : 231 (resp.args[0] == LPC_STATE_FLASH ? 232 "Flash Device" : 233 "Invalid System State")); 234 235 out: 236 free(resp.args); 237 return rc; 238 } 239 240 static int handle_cmd_kill(struct mboxctl_context *context) 241 { 242 struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; 243 int rc; 244 245 msg.cmd = DBUS_C_KILL; 246 247 rc = send_dbus_msg(context, &msg, &resp); 248 if (rc < 0) { 249 MSG_ERR("Failed to send kill command\n"); 250 return rc; 251 } 252 253 rc = -resp.cmd; 254 MSG_OUT("Kill: %s\n", dbus_err_str[-rc]); 255 256 return rc; 257 } 258 259 static int handle_cmd_reset(struct mboxctl_context *context) 260 { 261 struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; 262 int rc; 263 264 msg.cmd = DBUS_C_RESET; 265 266 rc = send_dbus_msg(context, &msg, &resp); 267 if (rc < 0) { 268 MSG_ERR("Failed to send reset command\n"); 269 return rc; 270 } 271 272 rc = -resp.cmd; 273 MSG_OUT("Reset: %s\n", dbus_err_str[-rc]); 274 275 return rc; 276 } 277 278 static int handle_cmd_suspend(struct mboxctl_context *context) 279 { 280 struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; 281 int rc; 282 283 msg.cmd = DBUS_C_SUSPEND; 284 285 rc = send_dbus_msg(context, &msg, &resp); 286 if (rc < 0) { 287 MSG_ERR("Failed to send suspend command\n"); 288 return rc; 289 } 290 291 rc = -resp.cmd; 292 MSG_OUT("Suspend: %s\n", dbus_err_str[-rc]); 293 294 return rc; 295 } 296 297 static int handle_cmd_resume(struct mboxctl_context *context, char *arg) 298 { 299 struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; 300 int rc; 301 302 if (!arg) { 303 MSG_ERR("Resume command takes an argument\n"); 304 return -E_DBUS_INVAL; 305 } 306 307 msg.cmd = DBUS_C_RESUME; 308 msg.num_args = RESUME_NUM_ARGS; 309 msg.args = calloc(msg.num_args, sizeof(*msg.args)); 310 if (!msg.args) { 311 MSG_ERR("Memory allocation failed\n"); 312 return -E_DBUS_NO_MEM; 313 } 314 315 if (!strncmp(arg, "clean", strlen("clean"))) { 316 msg.args[0] = RESUME_NOT_MODIFIED; 317 } else if (!strncmp(arg, "modified", strlen("modified"))) { 318 msg.args[0] = RESUME_FLASH_MODIFIED; 319 } else { 320 MSG_ERR("Resume command takes argument < \"clean\" | " 321 "\"modified\" >\n"); 322 rc = -E_DBUS_INVAL; 323 goto out; 324 } 325 326 rc = send_dbus_msg(context, &msg, &resp); 327 if (rc < 0) { 328 MSG_ERR("Failed to send resume command\n"); 329 goto out; 330 } 331 332 rc = -resp.cmd; 333 MSG_OUT("Resume: %s\n", dbus_err_str[-rc]); 334 335 out: 336 free(msg.args); 337 return rc; 338 } 339 340 static int handle_cmd_modified(struct mboxctl_context *context) 341 { 342 struct mbox_dbus_msg msg = { 0 }, resp = { 0 }; 343 int rc; 344 345 msg.cmd = DBUS_C_MODIFIED; 346 347 rc = send_dbus_msg(context, &msg, &resp); 348 if (rc < 0) { 349 MSG_ERR("Failed to send flash modified command\n"); 350 return rc; 351 } 352 353 rc = -resp.cmd; 354 MSG_OUT("Clear Cache: %s\n", dbus_err_str[-rc]); 355 356 return rc; 357 } 358 359 static int parse_cmdline(struct mboxctl_context *context, int argc, char **argv) 360 { 361 int opt, rc = -1; 362 363 static const struct option long_options[] = { 364 { "silent", no_argument, 0, 's' }, 365 { "ping", no_argument, 0, 'p' }, 366 { "daemon-state", no_argument, 0, 'd' }, 367 { "lpc-state", no_argument, 0, 'l' }, 368 { "kill", no_argument, 0, 'k' }, 369 { "reset", no_argument, 0, 'r' }, 370 { "point-to-flash", no_argument, 0, 'f' }, 371 { "suspend", no_argument, 0, 'u' }, 372 { "resume", required_argument, 0, 'e' }, 373 { "clear-cache", no_argument, 0, 'c' }, 374 { "version", no_argument, 0, 'v' }, 375 { "help", no_argument, 0, 'h' }, 376 { 0, 0, 0, 0 } 377 }; 378 379 if (argc <= 1) { 380 usage(argv[0]); 381 return -E_DBUS_INVAL; 382 } 383 384 while ((opt = getopt_long(argc, argv, "spdlkrfue:cvh", long_options, 385 NULL)) != -1) { 386 switch (opt) { 387 case 's': 388 silent = true; 389 continue; 390 case 'p': 391 rc = handle_cmd_ping(context); 392 break; 393 case 'd': 394 rc = handle_cmd_daemon_state(context); 395 break; 396 case 'l': 397 rc = handle_cmd_lpc_state(context); 398 break; 399 case 'k': 400 rc = handle_cmd_kill(context); 401 break; 402 case 'r': /* These are the same for now (reset may change) */ 403 case 'f': 404 rc = handle_cmd_reset(context); 405 break; 406 case 'u': 407 rc = handle_cmd_suspend(context); 408 break; 409 case 'e': 410 rc = handle_cmd_resume(context, optarg); 411 break; 412 case 'c': 413 rc = handle_cmd_modified(context); 414 break; 415 case 'v': 416 MSG_OUT("%s V%s\n", NAME, PACKAGE_VERSION); 417 rc = 0; 418 break; 419 case 'h': 420 usage(argv[0]); 421 rc = 0; 422 break; 423 default: 424 usage(argv[0]); 425 rc = -E_DBUS_INVAL; 426 break; 427 } 428 } 429 430 return rc; 431 } 432 433 int main(int argc, char **argv) 434 { 435 struct mboxctl_context context; 436 int rc; 437 438 silent = false; 439 440 rc = init_mboxctl_dbus(&context); 441 if (rc < 0) { 442 MSG_ERR("Failed to init dbus\n"); 443 return rc; 444 } 445 446 rc = parse_cmdline(&context, argc, argv); 447 448 return rc; 449 } 450