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