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 }