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