2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
6 * Author: Harti Brandt <harti@freebsd.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * $Begemot: bsnmp/snmpd/config.c,v 1.25 2006/02/14 09:04:20 brandt_h Exp $
31 * Parse configuration file.
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/socket.h>
59 * config_file: EMPTY | config_file line
63 * | STRING := REST_OF_LINE
64 * | STRING ?= REST_OF_LINE
69 * suboid: EMPTY | suboid '.' subid
71 * subid: NUM | STRING | '[' STRING ']'
73 * value: EMPTY | STRING | NUM
77 * Input context for macros and includes
98 LIST_ENTRY(input) link;
100 static LIST_HEAD(, input) inputs;
102 #define input_fp u.file.fp
103 #define input_filename u.file.filename
104 #define input_lno u.file.lno
105 #define input_macro u.str.macro
106 #define input_str u.str.str
107 #define input_ptr u.str.ptr
108 #define input_left u.str.left
110 static int input_push;
111 static int input_buf[2];
114 * Configuration data. The configuration file is handled as one single
115 * SNMP transaction. So we need to keep the assignment data for the
116 * commit or rollback pass. Note, that dependencies and finish functions
117 * are NOT allowed here.
120 struct snmp_value value;
121 struct snmp_scratch scratch;
122 const char *node_name;
124 TAILQ_ENTRY(assign) link;
126 static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns);
129 static struct snmp_context *snmp_ctx;
135 LIST_ENTRY(macro) link;
138 static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(macros);
150 /* lexer values and last token */
151 static uint64_t numval;
152 static char strval[_POSIX2_LINE_MAX];
153 static size_t strvallen;
157 static jmp_buf errjmp[4];
158 static volatile int errstk;
160 # define ERRPUSH() (setjmp(errjmp[errstk++]))
161 # define ERRPOP() ((void)(errstk--))
162 # define ERRNEXT() (longjmp(errjmp[--errstk], 1))
163 # define ERR() (longjmp(errjmp[--errstk], 1))
165 /* section context */
169 * Report an error and jump to the error label
171 static void report(const char *fmt, ...) __dead2 __printflike(1, 2);
174 report(const char *fmt, ...)
177 const struct input *input;
180 vsyslog(LOG_ERR, fmt, ap);
183 LIST_FOREACH(input, &inputs, link) {
184 switch (input->type) {
187 syslog(LOG_ERR, " in file %s line %u",
188 input->input_filename, input->input_lno);
192 syslog(LOG_ERR, " in macro %s pos %td",
194 input->input_ptr - input->input_str);
202 * Open a file for input
205 input_open_file(const char *fname, int sysdir)
209 char path[PATH_MAX + 1];
216 while (*ptr != '\0') {
217 if ((col = strchr(ptr, ':')) == NULL) {
218 snprintf(path, sizeof(path), "%s/%s",
220 col = ptr + strlen(ptr) - 1;
221 } else if (col == ptr)
222 snprintf(path, sizeof(path), "./%s", fname);
224 snprintf(path, sizeof(path), "%.*s/%s",
225 (int)(col - ptr), ptr, fname);
226 if ((fp = fopen(path, "r")) != NULL)
231 fp = fopen(fname, "r");
234 report("%s: %m", fname);
236 if ((input = malloc(sizeof(*input))) == NULL) {
240 if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) {
245 strcpy(input->input_filename, fname);
246 input->input_fp = fp;
247 input->input_lno = 1;
248 input->type = INPUT_FILE;
249 LIST_INSERT_HEAD(&inputs, input, link);
254 * Make a macro the next input
257 input_open_macro(struct macro *m)
261 if ((input = malloc(sizeof(*input))) == NULL)
263 input->type = INPUT_STRING;
264 input->input_macro = m->name;
265 if ((input->input_str = malloc(m->length)) == NULL) {
269 memcpy(input->input_str, m->value, m->length);
270 input->input_ptr = input->input_str;
271 input->input_left = m->length;
272 LIST_INSERT_HEAD(&inputs, input, link);
276 * Close top input source
283 if ((input = LIST_FIRST(&inputs)) == NULL)
285 switch (input->type) {
288 fclose(input->input_fp);
289 free(input->input_filename);
293 free(input->input_str);
296 LIST_REMOVE(input, link);
304 input_close_all(void)
306 while (!LIST_EMPTY(&inputs))
311 * Push back one character
317 report("pushing EOF");
319 report("pushing third char");
320 input_buf[input_push++] = c;
325 * Return next character from the input without preprocessing.
333 if (input_push != 0) {
334 c = input_buf[--input_push];
337 while ((input = LIST_FIRST(&inputs)) != NULL) {
338 switch (input->type) {
341 if ((c = getc(input->input_fp)) == EOF) {
342 if (ferror(input->input_fp))
343 report("read error: %m");
352 if (input->input_left-- == 0) {
356 c = *input->input_ptr++;
361 fprintf(stderr, "EOF");
367 if (!isascii(c) || !isprint(c))
368 fprintf(stderr, "'%#2x'", c);
370 fprintf(stderr, "'%c'", c);
376 * Get character with and \\n -> processing.
379 input_getc_plain(void)
384 if ((c = input_getc_raw()) == '\\') {
385 if ((c = input_getc_raw()) == '\n')
395 * Get next character with substitution of macros
402 char name[_POSIX2_LINE_MAX];
406 if ((c = input_getc_plain()) != '$')
409 if ((c = input_getc()) == EOF)
410 report("unexpected EOF");
412 report("expecting '(' after '$'");
415 while ((c = input_getc()) != EOF && c != ')') {
416 if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c)))
422 report("unexpected EOF");
423 name[namelen++] = '\0';
425 LIST_FOREACH(m, ¯os, link)
426 if (strcmp(m->name, name) == 0)
429 report("undefined macro '%s'", name);
435 if (!isascii(c) || !isprint(c))
436 report("unexpected character %#2x", (u_int)c);
438 report("bad character '%c'", c);
443 input_getnum(u_int base, u_int flen)
450 while (flen == 0 || cnt < flen) {
451 if ((c = input_getc()) == EOF) {
453 report("bad number");
457 if (base == 8 && (c == '8' || c == '9')) {
460 report("bad number");
463 numval = numval * base + (c - '0');
464 } else if (base == 16 && isxdigit(c)) {
466 numval = numval * base + (c - 'a' + 10);
468 numval = numval * base + (c - 'A' + 10);
472 report("bad number");
488 static const char esc[] = "abfnrtv";
489 static const char chr[] = "\a\b\f\n\r\t\v";
492 * Skip any whitespace before the next token
494 while ((c = input_getc()) != EOF) {
495 if (!isspace(c) || c == '\n')
499 return (token = TOK_EOF);
507 while ((c = input_getc_plain()) != EOF) {
509 return (token = TOK_EOL);
515 * Single character tokens
518 return (token = TOK_EOL);
519 if (c == '.' || c == '%' || c == '=' || c == '<' || c == '>')
522 if ((c = input_getc()) == '=')
523 return (token = TOK_ASSIGN);
525 return (token = ':');
528 if ((c = input_getc()) == '=')
529 return (token = TOK_QASSIGN);
539 if ((c = input_getc()) == 'x' || c == 'X') {
541 } else if (isdigit(c)) {
554 return (token = TOK_NUM);
558 * Must be a string then
562 # define GETC(C) do { \
563 if ((c = input_getc()) == EOF) \
565 if (!isascii(c) || (!isprint(c) && c != '\t')) \
573 strval[strvallen] = '\0';
577 strval[strvallen++] = c;
581 if ((end = strchr(esc, c)) != NULL) {
582 strval[strvallen++] = chr[end - esc];
588 } else if (c >= '0' && c <= '7') {
593 strval[strvallen++] = c;
597 } else if (c == '[') {
601 while ((c = input_getc()) != EOF && isspace(c))
605 while (c != ']' && !isspace(c)) {
606 if (!isalnum(c) && c != '.' && c != '-')
608 strval[strvallen++] = c;
609 if ((c = input_getc()) == EOF)
612 while (c != ']' && isspace(c)) {
613 if ((c = input_getc()) == EOF)
618 strval[strvallen] = '\0';
619 return (token = TOK_HOST);
621 } else if (!isalpha(c) && c != '_') {
626 strval[strvallen++] = c;
627 if ((c = input_getc()) == EOF)
629 if (!isalnum(c) && c != '_' && c != '-') {
631 strval[strvallen] = '\0';
637 return (token = TOK_STR);
640 report("unexpected EOF");
643 if (!isascii(c) || !isprint(c))
644 report("unexpected character %#2x", (u_int)c);
646 report("bad character '%c'", c);
654 if (isascii(token) && isprint(token))
655 printf("(%c)", token);
666 printf("(NUM %ju)", (uintmax_t)numval);
669 printf("(STR %.*s)", (int)strvallen, strval);
672 printf("(HOST %s)", strval);
675 printf("(%#2x)", token);
685 * Try to execute the assignment.
688 handle_assignment(const struct snmp_node *node, struct asn_oid *vindex,
689 const struct snmp_value *value)
696 if (node->type == SNMP_NODE_LEAF) {
697 /* index must be one single zero or no index at all */
698 if (vindex->len > 1 || (vindex->len == 1 &&
699 vindex->subs[0] != 0))
700 report("bad index on leaf node");
704 /* resulting oid must not be too long */
705 if (node->oid.len + vindex->len > ASN_MAXOIDLEN)
706 report("resulting OID too long");
710 * Get the next assignment entry for the transaction.
712 if ((tp = malloc(sizeof(*tp))) == NULL)
716 tp->node_name = node->name;
721 tp->value.var = node->oid;
722 for (i = 0; i < vindex->len; i++)
723 tp->value.var.subs[tp->value.var.len++] = vindex->subs[i];
726 * Puzzle together the variables for the call and call the
727 * set routine. The set routine may make our node pointer
728 * invalid (if we happend to call the module loader) so
729 * get a copy of the node name beforehands.
731 snprintf(nodename, sizeof(nodename), "%s", node->name);
732 snmp_ctx->scratch = &tp->scratch;
733 snmp_ctx->var_index = 0;
734 err = (*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index,
738 report("assignment to %s.%s returns %d", nodename,
739 asn_oid2str(vindex), err);
742 TAILQ_INSERT_TAIL(&assigns, tp, link);
747 * Parse the section statement
750 parse_section(const struct lmodule *mod)
752 if (token != TOK_STR)
753 report("expecting section name");
755 if (strcmp(strval, "snmpd") == 0) {
757 /* loading a module - ignore common stuff */
760 /* global configuration - don't ignore */
764 /* global configuration - ignore module stuff */
767 /* loading module - check if it's our section */
768 ignore = (strcmp(strval, mod->section) != 0);
775 * Convert a hostname to four u_chars
778 gethost(const char *host, u_char *ip)
780 struct addrinfo hints, *res;
782 struct sockaddr_in *sain;
784 memset(&hints, 0, sizeof(hints));
785 hints.ai_family = AF_INET;
786 hints.ai_socktype = SOCK_DGRAM;
787 hints.ai_protocol = IPPROTO_UDP;
788 hints.ai_flags = AI_PASSIVE;
789 error = getaddrinfo(host, NULL, &hints, &res);
791 report("%s: %s", host, gai_strerror(error));
793 report("%s: unknown hostname", host);
795 sain = (struct sockaddr_in *)(void *)res->ai_addr;
796 sain->sin_addr.s_addr = ntohl(sain->sin_addr.s_addr);
797 ip[0] = sain->sin_addr.s_addr >> 24;
798 ip[1] = sain->sin_addr.s_addr >> 16;
799 ip[2] = sain->sin_addr.s_addr >> 8;
800 ip[3] = sain->sin_addr.s_addr >> 0;
806 * Parse the left hand side of a config line.
808 static const struct snmp_node *
809 parse_oid(const char *varname, struct asn_oid *oid)
811 struct snmp_node *node;
814 struct asn_oid str_oid;
816 for (node = tree; node < &tree[tree_size]; node++)
817 if (strcmp(varname, node->name) == 0)
819 if (node == &tree[tree_size])
823 while (token == '.') {
824 if (gettoken() == TOK_NUM) {
825 if (numval > ASN_MAXID)
826 report("subid too large %#jx",
828 if (oid->len == ASN_MAXOIDLEN)
829 report("index too long");
830 if (gettoken() != ':')
831 oid->subs[oid->len++] = numval;
834 str_oid.subs[str_oid.len++] = numval;
835 while (gettoken() == TOK_NUM) {
836 str_oid.subs[str_oid.len++] = numval;
837 if (gettoken() != ':')
840 oid->subs[oid->len++] = str_oid.len;
841 asn_append_oid(oid, &str_oid);
844 } else if (token == TOK_STR) {
845 if (strvallen + oid->len + 1 > ASN_MAXOIDLEN)
846 report("oid too long");
847 oid->subs[oid->len++] = strvallen;
848 for (i = 0; i < strvallen; i++)
849 oid->subs[oid->len++] = strval[i];
852 } else if (token == TOK_HOST) {
854 if (oid->len + 4 > ASN_MAXOIDLEN)
855 report("index too long");
856 for (i = 0; i < 4; i++)
857 oid->subs[oid->len++] = ip[i];
860 report("bad token in index");
867 * Parse the value for an assignment.
870 parse_syntax_null(struct snmp_value *value __unused)
872 if (token != TOK_EOL)
873 report("bad NULL syntax");
877 parse_syntax_integer(struct snmp_value *value)
879 if (token != TOK_NUM)
880 report("bad INTEGER syntax");
881 if (numval > 0x7fffffff)
882 report("INTEGER too large %ju", (uintmax_t)numval);
884 value->v.integer = numval;
889 parse_syntax_counter64(struct snmp_value *value)
891 if (token != TOK_NUM)
892 report("bad COUNTER64 syntax");
894 value->v.counter64 = numval;
899 parse_syntax_octetstring(struct snmp_value *value)
904 if (token == TOK_STR) {
905 value->v.octetstring.len = strvallen;
906 value->v.octetstring.octets = malloc(strvallen);
907 (void)memcpy(value->v.octetstring.octets, strval, strvallen);
912 /* XX:XX:XX syntax */
913 value->v.octetstring.octets = NULL;
914 value->v.octetstring.len = 0;
916 if (token != TOK_NUM)
917 /* empty string is allowed */
921 free(value->v.octetstring.octets);
927 if (token != TOK_NUM)
928 report("bad OCTETSTRING syntax");
930 report("byte value too large");
931 if (alloc == value->v.octetstring.len) {
933 noct = realloc(value->v.octetstring.octets, alloc);
936 value->v.octetstring.octets = noct;
938 value->v.octetstring.octets[value->v.octetstring.len++]
940 if (gettoken() != ':')
948 parse_syntax_oid(struct snmp_value *value)
950 value->v.oid.len = 0;
952 if (token != TOK_NUM)
956 if (token != TOK_NUM)
957 report("bad OID syntax");
958 if (numval > ASN_MAXID)
959 report("subid too large");
960 if (value->v.oid.len == ASN_MAXOIDLEN)
961 report("OID too long");
962 value->v.oid.subs[value->v.oid.len++] = numval;
963 if (gettoken() != '.')
970 parse_syntax_ipaddress(struct snmp_value *value)
975 if (token == TOK_NUM) {
976 /* numerical address */
980 report("ip address part too large");
981 value->v.ipaddress[i++] = numval;
984 if (gettoken() != '.')
985 report("expecting '.' in ip address");
989 } else if (token == TOK_HOST) {
992 for (i = 0; i < 4; i++)
993 value->v.ipaddress[i] = ip[i];
997 report("bad ip address syntax");
1001 parse_syntax_uint32(struct snmp_value *value)
1004 if (token != TOK_NUM)
1005 report("bad number syntax");
1006 if (numval > 0xffffffff)
1007 report("number too large");
1008 value->v.uint32 = numval;
1013 * Parse an assignement line
1016 parse_assign(const char *varname)
1018 struct snmp_value value;
1019 struct asn_oid vindex;
1020 const struct snmp_node *node;
1022 node = parse_oid(varname, &vindex);
1024 report("'=' expected, got '%c'", token);
1028 /* skip rest of line */
1029 while (token != TOK_EOL && token != TOK_EOF)
1034 report("unknown variable");
1036 switch (value.syntax = node->syntax) {
1038 case SNMP_SYNTAX_NULL:
1039 parse_syntax_null(&value);
1042 case SNMP_SYNTAX_INTEGER:
1043 parse_syntax_integer(&value);
1046 case SNMP_SYNTAX_COUNTER64:
1047 parse_syntax_counter64(&value);
1050 case SNMP_SYNTAX_OCTETSTRING:
1051 parse_syntax_octetstring(&value);
1054 case SNMP_SYNTAX_OID:
1055 parse_syntax_oid(&value);
1058 case SNMP_SYNTAX_IPADDRESS:
1059 parse_syntax_ipaddress(&value);
1062 case SNMP_SYNTAX_COUNTER:
1063 case SNMP_SYNTAX_GAUGE:
1064 case SNMP_SYNTAX_TIMETICKS:
1065 parse_syntax_uint32(&value);
1068 case SNMP_SYNTAX_NOSUCHOBJECT:
1069 case SNMP_SYNTAX_NOSUCHINSTANCE:
1070 case SNMP_SYNTAX_ENDOFMIBVIEW:
1075 snmp_value_free(&value);
1079 handle_assignment(node, &vindex, &value);
1085 * Handle macro definition line
1086 * We have already seen the := and the input now stands at the character
1087 * after the =. Skip whitespace and then call the input routine directly to
1088 * eat up characters.
1091 parse_define(const char *varname)
1093 char *volatile string;
1095 volatile size_t alloc, length;
1102 if ((string = malloc(alloc)) == NULL)
1110 while ((c = input_getc_plain()) != EOF) {
1111 if (c == '\n' || !isspace(c))
1115 while (c != EOF && c != '#' && c != '\n') {
1116 if (alloc == length) {
1118 if ((new = realloc(string, alloc)) == NULL)
1122 string[length++] = c;
1123 c = input_getc_plain();
1126 while ((c = input_getc_plain()) != EOF && c != '\n')
1130 report("EOF in macro definition");
1132 LIST_FOREACH(m, ¯os, link)
1133 if (strcmp(m->name, varname) == 0)
1137 if ((m = malloc(sizeof(*m))) == NULL)
1139 if ((m->name = malloc(strlen(varname) + 1)) == NULL) {
1143 strcpy(m->name, varname);
1145 LIST_INSERT_HEAD(¯os, m, link);
1150 if (t == TOK_ASSIGN) {
1167 macro_free_all(void)
1169 static struct macro *m, *m1;
1171 m = LIST_FIRST(¯os);
1173 m1 = LIST_NEXT(m, link);
1177 LIST_REMOVE(m, link);
1185 * Parse an include directive and switch to the new file
1191 char fname[_POSIX2_LINE_MAX];
1193 if (gettoken() == '<') {
1195 if (gettoken() != TOK_STR)
1196 report("expecting filename after in .include");
1197 } else if (token != TOK_STR)
1198 report("expecting filename after in .include");
1200 strcpy(fname, strval);
1201 if (sysdir && gettoken() != '>')
1202 report("expecting '>'");
1204 if (input_open_file(fname, sysdir) == -1)
1205 report("%s: %m", fname);
1209 * Parse the configuration file
1212 parse_file(const struct lmodule *mod)
1214 char varname[_POSIX2_LINE_MAX];
1216 while (gettoken() != TOK_EOF) {
1217 if (token == TOK_EOL)
1223 } else if (token == '.') {
1224 if (gettoken() != TOK_STR)
1225 report("keyword expected after '.'");
1226 if (strcmp(strval, "include") == 0)
1229 report("unknown keyword '%s'", strval);
1230 } else if (token == TOK_STR) {
1231 strcpy(varname, strval);
1232 if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN)
1233 parse_define(varname);
1235 parse_assign(varname);
1237 if (token != TOK_EOL)
1238 report("eol expected");
1243 * Do rollback on errors
1249 struct snmp_node *node;
1251 while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) {
1252 TAILQ_REMOVE(&assigns, tp, link);
1253 for (node = tree; node < &tree[tree_size]; node++)
1254 if (node->name == tp->node_name) {
1255 snmp_ctx->scratch = &tp->scratch;
1256 (void)(*node->op)(snmp_ctx, &tp->value,
1257 node->oid.len, node->index,
1261 if (node == &tree[tree_size])
1262 syslog(LOG_ERR, "failed to find node for "
1264 snmp_value_free(&tp->value);
1276 struct snmp_node *node;
1278 while ((tp = TAILQ_FIRST(&assigns)) != NULL) {
1279 TAILQ_REMOVE(&assigns, tp, link);
1280 for (node = tree; node < &tree[tree_size]; node++)
1281 if (node->name == tp->node_name) {
1282 snmp_ctx->scratch = &tp->scratch;
1283 (void)(*node->op)(snmp_ctx, &tp->value,
1284 node->oid.len, node->index, SNMP_OP_COMMIT);
1287 if (node == &tree[tree_size])
1288 syslog(LOG_ERR, "failed to find node for commit");
1289 snmp_value_free(&tp->value);
1295 * Read the configuration file. Handle the entire file as one transaction.
1297 * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are
1298 * executed. If it is not NULL, only the sections for that module are handled.
1301 read_config(const char *fname, struct lmodule *lodmod)
1304 char objbuf[ASN_OIDSTRLEN];
1305 char idxbuf[ASN_OIDSTRLEN];
1313 if (input_open_file(fname, 0) == -1) {
1314 syslog(LOG_ERR, "%s: %m", fname);
1318 community = COMM_INITIALIZE;
1320 if ((snmp_ctx = snmp_init_context()) == NULL) {
1322 syslog(LOG_ERR, "%m");
1336 if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) {
1337 syslog(LOG_ERR, "init dep failed: %u %s %s", err,
1338 asn_oid2str_r(&snmp_ctx->dep->obj, objbuf),
1339 asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf));
1340 snmp_dep_rollback(snmp_ctx);
1349 snmp_dep_finish(snmp_ctx);
1359 * Define a permanent macro
1362 define_macro(const char *name, const char *value)
1366 if ((m = malloc(sizeof(*m))) == NULL)
1368 if ((m->name = malloc(strlen(name) + 1)) == NULL) {
1372 strcpy(m->name, name);
1373 if ((m->value = malloc(strlen(value) + 1)) == NULL) {
1378 strcpy(m->value, value);
1379 m->length = strlen(value);
1381 LIST_INSERT_HEAD(¯os, m, link);