1 /* Copyright (c) 2008, 2009 Nicira Networks
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * In addition, as a special exception, Nicira Networks gives permission
17 * to link the code of its release of vswitchd with the OpenSSL project's
18 * "OpenSSL" library (or with modified versions of it that use the same
19 * license as the "OpenSSL" library), and distribute the linked
20 * executables. You must obey the GNU General Public License in all
21 * respects for all of the code used other than "OpenSSL". If you modify
22 * this file, you may extend this exception to your version of the file,
23 * but you are not obligated to do so. If you do not wish to do so,
24 * delete this exception statement from your version.
29 #include <arpa/inet.h>
36 #include <netinet/in.h>
42 #include "dynamic-string.h"
49 #define THIS_MODULE VLM_cfg
52 /* XXX This file really needs a unit test! For a while, cfg_get_string(0,
53 * "bridge.a.controller") would return the value of
54 * "bridge.a.controller.in-band", if it existed, and I'm really not certain
55 * that the fix didn't break other things. */
57 /* Configuration file name. */
58 static char *cfg_name;
60 /* Put the temporary file in the same directory as cfg_name, so that
61 * they are guaranteed to be in the same file system and therefore we can
62 * rename() tmp_name over cfg_name. */
63 static char *tmp_name;
65 /* Lock information. */
66 static char *lock_name;
67 static int lock_fd = -1;
69 /* Flag to indicate whether local modifications have been made. */
72 static uint8_t cfg_cookie[CFG_COOKIE_LEN];
74 /* Current configuration. Maintained in sorted order. */
75 static struct svec cfg = SVEC_EMPTY_INITIALIZER;
77 static bool has_double_dot(const char *key, size_t len);
78 static bool is_valid_key(const char *key, size_t len,
79 const char *file_name, int line_number,
81 static char *parse_section(const char *file_name, int line_number,
83 static void parse_setting(const char *file_name, int line_number,
84 const char *section, const char *);
85 static int compare_key(const char *a, const char *b);
86 static char **find_key_le(const char *key);
87 static char **find_key_ge(const char *key);
88 static char *find_key(const char *);
89 static bool parse_mac(const char *, uint8_t mac[6]);
90 static bool parse_dpid(const char *, uint64_t *);
91 static bool is_key(const char *);
92 static bool is_int(const char *);
93 static bool is_bool(const char *);
94 static const char *extract_value(const char *key);
95 static const char *get_nth_value(int idx, const char *key);
96 static bool is_type(const char *s, enum cfg_flags);
98 #define CC_ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
99 #define CC_DIGIT "0123456789"
100 #define CC_ALNUM CC_ALPHA CC_DIGIT
101 #define CC_SPACE " \t\r\n\v"
103 #define CC_FILE_NAME CC_ALNUM "._-"
104 #define CC_KEY CC_ALNUM "._-@$:+"
106 /* Sets 'file_name' as the configuration file read by cfg_read(). Returns 0 on
107 * success, otherwise a positive errno value if 'file_name' cannot be opened.
109 * This function does not actually read the named file or directory. Use
110 * cfg_read() to (re)read all the configuration files. */
112 cfg_set_file(const char *file_name)
122 cfg_name = lock_name = tmp_name = NULL;
125 /* Make sure that we can open this file for reading. */
126 fd = open(file_name, O_RDONLY);
132 cfg_name = xstrdup(file_name);
134 /* Put the temporary file in the same directory as cfg_name, so that they
135 * are guaranteed to be in the same file system, to guarantee that
136 * rename(tmp_name, cfg_name) will work. */
137 tmp_name = xasprintf("%s.~tmp~", file_name);
139 /* Put the lock file in the same directory as cfg_name, but prefixed by
140 * a dot so as not to garner administrator interest. */
141 slash = strrchr(file_name, '/');
143 lock_name = xasprintf("%.*s/.%s.~lock~",
144 slash - file_name, file_name, slash + 1);
146 lock_name = xasprintf(".%s.~lock~", file_name);
149 VLOG_INFO("using \"%s\" as configuration file, \"%s\" as lock file",
150 file_name, lock_name);
160 if (SHA1Reset(&context) != shaSuccess) {
163 for (i = 0; i < cfg.n; i++) {
164 if (SHA1Input(&context, (uint8_t *)cfg.names[i],
165 strlen(cfg.names[i])) != shaSuccess) {
168 SHA1Input(&context, (uint8_t *)"\n", 1);
170 if (SHA1Result(&context, cfg_cookie) != shaSuccess) {
177 /* Reads all of the configuration files or directories that have been added
178 * with cfg_add_file(), merges their content. Any previous configuration is
179 * replaced. Returns 0 if successful, otherwise a positive errno value. */
194 /* Save old configuration data and clear the active configuration. */
196 svec_swap(&old_cfg, &cfg);
198 /* Read new configuration. */
199 VLOG_DBG("reading configuration from %s", cfg_name);
201 file = fopen(cfg_name, "r");
203 VLOG_ERR("failed to open \"%s\": %s", cfg_name, strerror(errno));
210 while (!ds_get_line(&ds, file)) {
211 const char *s = ds_cstr(&ds);
212 size_t indent = strspn(s, CC_SPACE);
216 if (*s == '#' || *s == '\0') {
217 /* Ignore comments and lines that contain only white space. */
218 } else if (*s == '[') {
221 section = parse_section(cfg_name, line_number, s);
223 VLOG_ERR("%s:%d: ignoring indented section header",
224 cfg_name, line_number);
226 } else if (indent && !section) {
227 VLOG_ERR("%s:%d: ignoring indented line outside any section",
228 cfg_name, line_number);
234 parse_setting(cfg_name, line_number, section, s);
241 svec_terminate(&cfg);
246 if (VLOG_IS_DBG_ENABLED()) {
247 struct svec removed, added;
250 svec_diff(&old_cfg, &cfg, &removed, NULL, &added);
251 if (removed.n || added.n) {
252 VLOG_DBG("configuration changes:");
253 for (i = 0; i < removed.n; i++) {
254 VLOG_DBG("-%s", removed.names[i]);
256 for (i = 0; i < added.n; i++) {
257 VLOG_DBG("+%s", added.names[i]);
260 VLOG_DBG("configuration unchanged");
262 svec_destroy(&added);
263 svec_destroy(&removed);
265 svec_destroy(&old_cfg);
272 /* Fills 'svec' with the entire configuration file. */
274 cfg_get_all(struct svec *svec)
277 svec_append(svec, &cfg);
281 cfg_get_cookie(uint8_t *cookie)
287 memcpy(cookie, cfg_cookie, sizeof(cfg_cookie));
295 COVERAGE_INC(cfg_unlock);
302 open_lockfile(const char *name)
305 /* Try to open an existing lock file. */
306 int fd = open(name, O_RDWR);
309 } else if (errno != ENOENT) {
310 VLOG_WARN("%s: failed to open lock file: %s",
311 name, strerror(errno));
315 /* Try to create a new lock file. */
316 VLOG_INFO("%s: lock file does not exist, creating", name);
317 fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
320 } else if (errno != EEXIST) {
321 VLOG_WARN("%s: failed to create lock file: %s",
322 name, strerror(errno));
326 /* Someone else created the lock file. Try again. */
331 try_lock(int fd, bool block)
334 memset(&l, 0, sizeof l);
336 l.l_whence = SEEK_SET;
339 return fcntl(fd, block ? F_SETLKW : F_SETLK, &l) == -1 ? errno : 0;
342 /* Locks the configuration file against modification by other processes and
343 * re-reads it from disk.
345 * The 'timeout' specifies the maximum number of milliseconds to wait for the
346 * config file to become free. Use 0 to avoid waiting or INT_MAX to wait
349 * Returns 0 on success, otherwise a positive errno value. */
351 cfg_lock(uint8_t *cookie, int timeout)
353 long long int start = time_msec();
354 long long int elapsed = 0;
356 uint8_t curr_cookie[CFG_COOKIE_LEN];
359 COVERAGE_INC(cfg_lock);
363 /* Open lock file. */
364 fd = open_lockfile(lock_name);
369 /* Try to lock it. This will block (if 'timeout' > 0). */
370 error = try_lock(fd, timeout > 0);
372 elapsed = time_msec() - start;
378 /* Lock failed. Close the lock file and reopen it on the next
379 * iteration, just in case someone deletes it underneath us (even
380 * though that should not happen). */
382 if (error != EINTR) {
383 /* Hard error, give up. */
384 COVERAGE_INC(cfg_lock_error);
385 VLOG_WARN("%s: failed to lock file "
386 "(after %lld ms, with %d-ms timeout): %s",
387 lock_name, elapsed, timeout, strerror(error));
391 /* Probably, the periodic timer set up by time_init() woke up us. Just
392 * check whether it's time to give up. */
393 if (timeout != INT_MAX && elapsed >= timeout) {
394 COVERAGE_INC(cfg_lock_timeout);
395 VLOG_WARN("%s: giving up on lock file after %lld ms",
399 COVERAGE_INC(cfg_lock_retry);
402 VLOG_WARN("%s: waited %lld ms for lock file", lock_name, elapsed);
409 cfg_get_cookie(curr_cookie);
411 if (memcmp(curr_cookie, cookie, sizeof *curr_cookie)) {
412 /* Configuration has changed, so reject. */
422 do_write_config(const void *data, size_t len)
427 file = fopen(tmp_name, "w");
429 VLOG_WARN("could not open %s for writing: %s",
430 tmp_name, strerror(errno));
434 fwrite(data, 1, len, file);
436 /* This is essentially equivalent to:
437 * error = ferror(file) || fflush(file) || fclose(file);
438 * but it doesn't short-circuit, so that it always closes 'file'. */
439 error = ferror(file);
440 error = fflush(file) || error;
441 error = fclose(file) || error;
443 VLOG_WARN("problem writing to %s: %s", tmp_name, strerror(errno));
447 if (rename(tmp_name, cfg_name) < 0) {
448 VLOG_WARN("could not rename %s to %s: %s",
449 tmp_name, cfg_name, strerror(errno));
458 /* Write the current configuration into the configuration file. Returns 0 if
459 * successful, otherwise a negative errno value. */
468 ? svec_join(&cfg, "\n", "\n")
469 : xstrdup("# This file intentionally left blank.\n"));
470 retval = do_write_config(content, strlen(content));
477 cfg_write_data(uint8_t *data, size_t len)
479 int retval = do_write_config(data, len);
486 /* Returns true if the configuration has changed since the last time it was
487 * read or written. */
495 cfg_buf_put(struct ofpbuf *buffer)
499 for (i = 0; i < cfg.n; i++) {
500 ofpbuf_put(buffer, cfg.names[i], strlen(cfg.names[i]));
501 ofpbuf_put(buffer, "\n", 1);
505 /* Formats the printf()-style format string in the parameter 'format', which
506 * must be the function's last parameter, into string variable 'dst'. The
507 * function is responsible for freeing 'dst'. */
508 #define FORMAT_KEY(FORMAT, DST) \
511 va_start(args__, FORMAT); \
512 (DST) = xvasprintf(FORMAT, args__); \
516 /* Returns true if the configuration includes a key named 'key'. */
518 cfg_has(const char *key_, ...)
523 FORMAT_KEY(key_, key);
524 retval = find_key(key) != NULL;
530 cfg_is_valid(enum cfg_flags flags, const char *key_, ...)
532 char *key, **first, **last, **p;
536 FORMAT_KEY(key_, key);
537 first = find_key_le(key);
538 last = find_key_ge(key);
540 retval = ((!(flags & CFG_REQUIRED) || n)
541 && (!(flags & CFG_MULTIPLE) || n <= 1));
542 for (p = first; retval && p < last; p++) {
543 retval = is_type(strchr(*p, '=') + 1, flags);
549 /* Returns true if the configuration includes at least one key whose name
550 * begins with 'section' followed by a dot. */
552 cfg_has_section(const char *section_, ...)
560 va_start(args, section_);
561 ds_put_format_valist(§ion, section_, args);
562 ds_put_char(§ion, '.');
565 for (p = cfg.names; *p; p++) { /* XXX this is inefficient */
566 if (!strncmp(section.string, *p, section.length)) {
572 ds_destroy(§ion);
576 /* Returns the number of values for the given 'key'. The return value is 0 if
577 * no values exist for 'key'. */
579 cfg_count(const char *key_, ...)
584 FORMAT_KEY(key_, key);
585 retval = find_key_ge(key) - find_key_le(key);
590 /* Fills 'svec' with all of the immediate subsections of 'section'. For
591 * example, if 'section' is "bridge" and keys bridge.a, bridge.b, bridge.b.c,
592 * and bridge.c.x.y.z exist, then 'svec' would be initialized to a, b, and
593 * c. The caller must first initialize 'svec'. */
595 cfg_get_subsections(struct svec *svec, const char *section_, ...)
602 va_start(args, section_);
603 ds_put_format_valist(§ion, section_, args);
604 ds_put_char(§ion, '.');
608 for (p = cfg.names; *p; p++) { /* XXX this is inefficient */
609 if (!strncmp(section.string, *p, section.length)) {
610 const char *ss = *p + section.length;
611 size_t ss_len = strcspn(ss, ".=");
612 svec_add_nocopy(svec, xmemdup0(ss, ss_len));
616 ds_destroy(§ion);
620 cfg_add_entry(const char *entry_, ...)
624 FORMAT_KEY(entry_, entry);
625 svec_add_nocopy(&cfg, entry);
627 svec_terminate(&cfg);
632 cfg_del_entry(const char *entry_, ...)
636 FORMAT_KEY(entry_, entry);
637 svec_del(&cfg, entry);
638 svec_terminate(&cfg);
644 cfg_del_section(const char *section_, ...)
651 va_start(args, section_);
652 ds_put_format_valist(§ion, section_, args);
653 ds_put_char(§ion, '.');
656 for (p = cfg.names; *p; p++) {
657 if (!strncmp(section.string, *p, section.length)) {
663 svec_terminate(&cfg);
665 ds_destroy(§ion);
670 cfg_del_match(const char *pattern_, ...)
672 bool matched = false;
676 FORMAT_KEY(pattern_, pattern);
678 for (p = cfg.names; *p; p++) {
679 if (!fnmatch(pattern, *p, 0)) {
687 svec_terminate(&cfg);
694 /* Fills 'svec' with all of the key-value pairs that have sections that
695 * begin with 'section'. The caller must first initialize 'svec'. */
697 cfg_get_section(struct svec *svec, const char *section_, ...)
704 va_start(args, section_);
705 ds_put_format_valist(§ion, section_, args);
706 ds_put_char(§ion, '.');
709 for (p = cfg.names; *p; p++) { /* XXX this is inefficient */
710 if (!strncmp(section.string, *p, section.length)) {
714 ds_destroy(§ion);
717 /* Returns the value numbered 'idx' of 'key'. Returns a null pointer if 'idx'
718 * is greater than or equal to cfg_count(key). The caller must not modify or
719 * free the returned string or retain its value beyond the next call to
722 cfg_get_string(int idx, const char *key_, ...)
727 FORMAT_KEY(key_, key);
728 retval = get_nth_value(idx, key);
733 /* Returns the value numbered 'idx' of 'key'. Returns a null pointer if 'idx'
734 * is greater than or equal to cfg_count(key) or if the value 'idx' of 'key' is
735 * not a valid key. The caller must not modify or free the returned string or
736 * retain its value beyond the next call to cfg_read(). */
738 cfg_get_key(int idx, const char *key_, ...)
740 const char *value, *retval;
743 FORMAT_KEY(key_, key);
744 value = get_nth_value(idx, key);
745 retval = value && is_key(value) ? value : NULL;
750 /* Returns the value numbered 'idx' of 'key', converted to an integer. Returns
751 * 0 if 'idx' is greater than or equal to cfg_count(key) or if the value 'idx'
752 * of 'key' is not a valid integer. */
754 cfg_get_int(int idx, const char *key_, ...)
760 FORMAT_KEY(key_, key);
761 value = get_nth_value(idx, key);
762 retval = value && is_int(value) ? atoi(value) : 0;
767 /* Returns the value numbered 'idx' of 'key', converted to a boolean value.
768 * Returns false if 'idx' is greater than or equal to cfg_count(key) or if the
769 * value 'idx' of 'key' is not a valid boolean. */
771 cfg_get_bool(int idx, const char *key_, ...)
777 FORMAT_KEY(key_, key);
778 value = get_nth_value(idx, key);
779 retval = value && is_bool(value) ? !strcmp(value, "true") : false;
784 /* Returns the value numbered 'idx' of 'key', converted to an IP address in
785 * network byte order. Returns 0 if 'idx' is greater than or equal to
786 * cfg_count(key) or if the value 'idx' of 'key' is not a valid IP address (as
787 * determined by inet_aton()). */
789 cfg_get_ip(int idx, const char *key_, ...)
795 FORMAT_KEY(key_, key);
796 value = get_nth_value(idx, key);
797 if (!value || !inet_aton(value, &addr)) {
798 addr.s_addr = htonl(0);
804 /* Returns the value numbered 'idx' of 'key', converted to an MAC address in
805 * host byte order. Returns 0 if 'idx' is greater than or equal to
806 * cfg_count(key) or if the value 'idx' of 'key' is not a valid MAC address in
807 * the format "##:##:##:##:##:##". */
809 cfg_get_mac(int idx, const char *key_, ...)
811 uint8_t mac[ETH_ADDR_LEN];
815 FORMAT_KEY(key_, key);
816 value = get_nth_value(idx, key);
817 if (!value || !parse_mac(value, mac)) {
818 memset(mac, 0, sizeof mac);
821 return eth_addr_to_uint64(mac);
824 /* Returns the value numbered 'idx' of 'key', parsed as an datapath ID.
825 * Returns 0 if 'idx' is greater than or equal to cfg_count(key) or if the
826 * value 'idx' of 'key' is not a valid datapath ID consisting of exactly 12
827 * hexadecimal digits. */
829 cfg_get_dpid(int idx, const char *key_, ...)
835 FORMAT_KEY(key_, key);
836 value = get_nth_value(idx, key);
837 if (!value || !parse_dpid(value, &dpid)) {
844 /* Returns the value numbered 'idx' of 'key', converted to an integer. Returns
845 * -1 if 'idx' is greater than or equal to cfg_count(key) or if the value 'idx'
846 * of 'key' is not a valid integer between 0 and 4095. */
848 cfg_get_vlan(int idx, const char *key_, ...)
854 FORMAT_KEY(key_, key);
855 value = get_nth_value(idx, key);
856 if (value && is_int(value)) {
857 retval = atoi(value);
858 if (retval < 0 || retval > 4095) {
868 /* Fills 'svec' with all of the string values of 'key'. The caller must
869 * first initialize 'svec'. */
871 cfg_get_all_strings(struct svec *svec, const char *key_, ...)
876 FORMAT_KEY(key_, key);
878 for (p = find_key_le(key), q = find_key_ge(key); p < q; p++) {
879 svec_add(svec, extract_value(*p));
884 /* Fills 'svec' with all of the values of 'key' that are valid keys.
885 * Values of 'key' that are not valid keys are omitted. The caller
886 * must first initialize 'svec'. */
888 cfg_get_all_keys(struct svec *svec, const char *key_, ...)
893 FORMAT_KEY(key_, key);
895 for (p = find_key_le(key), q = find_key_ge(key); p < q; p++) {
896 const char *value = extract_value(*p);
898 svec_add(svec, value);
905 has_double_dot(const char *key, size_t len)
910 for (i = 0; i < len - 1; i++) {
911 if (key[i] == '.' && key[i + 1] == '.') {
920 is_valid_key(const char *key, size_t len,
921 const char *file_name, int line_number, const char *id)
924 VLOG_ERR("%s:%d: missing %s name", file_name, line_number, id);
926 } else if (key[0] == '.') {
927 VLOG_ERR("%s:%d: %s name \"%.*s\" begins with invalid character '.'",
928 file_name, line_number, id, (int) len, key);
930 } else if (key[len - 1] == '.') {
931 VLOG_ERR("%s:%d: %s name \"%.*s\" ends with invalid character '.'",
932 file_name, line_number, id, (int) len, key);
934 } else if (has_double_dot(key, len)) {
935 VLOG_ERR("%s:%d: %s name \"%.*s\" contains '..', which is not allowed",
936 file_name, line_number, id, (int) len, key);
944 parse_section(const char *file_name, int line_number, const char *s)
951 /* Skip [ and any white space. */
953 s += strspn(s, CC_SPACE);
955 /* Obtain the section name. */
956 len = strspn(s, CC_KEY);
957 if (!is_valid_key(s, len, file_name, line_number, "section")) {
960 ds_put_buffer(§ion, s, len);
963 /* Obtain the subsection name, if any. */
964 s += strspn(s, CC_SPACE);
967 len = strspn(s, CC_KEY);
968 if (!is_valid_key(s, len, file_name, line_number, "subsection")) {
971 ds_put_char(§ion, '.');
972 ds_put_buffer(§ion, s, len);
975 VLOG_ERR("%s:%d: missing '\"' following subsection name",
976 file_name, line_number);
980 s += strspn(s, CC_SPACE);
985 VLOG_ERR("%s:%d: missing ']' following section name",
986 file_name, line_number);
990 s += strspn(s, CC_SPACE);
992 VLOG_ERR("%s:%d: trailing garbage following ']'",
993 file_name, line_number);
997 return ds_cstr(§ion);
1000 ds_destroy(§ion);
1005 parse_setting(const char *file_name, int line_number, const char *section,
1008 struct ds key = DS_EMPTY_INITIALIZER;
1009 struct ds value = DS_EMPTY_INITIALIZER;
1013 ds_put_format(&key, "%s.", section);
1016 /* Obtain the key. */
1017 len = strspn(s, CC_KEY);
1019 VLOG_ERR("%s:%d: missing key name", file_name, line_number);
1022 if (!is_valid_key(s, len, file_name, line_number, "key")) {
1025 ds_put_buffer(&key, s, len);
1029 s += strspn(s, CC_SPACE);
1031 VLOG_ERR("%s:%d: missing '=' following key", file_name, line_number);
1035 s += strspn(s, CC_SPACE);
1037 /* Obtain the value. */
1038 ds_put_cstr(&value, s);
1039 while (value.length > 0 && strchr(CC_SPACE, ds_last(&value))) {
1043 /* Add the setting. */
1044 svec_add_nocopy(&cfg, xasprintf("%s=%s", ds_cstr(&key), ds_cstr(&value)));
1052 compare_key(const char *a, const char *b)
1055 int ac = *a == '\0' || *a == '=' ? INT_MAX : *a;
1056 int bc = *b == '\0' || *b == '=' ? INT_MAX : *b;
1058 return ac < bc ? -1 : 1;
1059 } else if (ac == INT_MAX) {
1067 /* Returns the address of the greatest configuration string with a key less
1068 * than or equal to 'key'. Returns the address of the null terminator if all
1069 * configuration strings are greater than 'key'. */
1071 find_key_le(const char *key)
1076 int half = len >> 1;
1077 int middle = low + half;
1078 if (compare_key(cfg.names[middle], key) < 0) {
1085 return &cfg.names[low];
1088 /* Returns the address of the least configuration string with a key greater
1089 * than or equal to 'key'. Returns the address of the null terminator if all
1090 * configuration strings are less than 'key'. */
1092 find_key_ge(const char *key)
1097 int half = len >> 1;
1098 int middle = low + half;
1099 if (compare_key(cfg.names[middle], key) > 0) {
1106 return &cfg.names[low];
1110 find_key(const char *key)
1112 char **p = find_key_le(key);
1113 return p < &cfg.names[cfg.n] && !compare_key(*p, key) ? *p : NULL;
1117 parse_mac(const char *s, uint8_t mac[6])
1119 return sscanf(s, "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8,
1120 &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6;
1124 parse_dpid(const char *s, uint64_t *dpid)
1126 if (strlen(s) == 12 && strspn(s, "0123456789abcdefABCDEF") == 12) {
1127 *dpid = strtoll(s, NULL, 16);
1135 is_key(const char *s)
1137 /* XXX needs to check the same things as is_valid_key() too. */
1138 return *s && s[strspn(s, CC_KEY)] == '\0';
1142 is_int(const char *s)
1144 return *s && s[strspn(s, CC_DIGIT)] == '\0';
1148 is_bool(const char *s)
1150 return !strcmp(s, "true") || !strcmp(s, "false");
1154 extract_value(const char *key)
1156 const char *p = strchr(key, '=');
1157 return p ? p + 1 : NULL;
1161 get_nth_value(int idx, const char *key)
1163 char **p = find_key_le(key);
1164 char **q = find_key_ge(key);
1165 return idx < q - p ? extract_value(p[idx]) : NULL;
1169 is_type(const char *s, enum cfg_flags flags)
1171 uint8_t mac[ETH_ADDR_LEN];
1172 struct in_addr addr;
1175 return (flags & CFG_STRING
1176 || (flags & CFG_KEY && is_key(s))
1177 || (flags & CFG_INT && is_int(s))
1178 || (flags & CFG_BOOL && is_bool(s))
1179 || (flags & CFG_IP && inet_aton(s, &addr))
1180 || (flags & CFG_MAC && parse_mac(s, mac))
1181 || (flags & CFG_DPID && parse_dpid(s, &dpid)));