1 /* 2 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 * OF SUCH DAMAGE. 26 * 27 * This file is part of the lwIP TCP/IP stack. 28 * 29 * Author: Martin Hentschel <info@cl-soft.de> 30 * 31 */ 32 33 using System; 34 using System.Collections.Generic; 35 using System.Text; 36 using CCodeGeneration; 37 38 namespace LwipSnmpCodeGeneration 39 { 40 public class SnmpTableNode: SnmpScalarAggregationNode 41 { 42 private readonly List<SnmpScalarNode> cellNodes = new List<SnmpScalarNode>(); 43 private readonly List<SnmpScalarNode> indexNodes = new List<SnmpScalarNode>(); 44 private string augmentedTableRow = null; 45 46 SnmpTableNode(SnmpTreeNode parentNode)47 public SnmpTableNode(SnmpTreeNode parentNode) 48 : base(parentNode) 49 { 50 } 51 52 public List<SnmpScalarNode> CellNodes 53 { 54 get { return cellNodes; } 55 } 56 57 public List<SnmpScalarNode> IndexNodes 58 { 59 get { return indexNodes; } 60 } 61 62 public string AugmentedTableRow 63 { 64 get { return this.augmentedTableRow; } 65 set { this.augmentedTableRow = value; } 66 } 67 68 public override string FullNodeName 69 { 70 get 71 { 72 string result = this.Name.ToLowerInvariant(); 73 if (!result.Contains("table")) 74 { 75 result += "_table"; 76 } 77 78 return result; 79 } 80 } 81 82 protected override IEnumerable<SnmpScalarNode> AggregatedScalarNodes 83 { 84 get { return this.cellNodes; } 85 } 86 GenerateCode(MibCFile mibFile)87 public override void GenerateCode(MibCFile mibFile) 88 { 89 FunctionDeclaration getInstanceMethodDecl = new FunctionDeclaration(this.FullNodeName + LwipDefs.FnctSuffix_GetInstance, isStatic: true); 90 getInstanceMethodDecl.Parameter.Add(new VariableType("column", LwipDefs.Vt_U32, "*", ConstType.Value)); 91 getInstanceMethodDecl.Parameter.Add(new VariableType("row_oid", LwipDefs.Vt_U32, "*", ConstType.Value)); 92 getInstanceMethodDecl.Parameter.Add(new VariableType("row_oid_len", LwipDefs.Vt_U8, "")); 93 getInstanceMethodDecl.Parameter.Add(new VariableType("cell_instance", LwipDefs.Vt_StNodeInstance, "*")); 94 getInstanceMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_Snmp_err); 95 mibFile.Declarations.Add(getInstanceMethodDecl); 96 97 Function getInstanceMethod = Function.FromDeclaration(getInstanceMethodDecl); 98 GenerateGetInstanceMethodCode(getInstanceMethod); 99 mibFile.Implementation.Add(getInstanceMethod); 100 101 102 FunctionDeclaration getNextInstanceMethodDecl = new FunctionDeclaration(this.FullNodeName + LwipDefs.FnctSuffix_GetNextInstance, isStatic: true); 103 getNextInstanceMethodDecl.Parameter.Add(new VariableType("column", LwipDefs.Vt_U32, "*", ConstType.Value)); 104 getNextInstanceMethodDecl.Parameter.Add(new VariableType("row_oid", LwipDefs.Vt_StObjectId, "*")); 105 getNextInstanceMethodDecl.Parameter.Add(new VariableType("cell_instance", LwipDefs.Vt_StNodeInstance, "*")); 106 getNextInstanceMethodDecl.ReturnType = new VariableType(null, LwipDefs.Vt_Snmp_err); 107 mibFile.Declarations.Add(getNextInstanceMethodDecl); 108 109 Function getNextInstanceMethod = Function.FromDeclaration(getNextInstanceMethodDecl); 110 GenerateGetNextInstanceMethodCode(getNextInstanceMethod); 111 mibFile.Implementation.Add(getNextInstanceMethod); 112 113 114 VariableType instanceType = new VariableType("cell_instance", LwipDefs.Vt_StNodeInstance, "*"); 115 GenerateAggregatedCode( 116 mibFile, 117 instanceType, 118 String.Format("SNMP_TABLE_GET_COLUMN_FROM_OID({0}->instance_oid.id)", instanceType.Name)); 119 120 121 #region create and add column/table definitions 122 123 StringBuilder colDefs = new StringBuilder(); 124 foreach (SnmpScalarNode colNode in this.cellNodes) 125 { 126 colDefs.AppendFormat(" {{{0}, {1}, {2}}}, /* {3} */ \n", 127 colNode.Oid, 128 LwipDefs.GetAsn1DefForSnmpDataType(colNode.DataType), 129 LwipDefs.GetLwipDefForSnmpAccessMode(colNode.AccessMode), 130 colNode.Name); 131 } 132 if (colDefs.Length > 0) 133 { 134 colDefs.Length--; 135 } 136 137 VariableDeclaration colDefsDecl = new VariableDeclaration( 138 new VariableType(this.FullNodeName + "_columns", LwipDefs.Vt_StTableColumnDef, null, ConstType.Value, String.Empty), 139 "{\n" + colDefs + "\n}", 140 isStatic: true); 141 142 mibFile.Declarations.Add(colDefsDecl); 143 144 string nodeInitialization = String.Format("SNMP_TABLE_CREATE({0}, {1}, {2}, {3}, {4}, {5}, {6})", 145 this.Oid, 146 colDefsDecl.Type.Name, 147 getInstanceMethodDecl.Name, getNextInstanceMethodDecl.Name, 148 (this.GetMethodRequired) ? this.GetMethodName : LwipDefs.Null, 149 (this.TestMethodRequired) ? this.TestMethodName : LwipDefs.Null, 150 (this.SetMethodRequired) ? this.SetMethodName : LwipDefs.Null 151 ); 152 153 mibFile.Declarations.Add(new VariableDeclaration( 154 new VariableType(this.FullNodeName, LwipDefs.Vt_StTableNode, null, ConstType.Value), 155 nodeInitialization, 156 isStatic: true)); 157 158 #endregion 159 } 160 GenerateGetInstanceMethodCode(Function getInstanceMethod)161 protected virtual void GenerateGetInstanceMethodCode(Function getInstanceMethod) 162 { 163 VariableDeclaration returnValue = new VariableDeclaration((VariableType)getInstanceMethod.ReturnType.Clone(), LwipDefs.Def_ErrorCode_NoSuchInstance); 164 returnValue.Type.Name = "err"; 165 getInstanceMethod.Declarations.Add(returnValue); 166 167 int instanceOidLength = 0; 168 StringBuilder indexColumns = new StringBuilder(); 169 foreach (SnmpScalarNode indexNode in this.indexNodes) 170 { 171 if (instanceOidLength >= 0) 172 { 173 if (indexNode.OidRepresentationLen >= 0) 174 { 175 instanceOidLength += indexNode.OidRepresentationLen; 176 } 177 else 178 { 179 // at least one index column has a variable length -> we cannot perform a static check 180 instanceOidLength = -1; 181 } 182 } 183 184 indexColumns.AppendFormat( 185 " {0} ({1}, OID length = {2})\n", 186 indexNode.Name, 187 indexNode.DataType, 188 (indexNode.OidRepresentationLen >= 0) ? indexNode.OidRepresentationLen.ToString() : "variable"); 189 } 190 if (indexColumns.Length > 0) 191 { 192 indexColumns.Length--; 193 194 getInstanceMethod.Declarations.Insert(0, new Comment(String.Format( 195 "The instance OID of this table consists of following (index) column(s):\n{0}", 196 indexColumns))); 197 } 198 199 string augmentsHint = ""; 200 if (!String.IsNullOrWhiteSpace(this.augmentedTableRow)) 201 { 202 augmentsHint = String.Format( 203 "This table augments table '{0}'! Index columns therefore belong to table '{0}'!\n" + 204 "You may simply call the '*{1}' method of this table.\n\n", 205 (this.augmentedTableRow.ToLowerInvariant().EndsWith("entry")) ? this.augmentedTableRow.Substring(0, this.augmentedTableRow.Length-5) : this.augmentedTableRow, 206 LwipDefs.FnctSuffix_GetInstance); 207 } 208 209 CodeContainerBase ccb = getInstanceMethod; 210 if (instanceOidLength > 0) 211 { 212 IfThenElse ite = new IfThenElse(String.Format("{0} == {1}", getInstanceMethod.Parameter[2].Name, instanceOidLength)); 213 getInstanceMethod.AddElement(ite); 214 ccb = ite; 215 } 216 217 ccb.AddCodeFormat("LWIP_UNUSED_ARG({0});", getInstanceMethod.Parameter[0].Name); 218 ccb.AddCodeFormat("LWIP_UNUSED_ARG({0});", getInstanceMethod.Parameter[1].Name); 219 if (instanceOidLength <= 0) 220 { 221 ccb.AddCodeFormat("LWIP_UNUSED_ARG({0});", getInstanceMethod.Parameter[2].Name); 222 } 223 ccb.AddCodeFormat("LWIP_UNUSED_ARG({0});", getInstanceMethod.Parameter[3].Name); 224 225 ccb.AddElement(new Comment(String.Format( 226 "TODO: check if '{0}'/'{1}' params contain a valid instance oid for a row\n" + 227 "If so, set '{2} = {3};'\n\n" + 228 "snmp_oid_* methods may be used for easier processing of oid\n\n" + 229 "{4}" + 230 "In order to avoid decoding OID a second time in subsequent get_value/set_test/set_value methods,\n" + 231 "you may store an arbitrary value (like a pointer to target value object) in '{5}->reference'/'{5}->reference_len'.\n" + 232 "But be aware that not always a subsequent method is called -> Do NOT allocate memory here and try to release it in subsequent methods!\n\n" + 233 "You also may replace function pointers in '{5}' param for get/test/set methods which contain the default values from table definition,\n" + 234 "in order to provide special methods, for the currently processed cell. Changed pointers are only valid for current request.", 235 getInstanceMethod.Parameter[1].Name, 236 getInstanceMethod.Parameter[2].Name, 237 returnValue.Type.Name, 238 LwipDefs.Def_ErrorCode_Ok, 239 augmentsHint, 240 getInstanceMethod.Parameter[3].Name 241 ))); 242 243 getInstanceMethod.AddCodeFormat("return {0};", returnValue.Type.Name); 244 } 245 GenerateGetNextInstanceMethodCode(Function getNextInstanceMethod)246 protected virtual void GenerateGetNextInstanceMethodCode(Function getNextInstanceMethod) 247 { 248 getNextInstanceMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getNextInstanceMethod.Parameter[0].Name); 249 getNextInstanceMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getNextInstanceMethod.Parameter[1].Name); 250 getNextInstanceMethod.AddCodeFormat("LWIP_UNUSED_ARG({0});", getNextInstanceMethod.Parameter[2].Name); 251 252 VariableDeclaration returnValue = new VariableDeclaration((VariableType)getNextInstanceMethod.ReturnType.Clone(), LwipDefs.Def_ErrorCode_NoSuchInstance); 253 returnValue.Type.Name = "err"; 254 getNextInstanceMethod.Declarations.Add(returnValue); 255 256 StringBuilder indexColumns = new StringBuilder(); 257 foreach (SnmpScalarNode indexNode in this.indexNodes) 258 { 259 indexColumns.AppendFormat( 260 " {0} ({1}, OID length = {2})\n", 261 indexNode.Name, 262 indexNode.DataType, 263 (indexNode.OidRepresentationLen >= 0) ? indexNode.OidRepresentationLen.ToString() : "variable"); 264 } 265 if (indexColumns.Length > 0) 266 { 267 indexColumns.Length--; 268 269 getNextInstanceMethod.Declarations.Insert(0, new Comment(String.Format( 270 "The instance OID of this table consists of following (index) column(s):\n{0}", 271 indexColumns))); 272 } 273 274 string augmentsHint = ""; 275 if (!String.IsNullOrWhiteSpace(this.augmentedTableRow)) 276 { 277 augmentsHint = String.Format( 278 "This table augments table '{0}'! Index columns therefore belong to table '{0}'!\n" + 279 "You may simply call the '*{1}' method of this table.\n\n", 280 (this.augmentedTableRow.ToLowerInvariant().EndsWith("entry")) ? this.augmentedTableRow.Substring(0, this.augmentedTableRow.Length-5) : this.augmentedTableRow, 281 LwipDefs.FnctSuffix_GetNextInstance); 282 } 283 284 getNextInstanceMethod.AddElement(new Comment(String.Format( 285 "TODO: analyze '{0}->id'/'{0}->len' and return the subsequent row instance\n" + 286 "Be aware that '{0}->id'/'{0}->len' must not point to a valid instance or have correct instance length.\n" + 287 "If '{0}->len' is 0, return the first instance. If '{0}->len' is longer than expected, cut superfluous OID parts.\n" + 288 "If a valid next instance is found, store it in '{0}->id'/'{0}->len' and set '{1} = {2};'\n\n" + 289 "snmp_oid_* methods may be used for easier processing of oid\n\n" + 290 "{3}" + 291 "In order to avoid decoding OID a second time in subsequent get_value/set_test/set_value methods,\n" + 292 "you may store an arbitrary value (like a pointer to target value object) in '{4}->reference'/'{4}->reference_len'.\n" + 293 "But be aware that not always a subsequent method is called -> Do NOT allocate memory here and try to release it in subsequent methods!\n\n" + 294 "You also may replace function pointers in '{4}' param for get/test/set methods which contain the default values from table definition,\n" + 295 "in order to provide special methods, for the currently processed cell. Changed pointers are only valid for current request.", 296 getNextInstanceMethod.Parameter[1].Name, 297 returnValue.Type.Name, 298 LwipDefs.Def_ErrorCode_Ok, 299 augmentsHint, 300 getNextInstanceMethod.Parameter[2].Name 301 ))); 302 303 getNextInstanceMethod.AddElement(new Comment(String.Format( 304 "For easier processing and getting the next instance, you may use the 'snmp_next_oid_*' enumerator.\n" + 305 "Simply pass all known instance OID's to it and it returns the next valid one:\n\n" + 306 "{0} state;\n" + 307 "{1} result_buf;\n" + 308 "snmp_next_oid_init(&state, {2}->id, {2}->len, result_buf.id, SNMP_MAX_OBJ_ID_LEN);\n" + 309 "while ({{not all instances passed}}) {{\n" + 310 " {1} test_oid;\n" + 311 " {{fill test_oid to create instance oid for next instance}}\n" + 312 " snmp_next_oid_check(&state, test_oid.id, test_oid.len, {{target_data_ptr}});\n" + 313 "}}\n" + 314 "if(state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {{\n" + 315 " snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);\n" + 316 " {3}->reference.ptr = state.reference; //==target_data_ptr, for usage in subsequent get/test/set\n" + 317 " {4} = {5};\n" + 318 "}}" 319 , 320 LwipDefs.Vt_StNextOidState, 321 LwipDefs.Vt_StObjectId, 322 getNextInstanceMethod.Parameter[1].Name, 323 getNextInstanceMethod.Parameter[2].Name, 324 returnValue.Type.Name, 325 LwipDefs.Def_ErrorCode_Ok 326 ))); 327 328 getNextInstanceMethod.AddCodeFormat("return {0};", returnValue.Type.Name); 329 } 330 331 } 332 } 333