149 lines
3.6 KiB
C
149 lines
3.6 KiB
C
|
/* Copyright 2021 The Chromium OS Authors. All rights reserved.
|
||
|
* Use of this source code is governed by a BSD-style license that can be
|
||
|
* found in the LICENSE file.
|
||
|
*/
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "config_parser.h"
|
||
|
|
||
|
#include "util.h"
|
||
|
|
||
|
#define LIST_DEFAULT_SIZE (100)
|
||
|
|
||
|
struct config_entry_list *new_config_entry_list(void)
|
||
|
{
|
||
|
/*
|
||
|
* There are <100 CLI options, configuration file will likely have
|
||
|
* a similar number of config entries.
|
||
|
*/
|
||
|
struct config_entry *entries =
|
||
|
calloc(LIST_DEFAULT_SIZE, sizeof(struct config_entry));
|
||
|
if (!entries)
|
||
|
return NULL;
|
||
|
|
||
|
struct config_entry_list *list =
|
||
|
calloc(1, sizeof(struct config_entry_list));
|
||
|
if (!list) {
|
||
|
free(entries);
|
||
|
return NULL;
|
||
|
}
|
||
|
list->entries = entries;
|
||
|
list->num_allocated_ = LIST_DEFAULT_SIZE;
|
||
|
list->num_entries = 0;
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
void clear_config_entry(struct config_entry *entry)
|
||
|
{
|
||
|
free((char *)entry->key);
|
||
|
free((char *)entry->value);
|
||
|
}
|
||
|
|
||
|
void free_config_entry_list(struct config_entry_list *list)
|
||
|
{
|
||
|
if (!list)
|
||
|
return;
|
||
|
for (size_t i = 0; i < list->num_entries; i++) {
|
||
|
clear_config_entry(&list->entries[i]);
|
||
|
}
|
||
|
free(list->entries);
|
||
|
free(list);
|
||
|
}
|
||
|
|
||
|
bool parse_config_line(const char *config_line, struct config_entry *entry)
|
||
|
{
|
||
|
/* Parsing will modify |config_line| in place, so make a copy. */
|
||
|
attribute_cleanup_str char *line = strdup(config_line);
|
||
|
if (!line)
|
||
|
return false;
|
||
|
char *value = line;
|
||
|
|
||
|
/* After tokenize call, |value| will point to a substring after '='.
|
||
|
* If there is no '=' in the string, |key| will contain the entire
|
||
|
* string while |value| will be NULL.
|
||
|
*/
|
||
|
char *key = tokenize(&value, "=");
|
||
|
if (key)
|
||
|
key = strip(key);
|
||
|
if (value)
|
||
|
value = strip(value);
|
||
|
if (!key || key[0] == '\0' || (value && value[0] == '\0')) {
|
||
|
warn("unable to parse %s", config_line);
|
||
|
return false;
|
||
|
}
|
||
|
entry->key = strdup(key);
|
||
|
entry->value = value ? strdup(value) : NULL;
|
||
|
if (!entry->key || (value && !entry->value)) {
|
||
|
clear_config_entry(entry);
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool match_special_directive(const char *line)
|
||
|
{
|
||
|
return (strcmp(line, "% minijail-config-file v0\n") == 0);
|
||
|
}
|
||
|
|
||
|
bool parse_config_file(FILE *config_file, struct config_entry_list *list)
|
||
|
{
|
||
|
attribute_cleanup_str char *line = NULL;
|
||
|
size_t len = 0;
|
||
|
|
||
|
/* The first line must match the special directive */
|
||
|
if (getline(&line, &len, config_file) == -1 ||
|
||
|
!match_special_directive(line))
|
||
|
return false;
|
||
|
while (getmultiline(&line, &len, config_file) != -1) {
|
||
|
char *stripped_line = strip(line);
|
||
|
/*
|
||
|
* Skip blank lines and all comments. Comment lines start with
|
||
|
* '#'.
|
||
|
*/
|
||
|
if (stripped_line[0] == '\0' || stripped_line[0] == '#')
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
* Check if the list is full, and reallocate with doubled
|
||
|
* capacity if so.
|
||
|
*/
|
||
|
if (list->num_entries >= list->num_allocated_) {
|
||
|
list->num_allocated_ = list->num_allocated_ * 2;
|
||
|
list->entries = realloc(
|
||
|
list->entries,
|
||
|
list->num_allocated_ * sizeof(struct config_entry));
|
||
|
if (list->entries == NULL) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct config_entry *entry = &list->entries[list->num_entries];
|
||
|
if (!parse_config_line(stripped_line, entry)) {
|
||
|
return false;
|
||
|
}
|
||
|
++list->num_entries;
|
||
|
}
|
||
|
/*
|
||
|
* getmultiline() behaves similarly with getline(3). It returns -1
|
||
|
* when read into EOF or the following errors.
|
||
|
*/
|
||
|
if (errno == EINVAL || errno == ENOMEM) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* Shrink the list to save memory. */
|
||
|
if (list->num_entries < list->num_allocated_) {
|
||
|
list->entries =
|
||
|
realloc(list->entries,
|
||
|
list->num_entries * sizeof(struct config_entry));
|
||
|
list->num_allocated_ = list->num_entries;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|