• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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