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