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