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