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