1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2019-2019 Bareos GmbH & Co. KG
5
6 This program is Free Software; you can redistribute it and/or
7 modify it under the terms of version three of the GNU Affero General Public
8 License as published by the Free Software Foundation and included
9 in the file LICENSE.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20 */
21
22 #include "include/bareos.h"
23 #include "bnet_network_dump_private.h"
24
25 #include "include/make_unique.h"
26 #include "lib/ascii_control_characters.h"
27 #include "lib/backtrace.h"
28 #include "lib/bareos_resource.h"
29 #include "lib/bnet.h"
30 #include "lib/bsock.h"
31 #include "lib/bsock_tcp.h"
32 #include "lib/bstringlist.h"
33 #include "lib/qualified_resource_name_type_converter.h"
34
35 #include <cassert>
36 #include <algorithm>
37 #include <iostream>
38 #include <fstream>
39 #include <set>
40
41 std::string BnetDumpPrivate::filename_;
42 bool BnetDumpPrivate::plantuml_mode_ = false;
43 std::size_t BnetDumpPrivate::max_data_dump_bytes_ = 100;
44 int BnetDumpPrivate::stack_level_start_ = 6;
45 int BnetDumpPrivate::stack_level_amount_ = 0;
46 std::set<std::string> BnetDumpPrivate::exclude_rcodes_; //= {"R_CONSOLE"};
47
OpenFile()48 void BnetDumpPrivate::OpenFile()
49 {
50 if (!filename_.empty()) {
51 output_file_.open(filename_, std::ios::app);
52 assert(output_file_.is_open());
53 }
54 }
55
CloseFile()56 void BnetDumpPrivate::CloseFile() { output_file_.close(); }
57
SetFilename(const char * filename)58 bool BnetDumpPrivate::SetFilename(const char* filename)
59 {
60 BnetDumpPrivate::filename_ = filename;
61 return true;
62 }
63
CreateDataString(int signal,const char * ptr,int nbytes) const64 std::string BnetDumpPrivate::CreateDataString(int signal,
65 const char* ptr,
66 int nbytes) const
67 {
68 std::size_t string_length = nbytes - BareosSocketTCP::header_length;
69 string_length = std::min(string_length, max_data_dump_bytes_);
70
71 std::string data_string(&ptr[BareosSocketTCP::header_length], string_length);
72
73 if (signal < 0) {
74 data_string
75 = BnetSignalToString(signal) + " - " + BnetSignalToDescription(signal);
76 }
77 std::replace(data_string.begin(), data_string.end(), '\n', ' ');
78 std::replace(data_string.begin(), data_string.end(), '\t', ' ');
79 data_string.erase(
80 std::remove_if(data_string.begin(), data_string.end(),
81 [](char c) { return !isprint(c) || c == '\r'; }),
82 data_string.end());
83
84 return data_string;
85 }
86
CreateFormatStringForNetworkMessage(int signal) const87 std::string BnetDumpPrivate::CreateFormatStringForNetworkMessage(
88 int signal) const
89 {
90 std::string s;
91 if (plantuml_mode_) {
92 if (signal > 998) { // signal set to 999
93 s = "\"%s\" -> \"%s\": (>%3d) %s\\n";
94 } else if (signal < 0) { // bnet signal
95 s = "\"%s\" -> \"%s\": (%4d) %s\\n";
96 } else {
97 s = "\"%s\" -> \"%s\": (%4d) %s\\n";
98 }
99 } else {
100 if (signal > 998) { // signal set to 999
101 s = "%12s -> %-12s: (>%3d) %s\n";
102 } else if (signal < 0) { // bnet signal
103 s = "%12s -> %-12s: (%4d) %s\n";
104 } else {
105 s = "%12s -> %-12s: (%4d) %s\n";
106 }
107 }
108 return s;
109 }
110
IsExcludedRcode(const BStringList & l) const111 bool BnetDumpPrivate::IsExcludedRcode(const BStringList& l) const
112 {
113 if (l.size() > 0) {
114 const std::string& probe = l[0];
115 if (exclude_rcodes_.find(probe) != exclude_rcodes_.end()) { return true; }
116 }
117 return false;
118 }
119
SuppressMessageIfRcodeIsInExcludeList() const120 bool BnetDumpPrivate::SuppressMessageIfRcodeIsInExcludeList() const
121 {
122 BStringList own_name(own_qualified_name_, "::");
123 BStringList destination_name(destination_qualified_name_, "::");
124
125 return IsExcludedRcode(own_name) || IsExcludedRcode(destination_name);
126 }
127
CreateAndWriteMessageToBuffer(const char * ptr,int nbytes)128 void BnetDumpPrivate::CreateAndWriteMessageToBuffer(const char* ptr, int nbytes)
129 {
130 static_assert(BareosSocketTCP::header_length == sizeof(int32_t),
131 "BareosSocket header size does not match");
132 int signal = ntohl(*((int32_t*)&ptr[0]));
133 if (signal > 999) { signal = 999; }
134
135 std::vector<char> buffer(1024);
136
137 snprintf(buffer.data(), buffer.size(),
138 CreateFormatStringForNetworkMessage(signal).c_str(),
139 own_qualified_name_.c_str(), destination_qualified_name_.c_str(),
140 signal, CreateDataString(signal, ptr, nbytes).c_str());
141 output_buffer_ = buffer.data();
142 }
143
CreateAndWriteStacktraceToBuffer()144 void BnetDumpPrivate::CreateAndWriteStacktraceToBuffer()
145 {
146 std::vector<BacktraceInfo> trace_lines(
147 Backtrace(stack_level_start_, stack_level_amount_));
148
149 std::vector<char> buffer(1024);
150 const char* fmt = plantuml_mode_ ? "(T%3d) %s\\n" : "(T%3d) %s\n";
151
152 for (const BacktraceInfo& bt : trace_lines) {
153 std::string s(bt.function_call_.c_str(),
154 std::min(bt.function_call_.size(), max_data_dump_bytes_));
155 snprintf(buffer.data(), buffer.size(), fmt, bt.frame_number_, s.c_str());
156 output_buffer_ += buffer.data();
157 }
158
159 if (plantuml_mode_) { output_buffer_ += "\n"; }
160 }
161
DumpToFile(const char * ptr,int nbytes)162 void BnetDumpPrivate::DumpToFile(const char* ptr, int nbytes)
163 {
164 if (SuppressMessageIfRcodeIsInExcludeList()) { return; }
165
166 if (state_ == State::kRunNormal) {
167 CreateAndWriteMessageToBuffer(ptr, nbytes);
168 CreateAndWriteStacktraceToBuffer();
169 output_file_ << output_buffer_;
170 output_file_.flush();
171
172 } else if (state_ == State::kWaitForDestinationName) {
173 return;
174 }
175 }
176
SaveAndSendMessageIfNoDestinationDefined(const char * ptr,int nbytes)177 void BnetDumpPrivate::SaveAndSendMessageIfNoDestinationDefined(const char* ptr,
178 int nbytes)
179 {
180 if (state_ != State::kWaitForDestinationName) { return; }
181
182 if (destination_qualified_name_.empty()) {
183 std::size_t amount = nbytes;
184 amount = std::min(amount, max_data_dump_bytes_);
185
186 std::vector<char> temp_data;
187 std::copy(ptr, ptr + amount, std::back_inserter(temp_data));
188
189 temporary_buffer_for_initial_messages_.push_back(temp_data);
190
191 if (temporary_buffer_for_initial_messages_.size() > 3) {
192 Dmsg0(100, "BnetDumpPrivate: destination_qualified_name_ not set\n");
193 }
194
195 } else { // !empty() -> send all buffered messages
196 state_ = State::kRunNormal;
197 for (auto& v : temporary_buffer_for_initial_messages_) {
198 DumpToFile(v.data(), v.size());
199 }
200 temporary_buffer_for_initial_messages_.clear();
201 } // destination_qualified_name_.empty()
202 }
203