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 = &currently_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