1 /* Copyright (c) 2008, 2009 Nicira Networks
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 #include <arpa/inet.h>
25 #include <netinet/in.h>
31 #include "dynamic-string.h"
38 #define THIS_MODULE VLM_cfg
41 /* XXX This file really needs a unit test! For a while, cfg_get_string(0,
42 * "bridge.a.controller") would return the value of
43 * "bridge.a.controller.in-band", if it existed, and I'm really not certain
44 * that the fix didn't break other things. */
46 /* Configuration file name. */
47 static char *cfg_name;
49 /* Put the temporary file in the same directory as cfg_name, so that
50 * they are guaranteed to be in the same file system and therefore we can
51 * rename() tmp_name over cfg_name. */
52 static char *tmp_name;
54 /* Lock information. */
55 static char *lock_name;
56 static int lock_fd = -1;
58 /* Flag to indicate whether local modifications have been made. */
61 static uint8_t cfg_cookie[CFG_COOKIE_LEN];
63 /* Current configuration. Maintained in sorted order. */
64 static struct svec cfg = SVEC_EMPTY_INITIALIZER;
66 static bool has_double_dot(const char *key, size_t len);
67 static bool is_valid_key(const char *key, size_t len,
68 const char *file_name, int line_number,
70 static char *parse_section(const char *file_name, int line_number,
72 static void parse_setting(const char *file_name, int line_number,
73 const char *section, const char *);
74 static int compare_key(const char *a, const char *b);
75 static char **find_key_le(const char *key);
76 static char **find_key_ge(const char *key);
77 static char *find_key(const char *);
78 static bool parse_mac(const char *, uint8_t mac[6]);
79 static bool parse_dpid(const char *, uint64_t *);
80 static bool is_key(const char *);
81 static bool is_int(const char *);
82 static bool is_bool(const char *);
83 static const char *extract_value(const char *key);
84 static const char *get_nth_value(int idx, const char *key);
85 static bool is_type(const char *s, enum cfg_flags);
87 #define CC_ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
88 #define CC_DIGIT "0123456789"
89 #define CC_ALNUM CC_ALPHA CC_DIGIT
90 #define CC_SPACE " \t\r\n\v"
92 #define CC_FILE_NAME CC_ALNUM "._-"
93 #define CC_KEY CC_ALNUM "._-@$:+"
101 /* Sets 'file_name' as the configuration file read by cfg_read(). Returns 0 on
102 * success, otherwise a positive errno value if 'file_name' cannot be opened.
104 * This function does not actually read the named file or directory. Use
105 * cfg_read() to (re)read all the configuration files. */
107 cfg_set_file(const char *file_name)
117 cfg_name = lock_name = tmp_name = NULL;
120 /* Make sure that we can open this file for reading. */
121 fd = open(file_name, O_RDONLY);
127 cfg_name = xstrdup(file_name);
129 /* Put the temporary file in the same directory as cfg_name, so that they
130 * are guaranteed to be in the same file system, to guarantee that
131 * rename(tmp_name, cfg_name) will work. */
132 tmp_name = xasprintf("%s.~tmp~", file_name);
134 /* Put the lock file in the same directory as cfg_name, but prefixed by
135 * a dot so as not to garner administrator interest. */
136 slash = strrchr(file_name, '/');
138 lock_name = xasprintf("%.*s/.%s.~lock~",
139 slash - file_name, file_name, slash + 1);
141 lock_name = xasprintf(".%s.~lock~", file_name);
144 VLOG_INFO("using \"%s\" as configuration file, \"%s\" as lock file",
145 file_name, lock_name);
152 struct sha1_ctx context;
156 for (i = 0; i < cfg.n; i++) {
157 sha1_update(&context, cfg.names[i], strlen(cfg.names[i]));
158 sha1_update(&context, "\n", 1);
160 sha1_final(&context, cfg_cookie);
165 /* Reads all of the configuration files or directories that have been added
166 * with cfg_add_file(), merges their content. Any previous configuration is
167 * replaced. Returns 0 if successful, otherwise a positive errno value. */
182 /* Save old configuration data and clear the active configuration. */
184 svec_swap(&old_cfg, &cfg);
186 /* Read new configuration. */
187 VLOG_DBG("reading configuration from %s", cfg_name);
189 file = fopen(cfg_name, "r");
191 VLOG_ERR("failed to open \"%s\": %s", cfg_name, strerror(errno));
192 svec_terminate(&cfg);
199 while (!ds_get_line(&ds, file)) {
200 const char *s = ds_cstr(&ds);
201 size_t indent = strspn(s, CC_SPACE);
205 if (*s == '#' || *s == '\0') {
206 /* Ignore comments and lines that contain only white space. */
207 } else if (*s == '[') {
210 section = parse_section(cfg_name, line_number, s);
212 VLOG_ERR("%s:%d: ignoring indented section header",
213 cfg_name, line_number);
215 } else if (indent && !section) {
216 VLOG_ERR("%s:%d: ignoring indented line outside any section",
217 cfg_name, line_number);
223 parse_setting(cfg_name, line_number, section, s);
230 svec_terminate(&cfg);
235 if (VLOG_IS_DBG_ENABLED()) {
236 struct svec removed, added;
239 svec_diff(&old_cfg, &cfg, &removed, NULL, &added);
240 if (removed.n || added.n) {
241 VLOG_DBG("configuration changes:");
242 for (i = 0; i < removed.n; i++) {
243 VLOG_DBG("-%s", removed.names[i]);
245 for (i = 0; i < added.n; i++) {
246 VLOG_DBG("+%s", added.names[i]);
249 VLOG_DBG("configuration unchanged");
251 svec_destroy(&added);
252 svec_destroy(&removed);
254 svec_destroy(&old_cfg);
261 /* Fills 'svec' with the entire configuration file. */
263 cfg_get_all(struct svec *svec)
266 svec_append(svec, &cfg);
270 cfg_get_cookie(uint8_t *cookie)
276 memcpy(cookie, cfg_cookie, sizeof(cfg_cookie));
284 COVERAGE_INC(cfg_unlock);
291 open_lockfile(const char *name)
294 /* Try to open an existing lock file. */
295 int fd = open(name, O_RDWR);
298 } else if (errno != ENOENT) {
299 VLOG_WARN("%s: failed to open lock file: %s",
300 name, strerror(errno));
304 /* Try to create a new lock file. */
305 VLOG_INFO("%s: lock file does not exist, creating", name);
306 fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
309 } else if (errno != EEXIST) {
310 VLOG_WARN("%s: failed to create lock file: %s",
311 name, strerror(errno));
315 /* Someone else created the lock file. Try again. */
320 try_lock(int fd, bool block)
325 memset(&l, 0, sizeof l);
327 l.l_whence = SEEK_SET;
331 time_disable_restart();
332 error = fcntl(fd, block ? F_SETLKW : F_SETLK, &l) == -1 ? errno : 0;
333 time_enable_restart();
338 /* Locks the configuration file against modification by other processes and
339 * re-reads it from disk.
341 * The 'timeout' specifies the maximum number of milliseconds to wait for the
342 * config file to become free. Use 0 to avoid waiting or INT_MAX to wait
345 * Returns 0 on success, otherwise a positive errno value. */
347 cfg_lock(uint8_t *cookie, int timeout)
350 long long int elapsed = 0;
352 uint8_t curr_cookie[CFG_COOKIE_LEN];
355 COVERAGE_INC(cfg_lock);
362 /* Open lock file. */
363 fd = open_lockfile(lock_name);
368 /* Try to lock it. This will block (if 'timeout' > 0). */
369 error = try_lock(fd, timeout > 0);
371 elapsed = time_msec() - start;
377 /* Lock failed. Close the lock file and reopen it on the next
378 * iteration, just in case someone deletes it underneath us (even
379 * though that should not happen). */
381 if (error != EINTR) {
382 /* Hard error, give up. */
383 COVERAGE_INC(cfg_lock_error);
384 VLOG_WARN("%s: failed to lock file "
385 "(after %lld ms, with %d-ms timeout): %s",
386 lock_name, elapsed, timeout, strerror(error));
390 /* Probably, the periodic timer set up by time_init() woke up us. Just
391 * check whether it's time to give up. */
392 if (timeout != INT_MAX && elapsed >= timeout) {
393 COVERAGE_INC(cfg_lock_timeout);
394 VLOG_WARN("%s: giving up on lock file after %lld ms",
398 COVERAGE_INC(cfg_lock_retry);
401 VLOG_WARN("%s: waited %lld ms for lock file", lock_name, elapsed);
408 cfg_get_cookie(curr_cookie);
410 if (memcmp(curr_cookie, cookie, sizeof *curr_cookie)) {
411 /* Configuration has changed, so reject. */
421 do_write_config(const void *data, size_t len)
426 file = fopen(tmp_name, "w");
428 VLOG_WARN("could not open %s for writing: %s",
429 tmp_name, strerror(errno));
433 fwrite(data, 1, len, file);
435 /* This is essentially equivalent to:
436 * error = ferror(file) || fflush(file) || fclose(file);
437 * but it doesn't short-circuit, so that it always closes 'file'. */
438 error = ferror(file);
439 error = fflush(file) || error;
440 error = fclose(file) || error;
442 VLOG_WARN("problem writing to %s: %s", tmp_name, strerror(errno));
446 if (rename(tmp_name, cfg_name) < 0) {
447 VLOG_WARN("could not rename %s to %s: %s",
448 tmp_name, cfg_name, strerror(errno));
457 /* Write the current configuration into the configuration file. Returns 0 if
458 * successful, otherwise a negative errno value. */
467 ? svec_join(&cfg, "\n", "\n")
468 : xstrdup("# This file intentionally left blank.\n"));
469 retval = do_write_config(content, strlen(content));
476 cfg_write_data(uint8_t *data, size_t len)
478 int retval = do_write_config(data, len);
485 /* Returns true if the configuration has changed since the last time it was
486 * read or written. */
494 cfg_buf_put(struct ofpbuf *buffer)
498 for (i = 0; i < cfg.n; i++) {
499 ofpbuf_put(buffer, cfg.names[i], strlen(cfg.names[i]));
500 ofpbuf_put(buffer, "\n", 1);
504 /* Formats the printf()-style format string in the parameter 'format', which
505 * must be the function's last parameter, into string variable 'dst'. The
506 * function is responsible for freeing 'dst'. */
507 #define FORMAT_KEY(FORMAT, DST) \
510 va_start(args__, FORMAT); \
511 (DST) = xvasprintf(FORMAT, args__); \
515 /* Returns true if the configuration includes a key named 'key'. */
517 cfg_has(const char *key_, ...)
522 FORMAT_KEY(key_, key);
523 retval = find_key(key) != NULL;
529 cfg_is_valid(enum cfg_flags flags, const char *key_, ...)
531 char *key, **first, **last, **p;
535 FORMAT_KEY(key_, key);
536 first = find_key_le(key);
537 last = find_key_ge(key);
539 retval = ((!(flags & CFG_REQUIRED) || n)
540 && (!(flags & CFG_MULTIPLE) || n <= 1));
541 for (p = first; retval && p < last; p++) {
542 retval = is_type(strchr(*p, '=') + 1, flags);
548 /* Returns true if the configuration includes at least one key whose name
549 * begins with 'section' followed by a dot. */
551 cfg_has_section(const char *section_, ...)
559 va_start(args, section_);
560 ds_put_format_valist(§ion, section_, args);
561 ds_put_char(§ion, '.');
564 for (p = cfg.names; *p; p++) { /* XXX this is inefficient */
565 if (!strncmp(section.string, *p, section.length)) {
571 ds_destroy(§ion);
575 /* Returns the number of values for the given 'key'. The return value is 0 if
576 * no values exist for 'key'. */
578 cfg_count(const char *key_, ...)
583 FORMAT_KEY(key_, key);
584 retval = find_key_ge(key) - find_key_le(key);
589 /* Fills 'svec' with all of the immediate subsections of 'section'. For
590 * example, if 'section' is "bridge" and keys bridge.a, bridge.b, bridge.b.c,
591 * and bridge.c.x.y.z exist, then 'svec' would be initialized to a, b, and
592 * c. The caller must first initialize 'svec'. */
594 cfg_get_subsections(struct svec *svec, const char *section_, ...)
601 va_start(args, section_);
602 ds_put_format_valist(§ion, section_, args);
603 ds_put_char(§ion, '.');
607 for (p = cfg.names; *p; p++) { /* XXX this is inefficient */
608 if (!strncmp(section.string, *p, section.length)) {
609 const char *ss = *p + section.length;
610 size_t ss_len = strcspn(ss, ".=");
611 svec_add_nocopy(svec, xmemdup0(ss, ss_len));
615 ds_destroy(§ion);
619 cfg_add_entry(const char *entry_, ...)
623 FORMAT_KEY(entry_, entry);
624 svec_add_nocopy(&cfg, entry);
626 svec_terminate(&cfg);
631 cfg_del_entry(const char *entry_, ...)
635 FORMAT_KEY(entry_, entry);
636 svec_del(&cfg, entry);
637 svec_terminate(&cfg);
643 cfg_del_section(const char *section_, ...)
650 va_start(args, section_);
651 ds_put_format_valist(§ion, section_, args);
652 ds_put_char(§ion, '.');
655 for (p = cfg.names; *p; p++) {
656 if (!strncmp(section.string, *p, section.length)) {
662 svec_terminate(&cfg);
664 ds_destroy(§ion);
669 cfg_del_match(const char *pattern_, ...)
671 bool matched = false;
675 FORMAT_KEY(pattern_, pattern);
677 for (p = cfg.names; *p; p++) {
678 if (!fnmatch(pattern, *p, 0)) {
686 svec_terminate(&cfg);
693 /* Fills 'svec' with all of the key-value pairs that match shell glob pattern
694 * 'pattern'. The caller must first initialize 'svec'. */
696 cfg_get_matches(struct svec *svec, const char *pattern_, ...)
701 FORMAT_KEY(pattern_, pattern);
703 for (p = cfg.names; *p; p++) {
704 if (!fnmatch(pattern, *p, 0)) {
712 /* Fills 'svec' with all of the key-value pairs that have sections that
713 * begin with 'section'. The caller must first initialize 'svec'. */
715 cfg_get_section(struct svec *svec, const char *section_, ...)
722 va_start(args, section_);
723 ds_put_format_valist(§ion, section_, args);
724 ds_put_char(§ion, '.');
727 for (p = cfg.names; *p; p++) { /* XXX this is inefficient */
728 if (!strncmp(section.string, *p, section.length)) {
732 ds_destroy(§ion);
735 /* Returns the value numbered 'idx' of 'key'. Returns a null pointer if 'idx'
736 * is greater than or equal to cfg_count(key). The caller must not modify or
737 * free the returned string or retain its value beyond the next call to
740 cfg_get_string(int idx, const char *key_, ...)
745 FORMAT_KEY(key_, key);
746 retval = get_nth_value(idx, key);
751 /* Returns the value numbered 'idx' of 'key'. Returns a null pointer if 'idx'
752 * is greater than or equal to cfg_count(key) or if the value 'idx' of 'key' is
753 * not a valid key. The caller must not modify or free the returned string or
754 * retain its value beyond the next call to cfg_read(). */
756 cfg_get_key(int idx, const char *key_, ...)
758 const char *value, *retval;
761 FORMAT_KEY(key_, key);
762 value = get_nth_value(idx, key);
763 retval = value && is_key(value) ? value : NULL;
768 /* Returns the value numbered 'idx' of 'key', converted to an integer. Returns
769 * 0 if 'idx' is greater than or equal to cfg_count(key) or if the value 'idx'
770 * of 'key' is not a valid integer. */
772 cfg_get_int(int idx, const char *key_, ...)
778 FORMAT_KEY(key_, key);
779 value = get_nth_value(idx, key);
780 retval = value && is_int(value) ? atoi(value) : 0;
785 /* Returns the value numbered 'idx' of 'key', converted to a boolean value.
786 * Returns false if 'idx' is greater than or equal to cfg_count(key) or if the
787 * value 'idx' of 'key' is not a valid boolean. */
789 cfg_get_bool(int idx, const char *key_, ...)
795 FORMAT_KEY(key_, key);
796 value = get_nth_value(idx, key);
797 retval = value && is_bool(value) ? !strcmp(value, "true") : false;
802 /* Returns the value numbered 'idx' of 'key', converted to an IP address in
803 * network byte order. Returns 0 if 'idx' is greater than or equal to
804 * cfg_count(key) or if the value 'idx' of 'key' is not a valid IP address (as
805 * determined by inet_aton()). */
807 cfg_get_ip(int idx, const char *key_, ...)
813 FORMAT_KEY(key_, key);
814 value = get_nth_value(idx, key);
815 if (!value || !inet_aton(value, &addr)) {
816 addr.s_addr = htonl(0);
822 /* Returns the value numbered 'idx' of 'key', converted to an MAC address in
823 * host byte order. Returns 0 if 'idx' is greater than or equal to
824 * cfg_count(key) or if the value 'idx' of 'key' is not a valid MAC address in
825 * the format "##:##:##:##:##:##". */
827 cfg_get_mac(int idx, const char *key_, ...)
829 uint8_t mac[ETH_ADDR_LEN];
833 FORMAT_KEY(key_, key);
834 value = get_nth_value(idx, key);
835 if (!value || !parse_mac(value, mac)) {
836 memset(mac, 0, sizeof mac);
839 return eth_addr_to_uint64(mac);
842 /* Returns the value numbered 'idx' of 'key', parsed as an datapath ID.
843 * Returns 0 if 'idx' is greater than or equal to cfg_count(key) or if the
844 * value 'idx' of 'key' is not a valid datapath ID consisting of exactly 12
845 * hexadecimal digits. */
847 cfg_get_dpid(int idx, const char *key_, ...)
853 FORMAT_KEY(key_, key);
854 value = get_nth_value(idx, key);
855 if (!value || !parse_dpid(value, &dpid)) {
862 /* Returns the value numbered 'idx' of 'key', converted to an integer. Returns
863 * -1 if 'idx' is greater than or equal to cfg_count(key) or if the value 'idx'
864 * of 'key' is not a valid integer between 0 and 4095. */
866 cfg_get_vlan(int idx, const char *key_, ...)
872 FORMAT_KEY(key_, key);
873 value = get_nth_value(idx, key);
874 if (value && is_int(value)) {
875 retval = atoi(value);
876 if (retval < 0 || retval > 4095) {
886 /* Fills 'svec' with all of the string values of 'key'. The caller must
887 * first initialize 'svec'. */
889 cfg_get_all_strings(struct svec *svec, const char *key_, ...)
894 FORMAT_KEY(key_, key);
896 for (p = find_key_le(key), q = find_key_ge(key); p < q; p++) {
897 svec_add(svec, extract_value(*p));
902 /* Fills 'svec' with all of the values of 'key' that are valid keys.
903 * Values of 'key' that are not valid keys are omitted. The caller
904 * must first initialize 'svec'. */
906 cfg_get_all_keys(struct svec *svec, const char *key_, ...)
911 FORMAT_KEY(key_, key);
913 for (p = find_key_le(key), q = find_key_ge(key); p < q; p++) {
914 const char *value = extract_value(*p);
916 svec_add(svec, value);
923 has_double_dot(const char *key, size_t len)
928 for (i = 0; i < len - 1; i++) {
929 if (key[i] == '.' && key[i + 1] == '.') {
938 is_valid_key(const char *key, size_t len,
939 const char *file_name, int line_number, const char *id)
942 VLOG_ERR("%s:%d: missing %s name", file_name, line_number, id);
944 } else if (key[0] == '.') {
945 VLOG_ERR("%s:%d: %s name \"%.*s\" begins with invalid character '.'",
946 file_name, line_number, id, (int) len, key);
948 } else if (key[len - 1] == '.') {
949 VLOG_ERR("%s:%d: %s name \"%.*s\" ends with invalid character '.'",
950 file_name, line_number, id, (int) len, key);
952 } else if (has_double_dot(key, len)) {
953 VLOG_ERR("%s:%d: %s name \"%.*s\" contains '..', which is not allowed",
954 file_name, line_number, id, (int) len, key);
962 parse_section(const char *file_name, int line_number, const char *s)
969 /* Skip [ and any white space. */
971 s += strspn(s, CC_SPACE);
973 /* Obtain the section name. */
974 len = strspn(s, CC_KEY);
975 if (!is_valid_key(s, len, file_name, line_number, "section")) {
978 ds_put_buffer(§ion, s, len);
981 /* Obtain the subsection name, if any. */
982 s += strspn(s, CC_SPACE);
985 len = strspn(s, CC_KEY);
986 if (!is_valid_key(s, len, file_name, line_number, "subsection")) {
989 ds_put_char(§ion, '.');
990 ds_put_buffer(§ion, s, len);
993 VLOG_ERR("%s:%d: missing '\"' following subsection name",
994 file_name, line_number);
998 s += strspn(s, CC_SPACE);
1003 VLOG_ERR("%s:%d: missing ']' following section name",
1004 file_name, line_number);
1008 s += strspn(s, CC_SPACE);
1010 VLOG_ERR("%s:%d: trailing garbage following ']'",
1011 file_name, line_number);
1015 return ds_cstr(§ion);
1018 ds_destroy(§ion);
1023 parse_setting(const char *file_name, int line_number, const char *section,
1026 struct ds key = DS_EMPTY_INITIALIZER;
1027 struct ds value = DS_EMPTY_INITIALIZER;
1031 ds_put_format(&key, "%s.", section);
1034 /* Obtain the key. */
1035 len = strspn(s, CC_KEY);
1037 VLOG_ERR("%s:%d: missing key name", file_name, line_number);
1040 if (!is_valid_key(s, len, file_name, line_number, "key")) {
1043 ds_put_buffer(&key, s, len);
1047 s += strspn(s, CC_SPACE);
1049 VLOG_ERR("%s:%d: missing '=' following key", file_name, line_number);
1053 s += strspn(s, CC_SPACE);
1055 /* Obtain the value. */
1056 ds_put_cstr(&value, s);
1057 while (value.length > 0 && strchr(CC_SPACE, ds_last(&value))) {
1061 /* Add the setting. */
1062 svec_add_nocopy(&cfg, xasprintf("%s=%s", ds_cstr(&key), ds_cstr(&value)));
1070 compare_key(const char *a, const char *b)
1073 int ac = *a == '\0' || *a == '=' ? INT_MAX : *a;
1074 int bc = *b == '\0' || *b == '=' ? INT_MAX : *b;
1076 return ac < bc ? -1 : 1;
1077 } else if (ac == INT_MAX) {
1085 /* Returns the address of the greatest configuration string with a key less
1086 * than or equal to 'key'. Returns the address of the null terminator if all
1087 * configuration strings are greater than 'key'. */
1089 find_key_le(const char *key)
1094 int half = len >> 1;
1095 int middle = low + half;
1096 if (compare_key(cfg.names[middle], key) < 0) {
1103 return &cfg.names[low];
1106 /* Returns the address of the least configuration string with a key greater
1107 * than or equal to 'key'. Returns the address of the null terminator if all
1108 * configuration strings are less than 'key'. */
1110 find_key_ge(const char *key)
1115 int half = len >> 1;
1116 int middle = low + half;
1117 if (compare_key(cfg.names[middle], key) > 0) {
1124 return &cfg.names[low];
1128 find_key(const char *key)
1130 char **p = find_key_le(key);
1131 return p < &cfg.names[cfg.n] && !compare_key(*p, key) ? *p : NULL;
1135 parse_mac(const char *s, uint8_t mac[6])
1137 return (sscanf(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
1138 == ETH_ADDR_SCAN_COUNT);
1142 parse_dpid(const char *s, uint64_t *dpid)
1144 if (strlen(s) == 12 && strspn(s, "0123456789abcdefABCDEF") == 12) {
1145 *dpid = strtoll(s, NULL, 16);
1153 is_key(const char *s)
1155 /* XXX needs to check the same things as is_valid_key() too. */
1156 return *s && s[strspn(s, CC_KEY)] == '\0';
1160 is_int(const char *s)
1162 return *s && s[strspn(s, CC_DIGIT)] == '\0';
1166 is_bool(const char *s)
1168 return !strcmp(s, "true") || !strcmp(s, "false");
1172 extract_value(const char *key)
1174 const char *p = strchr(key, '=');
1175 return p ? p + 1 : NULL;
1179 get_nth_value(int idx, const char *key)
1181 char **p = find_key_le(key);
1182 char **q = find_key_ge(key);
1183 return idx < q - p ? extract_value(p[idx]) : NULL;
1187 is_type(const char *s, enum cfg_flags flags)
1189 uint8_t mac[ETH_ADDR_LEN];
1190 struct in_addr addr;
1193 return (flags & CFG_STRING
1194 || (flags & CFG_KEY && is_key(s))
1195 || (flags & CFG_INT && is_int(s))
1196 || (flags & CFG_BOOL && is_bool(s))
1197 || (flags & CFG_IP && inet_aton(s, &addr))
1198 || (flags & CFG_MAC && parse_mac(s, mac))
1199 || (flags & CFG_DPID && parse_dpid(s, &dpid)));