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