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