1 /******************************************************************************
2 *
3 * Copyright (C) 2014 Google, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19 #define LOG_TAG "bt_osi_config"
20
21 #include "osi/include/config.h"
22
23 #include <base/logging.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <libgen.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33
34 #include "osi/include/allocator.h"
35 #include "osi/include/list.h"
36 #include "osi/include/log.h"
37 #include "log/log.h"
38
39 typedef struct {
40 char* key;
41 char* value;
42 } entry_t;
43
44 typedef struct {
45 char* name;
46 list_t* entries;
47 } section_t;
48
49 struct config_t {
50 list_t* sections;
51 };
52
53 // Empty definition; this type is aliased to list_node_t.
54 struct config_section_iter_t {};
55
56 static bool config_parse(FILE* fp, config_t* config);
57
58 static section_t* section_new(const char* name);
59 static void section_free(void* ptr);
60 static section_t* section_find(const config_t* config, const char* section);
61
62 static entry_t* entry_new(const char* key, const char* value);
63 static void entry_free(void* ptr);
64 static entry_t* entry_find(const config_t* config, const char* section,
65 const char* key);
66
config_new_empty(void)67 config_t* config_new_empty(void) {
68 config_t* config = static_cast<config_t*>(osi_calloc(sizeof(config_t)));
69
70 config->sections = list_new(section_free);
71 if (!config->sections) {
72 LOG_ERROR(LOG_TAG, "%s unable to allocate list for sections.", __func__);
73 goto error;
74 }
75
76 return config;
77
78 error:;
79 config_free(config);
80 return NULL;
81 }
82
config_new(const char * filename)83 config_t* config_new(const char* filename) {
84 CHECK(filename != NULL);
85
86 config_t* config = config_new_empty();
87 if (!config) return NULL;
88
89 FILE* fp = fopen(filename, "rt");
90 if (!fp) {
91 LOG_ERROR(LOG_TAG, "%s unable to open file '%s': %s", __func__, filename,
92 strerror(errno));
93 config_free(config);
94 return NULL;
95 }
96
97 if (!config_parse(fp, config)) {
98 config_free(config);
99 config = NULL;
100 }
101
102 fclose(fp);
103 return config;
104 }
105
config_new_clone(const config_t * src)106 config_t* config_new_clone(const config_t* src) {
107 CHECK(src != NULL);
108
109 config_t* ret = config_new_empty();
110
111 CHECK(ret != NULL);
112
113 for (const list_node_t* node = list_begin(src->sections);
114 node != list_end(src->sections); node = list_next(node)) {
115 section_t* sec = static_cast<section_t*>(list_node(node));
116
117 for (const list_node_t* node_entry = list_begin(sec->entries);
118 node_entry != list_end(sec->entries);
119 node_entry = list_next(node_entry)) {
120 entry_t* entry = static_cast<entry_t*>(list_node(node_entry));
121
122 config_set_string(ret, sec->name, entry->key, entry->value);
123 }
124 }
125
126 return ret;
127 }
128
config_free(config_t * config)129 void config_free(config_t* config) {
130 if (!config) return;
131
132 list_free(config->sections);
133 osi_free(config);
134 }
135
config_has_section(const config_t * config,const char * section)136 bool config_has_section(const config_t* config, const char* section) {
137 CHECK(config != NULL);
138 CHECK(section != NULL);
139
140 return (section_find(config, section) != NULL);
141 }
142
config_has_key(const config_t * config,const char * section,const char * key)143 bool config_has_key(const config_t* config, const char* section,
144 const char* key) {
145 CHECK(config != NULL);
146 CHECK(section != NULL);
147 CHECK(key != NULL);
148
149 return (entry_find(config, section, key) != NULL);
150 }
151
config_get_int(const config_t * config,const char * section,const char * key,int def_value)152 int config_get_int(const config_t* config, const char* section, const char* key,
153 int def_value) {
154 CHECK(config != NULL);
155 CHECK(section != NULL);
156 CHECK(key != NULL);
157
158 entry_t* entry = entry_find(config, section, key);
159 if (!entry) return def_value;
160
161 char* endptr;
162 int ret = strtol(entry->value, &endptr, 0);
163 return (*endptr == '\0') ? ret : def_value;
164 }
165
config_get_bool(const config_t * config,const char * section,const char * key,bool def_value)166 bool config_get_bool(const config_t* config, const char* section,
167 const char* key, bool def_value) {
168 CHECK(config != NULL);
169 CHECK(section != NULL);
170 CHECK(key != NULL);
171
172 entry_t* entry = entry_find(config, section, key);
173 if (!entry) return def_value;
174
175 if (!strcmp(entry->value, "true")) return true;
176 if (!strcmp(entry->value, "false")) return false;
177
178 return def_value;
179 }
180
config_get_string(const config_t * config,const char * section,const char * key,const char * def_value)181 const char* config_get_string(const config_t* config, const char* section,
182 const char* key, const char* def_value) {
183 CHECK(config != NULL);
184 CHECK(section != NULL);
185 CHECK(key != NULL);
186
187 entry_t* entry = entry_find(config, section, key);
188 if (!entry) return def_value;
189
190 return entry->value;
191 }
192
config_set_int(config_t * config,const char * section,const char * key,int value)193 void config_set_int(config_t* config, const char* section, const char* key,
194 int value) {
195 CHECK(config != NULL);
196 CHECK(section != NULL);
197 CHECK(key != NULL);
198
199 char value_str[32] = {0};
200 snprintf(value_str, sizeof(value_str), "%d", value);
201 config_set_string(config, section, key, value_str);
202 }
203
config_set_bool(config_t * config,const char * section,const char * key,bool value)204 void config_set_bool(config_t* config, const char* section, const char* key,
205 bool value) {
206 CHECK(config != NULL);
207 CHECK(section != NULL);
208 CHECK(key != NULL);
209
210 config_set_string(config, section, key, value ? "true" : "false");
211 }
212
config_set_string(config_t * config,const char * section,const char * key,const char * value)213 void config_set_string(config_t* config, const char* section, const char* key,
214 const char* value) {
215 section_t* sec = section_find(config, section);
216 if (!sec) {
217 sec = section_new(section);
218 list_append(config->sections, sec);
219 }
220
221 std::string value_string = value;
222 std::string value_no_newline;
223 size_t newline_position = value_string.find("\n");
224 if (newline_position != std::string::npos) {
225 android_errorWriteLog(0x534e4554, "70808273");
226 value_no_newline = value_string.substr(0, newline_position);
227 } else {
228 value_no_newline = value_string;
229 }
230
231 for (const list_node_t* node = list_begin(sec->entries);
232 node != list_end(sec->entries); node = list_next(node)) {
233 entry_t* entry = static_cast<entry_t*>(list_node(node));
234 if (!strcmp(entry->key, key)) {
235 osi_free(entry->value);
236 entry->value = osi_strdup(value_no_newline.c_str());
237 return;
238 }
239 }
240
241 entry_t* entry = entry_new(key, value_no_newline.c_str());
242 list_append(sec->entries, entry);
243 }
244
config_remove_section(config_t * config,const char * section)245 bool config_remove_section(config_t* config, const char* section) {
246 CHECK(config != NULL);
247 CHECK(section != NULL);
248
249 section_t* sec = section_find(config, section);
250 if (!sec) return false;
251
252 return list_remove(config->sections, sec);
253 }
254
config_remove_key(config_t * config,const char * section,const char * key)255 bool config_remove_key(config_t* config, const char* section, const char* key) {
256 CHECK(config != NULL);
257 CHECK(section != NULL);
258 CHECK(key != NULL);
259
260 section_t* sec = section_find(config, section);
261 entry_t* entry = entry_find(config, section, key);
262 if (!sec || !entry) return false;
263
264 return list_remove(sec->entries, entry);
265 }
266
config_section_begin(const config_t * config)267 const config_section_node_t* config_section_begin(const config_t* config) {
268 CHECK(config != NULL);
269 return (const config_section_node_t*)list_begin(config->sections);
270 }
271
config_section_end(const config_t * config)272 const config_section_node_t* config_section_end(const config_t* config) {
273 CHECK(config != NULL);
274 return (const config_section_node_t*)list_end(config->sections);
275 }
276
config_section_next(const config_section_node_t * node)277 const config_section_node_t* config_section_next(
278 const config_section_node_t* node) {
279 CHECK(node != NULL);
280 return (const config_section_node_t*)list_next((const list_node_t*)node);
281 }
282
config_section_name(const config_section_node_t * node)283 const char* config_section_name(const config_section_node_t* node) {
284 CHECK(node != NULL);
285 const list_node_t* lnode = (const list_node_t*)node;
286 const section_t* section = (const section_t*)list_node(lnode);
287 return section->name;
288 }
289
config_save(const config_t * config,const char * filename)290 bool config_save(const config_t* config, const char* filename) {
291 CHECK(config != NULL);
292 CHECK(filename != NULL);
293 CHECK(*filename != '\0');
294
295 // Steps to ensure content of config file gets to disk:
296 //
297 // 1) Open and write to temp file (e.g. bt_config.conf.new).
298 // 2) Sync the temp file to disk with fsync().
299 // 3) Rename temp file to actual config file (e.g. bt_config.conf).
300 // This ensures atomic update.
301 // 4) Sync directory that has the conf file with fsync().
302 // This ensures directory entries are up-to-date.
303 int dir_fd = -1;
304 FILE* fp = NULL;
305
306 // Build temp config file based on config file (e.g. bt_config.conf.new).
307 static const char* temp_file_ext = ".new";
308 const int filename_len = strlen(filename);
309 const int temp_filename_len = filename_len + strlen(temp_file_ext) + 1;
310 char* temp_filename = static_cast<char*>(osi_calloc(temp_filename_len));
311 snprintf(temp_filename, temp_filename_len, "%s%s", filename, temp_file_ext);
312
313 // Extract directory from file path (e.g. /data/misc/bluedroid).
314 char* temp_dirname = osi_strdup(filename);
315 const char* directoryname = dirname(temp_dirname);
316 if (!directoryname) {
317 LOG_ERROR(LOG_TAG, "%s error extracting directory from '%s': %s", __func__,
318 filename, strerror(errno));
319 goto error;
320 }
321
322 dir_fd = open(directoryname, O_RDONLY);
323 if (dir_fd < 0) {
324 LOG_ERROR(LOG_TAG, "%s unable to open dir '%s': %s", __func__,
325 directoryname, strerror(errno));
326 goto error;
327 }
328
329 fp = fopen(temp_filename, "wt");
330 if (!fp) {
331 LOG_ERROR(LOG_TAG, "%s unable to write file '%s': %s", __func__,
332 temp_filename, strerror(errno));
333 goto error;
334 }
335
336 for (const list_node_t* node = list_begin(config->sections);
337 node != list_end(config->sections); node = list_next(node)) {
338 const section_t* section = (const section_t*)list_node(node);
339 if (fprintf(fp, "[%s]\n", section->name) < 0) {
340 LOG_ERROR(LOG_TAG, "%s unable to write to file '%s': %s", __func__,
341 temp_filename, strerror(errno));
342 goto error;
343 }
344
345 for (const list_node_t* enode = list_begin(section->entries);
346 enode != list_end(section->entries); enode = list_next(enode)) {
347 const entry_t* entry = (const entry_t*)list_node(enode);
348 if (fprintf(fp, "%s = %s\n", entry->key, entry->value) < 0) {
349 LOG_ERROR(LOG_TAG, "%s unable to write to file '%s': %s", __func__,
350 temp_filename, strerror(errno));
351 goto error;
352 }
353 }
354
355 // Only add a separating newline if there are more sections.
356 if (list_next(node) != list_end(config->sections)) {
357 if (fputc('\n', fp) == EOF) {
358 LOG_ERROR(LOG_TAG, "%s unable to write to file '%s': %s", __func__,
359 temp_filename, strerror(errno));
360 goto error;
361 }
362 }
363 }
364
365 // Sync written temp file out to disk. fsync() is blocking until data makes it
366 // to disk.
367 if (fsync(fileno(fp)) < 0) {
368 LOG_WARN(LOG_TAG, "%s unable to fsync file '%s': %s", __func__,
369 temp_filename, strerror(errno));
370 }
371
372 if (fclose(fp) == EOF) {
373 LOG_ERROR(LOG_TAG, "%s unable to close file '%s': %s", __func__,
374 temp_filename, strerror(errno));
375 goto error;
376 }
377 fp = NULL;
378
379 // Change the file's permissions to Read/Write by User and Group
380 if (chmod(temp_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1) {
381 LOG_ERROR(LOG_TAG, "%s unable to change file permissions '%s': %s",
382 __func__, filename, strerror(errno));
383 goto error;
384 }
385
386 // Rename written temp file to the actual config file.
387 if (rename(temp_filename, filename) == -1) {
388 LOG_ERROR(LOG_TAG, "%s unable to commit file '%s': %s", __func__, filename,
389 strerror(errno));
390 goto error;
391 }
392
393 // This should ensure the directory is updated as well.
394 if (fsync(dir_fd) < 0) {
395 LOG_WARN(LOG_TAG, "%s unable to fsync dir '%s': %s", __func__,
396 directoryname, strerror(errno));
397 }
398
399 if (close(dir_fd) < 0) {
400 LOG_ERROR(LOG_TAG, "%s unable to close dir '%s': %s", __func__,
401 directoryname, strerror(errno));
402 goto error;
403 }
404
405 osi_free(temp_filename);
406 osi_free(temp_dirname);
407 return true;
408
409 error:
410 // This indicates there is a write issue. Unlink as partial data is not
411 // acceptable.
412 unlink(temp_filename);
413 if (fp) fclose(fp);
414 if (dir_fd != -1) close(dir_fd);
415 osi_free(temp_filename);
416 osi_free(temp_dirname);
417 return false;
418 }
419
trim(char * str)420 static char* trim(char* str) {
421 while (isspace(*str)) ++str;
422
423 if (!*str) return str;
424
425 char* end_str = str + strlen(str) - 1;
426 while (end_str > str && isspace(*end_str)) --end_str;
427
428 end_str[1] = '\0';
429 return str;
430 }
431
config_parse(FILE * fp,config_t * config)432 static bool config_parse(FILE* fp, config_t* config) {
433 CHECK(fp != NULL);
434 CHECK(config != NULL);
435
436 int line_num = 0;
437 char line[1024];
438 char section[1024];
439 strcpy(section, CONFIG_DEFAULT_SECTION);
440
441 while (fgets(line, sizeof(line), fp)) {
442 char* line_ptr = trim(line);
443 ++line_num;
444
445 // Skip blank and comment lines.
446 if (*line_ptr == '\0' || *line_ptr == '#') continue;
447
448 if (*line_ptr == '[') {
449 size_t len = strlen(line_ptr);
450 if (line_ptr[len - 1] != ']') {
451 LOG_DEBUG(LOG_TAG, "%s unterminated section name on line %d.", __func__,
452 line_num);
453 return false;
454 }
455 strncpy(section, line_ptr + 1, len - 2);
456 section[len - 2] = '\0';
457 } else {
458 char* split = strchr(line_ptr, '=');
459 if (!split) {
460 LOG_DEBUG(LOG_TAG, "%s no key/value separator found on line %d.",
461 __func__, line_num);
462 return false;
463 }
464
465 *split = '\0';
466 config_set_string(config, section, trim(line_ptr), trim(split + 1));
467 }
468 }
469 return true;
470 }
471
section_new(const char * name)472 static section_t* section_new(const char* name) {
473 section_t* section = static_cast<section_t*>(osi_calloc(sizeof(section_t)));
474
475 section->name = osi_strdup(name);
476 section->entries = list_new(entry_free);
477 return section;
478 }
479
section_free(void * ptr)480 static void section_free(void* ptr) {
481 if (!ptr) return;
482
483 section_t* section = static_cast<section_t*>(ptr);
484 osi_free(section->name);
485 list_free(section->entries);
486 osi_free(section);
487 }
488
section_find(const config_t * config,const char * section)489 static section_t* section_find(const config_t* config, const char* section) {
490 for (const list_node_t* node = list_begin(config->sections);
491 node != list_end(config->sections); node = list_next(node)) {
492 section_t* sec = static_cast<section_t*>(list_node(node));
493 if (!strcmp(sec->name, section)) return sec;
494 }
495
496 return NULL;
497 }
498
entry_new(const char * key,const char * value)499 static entry_t* entry_new(const char* key, const char* value) {
500 entry_t* entry = static_cast<entry_t*>(osi_calloc(sizeof(entry_t)));
501
502 entry->key = osi_strdup(key);
503 entry->value = osi_strdup(value);
504 return entry;
505 }
506
entry_free(void * ptr)507 static void entry_free(void* ptr) {
508 if (!ptr) return;
509
510 entry_t* entry = static_cast<entry_t*>(ptr);
511 osi_free(entry->key);
512 osi_free(entry->value);
513 osi_free(entry);
514 }
515
entry_find(const config_t * config,const char * section,const char * key)516 static entry_t* entry_find(const config_t* config, const char* section,
517 const char* key) {
518 section_t* sec = section_find(config, section);
519 if (!sec) return NULL;
520
521 for (const list_node_t* node = list_begin(sec->entries);
522 node != list_end(sec->entries); node = list_next(node)) {
523 entry_t* entry = static_cast<entry_t*>(list_node(node));
524 if (!strcmp(entry->key, key)) return entry;
525 }
526
527 return NULL;
528 }
529