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