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