1 2 /** 3 * Copyright © 2020 IBM Corporation 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 #include "journal.hpp" 19 20 #include <string.h> 21 #include <time.h> 22 23 #include <cstdint> 24 #include <ctime> 25 #include <stdexcept> 26 27 using namespace phosphor::logging; 28 29 namespace phosphor::power::regulators 30 { 31 32 class JournalCloser 33 { 34 public: 35 JournalCloser() = delete; 36 JournalCloser(const JournalCloser&) = delete; 37 JournalCloser(JournalCloser&&) = delete; 38 JournalCloser& operator=(const JournalCloser&) = delete; 39 JournalCloser& operator=(JournalCloser&&) = delete; 40 41 JournalCloser(sd_journal* journal) : journal{journal} 42 { 43 } 44 ~JournalCloser() 45 { 46 sd_journal_close(journal); 47 } 48 49 private: 50 sd_journal* journal{nullptr}; 51 }; 52 53 std::string SystemdJournal::getFieldValue(sd_journal* journal, 54 const char* field) const 55 { 56 const char* data{nullptr}; 57 size_t length{0}; 58 59 // Get field data 60 int rc = sd_journal_get_data(journal, field, (const void**)&data, &length); 61 if (rc < 0) 62 { 63 throw std::runtime_error{ 64 std::string{"Failed to read journal entry field: "} + 65 strerror(-rc)}; 66 } 67 68 // Get field value 69 size_t prefix{0}; 70 const void* eq = memchr(data, '=', length); 71 if (eq) 72 { 73 prefix = (const char*)eq - data + 1; 74 } 75 else 76 { 77 prefix = 0; 78 } 79 80 std::string value{data + prefix, length - prefix}; 81 82 return value; 83 } 84 85 std::vector<std::string> 86 SystemdJournal::getMessages(const std::string& field, 87 const std::string& fieldValue, unsigned int max) 88 { 89 sd_journal* journal; 90 std::vector<std::string> messages; 91 92 int rc = sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY); 93 if (rc < 0) 94 { 95 throw std::runtime_error{std::string{"Failed to open journal: "} + 96 strerror(-rc)}; 97 } 98 99 JournalCloser closer{journal}; 100 101 SD_JOURNAL_FOREACH_BACKWARDS(journal) 102 { 103 // Get input field 104 std::string value = getFieldValue(journal, field.c_str()); 105 106 // Compare field value and read data 107 if (value == fieldValue) 108 { 109 // Get SYSLOG_IDENTIFIER field 110 std::string syslog = getFieldValue(journal, "SYSLOG_IDENTIFIER"); 111 112 // Get _PID field 113 std::string pid = getFieldValue(journal, "_PID"); 114 115 // Get MESSAGE field 116 std::string message = getFieldValue(journal, "MESSAGE"); 117 118 // Get realtime 119 uint64_t usec{0}; 120 rc = sd_journal_get_realtime_usec(journal, &usec); 121 if (rc < 0) 122 { 123 throw std::runtime_error{ 124 std::string{"Failed to get journal entry timestamp: "} + 125 strerror(-rc)}; 126 } 127 128 // Convert realtime microseconds to date format 129 char dateBuffer[80]; 130 std::string date; 131 std::time_t timeInSecs = usec / 1000000; 132 strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S", 133 std::localtime(&timeInSecs)); 134 date = dateBuffer; 135 136 // Store value to messages 137 value = date + " " + syslog + "[" + pid + "]: " + message; 138 messages.insert(messages.begin(), value); 139 } 140 // Set the maximum number of messages 141 if ((max != 0) && (messages.size() >= max)) 142 { 143 break; 144 } 145 } 146 147 return messages; 148 } 149 150 } // namespace phosphor::power::regulators