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