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