1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "tscore/ink_platform.h"
25 #include "tscore/ink_memory.h"
26 #include "tscore/ink_file.h"
27 #include "tscore/I_Layout.h"
28 #include "tscore/Filenames.h"
29 #include "DiagsConfig.h"
30 #include "records/P_RecCore.h"
31
32 //////////////////////////////////////////////////////////////////////////////
33 //
34 // void reconfigure_diags()
35 //
36 // This function extracts the current diags configuration settings from
37 // records.config, and rebuilds the Diags data structures.
38 //
39 //////////////////////////////////////////////////////////////////////////////
40
41 void
reconfigure_diags()42 DiagsConfig::reconfigure_diags()
43 {
44 int i, e;
45 char *p, *dt, *at;
46 DiagsConfigState c;
47 bool found, all_found;
48
49 static struct {
50 const char *config_name;
51 DiagsLevel level;
52 } output_records[] = {
53 {"proxy.config.diags.output.diag", DL_Diag}, {"proxy.config.diags.output.debug", DL_Debug},
54 {"proxy.config.diags.output.status", DL_Status}, {"proxy.config.diags.output.note", DL_Note},
55 {"proxy.config.diags.output.warning", DL_Warning}, {"proxy.config.diags.output.error", DL_Error},
56 {"proxy.config.diags.output.fatal", DL_Fatal}, {"proxy.config.diags.output.alert", DL_Alert},
57 {"proxy.config.diags.output.emergency", DL_Emergency}, {nullptr, DL_Undefined},
58 };
59
60 if (!callbacks_established) {
61 register_diags_callbacks();
62 }
63 ////////////////////////////////////////////
64 // extract relevant records.config values //
65 ////////////////////////////////////////////
66
67 all_found = true;
68
69 // initial value set to 0 or 1 based on command line tags
70 c.enabled[DiagsTagType_Debug] = (diags->base_debug_tags != nullptr);
71 c.enabled[DiagsTagType_Action] = (diags->base_action_tags != nullptr);
72
73 // enabled if records.config set
74
75 e = static_cast<int>(REC_readInteger("proxy.config.diags.debug.enabled", &found));
76 if (e && found) {
77 c.enabled[DiagsTagType_Debug] = e; // implement OR logic
78 }
79 all_found = all_found && found;
80
81 e = static_cast<int>(REC_readInteger("proxy.config.diags.action.enabled", &found));
82 if (e && found) {
83 c.enabled[DiagsTagType_Action] = true; // implement OR logic
84 }
85 all_found = all_found && found;
86
87 e = static_cast<int>(REC_readInteger("proxy.config.diags.show_location", &found));
88 diags->show_location = ((e == 1 && found) ? SHOW_LOCATION_DEBUG : ((e == 2 && found) ? SHOW_LOCATION_ALL : SHOW_LOCATION_NONE));
89 all_found = all_found && found;
90
91 // read output routing values
92 for (i = 0;; i++) {
93 const char *record_name = output_records[i].config_name;
94 DiagsLevel l = output_records[i].level;
95
96 if (!record_name) {
97 break;
98 }
99
100 p = REC_readString(record_name, &found);
101 all_found = all_found && found;
102
103 if (found) {
104 parse_output_string(p, &(c.outputs[l]));
105 ats_free(p);
106 } else {
107 Error("can't find config variable '%s'", record_name);
108 }
109 }
110
111 p = REC_readString("proxy.config.diags.debug.tags", &found);
112 dt = (found ? p : nullptr); // NOTE: needs to be freed
113 all_found = all_found && found;
114
115 p = REC_readString("proxy.config.diags.action.tags", &found);
116 at = (found ? p : nullptr); // NOTE: needs to be freed
117 all_found = all_found && found;
118
119 ///////////////////////////////////////////////////////////////////
120 // if couldn't read all values, return without changing config, //
121 // otherwise rebuild taglists and change the diags config values //
122 ///////////////////////////////////////////////////////////////////
123
124 if (!all_found) {
125 Error("couldn't fetch all proxy.config.diags values");
126 } else {
127 //////////////////////////////
128 // clear out old tag tables //
129 //////////////////////////////
130
131 diags->deactivate_all(DiagsTagType_Debug);
132 diags->deactivate_all(DiagsTagType_Action);
133
134 //////////////////////////////////////////////////////////////////////
135 // add new tag tables from records.config or command line overrides //
136 //////////////////////////////////////////////////////////////////////
137
138 diags->activate_taglist((diags->base_debug_tags ? diags->base_debug_tags : dt), DiagsTagType_Debug);
139 diags->activate_taglist((diags->base_action_tags ? diags->base_action_tags : at), DiagsTagType_Action);
140
141 ////////////////////////////////////
142 // change the diags config values //
143 ////////////////////////////////////
144 #if !defined(__GNUC__)
145 diags->config = c;
146 #else
147 memcpy(((void *)&diags->config), ((void *)&c), sizeof(DiagsConfigState));
148 #endif
149 Note("updated diags config");
150 }
151
152 ////////////////////////////////////
153 // free the record.config strings //
154 ////////////////////////////////////
155 ats_free(dt);
156 ats_free(at);
157 }
158
159 //////////////////////////////////////////////////////////////////////////////
160 //
161 // static void *diags_config_callback(void *opaque_token, void *data)
162 //
163 // This is the records.config registration callback that is called
164 // when any diags value is changed. Each time a diags value changes
165 // the entire diags state is reconfigured.
166 //
167 //////////////////////////////////////////////////////////////////////////////
168 static int
diags_config_callback(const char *,RecDataT,RecData,void * opaque_token)169 diags_config_callback(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData /* data ATS_UNUSED */,
170 void *opaque_token)
171 {
172 DiagsConfig *diagsConfig;
173
174 diagsConfig = static_cast<DiagsConfig *>(opaque_token);
175 ink_assert(diags->magic == DIAGS_MAGIC);
176 diagsConfig->reconfigure_diags();
177 return (0);
178 }
179
180 //////////////////////////////////////////////////////////////////////////////
181 //
182 // void Diags::parse_output_string(char *s, DiagsModeOutput *o)
183 //
184 // This routine converts a diags outpur routing string <s> to the
185 // internal DiagsModeOutput structure. Currently there are 4 possible
186 // routing destinations:
187 // O stdout
188 // E stderr
189 // S syslog
190 // L diags.log
191 //
192 //////////////////////////////////////////////////////////////////////////////
193
194 void
parse_output_string(char * s,DiagsModeOutput * o)195 DiagsConfig::parse_output_string(char *s, DiagsModeOutput *o)
196 {
197 o->to_stdout = (s && strchr(s, 'O'));
198 o->to_stderr = (s && strchr(s, 'E'));
199 o->to_syslog = (s && strchr(s, 'S'));
200 o->to_diagslog = (s && strchr(s, 'L'));
201 }
202
203 //////////////////////////////////////////////////////////////////////////////
204 //
205 // void Diags::config_norecords()
206 //
207 // Builds the Diags data structures based on the command line values
208 // it does not use any of the records based config variables
209 //
210 //////////////////////////////////////////////////////////////////////////////
211 void
config_diags_norecords()212 DiagsConfig::config_diags_norecords()
213 {
214 DiagsConfigState c;
215 ink_zero(c);
216
217 //////////////////////////////
218 // clear out old tag tables //
219 //////////////////////////////
220 diags->deactivate_all(DiagsTagType_Debug);
221 diags->deactivate_all(DiagsTagType_Action);
222
223 //////////////////////////////////////////////////////////////////////
224 // add new tag tables from command line overrides only //
225 //////////////////////////////////////////////////////////////////////
226
227 if (diags->base_debug_tags) {
228 diags->activate_taglist(diags->base_debug_tags, DiagsTagType_Debug);
229 c.enabled[DiagsTagType_Debug] = true;
230 } else {
231 c.enabled[DiagsTagType_Debug] = false;
232 }
233
234 if (diags->base_action_tags) {
235 diags->activate_taglist(diags->base_action_tags, DiagsTagType_Action);
236 c.enabled[DiagsTagType_Action] = true;
237 } else {
238 c.enabled[DiagsTagType_Action] = false;
239 }
240
241 #if !defined(__GNUC__)
242 diags->config = c;
243 #else
244 memcpy(((void *)&diags->config), ((void *)&c), sizeof(DiagsConfigState));
245 #endif
246 }
247
DiagsConfig(std::string_view prefix_string,const char * filename,const char * tags,const char * actions,bool use_records)248 DiagsConfig::DiagsConfig(std::string_view prefix_string, const char *filename, const char *tags, const char *actions,
249 bool use_records)
250 : callbacks_established(false), diags_log(nullptr), diags(nullptr)
251 {
252 char diags_logpath[PATH_NAME_MAX];
253 ats_scoped_str logpath;
254
255 ////////////////////////////////////////////////////////////////////
256 // If we aren't using the manager records for configuration //
257 // just build the tables based on command line parameters and //
258 // exit //
259 ////////////////////////////////////////////////////////////////////
260
261 if (!use_records) {
262 diags = new Diags(prefix_string, tags, actions, nullptr);
263 config_diags_norecords();
264 return;
265 }
266
267 // Open the diagnostics log. If proxy.config.log.logfile_dir is set use that, otherwise fall
268 // back to the configured log directory.
269
270 logpath = RecConfigReadLogDir();
271 if (access(logpath, W_OK | R_OK) == -1) {
272 fprintf(stderr, "unable to access log directory '%s': %d, %s\n", (const char *)logpath, errno, strerror(errno));
273 fprintf(stderr, "please set 'proxy.config.log.logfile_dir'\n");
274 ::exit(1);
275 }
276
277 ink_filepath_make(diags_logpath, sizeof(diags_logpath), logpath, filename);
278
279 // Grab rolling intervals from configuration
280 // TODO error check these values
281 int output_log_roll_int = static_cast<int>(REC_ConfigReadInteger("proxy.config.output.logfile.rolling_interval_sec"));
282 int output_log_roll_size = static_cast<int>(REC_ConfigReadInteger("proxy.config.output.logfile.rolling_size_mb"));
283 int output_log_roll_enable = static_cast<int>(REC_ConfigReadInteger("proxy.config.output.logfile.rolling_enabled"));
284 int diags_log_roll_int = static_cast<int>(REC_ConfigReadInteger("proxy.config.diags.logfile.rolling_interval_sec"));
285 int diags_log_roll_size = static_cast<int>(REC_ConfigReadInteger("proxy.config.diags.logfile.rolling_size_mb"));
286 int diags_log_roll_enable = static_cast<int>(REC_ConfigReadInteger("proxy.config.diags.logfile.rolling_enabled"));
287
288 // Grab some perms for the actual files on disk
289 char *diags_perm = REC_ConfigReadString("proxy.config.diags.logfile_perm");
290 char *output_perm = REC_ConfigReadString("proxy.config.output.logfile_perm");
291 int diags_perm_parsed = diags_perm ? ink_fileperm_parse(diags_perm) : -1;
292 int output_perm_parsed = diags_perm ? ink_fileperm_parse(output_perm) : -1;
293
294 ats_free(diags_perm);
295 ats_free(output_perm);
296
297 // Set up diags, FILE streams are opened in Diags constructor
298 diags_log = new BaseLogFile(diags_logpath);
299 diags = new Diags(prefix_string, tags, actions, diags_log, diags_perm_parsed, output_perm_parsed);
300 diags->config_roll_diagslog(static_cast<RollingEnabledValues>(diags_log_roll_enable), diags_log_roll_int, diags_log_roll_size);
301 diags->config_roll_outputlog(static_cast<RollingEnabledValues>(output_log_roll_enable), output_log_roll_int,
302 output_log_roll_size);
303
304 Status("opened %s", diags_logpath);
305
306 register_diags_callbacks();
307
308 reconfigure_diags();
309 }
310
311 //////////////////////////////////////////////////////////////////////////////
312 //
313 // void DiagsConfig::register_diags_callbacks()
314 //
315 // set up management callbacks to update diags on every change --- //
316 // right now, this system kind of sucks, we rebuild the tag tables //
317 // from scratch for *every* proxy.config.diags value that changed; //
318 // dgourley is looking into changing the management API to provide //
319 // a callback each time records.config changed, possibly better. //
320 //
321 //////////////////////////////////////////////////////////////////////////////
322 void
register_diags_callbacks()323 DiagsConfig::register_diags_callbacks()
324 {
325 static const char *config_record_names[] = {
326 "proxy.config.diags.debug.enabled", "proxy.config.diags.debug.tags", "proxy.config.diags.action.enabled",
327 "proxy.config.diags.action.tags", "proxy.config.diags.show_location", "proxy.config.diags.output.diag",
328 "proxy.config.diags.output.debug", "proxy.config.diags.output.status", "proxy.config.diags.output.note",
329 "proxy.config.diags.output.warning", "proxy.config.diags.output.error", "proxy.config.diags.output.fatal",
330 "proxy.config.diags.output.alert", "proxy.config.diags.output.emergency", nullptr,
331 };
332
333 bool total_status = true;
334 bool status;
335 int i;
336 void *o = (void *)this;
337
338 // set triggers to call same callback for any diag config change
339 for (i = 0; config_record_names[i] != nullptr; i++) {
340 status = (REC_RegisterConfigUpdateFunc(config_record_names[i], diags_config_callback, o) == REC_ERR_OKAY);
341 if (!status) {
342 Warning("couldn't register variable '%s', is %s up to date?", config_record_names[i], ts::filename::RECORDS);
343 }
344 total_status = total_status && status;
345 }
346
347 if (total_status == false) {
348 Error("couldn't setup all diags callbacks, diagnostics may misbehave");
349 callbacks_established = false;
350 } else {
351 callbacks_established = true;
352 }
353 }
354
~DiagsConfig()355 DiagsConfig::~DiagsConfig()
356 {
357 delete diags;
358 }
359