1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23
24 #include "lib/parse_conf_state_machine.h"
25 #include "lib/parse_conf.h"
26 #include "lib/resource_item.h"
27 #include "lib/lex.h"
28 #include "lib/qualified_resource_name_type_converter.h"
29
ConfigParserStateMachine(const char * config_file_name,void * caller_ctx,LEX_ERROR_HANDLER * scan_error,LEX_WARNING_HANDLER * scan_warning,ConfigurationParser & my_config)30 ConfigParserStateMachine::ConfigParserStateMachine(
31 const char* config_file_name,
32 void* caller_ctx,
33 LEX_ERROR_HANDLER* scan_error,
34 LEX_WARNING_HANDLER* scan_warning,
35 ConfigurationParser& my_config)
36 : config_file_name_(config_file_name)
37 , caller_ctx_(caller_ctx)
38 , scan_error_(scan_error)
39 , scan_warning_(scan_warning)
40 , my_config_(my_config)
41 {
42 return;
43 };
44
~ConfigParserStateMachine()45 ConfigParserStateMachine::~ConfigParserStateMachine()
46 {
47 while (lexical_parser_) { lexical_parser_ = LexCloseFile(lexical_parser_); }
48 }
49
ParseAllTokens()50 bool ConfigParserStateMachine::ParseAllTokens()
51 {
52 int token;
53
54 while ((token = LexGetToken(lexical_parser_, BCT_ALL)) != BCT_EOF) {
55 Dmsg3(900, "parse state=%d parser_pass_number_=%d got token=%s\n", state,
56 parser_pass_number_, lex_tok_to_str(token));
57 switch (state) {
58 case ParseState::kInit:
59 switch (ParserInitResource(token)) {
60 case ParseInternalReturnCode::kGetNextToken:
61 case ParseInternalReturnCode::kNextState:
62 continue;
63 case ParseInternalReturnCode::kError:
64 return false;
65 default:
66 ASSERT(false);
67 }
68 break;
69 case ParseState::kResource:
70 switch (ScanResource(token)) {
71 case ParseInternalReturnCode::kGetNextToken:
72 continue;
73 case ParseInternalReturnCode::kError:
74 return false;
75 default:
76 ASSERT(false);
77 }
78 break;
79 default:
80 scan_err1(lexical_parser_, _("Unknown parser state %d\n"), state);
81 return false;
82 }
83 }
84 return true;
85 }
86
FreeUnusedMemoryFromPass2()87 void ConfigParserStateMachine::FreeUnusedMemoryFromPass2()
88 {
89 if (parser_pass_number_ == 2) {
90 // free all resource memory from second pass
91 if (currently_parsed_resource_.allocated_resource_) {
92 if (currently_parsed_resource_.allocated_resource_->resource_name_) {
93 free(currently_parsed_resource_.allocated_resource_->resource_name_);
94 }
95 delete currently_parsed_resource_.allocated_resource_;
96 }
97 currently_parsed_resource_.rcode_ = 0;
98 currently_parsed_resource_.resource_items_ = nullptr;
99 currently_parsed_resource_.allocated_resource_ = nullptr;
100 }
101 }
102
103 ConfigParserStateMachine::ParseInternalReturnCode
ScanResource(int token)104 ConfigParserStateMachine::ScanResource(int token)
105 {
106 switch (token) {
107 case BCT_BOB:
108 config_level_++;
109 return ParseInternalReturnCode::kGetNextToken;
110 case BCT_IDENTIFIER: {
111 if (config_level_ != 1) {
112 scan_err1(lexical_parser_, _("not in resource definition: %s"),
113 lexical_parser_->str);
114 return ParseInternalReturnCode::kError;
115 }
116
117 int resource_item_index = my_config_.GetResourceItemIndex(
118 currently_parsed_resource_.resource_items_, lexical_parser_->str);
119
120 if (resource_item_index >= 0) {
121 ResourceItem* item = nullptr;
122 item = ¤tly_parsed_resource_.resource_items_[resource_item_index];
123 if (!(item->flags & CFG_ITEM_NO_EQUALS)) {
124 token = LexGetToken(lexical_parser_, BCT_SKIP_EOL);
125 Dmsg1(900, "in BCT_IDENT got token=%s\n", lex_tok_to_str(token));
126 if (token != BCT_EQUALS) {
127 scan_err1(lexical_parser_, _("expected an equals, got: %s"),
128 lexical_parser_->str);
129 return ParseInternalReturnCode::kError;
130 }
131 }
132
133 if (parser_pass_number_ == 1 && item->flags & CFG_ITEM_DEPRECATED) {
134 my_config_.AddWarning(std::string("using deprecated keyword ")
135 + item->name + " on line "
136 + std::to_string(lexical_parser_->line_no)
137 + " of file " + lexical_parser_->fname);
138 }
139
140 Dmsg1(800, "calling handler for %s\n", item->name);
141
142 if (!my_config_.StoreResource(item->type, lexical_parser_, item,
143 resource_item_index,
144 parser_pass_number_)) {
145 if (my_config_.store_res_) {
146 my_config_.store_res_(lexical_parser_, item, resource_item_index,
147 parser_pass_number_);
148 }
149 }
150 } else {
151 Dmsg2(900, "config_level_=%d id=%s\n", config_level_,
152 lexical_parser_->str);
153 Dmsg1(900, "Keyword = %s\n", lexical_parser_->str);
154 scan_err1(lexical_parser_,
155 _("Keyword \"%s\" not permitted in this resource.\n"
156 "Perhaps you left the trailing brace off of the "
157 "previous resource."),
158 lexical_parser_->str);
159 return ParseInternalReturnCode::kError;
160 }
161 return ParseInternalReturnCode::kGetNextToken;
162 }
163 case BCT_EOB:
164 config_level_--;
165 state = ParseState::kInit;
166 Dmsg0(900, "BCT_EOB => define new resource\n");
167 if (!currently_parsed_resource_.allocated_resource_->resource_name_) {
168 scan_err0(lexical_parser_, _("Name not specified for resource"));
169 return ParseInternalReturnCode::kError;
170 }
171 /* save resource */
172 if (!my_config_.SaveResourceCb_(
173 currently_parsed_resource_.rcode_,
174 currently_parsed_resource_.resource_items_,
175 parser_pass_number_)) {
176 scan_err0(lexical_parser_, _("SaveResource failed"));
177 return ParseInternalReturnCode::kError;
178 }
179
180 FreeUnusedMemoryFromPass2();
181 return ParseInternalReturnCode::kGetNextToken;
182
183 case BCT_EOL:
184 return ParseInternalReturnCode::kGetNextToken;
185
186 default:
187 scan_err2(lexical_parser_,
188 _("unexpected token %d %s in resource definition"), token,
189 lex_tok_to_str(token));
190 return ParseInternalReturnCode::kError;
191 }
192 return ParseInternalReturnCode::kGetNextToken;
193 }
194
195 ConfigParserStateMachine::ParseInternalReturnCode
ParserInitResource(int token)196 ConfigParserStateMachine::ParserInitResource(int token)
197 {
198 const char* resource_identifier = lexical_parser_->str;
199
200 switch (token) {
201 case BCT_EOL:
202 case BCT_UTF8_BOM:
203 return ParseInternalReturnCode::kGetNextToken;
204 case BCT_UTF16_BOM:
205 scan_err0(lexical_parser_,
206 _("Currently we cannot handle UTF-16 source files. "
207 "Please convert the conf file to UTF-8\n"));
208 return ParseInternalReturnCode::kError;
209 default:
210 if (token != BCT_IDENTIFIER) {
211 scan_err1(lexical_parser_,
212 _("Expected a Resource name identifier, got: %s"),
213 resource_identifier);
214 return ParseInternalReturnCode::kError;
215 }
216 break;
217 }
218
219 ResourceTable* resource_table;
220 resource_table = my_config_.GetResourceTable(resource_identifier);
221
222 bool init_done = false;
223
224 if (resource_table && resource_table->items) {
225 currently_parsed_resource_.rcode_ = resource_table->rcode;
226 currently_parsed_resource_.resource_items_ = resource_table->items;
227
228 my_config_.InitResource(currently_parsed_resource_.rcode_,
229 currently_parsed_resource_.resource_items_,
230 parser_pass_number_,
231 resource_table->ResourceSpecificInitializer);
232
233 ASSERT(resource_table->allocated_resource_);
234 currently_parsed_resource_.allocated_resource_
235 = *resource_table->allocated_resource_;
236 ASSERT(currently_parsed_resource_.allocated_resource_);
237
238 currently_parsed_resource_.allocated_resource_->rcode_str_
239 = my_config_.GetQualifiedResourceNameTypeConverter()
240 ->ResourceTypeToString(resource_table->rcode);
241
242 state = ParseState::kResource;
243
244 init_done = true;
245 }
246
247 if (!init_done) {
248 scan_err1(lexical_parser_, _("expected resource identifier, got: %s"),
249 resource_identifier);
250 return ParseInternalReturnCode::kError;
251 }
252 return ParseInternalReturnCode::kNextState;
253 }
254
InitParserPass()255 bool ConfigParserStateMachine::InitParserPass()
256 {
257 parser_pass_number_++;
258 ASSERT(parser_pass_number_ < 3);
259
260 // close files from the pass before
261 while (lexical_parser_) { lexical_parser_ = LexCloseFile(lexical_parser_); }
262
263 Dmsg1(900, "ParseConfig parser_pass_number_ %d\n", parser_pass_number_);
264
265 lexical_parser_ = lex_open_file(lexical_parser_, config_file_name_.c_str(),
266 scan_error_, scan_warning_);
267 if (!lexical_parser_) {
268 my_config_.lex_error(config_file_name_.c_str(), scan_error_, scan_warning_);
269 return false;
270 }
271
272 LexSetErrorHandlerErrorType(lexical_parser_, my_config_.err_type_);
273
274 lexical_parser_->error_counter = 0;
275 lexical_parser_->caller_ctx = caller_ctx_;
276 return true;
277 }
278
DumpResourcesAfterSecondPass()279 void ConfigParserStateMachine::DumpResourcesAfterSecondPass()
280 {
281 if (debug_level >= 900 && parser_pass_number_ == 2) {
282 for (int i = my_config_.r_first_; i <= my_config_.r_last_; i++) {
283 my_config_.DumpResourceCb_(i,
284 my_config_.res_head_[i - my_config_.r_first_],
285 PrintMessage, nullptr, false, false);
286 }
287 }
288 }
289
GetParseError() const290 ConfigParserStateMachine::ParserError ConfigParserStateMachine::GetParseError()
291 const
292 {
293 // in this order
294 if (state != ParseState::kInit) {
295 return ParserError::kResourceIncomplete;
296 } else if (lexical_parser_->error_counter > 0) {
297 return ParserError::kParserError;
298 } else {
299 return ParserError::kNoError;
300 }
301 }
302