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.H"
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 
43 static void message_entry_close(messageEntry_t *m)
44 {
45 	free(m);
46 	return;
47 }
48 
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
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 
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, *saveptr;
103 
104 	rec = message_record_open(m->em, m->logid);
105 	if (!rec) {
106 		fprintf(stderr,"Warning missing event log for %lx\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 
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 %lx\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 
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 /////////////////////////////////////////////////////////////
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 
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 
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 }
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 	char *s;
297 	uint16_t logid;
298 	event_record_t rec;
299 	event_manager *em = (event_manager *) userdata;
300 
301 	rec.message     = (char*) "A Test event log just happened";
302 	rec.severity    = (char*) "Info";
303 	rec.association = (char*) "/org/openbmc/inventory/system/chassis/motherboard/dimm3 " \
304 				  "/org/openbmc/inventory/system/chassis/motherboard/dimm2";
305 	rec.reportedby  = (char*) "Test";
306 	rec.p           = (uint8_t*) p;
307 	rec.n           = 6;
308 
309 
310 	syslog(LOG_NOTICE, "%s %s (%s)", rec.severity, rec.message, rec.association);
311 	logid = message_create_new_log_event(em, &rec);
312 
313 	if (logid)
314 		send_log_to_dbus(em, logid, rec.association);
315 
316 	return sd_bus_reply_method_return(m, "q", logid);
317 }
318 
319 static int finish_delete_log(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
320 {
321 	return 0;
322 }
323 
324 static int method_clearall(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
325 {
326 	event_manager *em = (event_manager *) userdata;
327 	uint16_t logid;
328 	char buffer[32];
329 	int r;
330 	sd_bus_message *reply;
331 
332 	message_refresh_events(em);
333 
334 	while (logid = message_next_event(em)) {
335 		snprintf(buffer, sizeof(buffer),
336 			"%s/%d", event_path, logid);
337 
338 		r = sd_bus_call_method_async(bus,
339 					     NULL,
340 					     "org.openbmc.records.events",
341 					     buffer,
342 					     "org.openbmc.Object.Delete",
343 					     "delete",
344 					     finish_delete_log,
345 					     NULL,
346 					     NULL);
347 		if (r < 0) {
348 			fprintf(stderr,
349 				"sd_bus_call_method_async Failed : %s\n",
350 				strerror(-r));
351 			return -1;
352 		}
353 	}
354 
355 	return sd_bus_reply_method_return(m, "q", 0);
356 }
357 
358 
359 static int method_deletelog(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
360 {
361 	messageEntry_t *p = (messageEntry_t *) userdata;
362 
363 	message_delete_log(p->em, p->logid);
364 	remove_log_from_dbus(p);
365 	return sd_bus_reply_method_return(m, "q", 0);
366 }
367 
368 
369 
370 static const sd_bus_vtable recordlog_vtable[] = {
371 	SD_BUS_VTABLE_START(0),
372 	SD_BUS_METHOD("acceptHostMessage", "sssay", "q", method_accept_host_message, SD_BUS_VTABLE_UNPRIVILEGED),
373 	SD_BUS_METHOD("acceptBMCMessage", "sssay", "q", method_accept_bmc_message, SD_BUS_VTABLE_UNPRIVILEGED),
374 	SD_BUS_METHOD("acceptTestMessage", NULL, "q", method_accept_test_message, SD_BUS_VTABLE_UNPRIVILEGED),
375 	SD_BUS_METHOD("clear", NULL, "q", method_clearall, SD_BUS_VTABLE_UNPRIVILEGED),
376 	SD_BUS_VTABLE_END
377 };
378 
379 static const sd_bus_vtable log_vtable[] = {
380 	SD_BUS_VTABLE_START(0),
381 	SD_BUS_PROPERTY("message",     "s",  prop_message,    0, SD_BUS_VTABLE_PROPERTY_CONST),
382 	SD_BUS_PROPERTY("severity",    "s",  prop_message,    0, SD_BUS_VTABLE_PROPERTY_CONST),
383 	SD_BUS_PROPERTY("reported_by", "s",  prop_message,    0, SD_BUS_VTABLE_PROPERTY_CONST),
384 	SD_BUS_PROPERTY("time",        "s",  prop_message,    0, SD_BUS_VTABLE_PROPERTY_CONST),
385 	SD_BUS_PROPERTY("debug_data",  "ay", prop_message_dd ,0, SD_BUS_VTABLE_PROPERTY_CONST),
386 	SD_BUS_VTABLE_END
387 };
388 
389 
390 static const sd_bus_vtable recordlog_delete_vtable[] = {
391 	SD_BUS_VTABLE_START(0),
392 	SD_BUS_METHOD("delete", NULL, "q", method_deletelog, SD_BUS_VTABLE_UNPRIVILEGED),
393 	SD_BUS_VTABLE_END
394 };
395 
396 static const sd_bus_vtable recordlog_association_vtable[] = {
397 	SD_BUS_VTABLE_START(0),
398 	SD_BUS_PROPERTY("associations", "a(sss)",  prop_message_assoc, 0, SD_BUS_VTABLE_PROPERTY_CONST),
399 	SD_BUS_VTABLE_END
400 };
401 
402 static int remove_log_from_dbus(messageEntry_t *p)
403 {
404 	int r;
405 	char buffer[32];
406 
407 	snprintf(buffer, sizeof(buffer), "%s/%lu", event_path, p->logid);
408 
409 	printf("Attempting to delete %s\n", buffer);
410 
411 	r = sd_bus_emit_object_removed(bus, buffer);
412 	if (r < 0) {
413 		fprintf(stderr, "Failed to emit the delete signal %s\n", strerror(-r));
414 		return -1;
415 	}
416 	sd_bus_slot_unref(p->messageslot);
417 	sd_bus_slot_unref(p->deleteslot);
418 
419 	if (p->associationslot)
420 		sd_bus_slot_unref(p->associationslot);
421 
422 	message_entry_close(p);
423 
424 	return 0;
425 }
426 
427 int send_log_to_dbus(event_manager *em, const uint16_t logid, const char *association)
428 {
429 	char loglocation[64];
430 	int r;
431 	messageEntry_t *m;
432 
433 	snprintf(loglocation, sizeof(loglocation), "%s/%d", event_path, logid);
434 
435 	message_entry_new(&m, logid, em);
436 
437 	r = sd_bus_add_object_vtable(bus,
438 				     &m->messageslot,
439 				     loglocation,
440 				     "org.openbmc.record",
441 				     log_vtable,
442 				     m);
443 	if (r < 0) {
444 		fprintf(stderr, "Failed to acquire service name: %s %s\n",
445 			loglocation, strerror(-r));
446 		message_entry_close(m);
447 		return 0;
448 	}
449 
450 	r = sd_bus_add_object_vtable(bus,
451 				     &m->deleteslot,
452 				     loglocation,
453 				     "org.openbmc.Object.Delete",
454 				     recordlog_delete_vtable,
455 				     m);
456 
457 	if (r < 0) {
458 		fprintf(stderr, "Failed to add delete object for: %s, %s\n",
459 			loglocation, strerror(-r));
460 		message_entry_close(m);
461 		return 0;
462 	}
463 
464 	m->associationslot = NULL;
465 	if (strlen(association) > 0) {
466 		r = sd_bus_add_object_vtable(bus,
467 					     &m->associationslot,
468 					     loglocation,
469 					     "org.openbmc.Associations",
470 					     recordlog_association_vtable,
471 					     m);
472 		if (r < 0) {
473 			fprintf(stderr, "Failed to add association object for: %s %s\n",
474 				loglocation, strerror(-r));
475 			message_entry_close(m);
476 			return 0;
477 		}
478 	}
479 
480 	r = sd_bus_emit_object_added(bus, loglocation);
481 	if (r < 0) {
482 		fprintf(stderr, "Failed to emit signal %s\n", strerror(-r));
483 		message_entry_close(m);
484 		return 0;
485 	}
486 
487 	return logid;
488 }
489 
490 
491 int start_event_monitor(void)
492 {
493 	int r;
494 
495 	for (;;) {
496 
497 		r = sd_bus_process(bus, NULL);
498 		if (r < 0) {
499 			fprintf(stderr, "Error bus process: %s\n", strerror(-r));
500 			break;
501 		}
502 
503 		if (r > 0)
504 			continue;
505 
506 		r = sd_bus_wait(bus, (uint64_t) -1);
507 		if (r < 0) {
508 			fprintf(stderr, "Error in sd_bus_wait: %s\n", strerror(-r));
509 			break;
510 		}
511 	}
512 
513 	return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
514 }
515 
516 
517 /* Only thing we are doing in this function is to get a connection on the dbus */
518 int build_bus(event_manager *em)
519 {
520 
521 	int r = 0;
522 
523 	/* Connect to the system bus */
524 	r = sd_bus_open_system(&bus);
525 	if (r < 0) {
526 		fprintf(stderr, "Error connecting to system bus: %s\n", strerror(-r));
527 		goto finish;
528 	}
529 
530 	/* Install the object */
531 	r = sd_bus_add_object_vtable(bus,
532 				     &slot,
533 				     "/org/openbmc/records/events",
534 				     "org.openbmc.recordlog",
535 				     recordlog_vtable,
536 				     em);
537 	if (r < 0) {
538 		fprintf(stderr, "Error adding vtable: %s\n", strerror(-r));
539 		goto finish;
540 	}
541 
542 	r = sd_bus_request_name(bus, "org.openbmc.records.events", 0);
543 	if (r < 0) {
544 		fprintf(stderr, "Error requesting name: %s\n", strerror(-r));
545 	}
546 
547 	/* You want to add an object manager to support deleting stuff  */
548 	/* without it, dbus can show interfaces that no longer exist */
549 	r = sd_bus_add_object_manager(bus, NULL, event_path);
550 	if (r < 0) {
551 		fprintf(stderr, "Object Manager failure  %s\n", strerror(-r));
552 	}
553 
554 
555 	finish:
556 	return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
557 }
558 
559 void cleanup_event_monitor(void)
560 {
561 	sd_bus_slot_unref(slot);
562 	sd_bus_unref(bus);
563 }