1 | #include "globals.h"
|
---|
2 | #include "oscam-conf.h"
|
---|
3 | #include "oscam-config.h"
|
---|
4 | #include "oscam-files.h"
|
---|
5 | #include "oscam-garbage.h"
|
---|
6 | #include "oscam-string.h"
|
---|
7 |
|
---|
8 | #define CONFVARWIDTH 30
|
---|
9 |
|
---|
10 | /* Returns the default value if string length is zero, otherwise atoi is called*/
|
---|
11 | int32_t strToIntVal(char *value, int32_t defaultvalue){
|
---|
12 | if (strlen(value) == 0) return defaultvalue;
|
---|
13 | errno = 0; // errno should be set to 0 before calling strtol
|
---|
14 | int32_t i = strtol(value, NULL, 10);
|
---|
15 | return (errno == 0) ? i : defaultvalue;
|
---|
16 | }
|
---|
17 |
|
---|
18 | /* Returns the default value if string length is zero, otherwise strtoul is called*/
|
---|
19 | uint32_t strToUIntVal(char *value, uint32_t defaultvalue){
|
---|
20 | if (strlen(value) == 0) return defaultvalue;
|
---|
21 | errno = 0; // errno should be set to 0 before calling strtoul
|
---|
22 | uint32_t i = strtoul(value, NULL, 10);
|
---|
23 | return (errno == 0) ? i : defaultvalue;
|
---|
24 | }
|
---|
25 |
|
---|
26 | /* Replacement of fprintf which adds necessary whitespace to fill up the varname to a fixed width.
|
---|
27 | If varname is longer than CONFVARWIDTH, no whitespace is added*/
|
---|
28 | void fprintf_conf(FILE *f, const char *varname, const char *fmtstring, ...){
|
---|
29 | int32_t varlen = strlen(varname);
|
---|
30 | int32_t max = (varlen > CONFVARWIDTH) ? varlen : CONFVARWIDTH;
|
---|
31 | char varnamebuf[max + 3];
|
---|
32 | char *ptr = varnamebuf + varlen;
|
---|
33 | va_list argptr;
|
---|
34 |
|
---|
35 | cs_strncpy(varnamebuf, varname, sizeof(varnamebuf));
|
---|
36 | while(varlen < CONFVARWIDTH){
|
---|
37 | ptr[0] = ' ';
|
---|
38 | ++ptr;
|
---|
39 | ++varlen;
|
---|
40 | }
|
---|
41 | cs_strncpy(ptr, "= ", sizeof(varnamebuf)-(ptr-varnamebuf));
|
---|
42 | if (fwrite(varnamebuf, sizeof(char), strlen(varnamebuf), f)){
|
---|
43 | if(strlen(fmtstring) > 0){
|
---|
44 | va_start(argptr, fmtstring);
|
---|
45 | vfprintf(f, fmtstring, argptr);
|
---|
46 | va_end(argptr);
|
---|
47 | }
|
---|
48 | }
|
---|
49 | }
|
---|
50 |
|
---|
51 | int config_list_parse(const struct config_list *clist, const char *token, char *value, void *config_data) {
|
---|
52 | const struct config_list *c;
|
---|
53 | for (c = clist; c->opt_type != OPT_UNKNOWN; c++) {
|
---|
54 | if (c->opt_type == OPT_SAVE_FUNC || c->opt_type == OPT_FIXUP_FUNC)
|
---|
55 | continue;
|
---|
56 | if (strcasecmp(token, c->config_name) != 0)
|
---|
57 | continue;
|
---|
58 | void *var = config_data + c->var_offset;
|
---|
59 | switch (c->opt_type) {
|
---|
60 | case OPT_INT8: {
|
---|
61 | *(int8_t *)var = (int8_t)strToIntVal(value, c->def.d_int8);
|
---|
62 | return 1;
|
---|
63 | }
|
---|
64 | case OPT_UINT8: {
|
---|
65 | uint32_t tmp = strToUIntVal(value, c->def.d_uint8);
|
---|
66 | *(uint8_t *)var = (uint8_t)(tmp <= 0xff ? tmp : 0xff);
|
---|
67 | return 1;
|
---|
68 | }
|
---|
69 | case OPT_INT32: {
|
---|
70 | *(int32_t *)var = strToIntVal(value, c->def.d_int32);
|
---|
71 | return 1;
|
---|
72 | }
|
---|
73 | case OPT_UINT32: {
|
---|
74 | *(uint32_t *)var = strToUIntVal(value, c->def.d_uint32);
|
---|
75 | return 1;
|
---|
76 | }
|
---|
77 | case OPT_STRING: {
|
---|
78 | char **scfg = var;
|
---|
79 | if (c->def.d_char && strlen(value) == 0) // Set default
|
---|
80 | value = c->def.d_char;
|
---|
81 | NULLFREE(*scfg);
|
---|
82 | if (strlen(value))
|
---|
83 | *scfg = cs_strdup(value);
|
---|
84 | return 1;
|
---|
85 | }
|
---|
86 | case OPT_SSTRING: {
|
---|
87 | char *scfg = var;
|
---|
88 | if (c->def.d_char && strlen(value) == 0) // Set default
|
---|
89 | value = c->def.d_char;
|
---|
90 | scfg[0] = '\0';
|
---|
91 | unsigned int len = strlen(value);
|
---|
92 | if (len) {
|
---|
93 | strncpy(scfg, value, c->str_size - 1);
|
---|
94 | if (len > c->str_size) {
|
---|
95 | fprintf(stderr, "WARNING: Config value for '%s' (%s, len=%u) exceeds max length: %d (%s)\n",
|
---|
96 | token, value, len, c->str_size - 1, scfg);
|
---|
97 | }
|
---|
98 | }
|
---|
99 | return 1;
|
---|
100 | }
|
---|
101 | case OPT_HEX_ARRAY: {
|
---|
102 | uint8_t *hex_array = var;
|
---|
103 | if (!strlen(value))
|
---|
104 | memset(hex_array, 0, c->def.array_size);
|
---|
105 | else if (key_atob_l(value, hex_array, c->def.array_size * 2)) {
|
---|
106 | memset(hex_array, 0, c->def.array_size);
|
---|
107 | fprintf(stderr, "WARNING: Config value for '%s' (%s, len=%zu) requires %d chars.\n",
|
---|
108 | token, value, strlen(value), c->def.array_size * 2);
|
---|
109 | }
|
---|
110 | return 1;
|
---|
111 | }
|
---|
112 | case OPT_FUNC: {
|
---|
113 | c->ops.process_fn(token, value, var, NULL);
|
---|
114 | return 1;
|
---|
115 | }
|
---|
116 | case OPT_FUNC_EXTRA: {
|
---|
117 | c->ops.process_fn_extra(token, value, var, c->def.d_extra, NULL);
|
---|
118 | return 1;
|
---|
119 | }
|
---|
120 | case OPT_FIXUP_FUNC:
|
---|
121 | case OPT_SAVE_FUNC:
|
---|
122 | return 1;
|
---|
123 | case OPT_UNKNOWN: {
|
---|
124 | fprintf(stderr, "Unknown config type (%s = %s).", token, value);
|
---|
125 | break;
|
---|
126 | }
|
---|
127 | }
|
---|
128 | }
|
---|
129 | return 0;
|
---|
130 | }
|
---|
131 |
|
---|
132 | void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_data, int save_all,
|
---|
133 | bool (*check_func)(const struct config_list *clist, void *config_data, const char *setting))
|
---|
134 | {
|
---|
135 | const struct config_list *c;
|
---|
136 | for (c = clist; c->opt_type != OPT_UNKNOWN; c++) {
|
---|
137 | void *var = config_data + c->var_offset;
|
---|
138 | if (check_func && c->opt_type != OPT_UNKNOWN) {
|
---|
139 | if (!check_func(clist, config_data, c->config_name))
|
---|
140 | continue;
|
---|
141 | }
|
---|
142 | switch (c->opt_type) {
|
---|
143 | case OPT_INT8: {
|
---|
144 | int8_t val = *(int8_t *)var;
|
---|
145 | if (save_all || val != c->def.d_int8)
|
---|
146 | fprintf_conf(f, c->config_name, "%d\n", val);
|
---|
147 | continue;
|
---|
148 | }
|
---|
149 | case OPT_UINT8: {
|
---|
150 | uint8_t val = *(uint8_t *)var;
|
---|
151 | if (save_all || val != c->def.d_uint8)
|
---|
152 | fprintf_conf(f, c->config_name, "%u\n", val);
|
---|
153 | continue;
|
---|
154 | }
|
---|
155 | case OPT_INT32: {
|
---|
156 | int32_t val = *(int32_t *)var;
|
---|
157 | if (save_all || val != c->def.d_int32)
|
---|
158 | fprintf_conf(f, c->config_name, "%d\n", val);
|
---|
159 | continue;
|
---|
160 | }
|
---|
161 | case OPT_UINT32: {
|
---|
162 | uint32_t val = *(uint32_t *)var;
|
---|
163 | if (save_all || val != c->def.d_uint32)
|
---|
164 | fprintf_conf(f, c->config_name, "%u\n", val);
|
---|
165 | continue;
|
---|
166 | }
|
---|
167 | case OPT_STRING: {
|
---|
168 | char **val = var;
|
---|
169 | if (save_all || !streq(*val, c->def.d_char)) {
|
---|
170 | fprintf_conf(f, c->config_name, "%s\n", *val ? *val : "");
|
---|
171 | }
|
---|
172 | continue;
|
---|
173 | }
|
---|
174 | case OPT_SSTRING: {
|
---|
175 | char *val = var;
|
---|
176 | if (save_all || !streq(val, c->def.d_char)) {
|
---|
177 | fprintf_conf(f, c->config_name, "%s\n", val[0] ? val : "");
|
---|
178 | }
|
---|
179 | continue;
|
---|
180 | }
|
---|
181 | case OPT_HEX_ARRAY: {
|
---|
182 | uint8_t *hex_array = var;
|
---|
183 | uint32_t ok = check_filled(hex_array, c->def.array_size);
|
---|
184 | if (save_all || ok) {
|
---|
185 | fprintf_conf(f, c->config_name, "%s", ""); // it should not have \n at the end
|
---|
186 | if (ok) {
|
---|
187 | for (ok = 0; ok < c->def.array_size; ok++) {
|
---|
188 | fprintf(f, "%02X", hex_array[ok]);
|
---|
189 | }
|
---|
190 | }
|
---|
191 | fprintf(f, "\n");
|
---|
192 | }
|
---|
193 | continue;
|
---|
194 | }
|
---|
195 | case OPT_FUNC: {
|
---|
196 | c->ops.process_fn((const char *)c->config_name, NULL, var, f);
|
---|
197 | continue;
|
---|
198 | }
|
---|
199 | case OPT_FUNC_EXTRA: {
|
---|
200 | c->ops.process_fn_extra((const char *)c->config_name, NULL, var, c->def.d_extra, f);
|
---|
201 | continue;
|
---|
202 | }
|
---|
203 | case OPT_FIXUP_FUNC:
|
---|
204 | case OPT_SAVE_FUNC:
|
---|
205 | continue;
|
---|
206 | case OPT_UNKNOWN:
|
---|
207 | break;
|
---|
208 | }
|
---|
209 | }
|
---|
210 | }
|
---|
211 |
|
---|
212 | bool config_list_should_be_saved(const struct config_list *clist, void *var) {
|
---|
213 | const struct config_list *c;
|
---|
214 | for (c = clist; c->opt_type != OPT_UNKNOWN; c++) {
|
---|
215 | if (c->opt_type == OPT_SAVE_FUNC) {
|
---|
216 | return c->ops.should_save_fn(var);
|
---|
217 | }
|
---|
218 | }
|
---|
219 | return true;
|
---|
220 | }
|
---|
221 |
|
---|
222 | void config_list_apply_fixups(const struct config_list *clist, void *var) {
|
---|
223 | const struct config_list *c;
|
---|
224 | for (c = clist; c->opt_type != OPT_UNKNOWN; c++) {
|
---|
225 | if (c->opt_type == OPT_FIXUP_FUNC) {
|
---|
226 | c->ops.fixup_fn(var);
|
---|
227 | break;
|
---|
228 | }
|
---|
229 | }
|
---|
230 | }
|
---|
231 |
|
---|
232 | void config_list_set_defaults(const struct config_list *clist, void *config_data) {
|
---|
233 | const struct config_list *c;
|
---|
234 | for (c = clist; c->opt_type != OPT_UNKNOWN; c++) {
|
---|
235 | void *var = config_data + c->var_offset;
|
---|
236 | switch (c->opt_type) {
|
---|
237 | case OPT_INT8: {
|
---|
238 | *(int8_t *)var = c->def.d_int8;
|
---|
239 | break;
|
---|
240 | }
|
---|
241 | case OPT_UINT8: {
|
---|
242 | *(uint8_t *)var = c->def.d_uint8;
|
---|
243 | break;
|
---|
244 | }
|
---|
245 | case OPT_INT32: {
|
---|
246 | *(int32_t *)var = c->def.d_int32;
|
---|
247 | break;
|
---|
248 | }
|
---|
249 | case OPT_UINT32: {
|
---|
250 | *(uint32_t *)var = c->def.d_uint32;
|
---|
251 | break;
|
---|
252 | }
|
---|
253 | case OPT_STRING: {
|
---|
254 | char **scfg = var;
|
---|
255 | NULLFREE(*scfg);
|
---|
256 | if (c->def.d_char)
|
---|
257 | *scfg = cs_strdup(c->def.d_char);
|
---|
258 | break;
|
---|
259 | }
|
---|
260 | case OPT_SSTRING: {
|
---|
261 | char *scfg = var;
|
---|
262 | scfg[0] = '\0';
|
---|
263 | if (c->def.d_char && strlen(c->def.d_char))
|
---|
264 | cs_strncpy(scfg, c->def.d_char, c->str_size);
|
---|
265 | break;
|
---|
266 | }
|
---|
267 | case OPT_HEX_ARRAY: {
|
---|
268 | uint8_t *hex_array = var;
|
---|
269 | memset(hex_array, 0, c->def.array_size);
|
---|
270 | break;
|
---|
271 | }
|
---|
272 | case OPT_FUNC: {
|
---|
273 | c->ops.process_fn((const char *)c->config_name, "", var, NULL);
|
---|
274 | break;
|
---|
275 | }
|
---|
276 | case OPT_FUNC_EXTRA: {
|
---|
277 | c->ops.process_fn_extra((const char *)c->config_name, "", var, c->def.d_extra, NULL);
|
---|
278 | break;
|
---|
279 | }
|
---|
280 | case OPT_SAVE_FUNC:
|
---|
281 | case OPT_FIXUP_FUNC:
|
---|
282 | case OPT_UNKNOWN:
|
---|
283 | continue;
|
---|
284 | }
|
---|
285 | }
|
---|
286 | return;
|
---|
287 | }
|
---|
288 |
|
---|
289 | void config_list_free_values(const struct config_list *clist, void *config_data) {
|
---|
290 | const struct config_list *c;
|
---|
291 | for (c = clist; c->opt_type != OPT_UNKNOWN; c++) {
|
---|
292 | void *var = config_data + c->var_offset;
|
---|
293 | if (c->opt_type == OPT_STRING) {
|
---|
294 | char **scfg = var;
|
---|
295 | NULLFREE(*scfg);
|
---|
296 | }
|
---|
297 | }
|
---|
298 | return;
|
---|
299 | }
|
---|
300 |
|
---|
301 | void config_list_gc_values(const struct config_list *clist, void *config_data) {
|
---|
302 | const struct config_list *c;
|
---|
303 | for (c = clist; c->opt_type != OPT_UNKNOWN; c++) {
|
---|
304 | void *var = config_data + c->var_offset;
|
---|
305 | if (c->opt_type == OPT_STRING) {
|
---|
306 | char **scfg = var;
|
---|
307 | add_garbage(*scfg);
|
---|
308 | }
|
---|
309 | }
|
---|
310 | return;
|
---|
311 | }
|
---|
312 |
|
---|
313 | int config_section_is_active(const struct config_sections *sec) {
|
---|
314 | if (!sec)
|
---|
315 | return 0;
|
---|
316 | if (sec->config[0].opt_type == OPT_UNKNOWN)
|
---|
317 | return 0;
|
---|
318 | return 1;
|
---|
319 | }
|
---|
320 |
|
---|
321 | const struct config_sections *config_find_section(const struct config_sections *conf, char *section_name) {
|
---|
322 | const struct config_sections *sec;
|
---|
323 | for (sec = conf; sec && sec->section; sec++) {
|
---|
324 | if (streq(section_name, sec->section)) {
|
---|
325 | return sec;
|
---|
326 | }
|
---|
327 | }
|
---|
328 | return NULL;
|
---|
329 | }
|
---|
330 |
|
---|
331 | void config_sections_save(const struct config_sections *conf, FILE *f, void *var) {
|
---|
332 | const struct config_sections *sec;
|
---|
333 | for (sec = conf; sec && sec->section; sec++) {
|
---|
334 | if (config_section_is_active(sec) && config_list_should_be_saved(sec->config, var)) {
|
---|
335 | fprintf(f, "[%s]\n", sec->section);
|
---|
336 | config_list_apply_fixups(sec->config, var);
|
---|
337 | config_list_save(f, sec->config, var, cfg.http_full_cfg);
|
---|
338 | fprintf(f, "\n");
|
---|
339 | }
|
---|
340 | }
|
---|
341 | }
|
---|
342 |
|
---|
343 | void config_sections_set_defaults(const struct config_sections *conf, void *var) {
|
---|
344 | const struct config_sections *sec;
|
---|
345 | for (sec = conf; sec && sec->section; sec++) {
|
---|
346 | if (config_section_is_active(sec))
|
---|
347 | config_list_set_defaults(sec->config, var);
|
---|
348 | }
|
---|
349 | }
|
---|
350 |
|
---|
351 | void config_sections_free(const struct config_sections *conf, void *var) {
|
---|
352 | const struct config_sections *sec;
|
---|
353 | for (sec = conf; sec && sec->section; sec++) {
|
---|
354 | if (config_section_is_active(sec)) {
|
---|
355 | config_list_free_values(sec->config, var);
|
---|
356 | }
|
---|
357 | }
|
---|
358 | }
|
---|
359 |
|
---|
360 | void config_set_value(const struct config_sections *conf, char *section, const char *token, char *value, void *var) {
|
---|
361 | const struct config_sections *sec = config_find_section(conf, section);
|
---|
362 | if (!sec) {
|
---|
363 | fprintf(stderr, "WARNING: Unknown section '%s'.\n", section);
|
---|
364 | return;
|
---|
365 | }
|
---|
366 | if (config_section_is_active(sec)) {
|
---|
367 | if (!config_list_parse(sec->config, token, value, var)) {
|
---|
368 | fprintf(stderr, "WARNING: In section [%s] unknown setting '%s=%s' tried.\n",
|
---|
369 | section, token, value);
|
---|
370 | }
|
---|
371 | } else {
|
---|
372 | fprintf(stderr, "WARNING: Section is not active '%s'.\n", section);
|
---|
373 | }
|
---|
374 | }
|
---|
375 |
|
---|
376 | static FILE *__open_config_file(const char *conf_filename, bool die_on_err) {
|
---|
377 | char filename[256];
|
---|
378 | FILE *f = fopen(get_config_filename(filename, sizeof(filename), conf_filename), "r");
|
---|
379 | if (!f) {
|
---|
380 | if (die_on_err) {
|
---|
381 | fprintf(stderr, "ERROR: Cannot open file \"%s\" (errno=%d %s)", filename, errno, strerror(errno));
|
---|
382 | fprintf(stderr, "\n");
|
---|
383 | exit(1);
|
---|
384 | } else {
|
---|
385 | cs_debug_mask(D_TRACE, "INFO: Cannot open file \"%s\" (errno=%d %s)", filename, errno, strerror(errno));
|
---|
386 | }
|
---|
387 | return NULL;
|
---|
388 | }
|
---|
389 | return f;
|
---|
390 | }
|
---|
391 |
|
---|
392 | FILE *open_config_file(const char *conf_filename) {
|
---|
393 | return __open_config_file(conf_filename, false);
|
---|
394 | }
|
---|
395 |
|
---|
396 | FILE *open_config_file_or_die(const char *conf_filename) {
|
---|
397 | return __open_config_file(conf_filename, true);
|
---|
398 | }
|
---|
399 |
|
---|
400 |
|
---|
401 | FILE *create_config_file(const char *conf_filename) {
|
---|
402 | char temp_file[256];
|
---|
403 | get_config_filename(temp_file, sizeof(temp_file), conf_filename);
|
---|
404 | strncat(temp_file, ".tmp", sizeof(temp_file) - strlen(temp_file) - 1);
|
---|
405 | FILE *f = fopen(temp_file, "w");
|
---|
406 | if (!f) {
|
---|
407 | cs_log("ERROR: Cannot create file \"%s\" (errno=%d %s)", temp_file, errno, strerror(errno));
|
---|
408 | return NULL;
|
---|
409 | }
|
---|
410 | setvbuf(f, NULL, _IOFBF, 16 * 1024);
|
---|
411 | fprintf(f, "# %s generated automatically by Streamboard OSCAM %s SVN r%s\n",
|
---|
412 | conf_filename, CS_VERSION, CS_SVN_VERSION);
|
---|
413 | fprintf(f, "# Read more: http://www.streamboard.tv/svn/oscam/trunk/Distribution/doc/txt/%s.txt\n\n",
|
---|
414 | conf_filename);
|
---|
415 | return f;
|
---|
416 | }
|
---|
417 |
|
---|
418 | bool flush_config_file(FILE *f, const char *conf_filename) {
|
---|
419 | char dst_file[256], tmp_file[256], bak_file[256];
|
---|
420 | get_config_filename(dst_file, sizeof(dst_file), conf_filename);
|
---|
421 | memcpy(tmp_file, dst_file, sizeof(tmp_file));
|
---|
422 | memcpy(bak_file, dst_file, sizeof(bak_file));
|
---|
423 | strncat(tmp_file, ".tmp", sizeof(tmp_file) - strlen(tmp_file) - 1);
|
---|
424 | strncat(bak_file, ".bak", sizeof(bak_file) - strlen(bak_file) - 1);
|
---|
425 | fclose(f);
|
---|
426 | return safe_overwrite_with_bak(dst_file, tmp_file, bak_file, 0);
|
---|
427 | }
|
---|