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>
|
2019-07-08 07:47:48 +08:00
|
|
|
#include <linux/ptp_clock.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>
|
2017-02-15 03:34:47 +08:00
|
|
|
|
2018-10-04 00:41:49 +08:00
|
|
|
#include "as_capable.h"
|
2017-02-15 03:34:47 +08:00
|
|
|
#include "bmc.h"
|
2015-10-20 19:23:16 +08:00
|
|
|
#include "clock.h"
|
2011-12-31 16:14:18 +08:00
|
|
|
#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
|
|
|
|
2020-02-11 13:05:42 +08:00
|
|
|
struct interface {
|
|
|
|
STAILQ_ENTRY(interface) list;
|
|
|
|
};
|
|
|
|
|
2012-08-21 07:03:26 +08:00
|
|
|
enum config_section {
|
|
|
|
GLOBAL_SECTION,
|
2018-03-09 13:59:15 +08:00
|
|
|
UC_MTAB_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-10-20 19:23:16 +08:00
|
|
|
static struct config_enum clock_type_enu[] = {
|
|
|
|
{ "OC", CLOCK_TYPE_ORDINARY },
|
|
|
|
{ "BC", CLOCK_TYPE_BOUNDARY },
|
|
|
|
{ "P2P_TC", CLOCK_TYPE_P2P },
|
|
|
|
{ "E2E_TC", CLOCK_TYPE_E2E },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2017-02-15 03:34:47 +08:00
|
|
|
static struct config_enum dataset_comp_enu[] = {
|
|
|
|
{ "ieee1588", DS_CMP_IEEE1588 },
|
|
|
|
{ "G.8275.x", DS_CMP_G8275 },
|
|
|
|
{ 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 },
|
|
|
|
};
|
|
|
|
|
2019-07-08 07:47:48 +08:00
|
|
|
static struct config_enum extts_polarity_enu[] = {
|
|
|
|
{ "rising", PTP_RISING_EDGE },
|
|
|
|
{ "falling", PTP_FALLING_EDGE },
|
|
|
|
{ "both", PTP_RISING_EDGE | PTP_FALLING_EDGE },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2018-08-29 04:05:28 +08:00
|
|
|
static struct config_enum hwts_filter_enu[] = {
|
|
|
|
{ "normal", HWTS_FILTER_NORMAL },
|
|
|
|
{ "check", HWTS_FILTER_CHECK },
|
|
|
|
{ "full", HWTS_FILTER_FULL },
|
|
|
|
{ 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 },
|
2018-03-03 10:59:47 +08:00
|
|
|
{ "onestep", TS_ONESTEP },
|
|
|
|
{ "p2p1step", TS_P2P1STEP },
|
2015-08-16 03:45:19 +08:00
|
|
|
{ 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 },
|
|
|
|
};
|
|
|
|
|
2018-10-04 00:41:49 +08:00
|
|
|
static struct config_enum as_capable_enu[] = {
|
|
|
|
{ "true", AS_CAPABLE_TRUE },
|
|
|
|
{ "auto", AS_CAPABLE_AUTO },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2018-10-04 00:41:50 +08:00
|
|
|
static struct config_enum bmca_enu[] = {
|
|
|
|
{ "ptp", BMCA_PTP },
|
|
|
|
{ "noop", BMCA_NOOP },
|
|
|
|
{ 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),
|
2018-10-04 00:41:49 +08:00
|
|
|
PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu),
|
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),
|
2018-10-04 00:41:50 +08:00
|
|
|
PORT_ITEM_ENU("BMCA", BMCA_PTP, bmca_enu),
|
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),
|
2018-09-14 17:14:58 +08:00
|
|
|
GLOB_ITEM_STR("clockIdentity", "000000.0000.000000"),
|
2015-08-16 03:56:59 +08:00
|
|
|
GLOB_ITEM_ENU("clock_servo", CLOCK_SERVO_PI, clock_servo_enu),
|
2015-10-20 19:23:16 +08:00
|
|
|
GLOB_ITEM_ENU("clock_type", CLOCK_TYPE_ORDINARY, clock_type_enu),
|
2017-02-15 03:34:47 +08:00
|
|
|
GLOB_ITEM_ENU("dataset_comparison", DS_CMP_IEEE1588, dataset_comp_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),
|
2017-02-15 02:55:51 +08:00
|
|
|
GLOB_ITEM_INT("G.8275.defaultDS.localPriority", 128, 1, UINT8_MAX),
|
2017-02-15 03:03:15 +08:00
|
|
|
PORT_ITEM_INT("G.8275.portDS.localPriority", 128, 1, UINT8_MAX),
|
2015-08-15 20:19:11 +08:00
|
|
|
GLOB_ITEM_INT("gmCapable", 1, 0, 1),
|
2018-08-29 04:05:28 +08:00
|
|
|
GLOB_ITEM_ENU("hwts_filter", HWTS_FILTER_NORMAL, hwts_filter_enu),
|
2015-08-29 02:41:50 +08:00
|
|
|
PORT_ITEM_INT("hybrid_e2e", 0, 0, 1),
|
2018-10-04 00:41:52 +08:00
|
|
|
PORT_ITEM_INT("ignore_source_id", 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-10-04 00:41:51 +08:00
|
|
|
PORT_ITEM_INT("inhibit_announce", 0, 0, 1),
|
2019-03-29 11:32:31 +08:00
|
|
|
PORT_ITEM_INT("inhibit_delay_req", 0, 0, 1),
|
|
|
|
PORT_ITEM_INT("inhibit_multicast_service", 0, 0, 1),
|
2019-08-19 23:37:23 +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-02-15 03:12:01 +08:00
|
|
|
PORT_ITEM_INT("masterOnly", 0, 0, 1),
|
2018-09-14 16:57:30 +08:00
|
|
|
GLOB_ITEM_INT("maxStepsRemoved", 255, 2, UINT8_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),
|
2020-01-24 03:07:22 +08:00
|
|
|
PORT_ITEM_INT("msg_interval_request", 0, 0, 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),
|
2019-03-29 11:32:30 +08:00
|
|
|
PORT_ITEM_INT("operLogPdelayReqInterval", 0, INT8_MIN, INT8_MAX),
|
|
|
|
PORT_ITEM_INT("operLogSyncInterval", 0, INT8_MIN, INT8_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),
|
2019-03-29 11:32:30 +08:00
|
|
|
GLOB_ITEM_INT("servo_num_offset_values", 10, 0, INT_MAX),
|
|
|
|
GLOB_ITEM_INT("servo_offset_threshold", 0, 0, INT_MAX),
|
2020-04-14 09:57:16 +08:00
|
|
|
GLOB_ITEM_STR("slave_event_monitor", ""),
|
2015-08-16 01:16:32 +08:00
|
|
|
GLOB_ITEM_INT("slaveOnly", 0, 0, 1),
|
2019-08-07 11:31:18 +08:00
|
|
|
GLOB_ITEM_INT("socket_priority", 0, 0, 15),
|
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),
|
2018-05-17 17:56:09 +08:00
|
|
|
GLOB_ITEM_INT("tc_spanning_tree", 0, 0, 1),
|
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),
|
2019-07-08 07:47:48 +08:00
|
|
|
PORT_ITEM_INT("ts2phc.channel", 0, 0, INT_MAX),
|
|
|
|
PORT_ITEM_INT("ts2phc.extts_correction", 0, INT_MIN, INT_MAX),
|
|
|
|
PORT_ITEM_ENU("ts2phc.extts_polarity", PTP_RISING_EDGE, extts_polarity_enu),
|
|
|
|
PORT_ITEM_INT("ts2phc.master", 0, 0, 1),
|
2019-12-30 03:12:32 +08:00
|
|
|
GLOB_ITEM_STR("ts2phc.nmea_remote_host", ""),
|
|
|
|
GLOB_ITEM_STR("ts2phc.nmea_remote_port", ""),
|
|
|
|
GLOB_ITEM_STR("ts2phc.nmea_serialport", "/dev/ttyS0"),
|
ts2phc_phc_master: make use of new kernel API for perout waveform
This API was introduced for 2 reasons:
1. Some hardware can emit PPS signals but not starting from arbitrary
absolute times, but rather phase-aligned to the beginning of a
second. We _could_ patch ts2phc to always specify a start time of
0.000000000 to PTP_PEROUT_REQUEST, but in practice, we would never
know whether that would actually work with all in-tree PHC drivers.
So there was a need for a new flag that only specifies the phase of
the periodic signal, and not the absolute start time.
2. Some hardware can, rather unfortunately, not distinguish between a
rising and a falling extts edge. And, since whatever rises also has
to fall before rising again, the strategy in ts2phc is to set a
'large' pulse width (half the period) and ignore the extts event
corresponding to the mid-way between one second and another. This is
all fine, but currently, ts2phc.pulsewidth is a read-only property in
the config file. The kernel is not instructed in any way to use this
value, it is simply that must be configured based on prior knowledge
of the PHC's implementation. This API changes that.
The introduction of a phase adjustment for the master PHC means we have
to adjust our approximation of the precise perout timestamp. We put that
code into a common function and convert all call sites to call that. We
also need to do the same thing for the edge ignoring logic.
Signed-off-by: Vladimir Oltean <olteanv@gmail.com>
2020-05-25 15:57:44 +08:00
|
|
|
PORT_ITEM_INT("ts2phc.perout_phase", -1, 0, 999999999),
|
2019-07-08 07:47:48 +08:00
|
|
|
PORT_ITEM_INT("ts2phc.pin_index", 0, 0, INT_MAX),
|
|
|
|
GLOB_ITEM_INT("ts2phc.pulsewidth", 500000000, 1000000, 999000000),
|
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"),
|
2018-04-06 00:44:45 +08:00
|
|
|
PORT_ITEM_INT("unicast_listen", 0, 0, 1),
|
2018-03-09 13:59:15 +08:00
|
|
|
PORT_ITEM_INT("unicast_master_table", 0, 0, INT_MAX),
|
|
|
|
PORT_ITEM_INT("unicast_req_duration", 3600, 10, INT_MAX),
|
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),
|
2020-05-05 02:07:57 +08:00
|
|
|
GLOB_ITEM_INT("write_phase_mode", 0, 0, 1),
|
2015-08-13 00:34:04 +08:00
|
|
|
};
|
|
|
|
|
2018-03-09 13:59:15 +08:00
|
|
|
static struct unicast_master_table *current_uc_mtab;
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-03-09 13:59:15 +08:00
|
|
|
static int config_switch_unicast_mtab(struct config *cfg, int idx, int line_num)
|
|
|
|
{
|
|
|
|
struct unicast_master_table *table;
|
|
|
|
|
|
|
|
if (idx < 1) {
|
|
|
|
fprintf(stderr, "line %d: table_id %d is out of range. "
|
|
|
|
"Must be in the range %d to %d\n",
|
|
|
|
line_num, idx, 1, INT_MAX);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
STAILQ_FOREACH(table, &cfg->unicast_master_tables, list) {
|
|
|
|
if (table->table_index == idx) {
|
|
|
|
fprintf(stderr, "line %d: table_id %d already taken\n",
|
|
|
|
line_num, idx);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
table = calloc(1, sizeof(*table));
|
|
|
|
if (!table) {
|
|
|
|
fprintf(stderr, "low memory\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
STAILQ_INIT(&table->addrs);
|
|
|
|
table->table_index = idx;
|
|
|
|
memset(&table->peer_addr.portIdentity, 0xff,
|
|
|
|
sizeof(table->peer_addr.portIdentity));
|
|
|
|
STAILQ_INSERT_TAIL(&cfg->unicast_master_tables, table, list);
|
|
|
|
current_uc_mtab = table;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_unicast_mtab_address(enum transport_type type, char *address,
|
|
|
|
int line_num)
|
|
|
|
{
|
|
|
|
struct unicast_master_address *item;
|
|
|
|
|
|
|
|
if (!current_uc_mtab) {
|
|
|
|
fprintf(stderr, "line %d: missing table_id\n", line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
item = calloc(1, sizeof(*item));
|
|
|
|
if (!item) {
|
|
|
|
fprintf(stderr, "low memory\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (str2addr(type, address, &item->address)) {
|
|
|
|
fprintf(stderr, "line %d: bad address\n", line_num);
|
|
|
|
free(item);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memset(&item->portIdentity, 0xff, sizeof(item->portIdentity));
|
|
|
|
item->type = type;
|
|
|
|
STAILQ_INSERT_TAIL(¤t_uc_mtab->addrs, item, list);
|
|
|
|
current_uc_mtab->count++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_unicast_mtab_peer(char *address, int line_num)
|
|
|
|
{
|
|
|
|
if (!current_uc_mtab) {
|
|
|
|
fprintf(stderr, "line %d: missing table_id\n", line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (current_uc_mtab->peer_name) {
|
|
|
|
free(current_uc_mtab->peer_name);
|
|
|
|
}
|
|
|
|
current_uc_mtab->peer_name = strdup(address);
|
|
|
|
if (!current_uc_mtab->peer_name) {
|
|
|
|
fprintf(stderr, "low memory\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_unicast_mtab_query_interval(int lqi, int line_num)
|
|
|
|
{
|
|
|
|
if (!current_uc_mtab) {
|
|
|
|
fprintf(stderr, "line %d: missing table_id\n", line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (lqi < INT8_MIN || lqi > INT8_MAX) {
|
|
|
|
fprintf(stderr, "line %d: logQueryInterval %d out of range\n",
|
|
|
|
line_num, lqi);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
current_uc_mtab->logQueryInterval = lqi;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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;
|
2018-03-09 13:59:15 +08:00
|
|
|
} else if (!strcasecmp(s, "[unicast_master_table]")) {
|
|
|
|
*section = UC_MTAB_SECTION;
|
|
|
|
current_uc_mtab = NULL;
|
2012-08-21 07:03:26 +08:00
|
|
|
} 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
|
|
|
}
|
|
|
|
|
2018-03-09 13:59:15 +08:00
|
|
|
static int parse_unicast_mtab_line(struct config *cfg, char *line, int line_num)
|
|
|
|
{
|
|
|
|
char address[64 + 1] = {0}, transport[16 + 1] = {0};
|
|
|
|
enum transport_type type = TRANS_UDS;
|
|
|
|
struct config_enum *cte;
|
|
|
|
int cnt, lqi, table_id;
|
|
|
|
|
|
|
|
cnt = sscanf(line, " table_id %d", &table_id);
|
|
|
|
if (cnt == 1) {
|
|
|
|
return config_switch_unicast_mtab(cfg, table_id, line_num);
|
|
|
|
}
|
|
|
|
cnt = sscanf(line, " logQueryInterval %d", &lqi);
|
|
|
|
if (cnt == 1) {
|
|
|
|
return config_unicast_mtab_query_interval(lqi, line_num);
|
|
|
|
}
|
|
|
|
cnt = sscanf(line, " peer_address %64s", address);
|
|
|
|
if (cnt == 1) {
|
|
|
|
return config_unicast_mtab_peer(address, line_num);
|
|
|
|
}
|
|
|
|
cnt = sscanf(line, " %16s %64s", transport, address);
|
|
|
|
if (cnt != 2) {
|
|
|
|
fprintf(stderr, "bad master table at line %d\n", line_num);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
for (cte = nw_trans_enu; cte->label; cte++) {
|
|
|
|
if (!strcasecmp(cte->label, transport)) {
|
|
|
|
type = cte->value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return config_unicast_mtab_address(type, address, line_num);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-25 21:06:29 +08:00
|
|
|
static struct option *config_alloc_longopts(void)
|
2016-12-07 02:40:36 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-02-09 23:17:08 +08:00
|
|
|
int config_read(const 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
|
|
|
|
2018-03-09 13:59:15 +08:00
|
|
|
if (current_section == UC_MTAB_SECTION) {
|
|
|
|
if (parse_unicast_mtab_line(cfg, line, line_num)) {
|
|
|
|
goto parse_error;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
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 ?
|
2020-02-09 23:24:19 +08:00
|
|
|
"global" : interface_name(current_port));
|
2015-08-23 03:29:45 +08:00
|
|
|
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 ?
|
2020-02-09 23:24:19 +08:00
|
|
|
NULL : interface_name(current_port),
|
|
|
|
option, value);
|
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" :
|
2020-02-09 23:24:19 +08:00
|
|
|
interface_name(current_port));
|
2015-08-23 03:29:45 +08:00
|
|
|
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
|
|
|
|
2020-02-09 23:17:08 +08:00
|
|
|
struct interface *config_create_interface(const char *name, struct config *cfg)
|
2012-08-21 01:56:45 +08:00
|
|
|
{
|
|
|
|
struct interface *iface;
|
2020-02-09 23:24:19 +08:00
|
|
|
const char *ifname;
|
2012-08-21 01:56:45 +08:00
|
|
|
|
2014-08-14 21:56:06 +08:00
|
|
|
/* only create each interface once (by name) */
|
|
|
|
STAILQ_FOREACH(iface, &cfg->interfaces, list) {
|
2020-02-09 23:24:19 +08:00
|
|
|
ifname = interface_name(iface);
|
|
|
|
if (0 == strncmp(name, ifname, MAX_IFNAME_SIZE))
|
2014-08-14 21:56:06 +08:00
|
|
|
return iface;
|
2012-08-21 01:56:45 +08:00
|
|
|
}
|
|
|
|
|
2020-02-11 12:57:41 +08:00
|
|
|
iface = interface_create(name);
|
2014-08-14 21:56:06 +08:00
|
|
|
if (!iface) {
|
|
|
|
fprintf(stderr, "cannot allocate memory for a port\n");
|
|
|
|
return NULL;
|
2012-08-21 01:56:45 +08:00
|
|
|
}
|
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);
|
2018-03-09 13:59:15 +08:00
|
|
|
STAILQ_INIT(&cfg->unicast_master_tables);
|
2015-08-23 19:59:38 +08:00
|
|
|
|
2018-06-25 21:06:29 +08:00
|
|
|
cfg->opts = config_alloc_longopts();
|
2016-12-07 02:40:36 +08:00
|
|
|
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)
|
|
|
|
{
|
2018-03-09 13:59:15 +08:00
|
|
|
struct unicast_master_address *address;
|
|
|
|
struct unicast_master_table *table;
|
2014-08-14 21:56:06 +08:00
|
|
|
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);
|
2020-02-11 12:57:41 +08:00
|
|
|
interface_destroy(iface);
|
2014-08-14 21:56:06 +08:00
|
|
|
}
|
2018-03-09 13:59:15 +08:00
|
|
|
while ((table = STAILQ_FIRST(&cfg->unicast_master_tables))) {
|
|
|
|
while ((address = STAILQ_FIRST(&table->addrs))) {
|
|
|
|
STAILQ_REMOVE_HEAD(&table->addrs, list);
|
|
|
|
free(address);
|
|
|
|
}
|
|
|
|
if (table->peer_name) {
|
|
|
|
free(table->peer_name);
|
|
|
|
}
|
|
|
|
STAILQ_REMOVE_HEAD(&cfg->unicast_master_tables, list);
|
|
|
|
free(table);
|
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
2018-03-03 10:59:47 +08:00
|
|
|
int config_harmonize_onestep(struct config *cfg)
|
|
|
|
{
|
|
|
|
enum timestamp_type tstype = config_get_int(cfg, NULL, "time_stamping");
|
|
|
|
int two_step_flag = config_get_int(cfg, NULL, "twoStepFlag");
|
|
|
|
|
|
|
|
switch (tstype) {
|
|
|
|
case TS_SOFTWARE:
|
|
|
|
case TS_LEGACY_HW:
|
|
|
|
if (!two_step_flag) {
|
|
|
|
pr_err("one step is only possible "
|
|
|
|
"with hardware time stamping");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TS_HARDWARE:
|
|
|
|
if (!two_step_flag) {
|
|
|
|
pr_debug("upgrading to one step time stamping "
|
|
|
|
"in order to match the twoStepFlag");
|
|
|
|
if (config_set_int(cfg, "time_stamping", TS_ONESTEP)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TS_ONESTEP:
|
|
|
|
case TS_P2P1STEP:
|
|
|
|
if (two_step_flag) {
|
|
|
|
pr_debug("one step mode implies twoStepFlag=0, "
|
|
|
|
"clearing twoStepFlag to match");
|
|
|
|
if (config_set_int(cfg, "twoStepFlag", 0)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|