xref: /openbmc/hiomapd/mboxctl.c (revision 5320f6e0)
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