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