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 }