2011-12-31 16:14:18 +08:00
|
|
|
/**
|
|
|
|
* @file config.c
|
|
|
|
* @note Copyright (C) 2011 Richard Cochran <richardcochran@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
2013-06-10 15:16:26 +08:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <float.h>
|
|
|
|
#include <limits.h>
|
2011-12-31 16:14:18 +08:00
|
|
|
#include <stdio.h>
|
2014-08-14 21:56:06 +08:00
|
|
|
#include <stdlib.h>
|
2011-12-31 16:14:18 +08:00
|
|
|
#include <string.h>
|
|
|
|
#include "config.h"
|
2012-07-27 17:30:22 +08:00
|
|
|
#include "ether.h"
|
2015-08-09 01:17:29 +08:00
|
|
|
#include "hash.h"
|
2012-08-21 01:56:56 +08:00
|
|
|
#include "print.h"
|
2013-02-04 06:01:52 +08:00
|
|
|
#include "util.h"
|
2011-12-31 16:14:18 +08:00
|
|
|
|
2012-08-21 07:03:26 +08:00
|
|
|
enum config_section {
|
|
|
|
GLOBAL_SECTION,
|
2012-08-21 01:57:27 +08:00
|
|
|
PORT_SECTION,
|
2012-08-21 07:03:26 +08:00
|
|
|
UNKNOWN_SECTION,
|
|
|
|
};
|
|
|
|
|
2015-08-13 00:34:04 +08:00
|
|
|
enum config_type {
|
|
|
|
CFG_TYPE_INT,
|
|
|
|
CFG_TYPE_DOUBLE,
|
2015-08-16 02:49:52 +08:00
|
|
|
CFG_TYPE_ENUM,
|
2015-08-22 03:56:30 +08:00
|
|
|
CFG_TYPE_STRING,
|
2015-08-16 02:49:52 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct config_enum {
|
|
|
|
const char *label;
|
|
|
|
int value;
|
2015-08-13 00:34:04 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef union {
|
|
|
|
int i;
|
|
|
|
double d;
|
2015-08-22 03:56:30 +08:00
|
|
|
char *s;
|
2015-08-13 00:34:04 +08:00
|
|
|
} any_t;
|
|
|
|
|
|
|
|
#define CONFIG_LABEL_SIZE 32
|
|
|
|
|
|
|
|
#define CFG_ITEM_STATIC (1 << 0) /* statically allocated, not to be freed */
|
|
|
|
#define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */
|
|
|
|
#define CFG_ITEM_PORT (1 << 2) /* item may appear in port sections */
|
2015-08-22 03:56:30 +08:00
|
|
|
#define CFG_ITEM_DYNSTR (1 << 4) /* string value dynamically allocated */
|
2015-08-13 00:34:04 +08:00
|
|
|
|
|
|
|
struct config_item {
|
|
|
|
char label[CONFIG_LABEL_SIZE];
|
|
|
|
enum config_type type;
|
2015-08-16 02:49:52 +08:00
|
|
|
struct config_enum *tab;
|
2015-08-13 00:34:04 +08:00
|
|
|
unsigned int flags;
|
|
|
|
any_t val;
|
|
|
|
any_t min;
|
|
|
|
any_t max;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define N_CONFIG_ITEMS (sizeof(config_tab) / sizeof(config_tab[0]))
|
|
|
|
|
2015-08-12 18:14:45 +08:00
|
|
|
#define CONFIG_ITEM_DBL(_label, _port, _default, _min, _max) { \
|
|
|
|
.label = _label, \
|
|
|
|
.type = CFG_TYPE_DOUBLE, \
|
|
|
|
.flags = _port ? CFG_ITEM_PORT : 0, \
|
|
|
|
.val.d = _default, \
|
|
|
|
.min.d = _min, \
|
|
|
|
.max.d = _max, \
|
|
|
|
}
|
2015-08-16 02:49:52 +08:00
|
|
|
#define CONFIG_ITEM_ENUM(_label, _port, _default, _table) { \
|
|
|
|
.label = _label, \
|
|
|
|
.type = CFG_TYPE_ENUM, \
|
|
|
|
.flags = _port ? CFG_ITEM_PORT : 0, \
|
|
|
|
.tab = _table, \
|
|
|
|
.val.i = _default, \
|
|
|
|
}
|
2015-08-13 00:34:04 +08:00
|
|
|
#define CONFIG_ITEM_INT(_label, _port, _default, _min, _max) { \
|
|
|
|
.label = _label, \
|
|
|
|
.type = CFG_TYPE_INT, \
|
|
|
|
.flags = _port ? CFG_ITEM_PORT : 0, \
|
|
|
|
.val.i = _default, \
|
|
|
|
.min.i = _min, \
|
|
|
|
.max.i = _max, \
|
|
|
|
}
|
2015-08-22 03:56:30 +08:00
|
|
|
#define CONFIG_ITEM_STRING(_label, _port, _default) { \
|
|
|
|
.label = _label, \
|
|
|
|
.type = CFG_TYPE_STRING, \
|
|
|
|
.flags = _port ? CFG_ITEM_PORT : 0, \
|
|
|
|
.val.s = _default, \
|
|
|
|
}
|
2015-08-13 00:34:04 +08:00
|
|
|
|
2015-08-12 18:14:45 +08:00
|
|
|
#define GLOB_ITEM_DBL(label, _default, min, max) \
|
|
|
|
CONFIG_ITEM_DBL(label, 0, _default, min, max)
|
|
|
|
|
2015-08-16 02:49:52 +08:00
|
|
|
#define GLOB_ITEM_ENU(label, _default, table) \
|
|
|
|
CONFIG_ITEM_ENUM(label, 0, _default, table)
|
|
|
|
|
2015-08-13 00:34:04 +08:00
|
|
|
#define GLOB_ITEM_INT(label, _default, min, max) \
|
|
|
|
CONFIG_ITEM_INT(label, 0, _default, min, max)
|
|
|
|
|
2015-08-22 03:56:30 +08:00
|
|
|
#define GLOB_ITEM_STR(label, _default) \
|
|
|
|
CONFIG_ITEM_STRING(label, 0, _default)
|
|
|
|
|
2015-08-12 18:14:45 +08:00
|
|
|
#define PORT_ITEM_DBL(label, _default, min, max) \
|
|
|
|
CONFIG_ITEM_DBL(label, 1, _default, min, max)
|
|
|
|
|
2015-08-16 02:49:52 +08:00
|
|
|
#define PORT_ITEM_ENU(label, _default, table) \
|
|
|
|
CONFIG_ITEM_ENUM(label, 1, _default, table)
|
|
|
|
|
2015-08-13 00:34:04 +08:00
|
|
|
#define PORT_ITEM_INT(label, _default, min, max) \
|
|
|
|
CONFIG_ITEM_INT(label, 1, _default, min, max)
|
|
|
|
|
2015-08-22 03:56:30 +08:00
|
|
|
#define PORT_ITEM_STR(label, _default) \
|
|
|
|
CONFIG_ITEM_STRING(label, 1, _default)
|
|
|
|
|
2015-08-16 03:56:59 +08:00
|
|
|
static struct config_enum clock_servo_enu[] = {
|
|
|
|
{ "pi", CLOCK_SERVO_PI },
|
|
|
|
{ "linreg", CLOCK_SERVO_LINREG },
|
|
|
|
{ "ntpshm", CLOCK_SERVO_NTPSHM },
|
|
|
|
{ "nullf", CLOCK_SERVO_NULLF },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2015-08-16 03:25:47 +08:00
|
|
|
static struct config_enum delay_filter_enu[] = {
|
|
|
|
{ "moving_average", FILTER_MOVING_AVERAGE },
|
|
|
|
{ "moving_median", FILTER_MOVING_MEDIAN },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2015-08-16 03:03:02 +08:00
|
|
|
static struct config_enum delay_mech_enu[] = {
|
|
|
|
{ "Auto", DM_AUTO },
|
|
|
|
{ "E2E", DM_E2E },
|
|
|
|
{ "P2P", DM_P2P },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2015-08-16 02:52:09 +08:00
|
|
|
static struct config_enum nw_trans_enu[] = {
|
|
|
|
{ "L2", TRANS_IEEE_802_3 },
|
|
|
|
{ "UDPv4", TRANS_UDP_IPV4 },
|
|
|
|
{ "UDPv6", TRANS_UDP_IPV6 },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2015-08-16 03:45:19 +08:00
|
|
|
static struct config_enum timestamping_enu[] = {
|
|
|
|
{ "hardware", TS_HARDWARE },
|
|
|
|
{ "software", TS_SOFTWARE },
|
|
|
|
{ "legacy", TS_LEGACY_HW },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2015-08-16 03:20:19 +08:00
|
|
|
static struct config_enum tsproc_enu[] = {
|
|
|
|
{ "filter", TSPROC_FILTER },
|
|
|
|
{ "raw", TSPROC_RAW },
|
|
|
|
{ "filter_weight", TSPROC_FILTER_WEIGHT },
|
|
|
|
{ "raw_weight", TSPROC_RAW_WEIGHT },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2015-08-13 00:34:04 +08:00
|
|
|
struct config_item config_tab[] = {
|
2015-08-15 17:08:19 +08:00
|
|
|
PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX),
|
2015-08-13 00:47:00 +08:00
|
|
|
GLOB_ITEM_INT("assume_two_step", 0, 0, 1),
|
2015-08-15 20:04:42 +08:00
|
|
|
PORT_ITEM_INT("boundary_clock_jbod", 0, 0, 1),
|
2015-08-12 18:02:52 +08:00
|
|
|
GLOB_ITEM_INT("check_fup_sync", 0, 0, 1),
|
2015-08-16 01:31:05 +08:00
|
|
|
GLOB_ITEM_INT("clockAccuracy", 0xfe, 0, UINT8_MAX),
|
2015-08-16 01:27:13 +08:00
|
|
|
GLOB_ITEM_INT("clockClass", 248, 0, UINT8_MAX),
|
2015-08-16 03:56:59 +08:00
|
|
|
GLOB_ITEM_ENU("clock_servo", CLOCK_SERVO_PI, clock_servo_enu),
|
2015-08-15 16:46:05 +08:00
|
|
|
PORT_ITEM_INT("delayAsymmetry", 0, INT_MIN, INT_MAX),
|
2015-08-16 03:25:47 +08:00
|
|
|
PORT_ITEM_ENU("delay_filter", FILTER_MOVING_MEDIAN, delay_filter_enu),
|
2015-08-16 18:59:57 +08:00
|
|
|
PORT_ITEM_INT("delay_filter_length", 10, 1, INT_MAX),
|
2015-08-16 03:03:02 +08:00
|
|
|
PORT_ITEM_ENU("delay_mechanism", DM_E2E, delay_mech_enu),
|
2016-07-12 19:52:48 +08:00
|
|
|
GLOB_ITEM_INT("dscp_event", 0, 0, 63),
|
|
|
|
GLOB_ITEM_INT("dscp_general", 0, 0, 63),
|
2015-08-16 01:40:02 +08:00
|
|
|
GLOB_ITEM_INT("domainNumber", 0, 0, 127),
|
2015-08-15 19:23:45 +08:00
|
|
|
PORT_ITEM_INT("egressLatency", 0, INT_MIN, INT_MAX),
|
2015-08-16 00:44:31 +08:00
|
|
|
PORT_ITEM_INT("fault_badpeernet_interval", 16, INT32_MIN, INT32_MAX),
|
|
|
|
PORT_ITEM_INT("fault_reset_interval", 4, INT8_MIN, INT8_MAX),
|
2015-08-12 18:21:36 +08:00
|
|
|
GLOB_ITEM_DBL("first_step_threshold", 0.00002, 0.0, DBL_MAX),
|
2015-08-15 19:00:30 +08:00
|
|
|
PORT_ITEM_INT("follow_up_info", 0, 0, 1),
|
2015-08-15 20:11:17 +08:00
|
|
|
GLOB_ITEM_INT("free_running", 0, 0, 1),
|
2015-08-15 19:08:39 +08:00
|
|
|
PORT_ITEM_INT("freq_est_interval", 1, 0, INT_MAX),
|
2015-08-15 20:19:11 +08:00
|
|
|
GLOB_ITEM_INT("gmCapable", 1, 0, 1),
|
2015-08-29 02:41:50 +08:00
|
|
|
PORT_ITEM_INT("hybrid_e2e", 0, 0, 1),
|
2018-02-15 01:29:55 +08:00
|
|
|
PORT_ITEM_INT("ignore_transport_specific", 0, 0, 1),
|
2015-08-15 19:28:54 +08:00
|
|
|
PORT_ITEM_INT("ingressLatency", 0, INT_MIN, INT_MAX),
|
2018-02-15 19:48:39 +08:00
|
|
|
GLOB_ITEM_INT("initial_delay", 0, 0, INT_MAX),
|
2015-08-15 20:29:04 +08:00
|
|
|
GLOB_ITEM_INT("kernel_leap", 1, 0, 1),
|
2015-08-15 16:52:14 +08:00
|
|
|
PORT_ITEM_INT("logAnnounceInterval", 1, INT8_MIN, INT8_MAX),
|
2015-08-15 16:59:03 +08:00
|
|
|
PORT_ITEM_INT("logMinDelayReqInterval", 0, INT8_MIN, INT8_MAX),
|
2015-08-15 17:02:22 +08:00
|
|
|
PORT_ITEM_INT("logMinPdelayReqInterval", 0, INT8_MIN, INT8_MAX),
|
2015-08-15 16:56:10 +08:00
|
|
|
PORT_ITEM_INT("logSyncInterval", 0, INT8_MIN, INT8_MAX),
|
2015-08-14 04:25:39 +08:00
|
|
|
GLOB_ITEM_INT("logging_level", LOG_INFO, PRINT_LEVEL_MIN, PRINT_LEVEL_MAX),
|
2017-01-17 21:17:39 +08:00
|
|
|
GLOB_ITEM_STR("message_tag", NULL),
|
2015-08-23 02:59:56 +08:00
|
|
|
GLOB_ITEM_STR("manufacturerIdentity", "00:00:00"),
|
2015-08-12 21:32:55 +08:00
|
|
|
GLOB_ITEM_INT("max_frequency", 900000000, 0, INT_MAX),
|
2015-08-15 19:19:05 +08:00
|
|
|
PORT_ITEM_INT("min_neighbor_prop_delay", -20000000, INT_MIN, -1),
|
2015-08-15 19:15:59 +08:00
|
|
|
PORT_ITEM_INT("neighborPropDelayThresh", 20000000, 0, INT_MAX),
|
2017-11-26 10:26:52 +08:00
|
|
|
PORT_ITEM_INT("net_sync_monitor", 0, 0, 1),
|
2015-08-16 02:52:09 +08:00
|
|
|
PORT_ITEM_ENU("network_transport", TRANS_UDP_IPV4, nw_trans_enu),
|
2015-08-15 03:58:49 +08:00
|
|
|
GLOB_ITEM_INT("ntpshm_segment", 0, INT_MIN, INT_MAX),
|
2015-08-16 01:34:36 +08:00
|
|
|
GLOB_ITEM_INT("offsetScaledLogVariance", 0xffff, 0, UINT16_MAX),
|
2015-08-15 18:55:44 +08:00
|
|
|
PORT_ITEM_INT("path_trace_enabled", 0, 0, 1),
|
2015-08-14 04:54:22 +08:00
|
|
|
GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX),
|
2015-08-15 03:35:47 +08:00
|
|
|
GLOB_ITEM_DBL("pi_integral_exponent", 0.4, -DBL_MAX, DBL_MAX),
|
2015-08-15 03:41:21 +08:00
|
|
|
GLOB_ITEM_DBL("pi_integral_norm_max", 0.3, DBL_MIN, 2.0),
|
2015-08-15 03:31:16 +08:00
|
|
|
GLOB_ITEM_DBL("pi_integral_scale", 0.0, 0.0, DBL_MAX),
|
2015-08-14 04:48:25 +08:00
|
|
|
GLOB_ITEM_DBL("pi_proportional_const", 0.0, 0.0, DBL_MAX),
|
2015-08-15 03:21:25 +08:00
|
|
|
GLOB_ITEM_DBL("pi_proportional_exponent", -0.3, -DBL_MAX, DBL_MAX),
|
2015-08-15 03:25:27 +08:00
|
|
|
GLOB_ITEM_DBL("pi_proportional_norm_max", 0.7, DBL_MIN, 1.0),
|
2015-08-14 05:02:06 +08:00
|
|
|
GLOB_ITEM_DBL("pi_proportional_scale", 0.0, 0.0, DBL_MAX),
|
2015-08-16 01:22:05 +08:00
|
|
|
GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX),
|
|
|
|
GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX),
|
2015-08-23 00:14:46 +08:00
|
|
|
GLOB_ITEM_STR("productDescription", ";;"),
|
2015-08-22 04:44:29 +08:00
|
|
|
PORT_ITEM_STR("ptp_dst_mac", "01:1B:19:00:00:00"),
|
2015-08-22 04:59:28 +08:00
|
|
|
PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"),
|
2015-08-23 00:21:30 +08:00
|
|
|
GLOB_ITEM_STR("revisionData", ";;"),
|
2015-08-15 20:37:24 +08:00
|
|
|
GLOB_ITEM_INT("sanity_freq_limit", 200000000, 0, INT_MAX),
|
2015-08-16 01:16:32 +08:00
|
|
|
GLOB_ITEM_INT("slaveOnly", 0, 0, 1),
|
2015-08-12 18:14:45 +08:00
|
|
|
GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX),
|
2015-08-15 20:24:33 +08:00
|
|
|
GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX),
|
2015-08-15 17:24:41 +08:00
|
|
|
PORT_ITEM_INT("syncReceiptTimeout", 0, 0, UINT8_MAX),
|
2015-08-15 20:41:01 +08:00
|
|
|
GLOB_ITEM_INT("timeSource", INTERNAL_OSCILLATOR, 0x10, 0xfe),
|
2015-08-16 03:45:19 +08:00
|
|
|
GLOB_ITEM_ENU("time_stamping", TS_HARDWARE, timestamping_enu),
|
2015-08-15 18:42:31 +08:00
|
|
|
PORT_ITEM_INT("transportSpecific", 0, 0, 0x0F),
|
2015-08-16 03:20:19 +08:00
|
|
|
PORT_ITEM_ENU("tsproc_mode", TSPROC_FILTER, tsproc_enu),
|
2015-08-16 01:16:32 +08:00
|
|
|
GLOB_ITEM_INT("twoStepFlag", 1, 0, 1),
|
2015-08-13 00:47:32 +08:00
|
|
|
GLOB_ITEM_INT("tx_timestamp_timeout", 1, 1, INT_MAX),
|
2015-08-13 00:34:04 +08:00
|
|
|
PORT_ITEM_INT("udp_ttl", 1, 1, 255),
|
2015-08-16 00:53:43 +08:00
|
|
|
PORT_ITEM_INT("udp6_scope", 0x0E, 0x00, 0x0F),
|
2015-08-22 05:25:15 +08:00
|
|
|
GLOB_ITEM_STR("uds_address", "/var/run/ptp4l"),
|
2015-08-14 04:28:40 +08:00
|
|
|
GLOB_ITEM_INT("use_syslog", 1, 0, 1),
|
2015-08-23 00:26:24 +08:00
|
|
|
GLOB_ITEM_STR("userDescription", ""),
|
2017-01-18 01:25:26 +08:00
|
|
|
GLOB_ITEM_INT("utc_offset", CURRENT_UTC_OFFSET, 0, INT_MAX),
|
2015-08-14 04:33:23 +08:00
|
|
|
GLOB_ITEM_INT("verbose", 0, 0, 1),
|
2015-08-13 00:34:04 +08:00
|
|
|
};
|
|
|
|
|
2015-08-23 04:09:03 +08:00
|
|
|
static enum parser_result
|
|
|
|
parse_fault_interval(struct config *cfg, const char *section,
|
|
|
|
const char *option, const char *value);
|
|
|
|
|
2015-08-13 00:34:04 +08:00
|
|
|
static struct config_item *config_section_item(struct config *cfg,
|
|
|
|
const char *section,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
char buf[CONFIG_LABEL_SIZE + MAX_IFNAME_SIZE];
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%s.%s", section, name);
|
|
|
|
return hash_lookup(cfg->htab, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct config_item *config_global_item(struct config *cfg,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
return config_section_item(cfg, "global", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct config_item *config_find_item(struct config *cfg,
|
|
|
|
const char *section,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct config_item *ci;
|
|
|
|
if (section) {
|
|
|
|
ci = config_section_item(cfg, section, name);
|
|
|
|
if (ci) {
|
|
|
|
return ci;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return config_global_item(cfg, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct config_item *config_item_alloc(struct config *cfg,
|
|
|
|
const char *section,
|
|
|
|
const char *name,
|
|
|
|
enum config_type type)
|
|
|
|
{
|
|
|
|
struct config_item *ci;
|
|
|
|
char buf[CONFIG_LABEL_SIZE + MAX_IFNAME_SIZE];
|
|
|
|
|
|
|
|
ci = calloc(1, sizeof(*ci));
|
|
|
|
if (!ci) {
|
|
|
|
fprintf(stderr, "low memory\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
strncpy(ci->label, name, CONFIG_LABEL_SIZE - 1);
|
|
|
|
ci->type = type;
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%s.%s", section, ci->label);
|
|
|
|
if (hash_insert(cfg->htab, buf, ci)) {
|
|
|
|
fprintf(stderr, "low memory or duplicate item %s\n", name);
|
|
|
|
free(ci);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ci;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void config_item_free(void *ptr)
|
|
|
|
{
|
|
|
|
struct config_item *ci = ptr;
|
2015-08-22 03:56:30 +08:00
|
|
|
if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR)
|
|
|
|
free(ci->val.s);
|
2015-08-13 00:34:04 +08:00
|
|
|
if (ci->flags & CFG_ITEM_STATIC)
|
|
|
|
return;
|
|
|
|
free(ci);
|
|
|
|
}
|
|
|
|
|
2012-11-08 01:20:29 +08:00
|
|
|
static enum parser_result parse_section_line(char *s, enum config_section *section)
|
2012-08-21 07:03:26 +08:00
|
|
|
{
|
2012-11-08 01:20:29 +08:00
|
|
|
if (!strcasecmp(s, "[global]")) {
|
2012-08-21 07:03:26 +08:00
|
|
|
*section = GLOBAL_SECTION;
|
|
|
|
} else if (s[0] == '[') {
|
2012-08-21 01:57:27 +08:00
|
|
|
char c;
|
|
|
|
*section = PORT_SECTION;
|
|
|
|
/* Replace square brackets with white space. */
|
|
|
|
while (0 != (c = *s)) {
|
|
|
|
if (c == '[' || c == ']')
|
|
|
|
*s = ' ';
|
|
|
|
s++;
|
|
|
|
}
|
2012-11-08 01:20:29 +08:00
|
|
|
} else
|
|
|
|
return NOT_PARSED;
|
|
|
|
return PARSED_OK;
|
2012-08-21 07:03:26 +08:00
|
|
|
}
|
|
|
|
|
2015-08-13 00:34:04 +08:00
|
|
|
static enum parser_result parse_item(struct config *cfg,
|
2016-12-07 02:40:36 +08:00
|
|
|
int commandline,
|
2015-08-13 00:34:04 +08:00
|
|
|
const char *section,
|
|
|
|
const char *option,
|
|
|
|
const char *value)
|
|
|
|
{
|
2015-08-23 04:09:03 +08:00
|
|
|
enum parser_result r;
|
2015-08-16 02:49:52 +08:00
|
|
|
struct config_item *cgi, *dst;
|
|
|
|
struct config_enum *cte;
|
2015-08-13 00:34:04 +08:00
|
|
|
double df;
|
|
|
|
int val;
|
|
|
|
|
2015-08-23 04:09:03 +08:00
|
|
|
r = parse_fault_interval(cfg, section, option, value);
|
|
|
|
if (r != NOT_PARSED)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = BAD_VALUE;
|
|
|
|
|
2015-08-13 00:34:04 +08:00
|
|
|
/* If there is no default value, then the option is bogus. */
|
|
|
|
cgi = config_global_item(cfg, option);
|
|
|
|
if (!cgi) {
|
|
|
|
return NOT_PARSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cgi->type) {
|
|
|
|
case CFG_TYPE_INT:
|
|
|
|
r = get_ranged_int(value, &val, cgi->min.i, cgi->max.i);
|
|
|
|
break;
|
|
|
|
case CFG_TYPE_DOUBLE:
|
|
|
|
r = get_ranged_double(value, &df, cgi->min.d, cgi->max.d);
|
|
|
|
break;
|
2015-08-16 02:49:52 +08:00
|
|
|
case CFG_TYPE_ENUM:
|
|
|
|
for (cte = cgi->tab; cte->label; cte++) {
|
|
|
|
if (!strcasecmp(cte->label, value)) {
|
|
|
|
val = cte->value;
|
|
|
|
r = PARSED_OK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-08-22 03:56:30 +08:00
|
|
|
break;
|
|
|
|
case CFG_TYPE_STRING:
|
|
|
|
r = PARSED_OK;
|
|
|
|
break;
|
2015-08-13 00:34:04 +08:00
|
|
|
}
|
|
|
|
if (r != PARSED_OK) {
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (section) {
|
|
|
|
if (!(cgi->flags & CFG_ITEM_PORT)) {
|
|
|
|
return NOT_PARSED;
|
|
|
|
}
|
|
|
|
/* Create or update this port specific item. */
|
|
|
|
dst = config_section_item(cfg, section, option);
|
|
|
|
if (!dst) {
|
|
|
|
dst = config_item_alloc(cfg, section, option, cgi->type);
|
|
|
|
if (!dst) {
|
|
|
|
return NOT_PARSED;
|
|
|
|
}
|
|
|
|
}
|
2016-12-07 02:40:36 +08:00
|
|
|
} else if (!commandline && cgi->flags & CFG_ITEM_LOCKED) {
|
2015-08-13 00:34:04 +08:00
|
|
|
/* This global option was set on the command line. */
|
|
|
|
return PARSED_OK;
|
|
|
|
} else {
|
|
|
|
/* Update the global default value. */
|
|
|
|
dst = cgi;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (dst->type) {
|
|
|
|
case CFG_TYPE_INT:
|
2015-08-16 02:49:52 +08:00
|
|
|
case CFG_TYPE_ENUM:
|
2015-08-13 00:34:04 +08:00
|
|
|
dst->val.i = val;
|
|
|
|
break;
|
|
|
|
case CFG_TYPE_DOUBLE:
|
|
|
|
dst->val.d = df;
|
|
|
|
break;
|
2015-08-22 03:56:30 +08:00
|
|
|
case CFG_TYPE_STRING:
|
|
|
|
if (dst->flags & CFG_ITEM_DYNSTR) {
|
|
|
|
free(dst->val.s);
|
|
|
|
}
|
|
|
|
dst->val.s = strdup(value);
|
|
|
|
if (!dst->val.s) {
|
|
|
|
pr_err("low memory");
|
|
|
|
return NOT_PARSED;
|
|
|
|
}
|
|
|
|
dst->flags |= CFG_ITEM_DYNSTR;
|
|
|
|
break;
|
2015-08-13 00:34:04 +08:00
|
|
|
}
|
2016-12-07 02:40:36 +08:00
|
|
|
|
|
|
|
if (commandline) {
|
2016-12-14 02:55:39 +08:00
|
|
|
dst->flags |= CFG_ITEM_LOCKED;
|
2016-12-07 02:40:36 +08:00
|
|
|
}
|
2015-08-13 00:34:04 +08:00
|
|
|
return PARSED_OK;
|
|
|
|
}
|
|
|
|
|
2015-08-16 00:44:31 +08:00
|
|
|
static enum parser_result parse_fault_interval(struct config *cfg,
|
|
|
|
const char *section,
|
|
|
|
const char *option,
|
|
|
|
const char *value)
|
2012-08-21 01:57:06 +08:00
|
|
|
{
|
2015-08-16 00:44:31 +08:00
|
|
|
int i, val;
|
|
|
|
const char *str, *fault_options[2] = {
|
|
|
|
"fault_badpeernet_interval",
|
|
|
|
"fault_reset_interval",
|
|
|
|
};
|
|
|
|
int fault_values[2] = {
|
|
|
|
0, FRI_ASAP,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (strcasecmp("ASAP", value)) {
|
2012-11-08 01:20:29 +08:00
|
|
|
return NOT_PARSED;
|
2015-08-16 00:44:31 +08:00
|
|
|
}
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
str = fault_options[i];
|
|
|
|
val = fault_values[i];
|
|
|
|
if (!strcmp(option, str)) {
|
|
|
|
if (config_set_section_int(cfg, section, str, val)) {
|
|
|
|
pr_err("bug: failed to set option %s!", option);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
return PARSED_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NOT_PARSED;
|
2012-08-21 01:57:06 +08:00
|
|
|
}
|
|
|
|
|
2014-03-19 01:25:14 +08:00
|
|
|
static enum parser_result parse_setting_line(char *line,
|
|
|
|
const char **option,
|
|
|
|
const char **value)
|
2012-11-08 01:20:29 +08:00
|
|
|
{
|
|
|
|
*option = line;
|
2012-09-29 02:45:54 +08:00
|
|
|
|
2012-11-08 01:20:29 +08:00
|
|
|
while (!isspace(line[0])) {
|
|
|
|
if (line[0] == '\0')
|
|
|
|
return NOT_PARSED;
|
|
|
|
line++;
|
2011-12-31 16:14:18 +08:00
|
|
|
}
|
2012-11-08 01:20:29 +08:00
|
|
|
|
|
|
|
while (isspace(line[0])) {
|
|
|
|
line[0] = '\0';
|
|
|
|
line++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*value = line;
|
|
|
|
|
|
|
|
return PARSED_OK;
|
2011-12-31 16:14:18 +08:00
|
|
|
}
|
|
|
|
|
2014-03-19 01:25:14 +08:00
|
|
|
static void check_deprecated_options(const char **option)
|
|
|
|
{
|
|
|
|
const char *new_option = NULL;
|
|
|
|
|
|
|
|
if (!strcmp(*option, "pi_offset_const")) {
|
|
|
|
new_option = "step_threshold";
|
|
|
|
} else if (!strcmp(*option, "pi_f_offset_const")) {
|
|
|
|
new_option = "first_step_threshold";
|
|
|
|
} else if (!strcmp(*option, "pi_max_frequency")) {
|
|
|
|
new_option = "max_frequency";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_option) {
|
|
|
|
fprintf(stderr, "option %s is deprecated, please use %s instead\n",
|
|
|
|
*option, new_option);
|
|
|
|
*option = new_option;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-07 02:40:36 +08:00
|
|
|
static struct option *config_alloc_longopts(struct config *cfg)
|
|
|
|
{
|
|
|
|
struct config_item *ci;
|
|
|
|
struct option *opts;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
opts = calloc(1, (1 + N_CONFIG_ITEMS) * sizeof(*opts));
|
|
|
|
if (!opts) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (i = 0; i < N_CONFIG_ITEMS; i++) {
|
|
|
|
ci = &config_tab[i];
|
|
|
|
opts[i].name = ci->label;
|
|
|
|
opts[i].has_arg = required_argument;
|
|
|
|
}
|
|
|
|
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
2012-03-21 03:09:40 +08:00
|
|
|
int config_read(char *name, struct config *cfg)
|
2011-12-31 16:14:18 +08:00
|
|
|
{
|
2012-11-08 01:20:29 +08:00
|
|
|
enum config_section current_section = UNKNOWN_SECTION;
|
|
|
|
enum parser_result parser_res;
|
2011-12-31 16:14:18 +08:00
|
|
|
FILE *fp;
|
2014-03-19 01:25:14 +08:00
|
|
|
char buf[1024], *line, *c;
|
|
|
|
const char *option, *value;
|
2014-08-14 21:56:06 +08:00
|
|
|
struct interface *current_port = NULL;
|
|
|
|
int line_num;
|
2011-12-31 16:14:18 +08:00
|
|
|
|
2012-08-22 00:03:45 +08:00
|
|
|
fp = 0 == strncmp(name, "-", 2) ? stdin : fopen(name, "r");
|
2011-12-31 16:14:18 +08:00
|
|
|
|
|
|
|
if (!fp) {
|
2012-11-08 01:20:29 +08:00
|
|
|
fprintf(stderr, "failed to open configuration file %s: %m\n", name);
|
2011-12-31 16:14:18 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-11-08 01:20:29 +08:00
|
|
|
for (line_num = 1; fgets(buf, sizeof(buf), fp); line_num++) {
|
|
|
|
c = buf;
|
2012-11-07 02:00:00 +08:00
|
|
|
|
|
|
|
/* skip whitespace characters */
|
2012-11-08 01:20:29 +08:00
|
|
|
while (isspace(*c))
|
|
|
|
c++;
|
2012-11-07 02:00:00 +08:00
|
|
|
|
|
|
|
/* ignore empty lines and comments */
|
2012-11-08 01:20:29 +08:00
|
|
|
if (*c == '#' || *c == '\n' || *c == '\0')
|
2012-11-07 02:00:00 +08:00
|
|
|
continue;
|
|
|
|
|
2012-11-08 01:20:29 +08:00
|
|
|
line = c;
|
|
|
|
|
|
|
|
/* remove trailing whitespace characters and \n */
|
|
|
|
c += strlen(line) - 1;
|
|
|
|
while (c > line && (*c == '\n' || isspace(*c)))
|
|
|
|
*c-- = '\0';
|
|
|
|
|
|
|
|
if (parse_section_line(line, ¤t_section) == PARSED_OK) {
|
2012-08-21 01:57:27 +08:00
|
|
|
if (current_section == PORT_SECTION) {
|
|
|
|
char port[17];
|
|
|
|
if (1 != sscanf(line, " %16s", port)) {
|
2012-11-08 01:20:29 +08:00
|
|
|
fprintf(stderr, "could not parse port name on line %d\n",
|
|
|
|
line_num);
|
|
|
|
goto parse_error;
|
2012-08-21 01:57:27 +08:00
|
|
|
}
|
|
|
|
current_port = config_create_interface(port, cfg);
|
2014-08-14 21:56:06 +08:00
|
|
|
if (!current_port)
|
2012-11-08 01:20:29 +08:00
|
|
|
goto parse_error;
|
2012-08-21 01:57:27 +08:00
|
|
|
}
|
2012-08-21 07:03:26 +08:00
|
|
|
continue;
|
2012-08-21 01:57:27 +08:00
|
|
|
}
|
2012-08-21 07:03:26 +08:00
|
|
|
|
2015-08-23 03:29:45 +08:00
|
|
|
if (current_section == UNKNOWN_SECTION) {
|
|
|
|
fprintf(stderr, "line %d is not in a section\n", line_num);
|
|
|
|
goto parse_error;
|
|
|
|
}
|
2012-11-08 01:20:29 +08:00
|
|
|
|
2015-08-23 03:29:45 +08:00
|
|
|
if (parse_setting_line(line, &option, &value)) {
|
|
|
|
fprintf(stderr, "could not parse line %d in %s section\n",
|
|
|
|
line_num, current_section == GLOBAL_SECTION ?
|
|
|
|
"global" : current_port->name);
|
|
|
|
goto parse_error;
|
|
|
|
}
|
2014-03-19 01:25:14 +08:00
|
|
|
|
2015-08-23 03:29:45 +08:00
|
|
|
check_deprecated_options(&option);
|
2012-11-08 01:20:29 +08:00
|
|
|
|
2016-12-07 02:40:36 +08:00
|
|
|
parser_res = parse_item(cfg, 0, current_section == GLOBAL_SECTION ?
|
2015-08-23 04:09:03 +08:00
|
|
|
NULL : current_port->name, option, value);
|
2012-11-08 01:20:29 +08:00
|
|
|
|
2015-08-23 03:29:45 +08:00
|
|
|
switch (parser_res) {
|
|
|
|
case PARSED_OK:
|
2012-08-21 01:57:27 +08:00
|
|
|
break;
|
2015-08-23 03:29:45 +08:00
|
|
|
case NOT_PARSED:
|
|
|
|
fprintf(stderr, "unknown option %s at line %d in %s section\n",
|
|
|
|
option, line_num,
|
|
|
|
current_section == GLOBAL_SECTION ? "global" :
|
|
|
|
current_port->name);
|
|
|
|
goto parse_error;
|
|
|
|
case BAD_VALUE:
|
|
|
|
fprintf(stderr, "%s is a bad value for option %s at line %d\n",
|
|
|
|
value, option, line_num);
|
|
|
|
goto parse_error;
|
|
|
|
case MALFORMED:
|
|
|
|
fprintf(stderr, "%s is a malformed value for option %s at line %d\n",
|
|
|
|
value, option, line_num);
|
|
|
|
goto parse_error;
|
|
|
|
case OUT_OF_RANGE:
|
|
|
|
fprintf(stderr, "%s is an out of range value for option %s at line %d\n",
|
|
|
|
value, option, line_num);
|
2012-11-08 01:20:29 +08:00
|
|
|
goto parse_error;
|
2012-08-21 07:03:26 +08:00
|
|
|
}
|
2011-12-31 16:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
return 0;
|
2012-11-08 01:20:29 +08:00
|
|
|
|
|
|
|
parse_error:
|
|
|
|
fprintf(stderr, "failed to parse configuration file %s\n", name);
|
|
|
|
fclose(fp);
|
|
|
|
return -2;
|
2011-12-31 16:14:18 +08:00
|
|
|
}
|
2012-08-21 01:56:45 +08:00
|
|
|
|
2014-08-14 21:56:06 +08:00
|
|
|
struct interface *config_create_interface(char *name, struct config *cfg)
|
2012-08-21 01:56:45 +08:00
|
|
|
{
|
|
|
|
struct interface *iface;
|
|
|
|
|
2014-08-14 21:56:06 +08:00
|
|
|
/* only create each interface once (by name) */
|
|
|
|
STAILQ_FOREACH(iface, &cfg->interfaces, list) {
|
|
|
|
if (0 == strncmp(name, iface->name, MAX_IFNAME_SIZE))
|
|
|
|
return iface;
|
2012-08-21 01:56:45 +08:00
|
|
|
}
|
|
|
|
|
2014-08-14 21:56:06 +08:00
|
|
|
iface = calloc(1, sizeof(struct interface));
|
|
|
|
if (!iface) {
|
|
|
|
fprintf(stderr, "cannot allocate memory for a port\n");
|
|
|
|
return NULL;
|
2012-08-21 01:56:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(iface->name, name, MAX_IFNAME_SIZE);
|
2014-08-14 21:56:06 +08:00
|
|
|
STAILQ_INSERT_TAIL(&cfg->interfaces, iface, list);
|
2015-10-19 17:29:49 +08:00
|
|
|
cfg->n_interfaces++;
|
|
|
|
|
2014-08-14 21:56:06 +08:00
|
|
|
return iface;
|
|
|
|
}
|
|
|
|
|
2015-08-23 19:59:38 +08:00
|
|
|
struct config *config_create(void)
|
2015-08-09 01:17:29 +08:00
|
|
|
{
|
2015-08-13 00:34:04 +08:00
|
|
|
char buf[CONFIG_LABEL_SIZE + 8];
|
|
|
|
struct config_item *ci;
|
2015-08-23 19:59:38 +08:00
|
|
|
struct config *cfg;
|
2015-08-13 00:34:04 +08:00
|
|
|
int i;
|
|
|
|
|
2015-08-23 19:59:38 +08:00
|
|
|
cfg = calloc(1, sizeof(*cfg));
|
|
|
|
if (!cfg) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
STAILQ_INIT(&cfg->interfaces);
|
|
|
|
|
2016-12-07 02:40:36 +08:00
|
|
|
cfg->opts = config_alloc_longopts(cfg);
|
|
|
|
if (!cfg->opts) {
|
|
|
|
free(cfg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-08-09 01:17:29 +08:00
|
|
|
cfg->htab = hash_create();
|
|
|
|
if (!cfg->htab) {
|
2016-12-07 02:40:36 +08:00
|
|
|
free(cfg->opts);
|
2015-08-23 19:59:38 +08:00
|
|
|
free(cfg);
|
|
|
|
return NULL;
|
2015-08-09 01:17:29 +08:00
|
|
|
}
|
2015-08-13 00:34:04 +08:00
|
|
|
|
|
|
|
/* Populate the hash table with global defaults. */
|
|
|
|
for (i = 0; i < N_CONFIG_ITEMS; i++) {
|
|
|
|
ci = &config_tab[i];
|
|
|
|
ci->flags |= CFG_ITEM_STATIC;
|
|
|
|
snprintf(buf, sizeof(buf), "global.%s", ci->label);
|
|
|
|
if (hash_insert(cfg->htab, buf, ci)) {
|
|
|
|
fprintf(stderr, "duplicate item %s\n", ci->label);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform a Built In Self Test.*/
|
|
|
|
for (i = 0; i < N_CONFIG_ITEMS; i++) {
|
|
|
|
ci = &config_tab[i];
|
|
|
|
ci = config_global_item(cfg, ci->label);
|
|
|
|
if (ci != &config_tab[i]) {
|
|
|
|
fprintf(stderr, "config BIST failed at %s\n",
|
|
|
|
config_tab[i].label);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
2015-08-23 19:59:38 +08:00
|
|
|
return cfg;
|
2015-08-13 00:34:04 +08:00
|
|
|
fail:
|
|
|
|
hash_destroy(cfg->htab, NULL);
|
2016-12-07 02:40:36 +08:00
|
|
|
free(cfg->opts);
|
2015-08-23 19:59:38 +08:00
|
|
|
free(cfg);
|
|
|
|
return NULL;
|
2015-08-09 01:17:29 +08:00
|
|
|
}
|
|
|
|
|
2014-08-14 21:56:06 +08:00
|
|
|
void config_destroy(struct config *cfg)
|
|
|
|
{
|
|
|
|
struct interface *iface;
|
2012-08-21 01:56:45 +08:00
|
|
|
|
2014-08-14 21:56:06 +08:00
|
|
|
while ((iface = STAILQ_FIRST(&cfg->interfaces))) {
|
|
|
|
STAILQ_REMOVE_HEAD(&cfg->interfaces, list);
|
|
|
|
free(iface);
|
|
|
|
}
|
2015-08-13 00:34:04 +08:00
|
|
|
hash_destroy(cfg->htab, config_item_free);
|
2016-12-07 02:40:36 +08:00
|
|
|
free(cfg->opts);
|
2015-08-23 19:59:38 +08:00
|
|
|
free(cfg);
|
2015-08-13 00:34:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
double config_get_double(struct config *cfg, const char *section,
|
|
|
|
const char *option)
|
|
|
|
{
|
|
|
|
struct config_item *ci = config_find_item(cfg, section, option);
|
|
|
|
|
|
|
|
if (!ci || ci->type != CFG_TYPE_DOUBLE) {
|
|
|
|
pr_err("bug: config option %s missing or invalid!", option);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
pr_debug("config item %s.%s is %f", section, option, ci->val.d);
|
|
|
|
return ci->val.d;
|
|
|
|
}
|
|
|
|
|
|
|
|
int config_get_int(struct config *cfg, const char *section, const char *option)
|
|
|
|
{
|
|
|
|
struct config_item *ci = config_find_item(cfg, section, option);
|
|
|
|
|
|
|
|
if (!ci) {
|
|
|
|
pr_err("bug: config option %s missing!", option);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
switch (ci->type) {
|
|
|
|
case CFG_TYPE_DOUBLE:
|
2015-08-22 03:56:30 +08:00
|
|
|
case CFG_TYPE_STRING:
|
2015-08-13 00:34:04 +08:00
|
|
|
pr_err("bug: config option %s type mismatch!", option);
|
|
|
|
exit(-1);
|
|
|
|
case CFG_TYPE_INT:
|
2015-08-16 02:49:52 +08:00
|
|
|
case CFG_TYPE_ENUM:
|
2015-08-13 00:34:04 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
pr_debug("config item %s.%s is %d", section, option, ci->val.i);
|
|
|
|
return ci->val.i;
|
2012-08-21 01:56:45 +08:00
|
|
|
}
|
2015-08-13 02:53:58 +08:00
|
|
|
|
2015-08-22 03:56:30 +08:00
|
|
|
char *config_get_string(struct config *cfg, const char *section,
|
|
|
|
const char *option)
|
|
|
|
{
|
|
|
|
struct config_item *ci = config_find_item(cfg, section, option);
|
|
|
|
|
|
|
|
if (!ci || ci->type != CFG_TYPE_STRING) {
|
|
|
|
pr_err("bug: config option %s missing or invalid!", option);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
pr_debug("config item %s.%s is '%s'", section, option, ci->val.s);
|
|
|
|
return ci->val.s;
|
|
|
|
}
|
|
|
|
|
2016-12-07 02:40:36 +08:00
|
|
|
int config_parse_option(struct config *cfg, const char *opt, const char *val)
|
|
|
|
{
|
|
|
|
enum parser_result result;
|
|
|
|
|
|
|
|
result = parse_item(cfg, 1, NULL, opt, val);
|
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
case PARSED_OK:
|
|
|
|
return 0;
|
|
|
|
case NOT_PARSED:
|
|
|
|
fprintf(stderr, "unknown option %s\n", opt);
|
|
|
|
break;
|
|
|
|
case BAD_VALUE:
|
|
|
|
fprintf(stderr, "%s is a bad value for option %s\n", val, opt);
|
|
|
|
break;
|
|
|
|
case MALFORMED:
|
|
|
|
fprintf(stderr, "%s is a malformed value for option %s\n",
|
|
|
|
val, opt);
|
|
|
|
break;
|
|
|
|
case OUT_OF_RANGE:
|
|
|
|
fprintf(stderr, "%s is an out of range value for option %s\n",
|
|
|
|
val, opt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-08-13 02:53:58 +08:00
|
|
|
int config_set_double(struct config *cfg, const char *option, double val)
|
|
|
|
{
|
|
|
|
struct config_item *ci = config_find_item(cfg, NULL, option);
|
|
|
|
|
|
|
|
if (!ci || ci->type != CFG_TYPE_DOUBLE) {
|
|
|
|
pr_err("bug: config option %s missing or invalid!", option);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ci->flags |= CFG_ITEM_LOCKED;
|
|
|
|
ci->val.d = val;
|
|
|
|
pr_debug("locked item global.%s as %f", option, ci->val.d);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int config_set_section_int(struct config *cfg, const char *section,
|
|
|
|
const char *option, int val)
|
|
|
|
{
|
|
|
|
struct config_item *cgi, *dst;
|
|
|
|
|
|
|
|
cgi = config_find_item(cfg, NULL, option);
|
|
|
|
if (!cgi) {
|
|
|
|
pr_err("bug: config option %s missing!", option);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
switch (cgi->type) {
|
|
|
|
case CFG_TYPE_DOUBLE:
|
2015-08-22 03:56:30 +08:00
|
|
|
case CFG_TYPE_STRING:
|
2015-08-13 02:53:58 +08:00
|
|
|
pr_err("bug: config option %s type mismatch!", option);
|
|
|
|
return -1;
|
|
|
|
case CFG_TYPE_INT:
|
2015-08-16 02:49:52 +08:00
|
|
|
case CFG_TYPE_ENUM:
|
2015-08-13 02:53:58 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!section) {
|
|
|
|
cgi->flags |= CFG_ITEM_LOCKED;
|
|
|
|
cgi->val.i = val;
|
|
|
|
pr_debug("locked item global.%s as %d", option, cgi->val.i);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Create or update this port specific item. */
|
|
|
|
dst = config_section_item(cfg, section, option);
|
|
|
|
if (!dst) {
|
|
|
|
dst = config_item_alloc(cfg, section, option, cgi->type);
|
|
|
|
if (!dst) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dst->val.i = val;
|
|
|
|
pr_debug("section item %s.%s now %d", section, option, dst->val.i);
|
|
|
|
return 0;
|
|
|
|
}
|
2015-08-22 03:56:30 +08:00
|
|
|
|
|
|
|
int config_set_string(struct config *cfg, const char *option,
|
|
|
|
const char *val)
|
|
|
|
{
|
|
|
|
struct config_item *ci = config_find_item(cfg, NULL, option);
|
|
|
|
|
|
|
|
if (!ci || ci->type != CFG_TYPE_STRING) {
|
|
|
|
pr_err("bug: config option %s missing or invalid!", option);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ci->flags |= CFG_ITEM_LOCKED;
|
|
|
|
if (ci->flags & CFG_ITEM_DYNSTR) {
|
|
|
|
free(ci->val.s);
|
|
|
|
}
|
|
|
|
ci->val.s = strdup(val);
|
|
|
|
if (!ci->val.s) {
|
|
|
|
pr_err("low memory");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ci->flags |= CFG_ITEM_DYNSTR;
|
|
|
|
pr_debug("locked item global.%s as '%s'", option, ci->val.s);
|
|
|
|
return 0;
|
|
|
|
}
|