/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This module implements the routine to parse the configuration file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "picld_pluginutil.h" #include "picld_pluginutil_impl.h" /* error codes returned from syntax checking */ #define EC_SYNTAX_OK 0 #define EC_INSUFFICIENT_TOKEN 1 #define EC_SYNTAX_ERR 2 #define EC_UNSUPPORTED 3 #define EC_PATH_ERR 4 #define EC_NODE_MISMATCH 5 #define EC_FAILURE 6 #define EC_PICL_ERR 7 #define EC_TABLE_MISMATCH 8 #define EC_ROW_MISMATCH 9 #define EC_ROW_EMPTY 10 /* * Error message texts */ static char *err_msg[] = { "%s: Syntax OK", /* 0 */ "%s::%s[line %d]: Insufficient token\n", /* 1 */ "%s::%s[line %d]: Syntax error\n", /* 2 */ "%s::%s[line %d]: Unsupported or missing version\n", /* 3 */ "%s::%s[line %d]: Illegal use of nodepath or namepath\n", /* 4 */ "%s::%s[line %d]: Node and endnode mismatch\n", /* 5 */ "%s::%s[line %d]: General system failure\n", /* 6 */ "%s: PICL error code %d\n", /* 7 */ "%s::%s[line %d]: Table and endtable mismatch\n", /* 8 */ "%s::%s[line %d]: Row and endrow mismatch\n", /* 9 */ "%s::%s[line %d]: Row has no entries \n" /* 10 */ }; /* token per directive */ #define TOK_CLASSPATH 0 #define TOK_NAMEPATH 1 #define TOK_NODE 2 #define TOK_ENDNODE 3 #define TOK_PROP 4 #define TOK_REFPROP 5 #define TOK_VERSION 6 #define TOK_REFNODE 7 #define TOK_VERBOSE 8 #define TOK_TABLE 9 #define TOK_ENDTABLE 10 #define TOK_ROW 11 #define TOK_ENDROW 12 static const char *tokens[] = { "_class", /* _CLASS: */ "name", /* NAME: */ "node", /* NODE */ "endnode", /* ENDNODE */ "prop", /* PROP */ "refprop", /* REFPROP */ "version", /* VERSION */ "refnode", /* REFNODE WITH */ "verbose", /* VERBOSE */ "table", /* TABLE */ "endtable", /* ENDTABLE */ "row", /* ROW */ "endrow" /* ENDROW */ }; #define BUF_SIZE_MAX 1024 /* * print error message */ /*VARARGS2*/ static void verbose_log(int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsyslog(pri, fmt, ap); va_end(ap); } /* * Undo the commands which have created valid node/prop handle * The undo order is from last command to the first command. */ static void undo_commands(cmdbuf_t *cmds, int last_cmd_index) { int i; command_t *com = cmds->commands; for (i = last_cmd_index; i >= 0; i--) { switch (com[i].type) { case TOK_NODE: if (com[i].nodecmd_nodeh == NULL) break; (void) ptree_delete_node(com[i].nodecmd_nodeh); (void) ptree_destroy_node(com[i].nodecmd_nodeh); break; case TOK_REFNODE: if (com[i].refnodecmd_nodeh == NULL) break; (void) ptree_delete_node(com[i].refnodecmd_nodeh); (void) ptree_destroy_node(com[i].refnodecmd_nodeh); break; case TOK_PROP: if (com[i].propcmd_proph == NULL) break; (void) ptree_delete_prop(com[i].propcmd_proph); (void) ptree_destroy_prop(com[i].propcmd_proph); break; case TOK_REFPROP: if (com[i].refpropcmd_proph == NULL) break; (void) ptree_delete_prop(com[i].refpropcmd_proph); (void) ptree_destroy_prop(com[i].refpropcmd_proph); break; case TOK_TABLE: if ((com[i].tablecmd_tblh == NULL) || (com[i].tablecmd_newtbl == 0)) break; (void) ptree_delete_prop(com[i].tablecmd_tblh); (void) ptree_destroy_prop(com[i].tablecmd_tblh); break; case TOK_ENDTABLE: /*FALLTHROUGH*/ case TOK_ROW: /*FALLTHROUGH*/ case TOK_ENDROW: /*FALLTHROUGH*/ case TOK_NAMEPATH: /*FALLTHROUGH*/ case TOK_CLASSPATH: /*FALLTHROUGH*/ case TOK_ENDNODE: /*FALLTHROUGH*/ case TOK_VERBOSE: /*FALLTHROUGH*/ default: break; } } } /* * Get the token index from the tokens table */ static int get_token_id(char *t) { int i; for (i = 0; i < sizeof (tokens)/ sizeof (char *); ++i) if (strcasecmp(tokens[i], t) == 0) return (i); return (-1); } /* * Check the version syntax and set the version_no * * VERSION -- specify the configuration version */ static int parse_version(cmdbuf_t *cmds, char *line) { char *tok; char *vertok; char *last; char *endptr; /* get the VERSION directive */ tok = strtok_r(line, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); /* get the version number */ vertok = strtok_r(last, WHITESPACE, &last); if (vertok == NULL) return (EC_INSUFFICIENT_TOKEN); cmds->version_no = (float)strtod(vertok, &endptr); if (endptr != (vertok + strlen(vertok))) return (EC_UNSUPPORTED); if (cmds->version_no > (float)SUPPORTED_VERSION_NUM) return (EC_UNSUPPORTED); /* check if more tokens */ tok = strtok_r(last, WHITESPACE, &last); if (tok != NULL) return (EC_SYNTAX_ERR); return (EC_SYNTAX_OK); } /* * free path_cmd_t */ static void free_path(command_t *command) { free(command->pathcmd_name); } /* * Check the path syntax * NAMEPATH: -- gives the anchor node * or * CLASSPATH: -- gives the anchor node */ static int parse_path(char *line, command_t *command) { char *tok; char *pathtok; char *last; pathtok = strtok_r(line, WHITESPACE, &last); if (pathtok == NULL) return (EC_INSUFFICIENT_TOKEN); /* check if more tokens */ tok = strtok_r(last, WHITESPACE, &last); if (tok != NULL) return (EC_SYNTAX_ERR); command->pathcmd_name = strdup(pathtok); if (command->pathcmd_name == NULL) return (EC_FAILURE); return (EC_SYNTAX_OK); } /* * Process the path command and return PICL node handle */ static int process_path(command_t *command, picl_nodehdl_t *nodeh) { int err; err = ptree_get_node_by_path(command->pathcmd_name, nodeh); return (err); } /* * free node_cmd_t */ static void free_node(command_t *command) { free(command->nodecmd_nodename); free(command->nodecmd_classname); } /* * Check the NODE syntax * NODE */ static int parse_node(char *line, command_t *command) { char *tok; char *nametok; char *classtok; char *last; /* get the NODE directive */ tok = strtok_r(line, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); /* get name */ nametok = strtok_r(last, WHITESPACE, &last); if (nametok == NULL) return (EC_INSUFFICIENT_TOKEN); classtok = strtok_r(last, WHITESPACE, &last); if (classtok == NULL) return (EC_INSUFFICIENT_TOKEN); /* check if more tokens */ tok = strtok_r(last, WHITESPACE, &last); if (tok != NULL) return (EC_SYNTAX_ERR); command->nodecmd_nodename = strdup(nametok); command->nodecmd_classname = strdup(classtok); command->nodecmd_nodeh = NULL; if ((command->nodecmd_nodename == NULL) || (command->nodecmd_classname == NULL)) return (EC_FAILURE); return (EC_SYNTAX_OK); } /* * Process the NODE command and return PICL node handle */ static int process_node(command_t *command, picl_nodehdl_t parh, picl_nodehdl_t *nodeh) { int err; err = ptree_create_and_add_node(parh, command->nodecmd_nodename, command->nodecmd_classname, nodeh); if (err == PICL_SUCCESS) command->nodecmd_nodeh = *nodeh; return (err); } /* * get the PICL property type */ static int getpicltype(char *type) { if (strcasecmp(type, KEYWORD_INT_TYPE) == 0) return (PICL_PTYPE_INT); else if (strcasecmp(type, KEYWORD_UINT_TYPE) == 0) return (PICL_PTYPE_UNSIGNED_INT); else if (strcasecmp(type, KEYWORD_FLOAT_TYPE) == 0) return (PICL_PTYPE_FLOAT); else if (strcasecmp(type, KEYWORD_STRING_TYPE) == 0) return (PICL_PTYPE_CHARSTRING); else if (strcasecmp(type, KEYWORD_VOID_TYPE) == 0) return (PICL_PTYPE_VOID); else return (-1); } /* * get the PICL accessmode mode */ static int getpiclmode(char *mode) { if (strcasecmp(mode, KEYWORD_READ_MODE) == 0) return (PICL_READ); else if (strcasecmp(mode, KEYWORD_WRITE_MODE) == 0) return (PICL_WRITE); else if (strcasecmp(mode, KEYWORD_READWRITE_MODE) == 0) return (PICL_READ|PICL_WRITE); else return (-1); } /* * check if the size and value are valid given by the prop type */ static int validate_size_and_cvt_val(void *outbuf, size_t size, int type, char *val) { int64_t llval; int32_t intval; int16_t sval; int8_t cval; uint64_t ullval; uint32_t uintval; uint16_t usval; uint8_t ucval; float fval; double dval; char *endptr; switch (type) { case PICL_PTYPE_CHARSTRING: break; case PICL_PTYPE_INT: switch (size) { case sizeof (int64_t): llval = strtoll(val, &endptr, 0); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &llval, size); break; case sizeof (int32_t): intval = strtol(val, &endptr, 0); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &intval, size); break; case sizeof (int16_t): sval = (int16_t)strtol(val, &endptr, 0); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &sval, size); break; case sizeof (int8_t): cval = (int8_t)strtol(val, &endptr, 0); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &cval, size); break; default: /* invalid size */ return (EC_SYNTAX_ERR); } break; case PICL_PTYPE_UNSIGNED_INT: switch (size) { case sizeof (uint64_t): ullval = strtoull(val, &endptr, 0); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &ullval, size); break; case sizeof (uint32_t): uintval = strtoul(val, &endptr, 0); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &uintval, size); break; case sizeof (uint16_t): usval = (uint16_t)strtoul(val, &endptr, 0); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &usval, size); break; case sizeof (uint8_t): ucval = (uint8_t)strtoul(val, &endptr, 0); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &ucval, size); break; default: /* invalid size */ return (EC_SYNTAX_ERR); } break; case PICL_PTYPE_FLOAT: switch (size) { case sizeof (double): dval = strtod(val, &endptr); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &dval, size); break; case sizeof (float): fval = (float)strtod(val, &endptr); if (endptr != (val + strlen(val))) return (EC_SYNTAX_ERR); (void) memcpy(outbuf, &fval, size); break; default: /* invalid size */ return (EC_SYNTAX_ERR); } break; default: /* not supported type */ return (EC_SYNTAX_ERR); } return (EC_SYNTAX_OK); } /* * free prop_cmd_t */ static void free_prop(command_t *command) { free(command->propcmd_pname); if (command->propcmd_type != PICL_PTYPE_VOID) free(command->propcmd_valbuf); } /* * return the string token in two double quotes * The current version won't support multiple-line string */ static int get_string_token(char *line, char **valtok) { char *optr; /* ptr to the open quote */ char *cptr; /* ptr to the close quote */ char *ptr; char *tmpbuf; if (line == NULL) return (EC_INSUFFICIENT_TOKEN); /* skipping leading white spaces */ optr = line; while ((*optr == ' ') || (*optr == '\t') || (*optr == '\n')) optr++; /* reach end of string */ if (*optr == '\0') return (EC_INSUFFICIENT_TOKEN); /* it's not an open double quote */ if (*optr != '"') return (EC_SYNTAX_ERR); /* skipping ending white spaces */ cptr = line + strlen(line) - 1; while ((*cptr == ' ') || (*cptr == '\t') || (*cptr == '\n')) cptr--; /* it's not an close double quote */ if (*cptr != '"') return (EC_SYNTAX_ERR); /* close double quote is missing */ if (cptr == optr) return (EC_SYNTAX_ERR); /* replace close qoute by null to make a string */ *cptr = '\0'; /* move the begin pointer to the first char of string */ optr++; tmpbuf = malloc(strlen(optr) + 1); if (tmpbuf == NULL) return (EC_FAILURE); for (ptr = tmpbuf; *optr != '\0'; ptr++, optr++) { /* if escape character, go to next character */ if (*optr == '\\') { optr++; if (*optr == '\0') { /* for exampe, "xxx\" */ free(tmpbuf); return (EC_SYNTAX_ERR); } } *ptr = *optr; } *ptr = '\0'; *valtok = tmpbuf; return (EC_SYNTAX_OK); } /* * Check the PROP syntax * PROP [ ] * supported prop types: void, int, uint, float, string * supported prop access_modes: r, w, rw * For void prop, and are not needed * For string prop, will be set the actual string size if * is 0 */ static int parse_prop(char *line, command_t *command) { char *tok; char *pnametok; int typetok; size_t sizetok; int modetok; char *valtok; char *last; char *endptr; int err; /* get the PROP directive */ tok = strtok_r(line, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); /* get the property name */ pnametok = strtok_r(last, WHITESPACE, &last); if (pnametok == NULL) return (EC_INSUFFICIENT_TOKEN); /* get the type */ tok = strtok_r(last, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); if ((typetok = getpicltype(tok)) < 0) return (EC_SYNTAX_ERR); /* get mode */ tok = strtok_r(last, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); if ((modetok = getpiclmode(tok)) < 0) return (EC_SYNTAX_ERR); if (typetok == PICL_PTYPE_VOID) { /* ignore the rest of arguments */ command->propcmd_valbuf = NULL; command->propcmd_pname = strdup(pnametok); if (command->propcmd_pname == NULL) return (EC_FAILURE); command->propcmd_type = typetok; command->propcmd_accessmode = modetok; command->propcmd_size = 0; command->propcmd_proph = NULL; return (EC_SYNTAX_OK); } /* get size */ tok = strtok_r(last, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); sizetok = (size_t)strtol(tok, &endptr, 0); if (endptr != (tok + strlen(tok))) return (EC_SYNTAX_ERR); /* get val */ if (typetok == PICL_PTYPE_CHARSTRING) { err = get_string_token(last, &valtok); if (err != EC_SYNTAX_OK) return (err); if (sizetok == 0) sizetok = strlen(valtok) + 1; command->propcmd_valbuf = valtok; } else { valtok = strtok_r(last, WHITESPACE, &last); if (valtok == NULL) return (EC_INSUFFICIENT_TOKEN); /* check if more tokens */ tok = strtok_r(last, WHITESPACE, &last); if (tok != NULL) return (EC_SYNTAX_ERR); command->propcmd_valbuf = malloc(sizetok); if (command->propcmd_valbuf == NULL) return (EC_FAILURE); err = validate_size_and_cvt_val(command->propcmd_valbuf, sizetok, typetok, valtok); if (err != EC_SYNTAX_OK) { free(command->propcmd_valbuf); return (err); } } command->propcmd_pname = strdup(pnametok); if (command->propcmd_pname == NULL) return (EC_FAILURE); command->propcmd_type = typetok; command->propcmd_accessmode = modetok; command->propcmd_size = sizetok; command->propcmd_proph = NULL; return (EC_SYNTAX_OK); } /* * Add a property to the row, the row gets added to the node at endrow */ static int add_proph_to_row(command_t *command, picl_prophdl_t proph) { if (command->rowcmd_index >= command->rowcmd_nproph) return (PICL_FAILURE); command->rowcmd_prophs[command->rowcmd_index] = proph; command->rowcmd_index++; return (PICL_SUCCESS); } /* * Process the PROP command and add the specified property under the given * node handle */ static int process_prop(cmdbuf_t *cmds, command_t *command, picl_nodehdl_t nodeh) { ptree_propinfo_t propinfo; picl_prophdl_t proph; int err; /* prop in discarded row */ if (cmds->inside_row_block && cmds->commands[cmds->current_row].rowcmd_nproph == 0) return (PICL_SUCCESS); err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, command->propcmd_type, command->propcmd_accessmode, command->propcmd_size, command->propcmd_pname, NULL, NULL); if (err != PICL_SUCCESS) return (err); err = ptree_create_prop(&propinfo, command->propcmd_valbuf, &proph); if (err != PICL_SUCCESS) return (err); command->propcmd_proph = proph; if (cmds->inside_row_block) { err = add_proph_to_row(&cmds->commands[cmds->current_row], proph); } else { err = ptree_add_prop(nodeh, proph); } return (err); } /* * free refnode_cmd_t */ static void free_refnode(command_t *command) { free(command->refnodecmd_name); free(command->refnodecmd_class); free(command->refnodecmd_dstnode); } /* * Check the REFNODE syntax * * REFNODE with -- if exists, * create node with nodename and piclclass */ static int parse_refnode(char *line, command_t *command) { char *tok; char *dsttok; char *classnm; char *nodenm; char *last; /* get the directive */ tok = strtok_r(line, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); /* get the nodename */ nodenm = strtok_r(last, WHITESPACE, &last); if (nodenm == NULL) return (EC_INSUFFICIENT_TOKEN); /* get the class */ classnm = strtok_r(last, WHITESPACE, &last); if (classnm == NULL) return (EC_INSUFFICIENT_TOKEN); /* get the WITH keyword */ tok = strtok_r(last, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); if (strcasecmp(tok, KEYWORD_WITH_STR) != 0) return (EC_SYNTAX_ERR); /* get the dst node */ dsttok = strtok_r(last, WHITESPACE, &last); if (dsttok == NULL) return (EC_INSUFFICIENT_TOKEN); /* check if more tokens */ tok = strtok_r(last, WHITESPACE, &last); if (tok != NULL) return (EC_SYNTAX_ERR); command->refnodecmd_name = strdup(nodenm); command->refnodecmd_class = strdup(classnm); command->refnodecmd_dstnode = strdup(dsttok); command->refnodecmd_nodeh = NULL; if ((command->refnodecmd_name == NULL) || (command->refnodecmd_class == NULL) || (command->refnodecmd_dstnode == NULL)) return (EC_FAILURE); return (EC_SYNTAX_OK); } /* * Process the REFNODE command */ static int process_refnode(command_t *command, picl_nodehdl_t parh) { picl_nodehdl_t dsth; picl_nodehdl_t nodeh; int err; if ((ptree_get_node_by_path(command->refnodecmd_dstnode, &dsth) == PICL_SUCCESS)) { err = ptree_create_and_add_node(parh, command->refnodecmd_name, command->refnodecmd_class, &nodeh); if (err == PICL_SUCCESS) command->refnodecmd_nodeh = nodeh; return (err); } return (PICL_SUCCESS); } /* * free refprop_cmd_t */ static void free_refprop(command_t *command) { free(command->refpropcmd_pname); free(command->refpropcmd_dstnode); } /* * Check the REFPROP syntax * * REFPROP -- creates a reference property to */ static int parse_refprop(char *line, command_t *command) { char *tok; char *pnametok; char *dsttok; char *last; /* get the REFPROP directive */ tok = strtok_r(line, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); /* get the propname */ pnametok = strtok_r(last, WHITESPACE, &last); if (pnametok == NULL) return (EC_INSUFFICIENT_TOKEN); dsttok = strtok_r(last, WHITESPACE, &last); if (dsttok == NULL) return (EC_INSUFFICIENT_TOKEN); /* check if more tokens */ tok = strtok_r(last, WHITESPACE, &last); if (tok != NULL) return (EC_SYNTAX_ERR); command->refpropcmd_pname = strdup(pnametok); command->refpropcmd_dstnode = strdup(dsttok); command->refpropcmd_proph = NULL; if ((command->refpropcmd_pname == NULL) || (command->refpropcmd_dstnode == NULL)) return (EC_FAILURE); return (EC_SYNTAX_OK); } /* * Process the REFPROP command */ static int process_refprop(cmdbuf_t *cmds, command_t *command, picl_nodehdl_t nodeh) { int err; picl_nodehdl_t dsth; picl_prophdl_t proph; ptree_propinfo_t propinfo; /* refprop in discarded row */ if (cmds->inside_row_block && cmds->commands[cmds->current_row].rowcmd_nproph == 0) return (PICL_SUCCESS); /* try finding the refprop's dstnode */ err = ptree_get_node_by_path(command->refpropcmd_dstnode, &dsth); /* dstnode doesn't exist, return */ if (err != PICL_SUCCESS) return (err); /* dstnode exists, try adding the refprop to nodeh */ err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_REFERENCE, PICL_READ, sizeof (picl_nodehdl_t), command->refpropcmd_pname, NULL, NULL); if (err != PICL_SUCCESS) return (err); err = ptree_create_prop(&propinfo, &dsth, &proph); if (err != PICL_SUCCESS) return (err); command->refpropcmd_proph = proph; if (cmds->inside_row_block) { err = add_proph_to_row(&cmds->commands[cmds->current_row], proph); } else { err = ptree_add_prop(nodeh, proph); } return (err); } /* * free table_cmd_t */ static void free_table(command_t *command) { if (command->tablecmd_tname) free(command->tablecmd_tname); } /* * Check the TABLE syntax * TABLE * */ static int parse_table(char *line, command_t *command) { char *tok = NULL; char *tnametok = NULL; char *last = NULL; /* get the TABLE directive */ tok = strtok_r(line, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); /* get the property name */ tnametok = strtok_r(last, WHITESPACE, &last); if (tnametok == NULL) return (EC_INSUFFICIENT_TOKEN); command->tablecmd_tname = strdup(tnametok); if (command->tablecmd_tname == NULL) return (EC_FAILURE); command->tablecmd_newtbl = 0; command->tablecmd_tblh = NULL; return (EC_SYNTAX_OK); } /* * Process the TABLE command and add the specified property under the given * node handle */ static int process_table(command_t *command, picl_nodehdl_t nodeh) { int err; picl_prophdl_t tblh; picl_prophdl_t proph; ptree_propinfo_t propinfo; /* find if table already exists */ err = ptree_get_prop_by_name(nodeh, command->tablecmd_tname, &tblh); if (err == PICL_SUCCESS) { err = ptree_get_propinfo(tblh, &propinfo); if (err != PICL_SUCCESS) return (err); /* prop with the same name as table? */ if (propinfo.piclinfo.type != PICL_PTYPE_TABLE) return (EC_SYNTAX_ERR); command->tablecmd_newtbl = 0; command->tablecmd_tblh = tblh; return (PICL_SUCCESS); } /* init and create a new table */ err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, PICL_PTYPE_TABLE, PICL_READ|PICL_WRITE, sizeof (picl_prophdl_t), command->tablecmd_tname, NULL, NULL); if (err != PICL_SUCCESS) return (err); err = ptree_create_table(&tblh); if (err != PICL_SUCCESS) return (err); command->tablecmd_newtbl = 1; command->tablecmd_tblh = tblh; err = ptree_create_prop(&propinfo, &tblh, &proph); if (err != PICL_SUCCESS) return (err); err = ptree_add_prop(nodeh, proph); return (err); } /* * Process the ROW command by alloc'ing space to store the prop handles for * the whole row. The number of props in the row gets known while parsing. */ static int process_row(command_t *command) { command->rowcmd_index = 0; command->rowcmd_prophs = malloc(command->rowcmd_nproph * sizeof (picl_prophdl_t)); if (command->rowcmd_prophs == NULL) return (PICL_FAILURE); return (PICL_SUCCESS); } /* * Process the ENDROW command. If a valid row, add the row to the ptree. */ static int process_endrow(cmdbuf_t *cmds) { int err; int i; command_t *curr_row; curr_row = &cmds->commands[cmds->current_row]; /* if nproph == 0, some row prop had problems, don't add */ if (curr_row->rowcmd_nproph == 0) { for (i = 0; i < curr_row->rowcmd_index; i++) { (void) ptree_delete_prop(curr_row->rowcmd_prophs[i]); (void) ptree_destroy_prop(curr_row->rowcmd_prophs[i]); } err = PICL_SUCCESS; } else err = ptree_add_row_to_table( cmds->commands[cmds->current_tbl].tablecmd_tblh, curr_row->rowcmd_nproph, curr_row->rowcmd_prophs); /* let go the space alloc'd in process_row */ free(curr_row->rowcmd_prophs); curr_row->rowcmd_prophs = NULL; return (err); } /* * Check the VERBOSE syntax * VERBOSE */ static int parse_verbose(cmdbuf_t *cmds, char *line, command_t *command) { char *tok; char *level; char *last; char *endptr; int verbose_level; /* get the VERBOSE directive */ tok = strtok_r(line, WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); /* get verbose level */ level = strtok_r(last, WHITESPACE, &last); if (level == NULL) return (EC_INSUFFICIENT_TOKEN); verbose_level = strtol(level, &endptr, 0); if (endptr != (level + strlen(level))) return (EC_SYNTAX_ERR); /* check if more tokens */ tok = strtok_r(last, WHITESPACE, &last); if (tok != NULL) return (EC_SYNTAX_ERR); cmds->verbose = verbose_level; command->verbosecmd_level = verbose_level; return (EC_SYNTAX_OK); } /* * Process the VERBOSE command to set the verbose level */ static int process_verbose(cmdbuf_t *cmds, command_t *command) { cmds->verbose = command->verbosecmd_level; return (PICL_SUCCESS); } /* * parse and tokenize the line */ static int parse_and_tokenize_line(cmdbuf_t *cmds, char *buf, command_t *command) { char rec[RECORD_SIZE_MAX]; char *tok; int err; char *last; int id; (void) strcpy(rec, buf); tok = strtok_r(rec, RECORD_WHITESPACE, &last); if (tok == NULL) return (EC_INSUFFICIENT_TOKEN); id = get_token_id(tok); (void) strcpy(rec, buf); switch (id) { case TOK_VERSION: err = parse_version(cmds, rec); break; case TOK_CLASSPATH: case TOK_NAMEPATH: if (cmds->inside_node_block != 0) return (EC_PATH_ERR); err = parse_path(rec, command); if (err != EC_SYNTAX_OK) return (err); break; case TOK_NODE: /* Check for NODE outside of TABLE, ROW */ if ((cmds->inside_table_block != 0) || (cmds->inside_row_block != 0)) return (EC_SYNTAX_ERR); err = parse_node(rec, command); if (err != EC_SYNTAX_OK) return (err); cmds->inside_node_block++; break; case TOK_ENDNODE: /* Check for ENDNODE outside of TABLE, ROW */ if ((cmds->inside_table_block != 0) || (cmds->inside_row_block != 0)) return (EC_SYNTAX_ERR); cmds->inside_node_block--; err = EC_SYNTAX_OK; break; case TOK_PROP: /* Check if inside TABLE, but not in ROW */ if ((cmds->inside_table_block != 0) && (cmds->inside_row_block == 0)) return (EC_SYNTAX_ERR); err = parse_prop(rec, command); if (err != EC_SYNTAX_OK) return (err); if (cmds->inside_row_block) { cmds->commands[cmds->current_row].rowcmd_nproph++; } break; case TOK_REFNODE: err = parse_refnode(rec, command); if (err != EC_SYNTAX_OK) return (err); break; case TOK_REFPROP: /* Check if inside TABLE, but not in ROW */ if ((cmds->inside_table_block != 0) && (cmds->inside_row_block == 0)) return (EC_SYNTAX_ERR); err = parse_refprop(rec, command); if (err != EC_SYNTAX_OK) return (err); if (cmds->inside_row_block) { cmds->commands[cmds->current_row].rowcmd_nproph++; } break; case TOK_TABLE: /* Table/Row supported in version 1.1 and above */ if (cmds->version_no < (float)SUPPORTED_VERSION_NUM) return (EC_UNSUPPORTED); if (cmds->inside_table_block != 0) return (EC_SYNTAX_ERR); err = parse_table(rec, command); if (err != EC_SYNTAX_OK) return (err); cmds->inside_table_block = 1; break; case TOK_ENDTABLE: /* Check for ENDTABLE before TABLE */ if (cmds->inside_table_block == 0) return (EC_SYNTAX_ERR); cmds->inside_table_block = 0; break; case TOK_ROW: /* Check for ROW outside of TABLE, ROW inside ROW */ if ((cmds->inside_table_block == 0) || (cmds->inside_row_block != 0)) return (EC_SYNTAX_ERR); cmds->inside_row_block = 1; break; case TOK_ENDROW: /* Check for ENDROW outside of TABLE, ENDROW before ROW */ if ((cmds->inside_table_block == 0) || (cmds->inside_row_block == 0)) return (EC_SYNTAX_ERR); else err = EC_SYNTAX_OK; cmds->inside_row_block = 0; /* error if row is empty */ if (cmds->commands[cmds->current_row].rowcmd_nproph <= 0) return (EC_ROW_EMPTY); break; case TOK_VERBOSE: err = parse_verbose(cmds, rec, command); if (err != EC_SYNTAX_OK) return (err); break; default: /* unsupported command */ return (EC_SYNTAX_ERR); } command->type = id; return (EC_SYNTAX_OK); } /* * Check the syntax and save the tokens in the commands buffer */ static int check_line_syntax(cmdbuf_t *cmds, char *buf) { int err; command_t command; (void) memset(&command, 0, sizeof (command_t)); err = parse_and_tokenize_line(cmds, buf, &command); if (err != EC_SYNTAX_OK) return (err); /* * don't add and count version command in the command buffer */ if (command.type == TOK_VERSION) return (EC_SYNTAX_OK); /* * check if the commands buffer has been filled * If it is full, reallocate the buffer. */ if (cmds->count == cmds->allocated) { cmds->commands = realloc(cmds->commands, sizeof (command_t) * (cmds->allocated + PER_ALLOC_COUNT)); if (cmds->commands == NULL) return (EC_FAILURE); cmds->allocated += PER_ALLOC_COUNT; } cmds->commands[cmds->count] = command; /* copy */ /* * make a note of the row/endrow command, to keep track of # of props */ if (command.type == TOK_ROW) cmds->current_row = cmds->count; if (command.type == TOK_ENDROW) cmds->current_row = 0; cmds->count++; return (EC_SYNTAX_OK); } /* * get the line control information * return 1 if it's the line control information, else return 0 */ static int get_line_control_info(char *buf, uint32_t *linenum, char *filename) { char *ptr; char *last; uint32_t num; char *fname; char *endptr; /* skip # and get next string */ ptr = strtok_r(buf + 1, WHITESPACE, &last); if (ptr == NULL) { return (0); } num = strtoul(ptr, &endptr, 0); /* * It's not the line control information */ if (endptr != (ptr + strlen(ptr))) { return (0); } /* * get the filename */ /* get the beginning double quote */ last = strchr(last, '"'); if (last == NULL) return (0); last++; /* get the ending double quote */ fname = strtok_r(last, DOUBLE_QUOTE, &last); if (fname == NULL) return (0); *linenum = num; (void) strlcpy(filename, fname, PATH_MAX); return (1); } /* * check the syntax of the configuration file */ static int check_conffile_syntax(cmdbuf_t *cmds, FILE *fp) { char lbuf[RECORD_SIZE_MAX]; char buf[RECORD_SIZE_MAX]; uint32_t linenum; char cppfile[PATH_MAX] = ""; int err = EC_SYNTAX_OK; linenum = 0; while (fgets(buf, sizeof (buf), fp) != NULL) { /* * get cpp line control information, if any */ if (buf[0] == '#') { if (!get_line_control_info(buf, &linenum, cppfile)) ++linenum; continue; } ++linenum; /* * skip line whose first char is a newline char */ if (buf[0] == '\n') { continue; } if (err == EC_SYNTAX_OK) (void) strlcpy(lbuf, buf, RECORD_SIZE_MAX); else if (strlcat(lbuf, buf, RECORD_SIZE_MAX) >= RECORD_SIZE_MAX) { /* buffer overflow */ err = EC_FAILURE; break; } err = check_line_syntax(cmds, lbuf); if ((err != EC_INSUFFICIENT_TOKEN) && (err != EC_SYNTAX_OK)) break; } if (err != EC_SYNTAX_OK) { if (cmds->verbose) { verbose_log(LOG_ERR, err_msg[err], cmds->fname, cppfile, linenum); } return (err); } /* * check if the version has been set */ if (cmds->version_no > (float)SUPPORTED_VERSION_NUM) { if (cmds->verbose) { verbose_log(LOG_ERR, err_msg[EC_UNSUPPORTED], cmds->fname, cppfile, linenum); } return (EC_UNSUPPORTED); } /* * check if node and endnode command mismatch */ if (cmds->inside_node_block != 0) { if (cmds->verbose) { verbose_log(LOG_ERR, err_msg[EC_NODE_MISMATCH], cmds->fname, cppfile, linenum); } return (EC_NODE_MISMATCH); } /* * check if row and endrow command mismatch */ if (cmds->inside_row_block != 0) { if (cmds->verbose) { verbose_log(LOG_ERR, err_msg[EC_ROW_MISMATCH], cmds->fname, cppfile, linenum); } return (EC_ROW_MISMATCH); } /* * check if table and endtable command mismatch */ if (cmds->inside_table_block != 0) { if (cmds->verbose) { verbose_log(LOG_ERR, err_msg[EC_TABLE_MISMATCH], cmds->fname, cppfile, linenum); } return (EC_TABLE_MISMATCH); } return (EC_SYNTAX_OK); } /* * If classpath/namepath given is not found in the picl tree, * skip the whole blocks until next valid classpath or namepath */ static void skip_to_next_valid_path(cmdbuf_t *cmds, int starting_index, picl_nodehdl_t *parent, int *last_processed_index) { int err; int index; for (index = starting_index; index < cmds->count; ++index) { switch (cmds->commands[index].type) { case TOK_CLASSPATH: case TOK_NAMEPATH: err = process_path(&cmds->commands[index], parent); if (err == PICL_SUCCESS) { *last_processed_index = index; return; } default: /* skipped this line */ break; } } /* reach last command */ *last_processed_index = cmds->count - 1; } /* * Process the command buffer and return last command index and the new head of * the handle list */ static int process_commands(cmdbuf_t *cmds, int starting_index, picl_nodehdl_t parent, int *last_processed_index) { int err; int index; picl_nodehdl_t rooth; picl_nodehdl_t nodeh; command_t *commands = cmds->commands; for (index = starting_index; index < cmds->count; ++index) { switch (commands[index].type) { case TOK_CLASSPATH: case TOK_NAMEPATH: err = process_path(&commands[index], &rooth); if (err != PICL_SUCCESS) { index++; (void) skip_to_next_valid_path(cmds, index, &rooth, &index); } parent = rooth; continue; case TOK_NODE: err = process_node(&commands[index], parent, &nodeh); if (err == PICL_SUCCESS) { index++; err = process_commands(cmds, index, nodeh, &index); } break; case TOK_ENDNODE: *last_processed_index = index; return (PICL_SUCCESS); case TOK_PROP: err = process_prop(cmds, &commands[index], parent); break; case TOK_REFPROP: err = process_refprop(cmds, &commands[index], parent); /* no reference node */ if (err == PICL_NOTNODE) { err = PICL_SUCCESS; /* discard prop */ /* discard row by setting nproph = 0 */ if (cmds->inside_row_block) cmds->commands[cmds->current_row] .rowcmd_nproph = 0; } break; case TOK_REFNODE: err = process_refnode(&commands[index], parent); break; case TOK_TABLE: cmds->inside_table_block = 1; err = process_table(&commands[index], parent); cmds->current_tbl = index; break; case TOK_ENDTABLE: cmds->inside_table_block = 0; cmds->current_tbl = 0; break; case TOK_ROW: cmds->inside_row_block = 1; err = process_row(&commands[index]); cmds->current_row = index; break; case TOK_ENDROW: err = process_endrow(cmds); cmds->inside_row_block = 0; cmds->current_row = 0; break; case TOK_VERBOSE: err = process_verbose(cmds, &commands[index]); break; default: /* won't reach here */ err = PICL_FAILURE; break; } if ((err != PICL_SUCCESS) && (err != PICL_PROPEXISTS)) { *last_processed_index = index; return (err); } } /* reach last command */ *last_processed_index = cmds->count - 1; return (PICL_SUCCESS); } /* * clean up the commands buffer */ static void clean_up(cmdbuf_t *cmds) { int cmd_index; for (cmd_index = 0; cmd_index < cmds->count; cmd_index++) { switch (cmds->commands[cmd_index].type) { case TOK_CLASSPATH: case TOK_NAMEPATH: free_path(&cmds->commands[cmd_index]); break; case TOK_NODE: free_node(&cmds->commands[cmd_index]); break; case TOK_PROP: free_prop(&cmds->commands[cmd_index]); break; case TOK_REFPROP: free_refprop(&cmds->commands[cmd_index]); break; case TOK_REFNODE: free_refnode(&cmds->commands[cmd_index]); break; case TOK_TABLE: free_table(&cmds->commands[cmd_index]); break; case TOK_ENDTABLE: case TOK_ROW: case TOK_ENDROW: case TOK_ENDNODE: case TOK_VERBOSE: default: break; } } if (cmds->commands) free(cmds->commands); } /* * Parse the configuration file and create nodes/properties under nh * * It checks the syntax first. If there is any syntax error, * it returns 1 and won't continue processing the file to add nodes or props. * * If any error happens during command processing, all nodes * and properties just created will be deleted, i.e. undo * commands which have been processed. It returns 1. * * If success, return 0. */ int picld_pluginutil_parse_config_file(picl_nodehdl_t nh, const char *filename) { FILE *ifp; int last_processed_index; int err; cmdbuf_t *cmds; /* set correct locale for use inside pluginutil */ setlocale(LC_ALL, "C"); /* * Initialize the command buffer */ cmds = malloc(sizeof (*cmds)); if (cmds == NULL) { setlocale(LC_ALL, ""); return (1); } memset(cmds, 0, sizeof (cmdbuf_t)); cmds->fname = filename; ifp = fopen(filename, "r"); if (ifp == NULL) { setlocale(LC_ALL, ""); free(cmds); return (1); } /* * check the syntax of the configuration file */ err = check_conffile_syntax(cmds, ifp); (void) fclose(ifp); if (err != EC_SYNTAX_OK) { clean_up(cmds); free(cmds); setlocale(LC_ALL, ""); return (1); } /* * Process the commands */ err = process_commands(cmds, STARTING_INDEX, nh, &last_processed_index); /* * If any PICL error, remove the newly created node/prop * handles from the PICL tree. */ if (err != PICL_SUCCESS) { undo_commands(cmds, last_processed_index); if (cmds->verbose) verbose_log(LOG_ERR, err_msg[EC_PICL_ERR], filename, err); } clean_up(cmds); free(cmds); /* reset the locale */ setlocale(LC_ALL, ""); return ((err == PICL_SUCCESS) ? 0 : 1); }