xref: /openbmc/phosphor-event/event_messaged_sdbus.c (revision acdc2a909e7464111b259fb94509dcf8dab7c626)
1  #define _GNU_SOURCE
2  #include <stdio.h>
3  #include <stdlib.h>
4  #include <errno.h>
5  #include <stddef.h>
6  #include <systemd/sd-bus.h>
7  #include "message.hpp"
8  #include "event_messaged_sdbus.h"
9  #include <syslog.h>
10  
11  /*****************************************************************************/
12  /* This set of functions are responsible for interactions with events over   */
13  /* dbus.  Logs come in a couple of different ways...                         */
14  /*     1) From the calls from acceptHostMessage, acceptTestMessage           */
15  /*     2) At startup and logs that exist alreafy are re-added                */
16  /*                                                                           */
17  /* event_record_t when loaded contain all strings and data stream for a log  */
18  /*                                                                           */
19  /* Functions naming convention                                               */
20  /*     prop_x    : callable dbus properties.                                 */
21  /*     method_x  : callable dbus functions.                                  */
22  /*                                                                           */
23  /*****************************************************************************/
24  const char *event_path = "/org/openbmc/records/events";
25  
26  sd_bus      *bus   = NULL;
27  sd_bus_slot *slot  = NULL;
28  
29  event_record_t *gCachedRec = NULL;
30  
31  typedef struct messageEntry_t {
32  
33  	size_t         logid;
34  	sd_bus_slot   *messageslot;
35  	sd_bus_slot   *deleteslot;
36  	sd_bus_slot   *associationslot;
37  	event_manager *em;
38  
39  } messageEntry_t;
40  
41  static int remove_log_from_dbus(messageEntry_t *node);
42  
message_entry_close(messageEntry_t * m)43  static void message_entry_close(messageEntry_t *m)
44  {
45  	free(m);
46  	return;
47  }
48  
message_entry_new(messageEntry_t ** m,uint16_t logid,event_manager * em)49  static void message_entry_new(messageEntry_t **m, uint16_t logid, event_manager *em)
50  {
51  	*m          = malloc(sizeof(messageEntry_t));
52  	(*m)->logid = logid;
53  	(*m)->em    = em;
54  	return;
55  }
56  
57  // After calling this function the gCachedRec will be set
message_record_open(event_manager * em,uint16_t logid)58  static event_record_t* message_record_open(event_manager *em, uint16_t logid)
59  {
60  
61  	int r = 0;
62  	event_record_t *rec;
63  
64  	// A simple caching technique because each
65  	// property needs to extract data from the
66  	// same data blob.
67  	if (gCachedRec == NULL) {
68  		if (message_load_log(em, logid, &rec)) {
69  			gCachedRec = rec;
70  			return gCachedRec;
71  		} else
72  			return NULL;
73  	}
74  
75  	if (logid == gCachedRec->logid) {
76  		r = 1;
77  
78  	} else {
79  		message_free_log(em, gCachedRec);
80  		gCachedRec = NULL;
81  
82  		r = message_load_log(em, logid, &rec);
83  		if (r)
84  			gCachedRec = rec;
85  	}
86  
87  	return (r ? gCachedRec : NULL);
88  }
89  
prop_message_assoc(sd_bus * bus,const char * path,const char * interface,const char * property,sd_bus_message * reply,void * userdata,sd_bus_error * error)90  static int prop_message_assoc(sd_bus *bus,
91  			const char *path,
92  			const char *interface,
93  			const char *property,
94  			sd_bus_message *reply,
95  			void *userdata,
96  			sd_bus_error *error)
97  {
98  	int r=0;
99  	messageEntry_t *m = (messageEntry_t*) userdata;
100  	event_record_t *rec;
101  	char *p;
102  	char *token;
103  
104  	rec = message_record_open(m->em, m->logid);
105  	if (!rec) {
106  		fprintf(stderr,"Warning missing event log for %zx\n", m->logid);
107  		sd_bus_error_set(error,
108  			SD_BUS_ERROR_FILE_NOT_FOUND,
109  			"Could not find log file");
110  		return -1;
111  	}
112  
113  	/* strtok manipulates a string.  It turns out that message_record_open */
114  	/* implements a caching mechcanism which means the oiginal string is   */
115  	/* To avoid that, I will make a copy and mess with that                */
116  	p = strdup(rec->association);
117  
118  	if (!p) {
119  		/* no association string == no associations */
120  		sd_bus_error_set(error,
121  			SD_BUS_ERROR_NO_MEMORY,
122  			"Not enough memory for association");
123  		return -1;
124  	}
125  
126  	token = strtok(p, " ");
127  
128  	if (token) {
129  
130  		r = sd_bus_message_open_container(reply, 'a', "(sss)");
131  		if (r < 0) {
132  			fprintf(stderr,"Error opening container %s to reply %s\n", token, strerror(-r));
133  		}
134  
135  		while(token) {
136  			r = sd_bus_message_append(reply, "(sss)", "fru", "event", token);
137  			if (r < 0) {
138  				fprintf(stderr,"Error adding properties for %s to reply %s\n", token, strerror(-r));
139  			}
140  
141  			token = strtok(NULL, " ");
142  		}
143  
144  		r = sd_bus_message_close_container(reply);
145  	}
146  
147  	free(p);
148  
149  	return r;
150  }
151  
152  
prop_message(sd_bus * bus,const char * path,const char * interface,const char * property,sd_bus_message * reply,void * userdata,sd_bus_error * error)153  static int prop_message(sd_bus *bus,
154  			const char *path,
155  			const char *interface,
156  			const char *property,
157  			sd_bus_message *reply,
158  			void *userdata,
159  			sd_bus_error *error)
160  {
161  	int r=0;
162  	messageEntry_t *m = (messageEntry_t*) userdata;
163  	char *p;
164  	struct tm *tm_info;
165  	char buffer[36];
166  	event_record_t *rec;
167  
168  	rec = message_record_open(m->em, m->logid);
169  	if (!rec) {
170  		fprintf(stderr,"Warning missing event log for %zx\n", m->logid);
171  		sd_bus_error_set(error,
172  			SD_BUS_ERROR_FILE_NOT_FOUND,
173  			"Could not find log file");
174  		return -1;
175  	}
176  
177  	if (!strncmp("message", property, 7)) {
178  		p = rec->message;
179  	} else if (!strncmp("severity", property, 8)) {
180  		p = rec->severity;
181  	} else if (!strncmp("reported_by", property, 11)) {
182  		p = rec->reportedby;
183  	} else if (!strncmp("time", property, 4)) {
184  		tm_info = localtime(&rec->timestamp);
185  		strftime(buffer, 26, "%Y:%m:%d %H:%M:%S", tm_info);
186  		p = buffer;
187  	} else {
188  		p = "";
189  	}
190  
191  	r = sd_bus_message_append(reply, "s", p);
192  	if (r < 0) {
193  	    fprintf(stderr,"Error adding property to reply %s\n", strerror(-r));
194  	}
195  
196  	return r;
197  }
198  
199  
prop_message_dd(sd_bus * bus,const char * path,const char * interface,const char * property,sd_bus_message * reply,void * userdata,sd_bus_error * error)200  static int prop_message_dd(sd_bus *bus,
201  		       const char *path,
202  		       const char *interface,
203  		       const char *property,
204  		       sd_bus_message *reply,
205  		       void *userdata,
206  		       sd_bus_error *error)
207  {
208  
209  	event_record_t *rec;
210  	messageEntry_t *m = (messageEntry_t*) userdata;
211  
212  
213  	rec = message_record_open(m->em, m->logid);
214  
215  	if (!rec) {
216  		sd_bus_error_set(error,
217  				 SD_BUS_ERROR_FILE_NOT_FOUND,
218  				 "Could not find log file");
219  
220  		return -1;
221  	}
222  	return sd_bus_message_append_array(reply, 'y', rec->p, rec->n);
223  }
224  
225  /////////////////////////////////////////////////////////////
226  // Receives an array of bytes as an esel error log
227  // returns the messageid in 2 byte format
228  //
229  //  S1 - Message - Simple sentence about the fail
230  //  S2 - Severity - How bad of a problem is this
231  //  S3 - Association - sensor path
232  //  ay - Detailed data - developer debug information
233  //
234  /////////////////////////////////////////////////////////////
accept_message(sd_bus_message * m,void * userdata,sd_bus_error * ret_error,char * reportedby)235  static int accept_message(sd_bus_message *m,
236  				      void *userdata,
237  				      sd_bus_error *ret_error,
238  				      char *reportedby)
239  {
240  	char *message, *severity, *association;
241  	size_t   n = 4;
242  	uint8_t *p;
243  	int r;
244  	uint16_t logid;
245  	event_record_t rec;
246  	event_manager *em = (event_manager *) userdata;
247  
248  	r = sd_bus_message_read(m, "sss", &message, &severity, &association);
249  	if (r < 0) {
250  		fprintf(stderr, "Error parsing strings: %s\n", 	strerror(-r));
251  		return r;
252  	}
253  
254  	r = sd_bus_message_read_array(m, 'y', (const void **)&p, &n);
255  	if (r < 0) {
256  		fprintf(stderr, "Error parsing debug data: %s\n", strerror(-r));
257  		return r;
258  	}
259  
260  	rec.message     = (char*) message;
261  	rec.severity    = (char*) severity;
262  	rec.association = (char*) association;
263  	rec.reportedby  = reportedby;
264  	rec.p           = (uint8_t*) p;
265  	rec.n           = n;
266  
267  	syslog(LOG_NOTICE, "%s %s (%s)", rec.severity, rec.message, rec.association);
268  
269  	logid = message_create_new_log_event(em, &rec);
270  
271  	if (logid)
272  		r = send_log_to_dbus(em, logid, rec.association);
273  
274  	return sd_bus_reply_method_return(m, "q", logid);
275  }
276  
method_accept_host_message(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)277  static int method_accept_host_message(sd_bus_message *m,
278  				      void *userdata,
279  				      sd_bus_error *ret_error)
280  {
281  	return accept_message(m, userdata, ret_error, "Host");
282  }
283  
method_accept_bmc_message(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)284  static int method_accept_bmc_message(sd_bus_message *m,
285  				      void *userdata,
286  				      sd_bus_error *ret_error)
287  {
288  	return accept_message(m, userdata, ret_error, "BMC");
289  }
method_accept_test_message(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)290  static int method_accept_test_message(sd_bus_message *m,
291  				      void *userdata,
292  				      sd_bus_error *ret_error)
293  {
294  	//  Random debug data including, ascii, null, >signed int, max
295  	uint8_t p[] = {0x30, 0x00, 0x13, 0x7F, 0x88, 0xFF};
296  	uint16_t logid;
297  	event_record_t rec;
298  	event_manager *em = (event_manager *) userdata;
299  
300  	rec.message     = (char*) "A Test event log just happened";
301  	rec.severity    = (char*) "Info";
302  	rec.association = (char*) "/org/openbmc/inventory/system/chassis/motherboard/dimm3 " \
303  				  "/org/openbmc/inventory/system/chassis/motherboard/dimm2";
304  	rec.reportedby  = (char*) "Test";
305  	rec.p           = (uint8_t*) p;
306  	rec.n           = 6;
307  
308  
309  	syslog(LOG_NOTICE, "%s %s (%s)", rec.severity, rec.message, rec.association);
310  	logid = message_create_new_log_event(em, &rec);
311  
312  	if (logid)
313  		send_log_to_dbus(em, logid, rec.association);
314  
315  	return sd_bus_reply_method_return(m, "q", logid);
316  }
317  
finish_delete_log(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)318  static int finish_delete_log(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
319  {
320  	return 0;
321  }
322  
method_clearall(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)323  static int method_clearall(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
324  {
325  	event_manager *em = (event_manager *) userdata;
326  	uint16_t logid;
327  	char buffer[32];
328  	int r;
329  
330  	message_refresh_events(em);
331  
332  	while ((logid = message_next_event(em))) {
333  		snprintf(buffer, sizeof(buffer),
334  			"%s/%d", event_path, logid);
335  
336  		r = sd_bus_call_method_async(bus,
337  					     NULL,
338  					     "org.openbmc.records.events",
339  					     buffer,
340  					     "org.openbmc.Object.Delete",
341  					     "delete",
342  					     finish_delete_log,
343  					     NULL,
344  					     NULL);
345  		if (r < 0) {
346  			fprintf(stderr,
347  				"sd_bus_call_method_async Failed : %s\n",
348  				strerror(-r));
349  			return -1;
350  		}
351  	}
352  
353  	return sd_bus_reply_method_return(m, "q", 0);
354  }
355  
356  
method_deletelog(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)357  static int method_deletelog(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
358  {
359  	messageEntry_t *p = (messageEntry_t *) userdata;
360  
361  	message_delete_log(p->em, p->logid);
362  	remove_log_from_dbus(p);
363  	return sd_bus_reply_method_return(m, "q", 0);
364  }
365  
366  
367  
368  static const sd_bus_vtable recordlog_vtable[] = {
369  	SD_BUS_VTABLE_START(0),
370  	SD_BUS_METHOD("acceptHostMessage", "sssay", "q", method_accept_host_message, SD_BUS_VTABLE_UNPRIVILEGED),
371  	SD_BUS_METHOD("acceptBMCMessage", "sssay", "q", method_accept_bmc_message, SD_BUS_VTABLE_UNPRIVILEGED),
372  	SD_BUS_METHOD("acceptTestMessage", NULL, "q", method_accept_test_message, SD_BUS_VTABLE_UNPRIVILEGED),
373  	SD_BUS_METHOD("clear", NULL, "q", method_clearall, SD_BUS_VTABLE_UNPRIVILEGED),
374  	SD_BUS_VTABLE_END
375  };
376  
377  static const sd_bus_vtable log_vtable[] = {
378  	SD_BUS_VTABLE_START(0),
379  	SD_BUS_PROPERTY("message",     "s",  prop_message,    0, SD_BUS_VTABLE_PROPERTY_CONST),
380  	SD_BUS_PROPERTY("severity",    "s",  prop_message,    0, SD_BUS_VTABLE_PROPERTY_CONST),
381  	SD_BUS_PROPERTY("reported_by", "s",  prop_message,    0, SD_BUS_VTABLE_PROPERTY_CONST),
382  	SD_BUS_PROPERTY("time",        "s",  prop_message,    0, SD_BUS_VTABLE_PROPERTY_CONST),
383  	SD_BUS_PROPERTY("debug_data",  "ay", prop_message_dd ,0, SD_BUS_VTABLE_PROPERTY_CONST),
384  	SD_BUS_VTABLE_END
385  };
386  
387  
388  static const sd_bus_vtable recordlog_delete_vtable[] = {
389  	SD_BUS_VTABLE_START(0),
390  	SD_BUS_METHOD("delete", NULL, "q", method_deletelog, SD_BUS_VTABLE_UNPRIVILEGED),
391  	SD_BUS_VTABLE_END
392  };
393  
394  static const sd_bus_vtable recordlog_association_vtable[] = {
395  	SD_BUS_VTABLE_START(0),
396  	SD_BUS_PROPERTY("associations", "a(sss)",  prop_message_assoc, 0, SD_BUS_VTABLE_PROPERTY_CONST),
397  	SD_BUS_VTABLE_END
398  };
399  
remove_log_from_dbus(messageEntry_t * p)400  static int remove_log_from_dbus(messageEntry_t *p)
401  {
402  	int r;
403  	char buffer[32];
404  
405  	snprintf(buffer, sizeof(buffer), "%s/%zu", event_path, p->logid);
406  
407  	printf("Attempting to delete %s\n", buffer);
408  
409  	r = sd_bus_emit_object_removed(bus, buffer);
410  	if (r < 0) {
411  		fprintf(stderr, "Failed to emit the delete signal %s\n", strerror(-r));
412  		return -1;
413  	}
414  	sd_bus_slot_unref(p->messageslot);
415  	sd_bus_slot_unref(p->deleteslot);
416  
417  	if (p->associationslot)
418  		sd_bus_slot_unref(p->associationslot);
419  
420  	message_entry_close(p);
421  
422  	return 0;
423  }
424  
send_log_to_dbus(event_manager * em,const uint16_t logid,const char * association)425  int send_log_to_dbus(event_manager *em, const uint16_t logid, const char *association)
426  {
427  	char loglocation[64];
428  	int r;
429  	messageEntry_t *m;
430  
431  	snprintf(loglocation, sizeof(loglocation), "%s/%d", event_path, logid);
432  
433  	message_entry_new(&m, logid, em);
434  
435  	r = sd_bus_add_object_vtable(bus,
436  				     &m->messageslot,
437  				     loglocation,
438  				     "org.openbmc.record",
439  				     log_vtable,
440  				     m);
441  	if (r < 0) {
442  		fprintf(stderr, "Failed to acquire service name: %s %s\n",
443  			loglocation, strerror(-r));
444  		message_entry_close(m);
445  		return 0;
446  	}
447  
448  	r = sd_bus_add_object_vtable(bus,
449  				     &m->deleteslot,
450  				     loglocation,
451  				     "org.openbmc.Object.Delete",
452  				     recordlog_delete_vtable,
453  				     m);
454  
455  	if (r < 0) {
456  		fprintf(stderr, "Failed to add delete object for: %s, %s\n",
457  			loglocation, strerror(-r));
458  		message_entry_close(m);
459  		return 0;
460  	}
461  
462  	m->associationslot = NULL;
463  	if (strlen(association) > 0) {
464  		r = sd_bus_add_object_vtable(bus,
465  					     &m->associationslot,
466  					     loglocation,
467  					     "org.openbmc.Associations",
468  					     recordlog_association_vtable,
469  					     m);
470  		if (r < 0) {
471  			fprintf(stderr, "Failed to add association object for: %s %s\n",
472  				loglocation, strerror(-r));
473  			message_entry_close(m);
474  			return 0;
475  		}
476  	}
477  
478  	r = sd_bus_emit_object_added(bus, loglocation);
479  	if (r < 0) {
480  		fprintf(stderr, "Failed to emit signal %s\n", strerror(-r));
481  		message_entry_close(m);
482  		return 0;
483  	}
484  
485  	return logid;
486  }
487  
488  
start_event_monitor(void)489  int start_event_monitor(void)
490  {
491  	int r;
492  
493  	for (;;) {
494  
495  		r = sd_bus_process(bus, NULL);
496  		if (r < 0) {
497  			fprintf(stderr, "Error bus process: %s\n", strerror(-r));
498  			break;
499  		}
500  
501  		if (r > 0)
502  			continue;
503  
504  		r = sd_bus_wait(bus, (uint64_t) -1);
505  		if (r < 0) {
506  			fprintf(stderr, "Error in sd_bus_wait: %s\n", strerror(-r));
507  			break;
508  		}
509  	}
510  
511  	return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
512  }
513  
514  
515  /* Only thing we are doing in this function is to get a connection on the dbus */
build_bus(event_manager * em)516  int build_bus(event_manager *em)
517  {
518  
519  	int r = 0;
520  
521  	/* Connect to the system bus */
522  	r = sd_bus_open_system(&bus);
523  	if (r < 0) {
524  		fprintf(stderr, "Error connecting to system bus: %s\n", strerror(-r));
525  		goto finish;
526  	}
527  
528  	/* Install the object */
529  	r = sd_bus_add_object_vtable(bus,
530  				     &slot,
531  				     "/org/openbmc/records/events",
532  				     "org.openbmc.recordlog",
533  				     recordlog_vtable,
534  				     em);
535  	if (r < 0) {
536  		fprintf(stderr, "Error adding vtable: %s\n", strerror(-r));
537  		goto finish;
538  	}
539  
540  	r = sd_bus_request_name(bus, "org.openbmc.records.events", 0);
541  	if (r < 0) {
542  		fprintf(stderr, "Error requesting name: %s\n", strerror(-r));
543  	}
544  
545  	/* You want to add an object manager to support deleting stuff  */
546  	/* without it, dbus can show interfaces that no longer exist */
547  	r = sd_bus_add_object_manager(bus, NULL, event_path);
548  	if (r < 0) {
549  		fprintf(stderr, "Object Manager failure  %s\n", strerror(-r));
550  	}
551  
552  
553  	finish:
554  	return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
555  }
556  
cleanup_event_monitor(void)557  void cleanup_event_monitor(void)
558  {
559  	sd_bus_slot_unref(slot);
560  	sd_bus_unref(bus);
561  }
562