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, 96 const char *property, uint8_t *resp) 97 { 98 sd_bus_error error = SD_BUS_ERROR_NULL; 99 100 return sd_bus_get_property_trivial(context->bus, MBOX_DBUS_NAME, 101 MBOX_DBUS_OBJECT, 102 MBOX_DBUS_CONTROL_IFACE, 103 property, &error, 'y', resp); 104 } 105 106 static int handle_cmd_ping(struct mboxctl_context *context) 107 { 108 int rc; 109 110 rc = mboxctl_directive(context, "Ping"); 111 MSG_OUT("Ping: %s\n", rc ? strerror(-rc) : "Success"); 112 113 return rc; 114 } 115 116 static int handle_cmd_daemon_state(struct mboxctl_context *context) 117 { 118 uint8_t resp; 119 int rc; 120 121 rc = mboxctl_getter(context, "DaemonState", &resp); 122 if (rc < 0) 123 return rc; 124 125 MSG_OUT("Daemon State: %s\n", resp == DAEMON_STATE_ACTIVE ? 126 "Active" : "Suspended"); 127 return 0; 128 } 129 130 static int handle_cmd_lpc_state(struct mboxctl_context *context) 131 { 132 uint8_t resp; 133 int rc; 134 135 rc = mboxctl_getter(context, "LpcState", &resp); 136 if (rc < 0) 137 return rc; 138 139 MSG_OUT("LPC Bus Maps: %s\n", resp == LPC_STATE_MEM ? 140 "BMC Memory" : 141 (resp == LPC_STATE_FLASH ? 142 "Flash Device" : 143 "Invalid System State")); 144 145 return 0; 146 } 147 148 static int handle_cmd_kill(struct mboxctl_context *context) 149 { 150 int rc; 151 152 rc = mboxctl_directive(context, "Kill"); 153 MSG_OUT("Kill: %s\n", rc ? strerror(-rc) : "Success"); 154 155 return rc; 156 } 157 158 static int handle_cmd_reset(struct mboxctl_context *context) 159 { 160 int rc; 161 162 rc = mboxctl_directive(context, "Reset"); 163 MSG_OUT("Reset: %s\n", rc ? strerror(-rc) : "Success"); 164 165 return rc; 166 } 167 168 static int handle_cmd_suspend(struct mboxctl_context *context) 169 { 170 int rc; 171 172 rc = mboxctl_directive(context, "Suspend"); 173 MSG_OUT("Suspend: %s\n", rc ? strerror(-rc) : "Success"); 174 175 return rc; 176 } 177 178 static int handle_cmd_resume(struct mboxctl_context *context, char *sarg) 179 { 180 sd_bus_error error = SD_BUS_ERROR_NULL; 181 sd_bus_message *m = NULL, *n = NULL; 182 uint8_t arg; 183 int rc; 184 185 if (!sarg) { 186 MSG_ERR("Resume command takes an argument\n"); 187 return -EINVAL; 188 } 189 190 rc = sd_bus_message_new_method_call(context->bus, &m, 191 MBOX_DBUS_NAME, 192 MBOX_DBUS_OBJECT, 193 MBOX_DBUS_CONTROL_IFACE, 194 "Resume"); 195 if (rc < 0) { 196 MSG_ERR("Failed to init method call: %s\n", 197 strerror(-rc)); 198 goto out; 199 } 200 201 if (!strncmp(sarg, "clean", strlen("clean"))) { 202 arg = RESUME_NOT_MODIFIED; 203 } else if (!strncmp(sarg, "modified", strlen("modified"))) { 204 arg = RESUME_FLASH_MODIFIED; 205 } else { 206 MSG_ERR("Resume command takes argument < \"clean\" | " 207 "\"modified\" >\n"); 208 rc = -EINVAL; 209 goto out; 210 } 211 212 rc = sd_bus_message_append(m, "b", arg); 213 if (rc < 0) { 214 MSG_ERR("Failed to add args to message: %s\n", 215 strerror(-rc)); 216 goto out; 217 } 218 219 rc = sd_bus_call(context->bus, m, 0, &error, &n); 220 if (rc < 0) { 221 MSG_ERR("Failed to post message: %s\n", strerror(-rc)); 222 goto out; 223 } 224 225 MSG_OUT("Resume: %s\n", rc < 0 ? strerror(-rc) : "Success"); 226 227 out: 228 sd_bus_error_free(&error); 229 sd_bus_message_unref(m); 230 sd_bus_message_unref(n); 231 232 return rc < 0 ? rc : 0; 233 } 234 235 static int handle_cmd_modified(struct mboxctl_context *context) 236 { 237 int rc; 238 239 rc = mboxctl_directive(context, "MarkFlashModified"); 240 MSG_OUT("Clear Cache: %s\n", rc ? strerror(-rc) : "Success"); 241 242 return rc; 243 } 244 245 static int handle_cmd_backend(struct mboxctl_context *context, char *sarg) 246 { 247 sd_bus_error error = SD_BUS_ERROR_NULL; 248 sd_bus_message *m = NULL, *n = NULL; 249 char *delim = NULL; 250 char *strv[2]; 251 int rc; 252 253 if (!sarg) { 254 MSG_ERR("Backend command takes an argument\n"); 255 return -EINVAL; 256 } 257 258 rc = sd_bus_message_new_method_call(context->bus, &m, 259 MBOX_DBUS_NAME, 260 MBOX_DBUS_OBJECT, 261 MBOX_DBUS_CONTROL_IFACE, 262 "SetBackend"); 263 if (rc < 0) { 264 MSG_ERR("Failed to init method call: %s\n", 265 strerror(-rc)); 266 goto out; 267 } 268 269 if (!strncmp(sarg, "vpnor", strlen("vpnor"))) { 270 if (strchr(sarg, ':')) { 271 MSG_ERR("Path parameter not supported for vpnor\n"); 272 rc = -EINVAL; 273 goto out; 274 } 275 276 rc = sd_bus_message_append(m, "s", "vpnor"); 277 if (rc < 0) 278 goto out; 279 } else if (!strncmp(sarg, "mtd", strlen("mtd"))) { 280 rc = sd_bus_message_append(m, "s", "mtd"); 281 if (rc < 0) 282 goto out; 283 } else if (!strncmp(sarg, "file", strlen("file"))) { 284 rc = sd_bus_message_append(m, "s", "file"); 285 if (rc < 0) 286 goto out; 287 } else { 288 rc = -EINVAL; 289 goto out; 290 } 291 292 delim = strchr(sarg, ':'); 293 if (delim) { 294 char *path; 295 296 path = realpath(delim + 1, NULL); 297 if (!path) { 298 MSG_ERR("Failed to resolve path: %s\n", 299 strerror(errno)); 300 rc = -errno; 301 goto out; 302 } 303 304 strv[0] = path; 305 strv[1] = NULL; 306 307 rc = sd_bus_message_append_strv(m, &strv[0]); 308 free(path); 309 if (rc < 0) 310 goto out; 311 } else { 312 strv[0] = NULL; 313 strv[1] = NULL; 314 rc = sd_bus_message_append_strv(m, &strv[0]); 315 if (rc < 0) 316 goto out; 317 } 318 319 rc = sd_bus_call(context->bus, m, 0, &error, &n); 320 if (rc < 0) { 321 MSG_ERR("Failed to post message: %s\n", strerror(-rc)); 322 goto out; 323 } 324 325 MSG_OUT("SetBackend: %s\n", rc < 0 ? strerror(-rc) : "Success"); 326 327 out: 328 sd_bus_error_free(&error); 329 sd_bus_message_unref(m); 330 331 return rc < 0 ? rc : 0; 332 } 333 334 static int parse_cmdline(struct mboxctl_context *context, int argc, char **argv) 335 { 336 int opt, rc = -1; 337 338 static const struct option long_options[] = { 339 { "silent", no_argument, 0, 's' }, 340 { "ping", no_argument, 0, 'p' }, 341 { "daemon-state", no_argument, 0, 'd' }, 342 { "lpc-state", no_argument, 0, 'l' }, 343 { "kill", no_argument, 0, 'k' }, 344 { "reset", no_argument, 0, 'r' }, 345 { "point-to-flash", no_argument, 0, 'f' }, 346 { "suspend", no_argument, 0, 'u' }, 347 { "resume", required_argument, 0, 'e' }, 348 { "clear-cache", no_argument, 0, 'c' }, 349 { "backend", required_argument, 0, 'b' }, 350 { "version", no_argument, 0, 'v' }, 351 { "help", no_argument, 0, 'h' }, 352 { 0, 0, 0, 0 } 353 }; 354 355 if (argc <= 1) { 356 usage(argv[0]); 357 return -EINVAL; 358 } 359 360 while ((opt = getopt_long(argc, argv, "spdlkrfue:cvh", long_options, 361 NULL)) != -1) { 362 switch (opt) { 363 case 's': 364 silent = true; 365 continue; 366 case 'p': 367 rc = handle_cmd_ping(context); 368 break; 369 case 'd': 370 rc = handle_cmd_daemon_state(context); 371 break; 372 case 'l': 373 rc = handle_cmd_lpc_state(context); 374 break; 375 case 'k': 376 rc = handle_cmd_kill(context); 377 break; 378 case 'r': /* These are the same for now (reset may change) */ 379 case 'f': 380 rc = handle_cmd_reset(context); 381 break; 382 case 'u': 383 rc = handle_cmd_suspend(context); 384 break; 385 case 'e': 386 rc = handle_cmd_resume(context, optarg); 387 break; 388 case 'c': 389 rc = handle_cmd_modified(context); 390 break; 391 case 'b': 392 rc = handle_cmd_backend(context, optarg); 393 break; 394 case 'v': 395 MSG_OUT("%s V%s\n", NAME, PACKAGE_VERSION); 396 rc = 0; 397 break; 398 case 'h': 399 usage(argv[0]); 400 rc = 0; 401 break; 402 default: 403 usage(argv[0]); 404 rc = -EINVAL; 405 break; 406 } 407 } 408 409 return rc; 410 } 411 412 int main(int argc, char **argv) 413 { 414 struct mboxctl_context context; 415 int rc; 416 417 silent = false; 418 419 rc = init_mboxctl_dbus(&context); 420 if (rc < 0) { 421 MSG_ERR("Failed to init dbus\n"); 422 return rc; 423 } 424 425 rc = parse_cmdline(&context, argc, argv); 426 427 return rc; 428 } 429