Loading...
1#!/usr/bin/env python3
2# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
3
4import argparse
5import collections
6import filecmp
7import os
8import re
9import shutil
10import tempfile
11import yaml
12
13from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
14
15
16def c_upper(name):
17 return name.upper().replace('-', '_')
18
19
20def c_lower(name):
21 return name.lower().replace('-', '_')
22
23
24def limit_to_number(name):
25 """
26 Turn a string limit like u32-max or s64-min into its numerical value
27 """
28 if name[0] == 'u' and name.endswith('-min'):
29 return 0
30 width = int(name[1:-4])
31 if name[0] == 's':
32 width -= 1
33 value = (1 << width) - 1
34 if name[0] == 's' and name.endswith('-min'):
35 value = -value - 1
36 return value
37
38
39class BaseNlLib:
40 def get_family_id(self):
41 return 'ys->family_id'
42
43 def parse_cb_run(self, cb, data, is_dump=False, indent=1):
44 ind = '\n\t\t' + '\t' * indent + ' '
45 if is_dump:
46 return f"mnl_cb_run2(ys->rx_buf, len, 0, 0, {cb}, {data},{ind}ynl_cb_array, NLMSG_MIN_TYPE)"
47 else:
48 return f"mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,{ind}{cb}, {data},{ind}" + \
49 "ynl_cb_array, NLMSG_MIN_TYPE)"
50
51
52class Type(SpecAttr):
53 def __init__(self, family, attr_set, attr, value):
54 super().__init__(family, attr_set, attr, value)
55
56 self.attr = attr
57 self.attr_set = attr_set
58 self.type = attr['type']
59 self.checks = attr.get('checks', {})
60
61 self.request = False
62 self.reply = False
63
64 if 'len' in attr:
65 self.len = attr['len']
66
67 if 'nested-attributes' in attr:
68 self.nested_attrs = attr['nested-attributes']
69 if self.nested_attrs == family.name:
70 self.nested_render_name = c_lower(f"{family.name}")
71 else:
72 self.nested_render_name = c_lower(f"{family.name}_{self.nested_attrs}")
73
74 if self.nested_attrs in self.family.consts:
75 self.nested_struct_type = 'struct ' + self.nested_render_name + '_'
76 else:
77 self.nested_struct_type = 'struct ' + self.nested_render_name
78
79 self.c_name = c_lower(self.name)
80 if self.c_name in _C_KW:
81 self.c_name += '_'
82
83 # Added by resolve():
84 self.enum_name = None
85 delattr(self, "enum_name")
86
87 def get_limit(self, limit, default=None):
88 value = self.checks.get(limit, default)
89 if value is None:
90 return value
91 if not isinstance(value, int):
92 value = limit_to_number(value)
93 return value
94
95 def resolve(self):
96 if 'name-prefix' in self.attr:
97 enum_name = f"{self.attr['name-prefix']}{self.name}"
98 else:
99 enum_name = f"{self.attr_set.name_prefix}{self.name}"
100 self.enum_name = c_upper(enum_name)
101
102 def is_multi_val(self):
103 return None
104
105 def is_scalar(self):
106 return self.type in {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
107
108 def is_recursive(self):
109 return False
110
111 def is_recursive_for_op(self, ri):
112 return self.is_recursive() and not ri.op
113
114 def presence_type(self):
115 return 'bit'
116
117 def presence_member(self, space, type_filter):
118 if self.presence_type() != type_filter:
119 return
120
121 if self.presence_type() == 'bit':
122 pfx = '__' if space == 'user' else ''
123 return f"{pfx}u32 {self.c_name}:1;"
124
125 if self.presence_type() == 'len':
126 pfx = '__' if space == 'user' else ''
127 return f"{pfx}u32 {self.c_name}_len;"
128
129 def _complex_member_type(self, ri):
130 return None
131
132 def free_needs_iter(self):
133 return False
134
135 def free(self, ri, var, ref):
136 if self.is_multi_val() or self.presence_type() == 'len':
137 ri.cw.p(f'free({var}->{ref}{self.c_name});')
138
139 def arg_member(self, ri):
140 member = self._complex_member_type(ri)
141 if member:
142 arg = [member + ' *' + self.c_name]
143 if self.presence_type() == 'count':
144 arg += ['unsigned int n_' + self.c_name]
145 return arg
146 raise Exception(f"Struct member not implemented for class type {self.type}")
147
148 def struct_member(self, ri):
149 if self.is_multi_val():
150 ri.cw.p(f"unsigned int n_{self.c_name};")
151 member = self._complex_member_type(ri)
152 if member:
153 ptr = '*' if self.is_multi_val() else ''
154 if self.is_recursive_for_op(ri):
155 ptr = '*'
156 ri.cw.p(f"{member} {ptr}{self.c_name};")
157 return
158 members = self.arg_member(ri)
159 for one in members:
160 ri.cw.p(one + ';')
161
162 def _attr_policy(self, policy):
163 return '{ .type = ' + policy + ', }'
164
165 def attr_policy(self, cw):
166 policy = c_upper('nla-' + self.attr['type'])
167
168 spec = self._attr_policy(policy)
169 cw.p(f"\t[{self.enum_name}] = {spec},")
170
171 def _mnl_type(self):
172 # mnl does not have helpers for signed integer types
173 # turn signed type into unsigned
174 # this only makes sense for scalar types
175 t = self.type
176 if t[0] == 's':
177 t = 'u' + t[1:]
178 return t
179
180 def _attr_typol(self):
181 raise Exception(f"Type policy not implemented for class type {self.type}")
182
183 def attr_typol(self, cw):
184 typol = self._attr_typol()
185 cw.p(f'[{self.enum_name}] = {"{"} .name = "{self.name}", {typol}{"}"},')
186
187 def _attr_put_line(self, ri, var, line):
188 if self.presence_type() == 'bit':
189 ri.cw.p(f"if ({var}->_present.{self.c_name})")
190 elif self.presence_type() == 'len':
191 ri.cw.p(f"if ({var}->_present.{self.c_name}_len)")
192 ri.cw.p(f"{line};")
193
194 def _attr_put_simple(self, ri, var, put_type):
195 line = f"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name})"
196 self._attr_put_line(ri, var, line)
197
198 def attr_put(self, ri, var):
199 raise Exception(f"Put not implemented for class type {self.type}")
200
201 def _attr_get(self, ri, var):
202 raise Exception(f"Attr get not implemented for class type {self.type}")
203
204 def attr_get(self, ri, var, first):
205 lines, init_lines, local_vars = self._attr_get(ri, var)
206 if type(lines) is str:
207 lines = [lines]
208 if type(init_lines) is str:
209 init_lines = [init_lines]
210
211 kw = 'if' if first else 'else if'
212 ri.cw.block_start(line=f"{kw} (type == {self.enum_name})")
213 if local_vars:
214 for local in local_vars:
215 ri.cw.p(local)
216 ri.cw.nl()
217
218 if not self.is_multi_val():
219 ri.cw.p("if (ynl_attr_validate(yarg, attr))")
220 ri.cw.p("return MNL_CB_ERROR;")
221 if self.presence_type() == 'bit':
222 ri.cw.p(f"{var}->_present.{self.c_name} = 1;")
223
224 if init_lines:
225 ri.cw.nl()
226 for line in init_lines:
227 ri.cw.p(line)
228
229 for line in lines:
230 ri.cw.p(line)
231 ri.cw.block_end()
232 return True
233
234 def _setter_lines(self, ri, member, presence):
235 raise Exception(f"Setter not implemented for class type {self.type}")
236
237 def setter(self, ri, space, direction, deref=False, ref=None):
238 ref = (ref if ref else []) + [self.c_name]
239 var = "req"
240 member = f"{var}->{'.'.join(ref)}"
241
242 code = []
243 presence = ''
244 for i in range(0, len(ref)):
245 presence = f"{var}->{'.'.join(ref[:i] + [''])}_present.{ref[i]}"
246 if self.presence_type() == 'bit':
247 code.append(presence + ' = 1;')
248 code += self._setter_lines(ri, member, presence)
249
250 func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
251 free = bool([x for x in code if 'free(' in x])
252 alloc = bool([x for x in code if 'alloc(' in x])
253 if free and not alloc:
254 func_name = '__' + func_name
255 ri.cw.write_func('static inline void', func_name, body=code,
256 args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
257
258
259class TypeUnused(Type):
260 def presence_type(self):
261 return ''
262
263 def arg_member(self, ri):
264 return []
265
266 def _attr_get(self, ri, var):
267 return ['return MNL_CB_ERROR;'], None, None
268
269 def _attr_typol(self):
270 return '.type = YNL_PT_REJECT, '
271
272 def attr_policy(self, cw):
273 pass
274
275 def attr_put(self, ri, var):
276 pass
277
278 def attr_get(self, ri, var, first):
279 pass
280
281 def setter(self, ri, space, direction, deref=False, ref=None):
282 pass
283
284
285class TypePad(Type):
286 def presence_type(self):
287 return ''
288
289 def arg_member(self, ri):
290 return []
291
292 def _attr_typol(self):
293 return '.type = YNL_PT_IGNORE, '
294
295 def attr_put(self, ri, var):
296 pass
297
298 def attr_get(self, ri, var, first):
299 pass
300
301 def attr_policy(self, cw):
302 pass
303
304 def setter(self, ri, space, direction, deref=False, ref=None):
305 pass
306
307
308class TypeScalar(Type):
309 def __init__(self, family, attr_set, attr, value):
310 super().__init__(family, attr_set, attr, value)
311
312 self.byte_order_comment = ''
313 if 'byte-order' in attr:
314 self.byte_order_comment = f" /* {attr['byte-order']} */"
315
316 if 'enum' in self.attr:
317 enum = self.family.consts[self.attr['enum']]
318 low, high = enum.value_range()
319 if 'min' not in self.checks:
320 if low != 0 or self.type[0] == 's':
321 self.checks['min'] = low
322 if 'max' not in self.checks:
323 self.checks['max'] = high
324
325 if 'min' in self.checks and 'max' in self.checks:
326 if self.get_limit('min') > self.get_limit('max'):
327 raise Exception(f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}')
328 self.checks['range'] = True
329
330 low = min(self.get_limit('min', 0), self.get_limit('max', 0))
331 high = max(self.get_limit('min', 0), self.get_limit('max', 0))
332 if low < 0 and self.type[0] == 'u':
333 raise Exception(f'Invalid limit for "{self.name}" negative limit for unsigned type')
334 if low < -32768 or high > 32767:
335 self.checks['full-range'] = True
336
337 # Added by resolve():
338 self.is_bitfield = None
339 delattr(self, "is_bitfield")
340 self.type_name = None
341 delattr(self, "type_name")
342
343 def resolve(self):
344 self.resolve_up(super())
345
346 if 'enum-as-flags' in self.attr and self.attr['enum-as-flags']:
347 self.is_bitfield = True
348 elif 'enum' in self.attr:
349 self.is_bitfield = self.family.consts[self.attr['enum']]['type'] == 'flags'
350 else:
351 self.is_bitfield = False
352
353 if not self.is_bitfield and 'enum' in self.attr:
354 self.type_name = self.family.consts[self.attr['enum']].user_type
355 elif self.is_auto_scalar:
356 self.type_name = '__' + self.type[0] + '64'
357 else:
358 self.type_name = '__' + self.type
359
360 def mnl_type(self):
361 return self._mnl_type()
362
363 def _attr_policy(self, policy):
364 if 'flags-mask' in self.checks or self.is_bitfield:
365 if self.is_bitfield:
366 enum = self.family.consts[self.attr['enum']]
367 mask = enum.get_mask(as_flags=True)
368 else:
369 flags = self.family.consts[self.checks['flags-mask']]
370 flag_cnt = len(flags['entries'])
371 mask = (1 << flag_cnt) - 1
372 return f"NLA_POLICY_MASK({policy}, 0x{mask:x})"
373 elif 'full-range' in self.checks:
374 return f"NLA_POLICY_FULL_RANGE({policy}, &{c_lower(self.enum_name)}_range)"
375 elif 'range' in self.checks:
376 return f"NLA_POLICY_RANGE({policy}, {self.get_limit('min')}, {self.get_limit('max')})"
377 elif 'min' in self.checks:
378 return f"NLA_POLICY_MIN({policy}, {self.get_limit('min')})"
379 elif 'max' in self.checks:
380 return f"NLA_POLICY_MAX({policy}, {self.get_limit('max')})"
381 return super()._attr_policy(policy)
382
383 def _attr_typol(self):
384 return f'.type = YNL_PT_U{c_upper(self.type[1:])}, '
385
386 def arg_member(self, ri):
387 return [f'{self.type_name} {self.c_name}{self.byte_order_comment}']
388
389 def attr_put(self, ri, var):
390 self._attr_put_simple(ri, var, self.mnl_type())
391
392 def _attr_get(self, ri, var):
393 return f"{var}->{self.c_name} = mnl_attr_get_{self.mnl_type()}(attr);", None, None
394
395 def _setter_lines(self, ri, member, presence):
396 return [f"{member} = {self.c_name};"]
397
398
399class TypeFlag(Type):
400 def arg_member(self, ri):
401 return []
402
403 def _attr_typol(self):
404 return '.type = YNL_PT_FLAG, '
405
406 def attr_put(self, ri, var):
407 self._attr_put_line(ri, var, f"mnl_attr_put(nlh, {self.enum_name}, 0, NULL)")
408
409 def _attr_get(self, ri, var):
410 return [], None, None
411
412 def _setter_lines(self, ri, member, presence):
413 return []
414
415
416class TypeString(Type):
417 def arg_member(self, ri):
418 return [f"const char *{self.c_name}"]
419
420 def presence_type(self):
421 return 'len'
422
423 def struct_member(self, ri):
424 ri.cw.p(f"char *{self.c_name};")
425
426 def _attr_typol(self):
427 return f'.type = YNL_PT_NUL_STR, '
428
429 def _attr_policy(self, policy):
430 if 'exact-len' in self.checks:
431 mem = 'NLA_POLICY_EXACT_LEN(' + str(self.checks['exact-len']) + ')'
432 else:
433 mem = '{ .type = ' + policy
434 if 'max-len' in self.checks:
435 mem += ', .len = ' + str(self.get_limit('max-len'))
436 mem += ', }'
437 return mem
438
439 def attr_policy(self, cw):
440 if self.checks.get('unterminated-ok', False):
441 policy = 'NLA_STRING'
442 else:
443 policy = 'NLA_NUL_STRING'
444
445 spec = self._attr_policy(policy)
446 cw.p(f"\t[{self.enum_name}] = {spec},")
447
448 def attr_put(self, ri, var):
449 self._attr_put_simple(ri, var, 'strz')
450
451 def _attr_get(self, ri, var):
452 len_mem = var + '->_present.' + self.c_name + '_len'
453 return [f"{len_mem} = len;",
454 f"{var}->{self.c_name} = malloc(len + 1);",
455 f"memcpy({var}->{self.c_name}, mnl_attr_get_str(attr), len);",
456 f"{var}->{self.c_name}[len] = 0;"], \
457 ['len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr));'], \
458 ['unsigned int len;']
459
460 def _setter_lines(self, ri, member, presence):
461 return [f"free({member});",
462 f"{presence}_len = strlen({self.c_name});",
463 f"{member} = malloc({presence}_len + 1);",
464 f'memcpy({member}, {self.c_name}, {presence}_len);',
465 f'{member}[{presence}_len] = 0;']
466
467
468class TypeBinary(Type):
469 def arg_member(self, ri):
470 return [f"const void *{self.c_name}", 'size_t len']
471
472 def presence_type(self):
473 return 'len'
474
475 def struct_member(self, ri):
476 ri.cw.p(f"void *{self.c_name};")
477
478 def _attr_typol(self):
479 return f'.type = YNL_PT_BINARY,'
480
481 def _attr_policy(self, policy):
482 if 'exact-len' in self.checks:
483 mem = 'NLA_POLICY_EXACT_LEN(' + str(self.checks['exact-len']) + ')'
484 else:
485 mem = '{ '
486 if len(self.checks) == 1 and 'min-len' in self.checks:
487 mem += '.len = ' + str(self.get_limit('min-len'))
488 elif len(self.checks) == 0:
489 mem += '.type = NLA_BINARY'
490 else:
491 raise Exception('One or more of binary type checks not implemented, yet')
492 mem += ', }'
493 return mem
494
495 def attr_put(self, ri, var):
496 self._attr_put_line(ri, var, f"mnl_attr_put(nlh, {self.enum_name}, " +
497 f"{var}->_present.{self.c_name}_len, {var}->{self.c_name})")
498
499 def _attr_get(self, ri, var):
500 len_mem = var + '->_present.' + self.c_name + '_len'
501 return [f"{len_mem} = len;",
502 f"{var}->{self.c_name} = malloc(len);",
503 f"memcpy({var}->{self.c_name}, mnl_attr_get_payload(attr), len);"], \
504 ['len = mnl_attr_get_payload_len(attr);'], \
505 ['unsigned int len;']
506
507 def _setter_lines(self, ri, member, presence):
508 return [f"free({member});",
509 f"{presence}_len = len;",
510 f"{member} = malloc({presence}_len);",
511 f'memcpy({member}, {self.c_name}, {presence}_len);']
512
513
514class TypeBitfield32(Type):
515 def _complex_member_type(self, ri):
516 return "struct nla_bitfield32"
517
518 def _attr_typol(self):
519 return f'.type = YNL_PT_BITFIELD32, '
520
521 def _attr_policy(self, policy):
522 if not 'enum' in self.attr:
523 raise Exception('Enum required for bitfield32 attr')
524 enum = self.family.consts[self.attr['enum']]
525 mask = enum.get_mask(as_flags=True)
526 return f"NLA_POLICY_BITFIELD32({mask})"
527
528 def attr_put(self, ri, var):
529 line = f"mnl_attr_put(nlh, {self.enum_name}, sizeof(struct nla_bitfield32), &{var}->{self.c_name})"
530 self._attr_put_line(ri, var, line)
531
532 def _attr_get(self, ri, var):
533 return f"memcpy(&{var}->{self.c_name}, mnl_attr_get_payload(attr), sizeof(struct nla_bitfield32));", None, None
534
535 def _setter_lines(self, ri, member, presence):
536 return [f"memcpy(&{member}, {self.c_name}, sizeof(struct nla_bitfield32));"]
537
538
539class TypeNest(Type):
540 def is_recursive(self):
541 return self.family.pure_nested_structs[self.nested_attrs].recursive
542
543 def _complex_member_type(self, ri):
544 return self.nested_struct_type
545
546 def free(self, ri, var, ref):
547 at = '&'
548 if self.is_recursive_for_op(ri):
549 at = ''
550 ri.cw.p(f'if ({var}->{ref}{self.c_name})')
551 ri.cw.p(f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});')
552
553 def _attr_typol(self):
554 return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
555
556 def _attr_policy(self, policy):
557 return 'NLA_POLICY_NESTED(' + self.nested_render_name + '_nl_policy)'
558
559 def attr_put(self, ri, var):
560 at = '' if self.is_recursive_for_op(ri) else '&'
561 self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
562 f"{self.enum_name}, {at}{var}->{self.c_name})")
563
564 def _attr_get(self, ri, var):
565 get_lines = [f"if ({self.nested_render_name}_parse(&parg, attr))",
566 "return MNL_CB_ERROR;"]
567 init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
568 f"parg.data = &{var}->{self.c_name};"]
569 return get_lines, init_lines, None
570
571 def setter(self, ri, space, direction, deref=False, ref=None):
572 ref = (ref if ref else []) + [self.c_name]
573
574 for _, attr in ri.family.pure_nested_structs[self.nested_attrs].member_list():
575 if attr.is_recursive():
576 continue
577 attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref)
578
579
580class TypeMultiAttr(Type):
581 def __init__(self, family, attr_set, attr, value, base_type):
582 super().__init__(family, attr_set, attr, value)
583
584 self.base_type = base_type
585
586 def is_multi_val(self):
587 return True
588
589 def presence_type(self):
590 return 'count'
591
592 def mnl_type(self):
593 return self._mnl_type()
594
595 def _complex_member_type(self, ri):
596 if 'type' not in self.attr or self.attr['type'] == 'nest':
597 return self.nested_struct_type
598 elif self.attr['type'] in scalars:
599 scalar_pfx = '__' if ri.ku_space == 'user' else ''
600 return scalar_pfx + self.attr['type']
601 else:
602 raise Exception(f"Sub-type {self.attr['type']} not supported yet")
603
604 def free_needs_iter(self):
605 return 'type' not in self.attr or self.attr['type'] == 'nest'
606
607 def free(self, ri, var, ref):
608 if self.attr['type'] in scalars:
609 ri.cw.p(f"free({var}->{ref}{self.c_name});")
610 elif 'type' not in self.attr or self.attr['type'] == 'nest':
611 ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
612 ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
613 ri.cw.p(f"free({var}->{ref}{self.c_name});")
614 else:
615 raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
616
617 def _attr_policy(self, policy):
618 return self.base_type._attr_policy(policy)
619
620 def _attr_typol(self):
621 return self.base_type._attr_typol()
622
623 def _attr_get(self, ri, var):
624 return f'n_{self.c_name}++;', None, None
625
626 def attr_put(self, ri, var):
627 if self.attr['type'] in scalars:
628 put_type = self.mnl_type()
629 ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
630 ri.cw.p(f"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name}[i]);")
631 elif 'type' not in self.attr or self.attr['type'] == 'nest':
632 ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
633 self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
634 f"{self.enum_name}, &{var}->{self.c_name}[i])")
635 else:
636 raise Exception(f"Put of MultiAttr sub-type {self.attr['type']} not supported yet")
637
638 def _setter_lines(self, ri, member, presence):
639 # For multi-attr we have a count, not presence, hack up the presence
640 presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name
641 return [f"free({member});",
642 f"{member} = {self.c_name};",
643 f"{presence} = n_{self.c_name};"]
644
645
646class TypeArrayNest(Type):
647 def is_multi_val(self):
648 return True
649
650 def presence_type(self):
651 return 'count'
652
653 def _complex_member_type(self, ri):
654 if 'sub-type' not in self.attr or self.attr['sub-type'] == 'nest':
655 return self.nested_struct_type
656 elif self.attr['sub-type'] in scalars:
657 scalar_pfx = '__' if ri.ku_space == 'user' else ''
658 return scalar_pfx + self.attr['sub-type']
659 else:
660 raise Exception(f"Sub-type {self.attr['sub-type']} not supported yet")
661
662 def _attr_typol(self):
663 return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
664
665 def _attr_get(self, ri, var):
666 local_vars = ['const struct nlattr *attr2;']
667 get_lines = [f'attr_{self.c_name} = attr;',
668 'mnl_attr_for_each_nested(attr2, attr)',
669 f'\t{var}->n_{self.c_name}++;']
670 return get_lines, None, local_vars
671
672
673class TypeNestTypeValue(Type):
674 def _complex_member_type(self, ri):
675 return self.nested_struct_type
676
677 def _attr_typol(self):
678 return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
679
680 def _attr_get(self, ri, var):
681 prev = 'attr'
682 tv_args = ''
683 get_lines = []
684 local_vars = []
685 init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
686 f"parg.data = &{var}->{self.c_name};"]
687 if 'type-value' in self.attr:
688 tv_names = [c_lower(x) for x in self.attr["type-value"]]
689 local_vars += [f'const struct nlattr *attr_{", *attr_".join(tv_names)};']
690 local_vars += [f'__u32 {", ".join(tv_names)};']
691 for level in self.attr["type-value"]:
692 level = c_lower(level)
693 get_lines += [f'attr_{level} = mnl_attr_get_payload({prev});']
694 get_lines += [f'{level} = mnl_attr_get_type(attr_{level});']
695 prev = 'attr_' + level
696
697 tv_args = f", {', '.join(tv_names)}"
698
699 get_lines += [f"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"]
700 return get_lines, init_lines, local_vars
701
702
703class Struct:
704 def __init__(self, family, space_name, type_list=None, inherited=None):
705 self.family = family
706 self.space_name = space_name
707 self.attr_set = family.attr_sets[space_name]
708 # Use list to catch comparisons with empty sets
709 self._inherited = inherited if inherited is not None else []
710 self.inherited = []
711
712 self.nested = type_list is None
713 if family.name == c_lower(space_name):
714 self.render_name = c_lower(family.name)
715 else:
716 self.render_name = c_lower(family.name + '-' + space_name)
717 self.struct_name = 'struct ' + self.render_name
718 if self.nested and space_name in family.consts:
719 self.struct_name += '_'
720 self.ptr_name = self.struct_name + ' *'
721 # All attr sets this one contains, directly or multiple levels down
722 self.child_nests = set()
723
724 self.request = False
725 self.reply = False
726 self.recursive = False
727
728 self.attr_list = []
729 self.attrs = dict()
730 if type_list is not None:
731 for t in type_list:
732 self.attr_list.append((t, self.attr_set[t]),)
733 else:
734 for t in self.attr_set:
735 self.attr_list.append((t, self.attr_set[t]),)
736
737 max_val = 0
738 self.attr_max_val = None
739 for name, attr in self.attr_list:
740 if attr.value >= max_val:
741 max_val = attr.value
742 self.attr_max_val = attr
743 self.attrs[name] = attr
744
745 def __iter__(self):
746 yield from self.attrs
747
748 def __getitem__(self, key):
749 return self.attrs[key]
750
751 def member_list(self):
752 return self.attr_list
753
754 def set_inherited(self, new_inherited):
755 if self._inherited != new_inherited:
756 raise Exception("Inheriting different members not supported")
757 self.inherited = [c_lower(x) for x in sorted(self._inherited)]
758
759
760class EnumEntry(SpecEnumEntry):
761 def __init__(self, enum_set, yaml, prev, value_start):
762 super().__init__(enum_set, yaml, prev, value_start)
763
764 if prev:
765 self.value_change = (self.value != prev.value + 1)
766 else:
767 self.value_change = (self.value != 0)
768 self.value_change = self.value_change or self.enum_set['type'] == 'flags'
769
770 # Added by resolve:
771 self.c_name = None
772 delattr(self, "c_name")
773
774 def resolve(self):
775 self.resolve_up(super())
776
777 self.c_name = c_upper(self.enum_set.value_pfx + self.name)
778
779
780class EnumSet(SpecEnumSet):
781 def __init__(self, family, yaml):
782 self.render_name = c_lower(family.name + '-' + yaml['name'])
783
784 if 'enum-name' in yaml:
785 if yaml['enum-name']:
786 self.enum_name = 'enum ' + c_lower(yaml['enum-name'])
787 self.user_type = self.enum_name
788 else:
789 self.enum_name = None
790 else:
791 self.enum_name = 'enum ' + self.render_name
792
793 if self.enum_name:
794 self.user_type = self.enum_name
795 else:
796 self.user_type = 'int'
797
798 self.value_pfx = yaml.get('name-prefix', f"{family.name}-{yaml['name']}-")
799
800 super().__init__(family, yaml)
801
802 def new_entry(self, entry, prev_entry, value_start):
803 return EnumEntry(self, entry, prev_entry, value_start)
804
805 def value_range(self):
806 low = min([x.value for x in self.entries.values()])
807 high = max([x.value for x in self.entries.values()])
808
809 if high - low + 1 != len(self.entries):
810 raise Exception("Can't get value range for a noncontiguous enum")
811
812 return low, high
813
814
815class AttrSet(SpecAttrSet):
816 def __init__(self, family, yaml):
817 super().__init__(family, yaml)
818
819 if self.subset_of is None:
820 if 'name-prefix' in yaml:
821 pfx = yaml['name-prefix']
822 elif self.name == family.name:
823 pfx = family.name + '-a-'
824 else:
825 pfx = f"{family.name}-a-{self.name}-"
826 self.name_prefix = c_upper(pfx)
827 self.max_name = c_upper(self.yaml.get('attr-max-name', f"{self.name_prefix}max"))
828 self.cnt_name = c_upper(self.yaml.get('attr-cnt-name', f"__{self.name_prefix}max"))
829 else:
830 self.name_prefix = family.attr_sets[self.subset_of].name_prefix
831 self.max_name = family.attr_sets[self.subset_of].max_name
832 self.cnt_name = family.attr_sets[self.subset_of].cnt_name
833
834 # Added by resolve:
835 self.c_name = None
836 delattr(self, "c_name")
837
838 def resolve(self):
839 self.c_name = c_lower(self.name)
840 if self.c_name in _C_KW:
841 self.c_name += '_'
842 if self.c_name == self.family.c_name:
843 self.c_name = ''
844
845 def new_attr(self, elem, value):
846 if elem['type'] in scalars:
847 t = TypeScalar(self.family, self, elem, value)
848 elif elem['type'] == 'unused':
849 t = TypeUnused(self.family, self, elem, value)
850 elif elem['type'] == 'pad':
851 t = TypePad(self.family, self, elem, value)
852 elif elem['type'] == 'flag':
853 t = TypeFlag(self.family, self, elem, value)
854 elif elem['type'] == 'string':
855 t = TypeString(self.family, self, elem, value)
856 elif elem['type'] == 'binary':
857 t = TypeBinary(self.family, self, elem, value)
858 elif elem['type'] == 'bitfield32':
859 t = TypeBitfield32(self.family, self, elem, value)
860 elif elem['type'] == 'nest':
861 t = TypeNest(self.family, self, elem, value)
862 elif elem['type'] == 'array-nest':
863 t = TypeArrayNest(self.family, self, elem, value)
864 elif elem['type'] == 'nest-type-value':
865 t = TypeNestTypeValue(self.family, self, elem, value)
866 else:
867 raise Exception(f"No typed class for type {elem['type']}")
868
869 if 'multi-attr' in elem and elem['multi-attr']:
870 t = TypeMultiAttr(self.family, self, elem, value, t)
871
872 return t
873
874
875class Operation(SpecOperation):
876 def __init__(self, family, yaml, req_value, rsp_value):
877 super().__init__(family, yaml, req_value, rsp_value)
878
879 self.render_name = c_lower(family.name + '_' + self.name)
880
881 self.dual_policy = ('do' in yaml and 'request' in yaml['do']) and \
882 ('dump' in yaml and 'request' in yaml['dump'])
883
884 self.has_ntf = False
885
886 # Added by resolve:
887 self.enum_name = None
888 delattr(self, "enum_name")
889
890 def resolve(self):
891 self.resolve_up(super())
892
893 if not self.is_async:
894 self.enum_name = self.family.op_prefix + c_upper(self.name)
895 else:
896 self.enum_name = self.family.async_op_prefix + c_upper(self.name)
897
898 def mark_has_ntf(self):
899 self.has_ntf = True
900
901
902class Family(SpecFamily):
903 def __init__(self, file_name, exclude_ops):
904 # Added by resolve:
905 self.c_name = None
906 delattr(self, "c_name")
907 self.op_prefix = None
908 delattr(self, "op_prefix")
909 self.async_op_prefix = None
910 delattr(self, "async_op_prefix")
911 self.mcgrps = None
912 delattr(self, "mcgrps")
913 self.consts = None
914 delattr(self, "consts")
915 self.hooks = None
916 delattr(self, "hooks")
917
918 super().__init__(file_name, exclude_ops=exclude_ops)
919
920 self.fam_key = c_upper(self.yaml.get('c-family-name', self.yaml["name"] + '_FAMILY_NAME'))
921 self.ver_key = c_upper(self.yaml.get('c-version-name', self.yaml["name"] + '_FAMILY_VERSION'))
922
923 if 'definitions' not in self.yaml:
924 self.yaml['definitions'] = []
925
926 if 'uapi-header' in self.yaml:
927 self.uapi_header = self.yaml['uapi-header']
928 else:
929 self.uapi_header = f"linux/{self.name}.h"
930 if self.uapi_header.startswith("linux/") and self.uapi_header.endswith('.h'):
931 self.uapi_header_name = self.uapi_header[6:-2]
932 else:
933 self.uapi_header_name = self.name
934
935 def resolve(self):
936 self.resolve_up(super())
937
938 if self.yaml.get('protocol', 'genetlink') not in {'genetlink', 'genetlink-c', 'genetlink-legacy'}:
939 raise Exception("Codegen only supported for genetlink")
940
941 self.c_name = c_lower(self.name)
942 if 'name-prefix' in self.yaml['operations']:
943 self.op_prefix = c_upper(self.yaml['operations']['name-prefix'])
944 else:
945 self.op_prefix = c_upper(self.yaml['name'] + '-cmd-')
946 if 'async-prefix' in self.yaml['operations']:
947 self.async_op_prefix = c_upper(self.yaml['operations']['async-prefix'])
948 else:
949 self.async_op_prefix = self.op_prefix
950
951 self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
952
953 self.hooks = dict()
954 for when in ['pre', 'post']:
955 self.hooks[when] = dict()
956 for op_mode in ['do', 'dump']:
957 self.hooks[when][op_mode] = dict()
958 self.hooks[when][op_mode]['set'] = set()
959 self.hooks[when][op_mode]['list'] = []
960
961 # dict space-name -> 'request': set(attrs), 'reply': set(attrs)
962 self.root_sets = dict()
963 # dict space-name -> set('request', 'reply')
964 self.pure_nested_structs = dict()
965
966 self._mark_notify()
967 self._mock_up_events()
968
969 self._load_root_sets()
970 self._load_nested_sets()
971 self._load_attr_use()
972 self._load_hooks()
973
974 self.kernel_policy = self.yaml.get('kernel-policy', 'split')
975 if self.kernel_policy == 'global':
976 self._load_global_policy()
977
978 def new_enum(self, elem):
979 return EnumSet(self, elem)
980
981 def new_attr_set(self, elem):
982 return AttrSet(self, elem)
983
984 def new_operation(self, elem, req_value, rsp_value):
985 return Operation(self, elem, req_value, rsp_value)
986
987 def _mark_notify(self):
988 for op in self.msgs.values():
989 if 'notify' in op:
990 self.ops[op['notify']].mark_has_ntf()
991
992 # Fake a 'do' equivalent of all events, so that we can render their response parsing
993 def _mock_up_events(self):
994 for op in self.yaml['operations']['list']:
995 if 'event' in op:
996 op['do'] = {
997 'reply': {
998 'attributes': op['event']['attributes']
999 }
1000 }
1001
1002 def _load_root_sets(self):
1003 for op_name, op in self.msgs.items():
1004 if 'attribute-set' not in op:
1005 continue
1006
1007 req_attrs = set()
1008 rsp_attrs = set()
1009 for op_mode in ['do', 'dump']:
1010 if op_mode in op and 'request' in op[op_mode]:
1011 req_attrs.update(set(op[op_mode]['request']['attributes']))
1012 if op_mode in op and 'reply' in op[op_mode]:
1013 rsp_attrs.update(set(op[op_mode]['reply']['attributes']))
1014 if 'event' in op:
1015 rsp_attrs.update(set(op['event']['attributes']))
1016
1017 if op['attribute-set'] not in self.root_sets:
1018 self.root_sets[op['attribute-set']] = {'request': req_attrs, 'reply': rsp_attrs}
1019 else:
1020 self.root_sets[op['attribute-set']]['request'].update(req_attrs)
1021 self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs)
1022
1023 def _sort_pure_types(self):
1024 # Try to reorder according to dependencies
1025 pns_key_list = list(self.pure_nested_structs.keys())
1026 pns_key_seen = set()
1027 rounds = len(pns_key_list) ** 2 # it's basically bubble sort
1028 for _ in range(rounds):
1029 if len(pns_key_list) == 0:
1030 break
1031 name = pns_key_list.pop(0)
1032 finished = True
1033 for _, spec in self.attr_sets[name].items():
1034 if 'nested-attributes' in spec:
1035 nested = spec['nested-attributes']
1036 # If the unknown nest we hit is recursive it's fine, it'll be a pointer
1037 if self.pure_nested_structs[nested].recursive:
1038 continue
1039 if nested not in pns_key_seen:
1040 # Dicts are sorted, this will make struct last
1041 struct = self.pure_nested_structs.pop(name)
1042 self.pure_nested_structs[name] = struct
1043 finished = False
1044 break
1045 if finished:
1046 pns_key_seen.add(name)
1047 else:
1048 pns_key_list.append(name)
1049
1050 def _load_nested_sets(self):
1051 attr_set_queue = list(self.root_sets.keys())
1052 attr_set_seen = set(self.root_sets.keys())
1053
1054 while len(attr_set_queue):
1055 a_set = attr_set_queue.pop(0)
1056 for attr, spec in self.attr_sets[a_set].items():
1057 if 'nested-attributes' not in spec:
1058 continue
1059
1060 nested = spec['nested-attributes']
1061 if nested not in attr_set_seen:
1062 attr_set_queue.append(nested)
1063 attr_set_seen.add(nested)
1064
1065 inherit = set()
1066 if nested not in self.root_sets:
1067 if nested not in self.pure_nested_structs:
1068 self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit)
1069 else:
1070 raise Exception(f'Using attr set as root and nested not supported - {nested}')
1071
1072 if 'type-value' in spec:
1073 if nested in self.root_sets:
1074 raise Exception("Inheriting members to a space used as root not supported")
1075 inherit.update(set(spec['type-value']))
1076 elif spec['type'] == 'array-nest':
1077 inherit.add('idx')
1078 self.pure_nested_structs[nested].set_inherited(inherit)
1079
1080 for root_set, rs_members in self.root_sets.items():
1081 for attr, spec in self.attr_sets[root_set].items():
1082 if 'nested-attributes' in spec:
1083 nested = spec['nested-attributes']
1084 if attr in rs_members['request']:
1085 self.pure_nested_structs[nested].request = True
1086 if attr in rs_members['reply']:
1087 self.pure_nested_structs[nested].reply = True
1088
1089 self._sort_pure_types()
1090
1091 # Propagate the request / reply / recursive
1092 for attr_set, struct in reversed(self.pure_nested_structs.items()):
1093 for _, spec in self.attr_sets[attr_set].items():
1094 if 'nested-attributes' in spec:
1095 child_name = spec['nested-attributes']
1096 struct.child_nests.add(child_name)
1097 child = self.pure_nested_structs.get(child_name)
1098 if child:
1099 if not child.recursive:
1100 struct.child_nests.update(child.child_nests)
1101 child.request |= struct.request
1102 child.reply |= struct.reply
1103 if attr_set in struct.child_nests:
1104 struct.recursive = True
1105
1106 self._sort_pure_types()
1107
1108 def _load_attr_use(self):
1109 for _, struct in self.pure_nested_structs.items():
1110 if struct.request:
1111 for _, arg in struct.member_list():
1112 arg.request = True
1113 if struct.reply:
1114 for _, arg in struct.member_list():
1115 arg.reply = True
1116
1117 for root_set, rs_members in self.root_sets.items():
1118 for attr, spec in self.attr_sets[root_set].items():
1119 if attr in rs_members['request']:
1120 spec.request = True
1121 if attr in rs_members['reply']:
1122 spec.reply = True
1123
1124 def _load_global_policy(self):
1125 global_set = set()
1126 attr_set_name = None
1127 for op_name, op in self.ops.items():
1128 if not op:
1129 continue
1130 if 'attribute-set' not in op:
1131 continue
1132
1133 if attr_set_name is None:
1134 attr_set_name = op['attribute-set']
1135 if attr_set_name != op['attribute-set']:
1136 raise Exception('For a global policy all ops must use the same set')
1137
1138 for op_mode in ['do', 'dump']:
1139 if op_mode in op:
1140 req = op[op_mode].get('request')
1141 if req:
1142 global_set.update(req.get('attributes', []))
1143
1144 self.global_policy = []
1145 self.global_policy_set = attr_set_name
1146 for attr in self.attr_sets[attr_set_name]:
1147 if attr in global_set:
1148 self.global_policy.append(attr)
1149
1150 def _load_hooks(self):
1151 for op in self.ops.values():
1152 for op_mode in ['do', 'dump']:
1153 if op_mode not in op:
1154 continue
1155 for when in ['pre', 'post']:
1156 if when not in op[op_mode]:
1157 continue
1158 name = op[op_mode][when]
1159 if name in self.hooks[when][op_mode]['set']:
1160 continue
1161 self.hooks[when][op_mode]['set'].add(name)
1162 self.hooks[when][op_mode]['list'].append(name)
1163
1164
1165class RenderInfo:
1166 def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None):
1167 self.family = family
1168 self.nl = cw.nlib
1169 self.ku_space = ku_space
1170 self.op_mode = op_mode
1171 self.op = op
1172
1173 self.fixed_hdr = None
1174 if op and op.fixed_header:
1175 self.fixed_hdr = 'struct ' + c_lower(op.fixed_header)
1176
1177 # 'do' and 'dump' response parsing is identical
1178 self.type_consistent = True
1179 if op_mode != 'do' and 'dump' in op:
1180 if 'do' in op:
1181 if ('reply' in op['do']) != ('reply' in op["dump"]):
1182 self.type_consistent = False
1183 elif 'reply' in op['do'] and op["do"]["reply"] != op["dump"]["reply"]:
1184 self.type_consistent = False
1185 else:
1186 self.type_consistent = False
1187
1188 self.attr_set = attr_set
1189 if not self.attr_set:
1190 self.attr_set = op['attribute-set']
1191
1192 self.type_name_conflict = False
1193 if op:
1194 self.type_name = c_lower(op.name)
1195 else:
1196 self.type_name = c_lower(attr_set)
1197 if attr_set in family.consts:
1198 self.type_name_conflict = True
1199
1200 self.cw = cw
1201
1202 self.struct = dict()
1203 if op_mode == 'notify':
1204 op_mode = 'do'
1205 for op_dir in ['request', 'reply']:
1206 if op:
1207 type_list = []
1208 if op_dir in op[op_mode]:
1209 type_list = op[op_mode][op_dir]['attributes']
1210 self.struct[op_dir] = Struct(family, self.attr_set, type_list=type_list)
1211 if op_mode == 'event':
1212 self.struct['reply'] = Struct(family, self.attr_set, type_list=op['event']['attributes'])
1213
1214
1215class CodeWriter:
1216 def __init__(self, nlib, out_file=None, overwrite=True):
1217 self.nlib = nlib
1218 self._overwrite = overwrite
1219
1220 self._nl = False
1221 self._block_end = False
1222 self._silent_block = False
1223 self._ind = 0
1224 self._ifdef_block = None
1225 if out_file is None:
1226 self._out = os.sys.stdout
1227 else:
1228 self._out = tempfile.NamedTemporaryFile('w+')
1229 self._out_file = out_file
1230
1231 def __del__(self):
1232 self.close_out_file()
1233
1234 def close_out_file(self):
1235 if self._out == os.sys.stdout:
1236 return
1237 # Avoid modifying the file if contents didn't change
1238 self._out.flush()
1239 if not self._overwrite and os.path.isfile(self._out_file):
1240 if filecmp.cmp(self._out.name, self._out_file, shallow=False):
1241 return
1242 with open(self._out_file, 'w+') as out_file:
1243 self._out.seek(0)
1244 shutil.copyfileobj(self._out, out_file)
1245 self._out.close()
1246 self._out = os.sys.stdout
1247
1248 @classmethod
1249 def _is_cond(cls, line):
1250 return line.startswith('if') or line.startswith('while') or line.startswith('for')
1251
1252 def p(self, line, add_ind=0):
1253 if self._block_end:
1254 self._block_end = False
1255 if line.startswith('else'):
1256 line = '} ' + line
1257 else:
1258 self._out.write('\t' * self._ind + '}\n')
1259
1260 if self._nl:
1261 self._out.write('\n')
1262 self._nl = False
1263
1264 ind = self._ind
1265 if line[-1] == ':':
1266 ind -= 1
1267 if self._silent_block:
1268 ind += 1
1269 self._silent_block = line.endswith(')') and CodeWriter._is_cond(line)
1270 if line[0] == '#':
1271 ind = 0
1272 if add_ind:
1273 ind += add_ind
1274 self._out.write('\t' * ind + line + '\n')
1275
1276 def nl(self):
1277 self._nl = True
1278
1279 def block_start(self, line=''):
1280 if line:
1281 line = line + ' '
1282 self.p(line + '{')
1283 self._ind += 1
1284
1285 def block_end(self, line=''):
1286 if line and line[0] not in {';', ','}:
1287 line = ' ' + line
1288 self._ind -= 1
1289 self._nl = False
1290 if not line:
1291 # Delay printing closing bracket in case "else" comes next
1292 if self._block_end:
1293 self._out.write('\t' * (self._ind + 1) + '}\n')
1294 self._block_end = True
1295 else:
1296 self.p('}' + line)
1297
1298 def write_doc_line(self, doc, indent=True):
1299 words = doc.split()
1300 line = ' *'
1301 for word in words:
1302 if len(line) + len(word) >= 79:
1303 self.p(line)
1304 line = ' *'
1305 if indent:
1306 line += ' '
1307 line += ' ' + word
1308 self.p(line)
1309
1310 def write_func_prot(self, qual_ret, name, args=None, doc=None, suffix=''):
1311 if not args:
1312 args = ['void']
1313
1314 if doc:
1315 self.p('/*')
1316 self.p(' * ' + doc)
1317 self.p(' */')
1318
1319 oneline = qual_ret
1320 if qual_ret[-1] != '*':
1321 oneline += ' '
1322 oneline += f"{name}({', '.join(args)}){suffix}"
1323
1324 if len(oneline) < 80:
1325 self.p(oneline)
1326 return
1327
1328 v = qual_ret
1329 if len(v) > 3:
1330 self.p(v)
1331 v = ''
1332 elif qual_ret[-1] != '*':
1333 v += ' '
1334 v += name + '('
1335 ind = '\t' * (len(v) // 8) + ' ' * (len(v) % 8)
1336 delta_ind = len(v) - len(ind)
1337 v += args[0]
1338 i = 1
1339 while i < len(args):
1340 next_len = len(v) + len(args[i])
1341 if v[0] == '\t':
1342 next_len += delta_ind
1343 if next_len > 76:
1344 self.p(v + ',')
1345 v = ind
1346 else:
1347 v += ', '
1348 v += args[i]
1349 i += 1
1350 self.p(v + ')' + suffix)
1351
1352 def write_func_lvar(self, local_vars):
1353 if not local_vars:
1354 return
1355
1356 if type(local_vars) is str:
1357 local_vars = [local_vars]
1358
1359 local_vars.sort(key=len, reverse=True)
1360 for var in local_vars:
1361 self.p(var)
1362 self.nl()
1363
1364 def write_func(self, qual_ret, name, body, args=None, local_vars=None):
1365 self.write_func_prot(qual_ret=qual_ret, name=name, args=args)
1366 self.write_func_lvar(local_vars=local_vars)
1367
1368 self.block_start()
1369 for line in body:
1370 self.p(line)
1371 self.block_end()
1372
1373 def writes_defines(self, defines):
1374 longest = 0
1375 for define in defines:
1376 if len(define[0]) > longest:
1377 longest = len(define[0])
1378 longest = ((longest + 8) // 8) * 8
1379 for define in defines:
1380 line = '#define ' + define[0]
1381 line += '\t' * ((longest - len(define[0]) + 7) // 8)
1382 if type(define[1]) is int:
1383 line += str(define[1])
1384 elif type(define[1]) is str:
1385 line += '"' + define[1] + '"'
1386 self.p(line)
1387
1388 def write_struct_init(self, members):
1389 longest = max([len(x[0]) for x in members])
1390 longest += 1 # because we prepend a .
1391 longest = ((longest + 8) // 8) * 8
1392 for one in members:
1393 line = '.' + one[0]
1394 line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8)
1395 line += '= ' + str(one[1]) + ','
1396 self.p(line)
1397
1398 def ifdef_block(self, config):
1399 config_option = None
1400 if config:
1401 config_option = 'CONFIG_' + c_upper(config)
1402 if self._ifdef_block == config_option:
1403 return
1404
1405 if self._ifdef_block:
1406 self.p('#endif /* ' + self._ifdef_block + ' */')
1407 if config_option:
1408 self.p('#ifdef ' + config_option)
1409 self._ifdef_block = config_option
1410
1411
1412scalars = {'u8', 'u16', 'u32', 'u64', 's32', 's64', 'uint', 'sint'}
1413
1414direction_to_suffix = {
1415 'reply': '_rsp',
1416 'request': '_req',
1417 '': ''
1418}
1419
1420op_mode_to_wrapper = {
1421 'do': '',
1422 'dump': '_list',
1423 'notify': '_ntf',
1424 'event': '',
1425}
1426
1427_C_KW = {
1428 'auto',
1429 'bool',
1430 'break',
1431 'case',
1432 'char',
1433 'const',
1434 'continue',
1435 'default',
1436 'do',
1437 'double',
1438 'else',
1439 'enum',
1440 'extern',
1441 'float',
1442 'for',
1443 'goto',
1444 'if',
1445 'inline',
1446 'int',
1447 'long',
1448 'register',
1449 'return',
1450 'short',
1451 'signed',
1452 'sizeof',
1453 'static',
1454 'struct',
1455 'switch',
1456 'typedef',
1457 'union',
1458 'unsigned',
1459 'void',
1460 'volatile',
1461 'while'
1462}
1463
1464
1465def rdir(direction):
1466 if direction == 'reply':
1467 return 'request'
1468 if direction == 'request':
1469 return 'reply'
1470 return direction
1471
1472
1473def op_prefix(ri, direction, deref=False):
1474 suffix = f"_{ri.type_name}"
1475
1476 if not ri.op_mode or ri.op_mode == 'do':
1477 suffix += f"{direction_to_suffix[direction]}"
1478 else:
1479 if direction == 'request':
1480 suffix += '_req_dump'
1481 else:
1482 if ri.type_consistent:
1483 if deref:
1484 suffix += f"{direction_to_suffix[direction]}"
1485 else:
1486 suffix += op_mode_to_wrapper[ri.op_mode]
1487 else:
1488 suffix += '_rsp'
1489 suffix += '_dump' if deref else '_list'
1490
1491 return f"{ri.family.c_name}{suffix}"
1492
1493
1494def type_name(ri, direction, deref=False):
1495 return f"struct {op_prefix(ri, direction, deref=deref)}"
1496
1497
1498def print_prototype(ri, direction, terminate=True, doc=None):
1499 suffix = ';' if terminate else ''
1500
1501 fname = ri.op.render_name
1502 if ri.op_mode == 'dump':
1503 fname += '_dump'
1504
1505 args = ['struct ynl_sock *ys']
1506 if 'request' in ri.op[ri.op_mode]:
1507 args.append(f"{type_name(ri, direction)} *" + f"{direction_to_suffix[direction][1:]}")
1508
1509 ret = 'int'
1510 if 'reply' in ri.op[ri.op_mode]:
1511 ret = f"{type_name(ri, rdir(direction))} *"
1512
1513 ri.cw.write_func_prot(ret, fname, args, doc=doc, suffix=suffix)
1514
1515
1516def print_req_prototype(ri):
1517 print_prototype(ri, "request", doc=ri.op['doc'])
1518
1519
1520def print_dump_prototype(ri):
1521 print_prototype(ri, "request")
1522
1523
1524def put_typol_fwd(cw, struct):
1525 cw.p(f'extern struct ynl_policy_nest {struct.render_name}_nest;')
1526
1527
1528def put_typol(cw, struct):
1529 type_max = struct.attr_set.max_name
1530 cw.block_start(line=f'struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
1531
1532 for _, arg in struct.member_list():
1533 arg.attr_typol(cw)
1534
1535 cw.block_end(line=';')
1536 cw.nl()
1537
1538 cw.block_start(line=f'struct ynl_policy_nest {struct.render_name}_nest =')
1539 cw.p(f'.max_attr = {type_max},')
1540 cw.p(f'.table = {struct.render_name}_policy,')
1541 cw.block_end(line=';')
1542 cw.nl()
1543
1544
1545def _put_enum_to_str_helper(cw, render_name, map_name, arg_name, enum=None):
1546 args = [f'int {arg_name}']
1547 if enum:
1548 args = [enum.user_type + ' ' + arg_name]
1549 cw.write_func_prot('const char *', f'{render_name}_str', args)
1550 cw.block_start()
1551 if enum and enum.type == 'flags':
1552 cw.p(f'{arg_name} = ffs({arg_name}) - 1;')
1553 cw.p(f'if ({arg_name} < 0 || {arg_name} >= (int)MNL_ARRAY_SIZE({map_name}))')
1554 cw.p('return NULL;')
1555 cw.p(f'return {map_name}[{arg_name}];')
1556 cw.block_end()
1557 cw.nl()
1558
1559
1560def put_op_name_fwd(family, cw):
1561 cw.write_func_prot('const char *', f'{family.c_name}_op_str', ['int op'], suffix=';')
1562
1563
1564def put_op_name(family, cw):
1565 map_name = f'{family.c_name}_op_strmap'
1566 cw.block_start(line=f"static const char * const {map_name}[] =")
1567 for op_name, op in family.msgs.items():
1568 if op.rsp_value:
1569 # Make sure we don't add duplicated entries, if multiple commands
1570 # produce the same response in legacy families.
1571 if family.rsp_by_value[op.rsp_value] != op:
1572 cw.p(f'// skip "{op_name}", duplicate reply value')
1573 continue
1574
1575 if op.req_value == op.rsp_value:
1576 cw.p(f'[{op.enum_name}] = "{op_name}",')
1577 else:
1578 cw.p(f'[{op.rsp_value}] = "{op_name}",')
1579 cw.block_end(line=';')
1580 cw.nl()
1581
1582 _put_enum_to_str_helper(cw, family.c_name + '_op', map_name, 'op')
1583
1584
1585def put_enum_to_str_fwd(family, cw, enum):
1586 args = [enum.user_type + ' value']
1587 cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';')
1588
1589
1590def put_enum_to_str(family, cw, enum):
1591 map_name = f'{enum.render_name}_strmap'
1592 cw.block_start(line=f"static const char * const {map_name}[] =")
1593 for entry in enum.entries.values():
1594 cw.p(f'[{entry.value}] = "{entry.name}",')
1595 cw.block_end(line=';')
1596 cw.nl()
1597
1598 _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
1599
1600
1601def put_req_nested_prototype(ri, struct, suffix=';'):
1602 func_args = ['struct nlmsghdr *nlh',
1603 'unsigned int attr_type',
1604 f'{struct.ptr_name}obj']
1605
1606 ri.cw.write_func_prot('int', f'{struct.render_name}_put', func_args,
1607 suffix=suffix)
1608
1609
1610def put_req_nested(ri, struct):
1611 put_req_nested_prototype(ri, struct, suffix='')
1612 ri.cw.block_start()
1613 ri.cw.write_func_lvar('struct nlattr *nest;')
1614
1615 ri.cw.p("nest = mnl_attr_nest_start(nlh, attr_type);")
1616
1617 for _, arg in struct.member_list():
1618 arg.attr_put(ri, "obj")
1619
1620 ri.cw.p("mnl_attr_nest_end(nlh, nest);")
1621
1622 ri.cw.nl()
1623 ri.cw.p('return 0;')
1624 ri.cw.block_end()
1625 ri.cw.nl()
1626
1627
1628def _multi_parse(ri, struct, init_lines, local_vars):
1629 if struct.nested:
1630 iter_line = "mnl_attr_for_each_nested(attr, nested)"
1631 else:
1632 if ri.fixed_hdr:
1633 local_vars += ['void *hdr;']
1634 iter_line = "mnl_attr_for_each(attr, nlh, yarg->ys->family->hdr_len)"
1635
1636 array_nests = set()
1637 multi_attrs = set()
1638 needs_parg = False
1639 for arg, aspec in struct.member_list():
1640 if aspec['type'] == 'array-nest':
1641 local_vars.append(f'const struct nlattr *attr_{aspec.c_name};')
1642 array_nests.add(arg)
1643 if 'multi-attr' in aspec:
1644 multi_attrs.add(arg)
1645 needs_parg |= 'nested-attributes' in aspec
1646 if array_nests or multi_attrs:
1647 local_vars.append('int i;')
1648 if needs_parg:
1649 local_vars.append('struct ynl_parse_arg parg;')
1650 init_lines.append('parg.ys = yarg->ys;')
1651
1652 all_multi = array_nests | multi_attrs
1653
1654 for anest in sorted(all_multi):
1655 local_vars.append(f"unsigned int n_{struct[anest].c_name} = 0;")
1656
1657 ri.cw.block_start()
1658 ri.cw.write_func_lvar(local_vars)
1659
1660 for line in init_lines:
1661 ri.cw.p(line)
1662 ri.cw.nl()
1663
1664 for arg in struct.inherited:
1665 ri.cw.p(f'dst->{arg} = {arg};')
1666
1667 if ri.fixed_hdr:
1668 ri.cw.p('hdr = mnl_nlmsg_get_payload_offset(nlh, sizeof(struct genlmsghdr));')
1669 ri.cw.p(f"memcpy(&dst->_hdr, hdr, sizeof({ri.fixed_hdr}));")
1670 for anest in sorted(all_multi):
1671 aspec = struct[anest]
1672 ri.cw.p(f"if (dst->{aspec.c_name})")
1673 ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
1674
1675 ri.cw.nl()
1676 ri.cw.block_start(line=iter_line)
1677 ri.cw.p('unsigned int type = mnl_attr_get_type(attr);')
1678 ri.cw.nl()
1679
1680 first = True
1681 for _, arg in struct.member_list():
1682 good = arg.attr_get(ri, 'dst', first=first)
1683 # First may be 'unused' or 'pad', ignore those
1684 first &= not good
1685
1686 ri.cw.block_end()
1687 ri.cw.nl()
1688
1689 for anest in sorted(array_nests):
1690 aspec = struct[anest]
1691
1692 ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1693 ri.cw.p(f"dst->{aspec.c_name} = calloc({aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1694 ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1695 ri.cw.p('i = 0;')
1696 ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1697 ri.cw.block_start(line=f"mnl_attr_for_each_nested(attr, attr_{aspec.c_name})")
1698 ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1699 ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr, mnl_attr_get_type(attr)))")
1700 ri.cw.p('return MNL_CB_ERROR;')
1701 ri.cw.p('i++;')
1702 ri.cw.block_end()
1703 ri.cw.block_end()
1704 ri.cw.nl()
1705
1706 for anest in sorted(multi_attrs):
1707 aspec = struct[anest]
1708 ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1709 ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1710 ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1711 ri.cw.p('i = 0;')
1712 if 'nested-attributes' in aspec:
1713 ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1714 ri.cw.block_start(line=iter_line)
1715 ri.cw.block_start(line=f"if (mnl_attr_get_type(attr) == {aspec.enum_name})")
1716 if 'nested-attributes' in aspec:
1717 ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1718 ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))")
1719 ri.cw.p('return MNL_CB_ERROR;')
1720 elif aspec.type in scalars:
1721 ri.cw.p(f"dst->{aspec.c_name}[i] = mnl_attr_get_{aspec.mnl_type()}(attr);")
1722 else:
1723 raise Exception('Nest parsing type not supported yet')
1724 ri.cw.p('i++;')
1725 ri.cw.block_end()
1726 ri.cw.block_end()
1727 ri.cw.block_end()
1728 ri.cw.nl()
1729
1730 if struct.nested:
1731 ri.cw.p('return 0;')
1732 else:
1733 ri.cw.p('return MNL_CB_OK;')
1734 ri.cw.block_end()
1735 ri.cw.nl()
1736
1737
1738def parse_rsp_nested_prototype(ri, struct, suffix=';'):
1739 func_args = ['struct ynl_parse_arg *yarg',
1740 'const struct nlattr *nested']
1741 for arg in struct.inherited:
1742 func_args.append('__u32 ' + arg)
1743
1744 ri.cw.write_func_prot('int', f'{struct.render_name}_parse', func_args,
1745 suffix=suffix)
1746
1747
1748def parse_rsp_nested(ri, struct):
1749 parse_rsp_nested_prototype(ri, struct, suffix='')
1750
1751 local_vars = ['const struct nlattr *attr;',
1752 f'{struct.ptr_name}dst = yarg->data;']
1753 init_lines = []
1754
1755 _multi_parse(ri, struct, init_lines, local_vars)
1756
1757
1758def parse_rsp_msg(ri, deref=False):
1759 if 'reply' not in ri.op[ri.op_mode] and ri.op_mode != 'event':
1760 return
1761
1762 func_args = ['const struct nlmsghdr *nlh',
1763 'void *data']
1764
1765 local_vars = [f'{type_name(ri, "reply", deref=deref)} *dst;',
1766 'struct ynl_parse_arg *yarg = data;',
1767 'const struct nlattr *attr;']
1768 init_lines = ['dst = yarg->data;']
1769
1770 ri.cw.write_func_prot('int', f'{op_prefix(ri, "reply", deref=deref)}_parse', func_args)
1771
1772 if ri.struct["reply"].member_list():
1773 _multi_parse(ri, ri.struct["reply"], init_lines, local_vars)
1774 else:
1775 # Empty reply
1776 ri.cw.block_start()
1777 ri.cw.p('return MNL_CB_OK;')
1778 ri.cw.block_end()
1779 ri.cw.nl()
1780
1781
1782def print_req(ri):
1783 ret_ok = '0'
1784 ret_err = '-1'
1785 direction = "request"
1786 local_vars = ['struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };',
1787 'struct nlmsghdr *nlh;',
1788 'int err;']
1789
1790 if 'reply' in ri.op[ri.op_mode]:
1791 ret_ok = 'rsp'
1792 ret_err = 'NULL'
1793 local_vars += [f'{type_name(ri, rdir(direction))} *rsp;']
1794
1795 if ri.fixed_hdr:
1796 local_vars += ['size_t hdr_len;',
1797 'void *hdr;']
1798
1799 print_prototype(ri, direction, terminate=False)
1800 ri.cw.block_start()
1801 ri.cw.write_func_lvar(local_vars)
1802
1803 ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1804
1805 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1806 if 'reply' in ri.op[ri.op_mode]:
1807 ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1808 ri.cw.nl()
1809
1810 if ri.fixed_hdr:
1811 ri.cw.p("hdr_len = sizeof(req->_hdr);")
1812 ri.cw.p("hdr = mnl_nlmsg_put_extra_header(nlh, hdr_len);")
1813 ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);")
1814 ri.cw.nl()
1815
1816 for _, attr in ri.struct["request"].member_list():
1817 attr.attr_put(ri, "req")
1818 ri.cw.nl()
1819
1820 if 'reply' in ri.op[ri.op_mode]:
1821 ri.cw.p('rsp = calloc(1, sizeof(*rsp));')
1822 ri.cw.p('yrs.yarg.data = rsp;')
1823 ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;")
1824 if ri.op.value is not None:
1825 ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};')
1826 else:
1827 ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};')
1828 ri.cw.nl()
1829 ri.cw.p("err = ynl_exec(ys, nlh, &yrs);")
1830 ri.cw.p('if (err < 0)')
1831 if 'reply' in ri.op[ri.op_mode]:
1832 ri.cw.p('goto err_free;')
1833 else:
1834 ri.cw.p('return -1;')
1835 ri.cw.nl()
1836
1837 ri.cw.p(f"return {ret_ok};")
1838 ri.cw.nl()
1839
1840 if 'reply' in ri.op[ri.op_mode]:
1841 ri.cw.p('err_free:')
1842 ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}")
1843 ri.cw.p(f"return {ret_err};")
1844
1845 ri.cw.block_end()
1846
1847
1848def print_dump(ri):
1849 direction = "request"
1850 print_prototype(ri, direction, terminate=False)
1851 ri.cw.block_start()
1852 local_vars = ['struct ynl_dump_state yds = {};',
1853 'struct nlmsghdr *nlh;',
1854 'int err;']
1855
1856 if ri.fixed_hdr:
1857 local_vars += ['size_t hdr_len;',
1858 'void *hdr;']
1859
1860 ri.cw.write_func_lvar(local_vars)
1861
1862 ri.cw.p('yds.ys = ys;')
1863 ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});")
1864 ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;")
1865 if ri.op.value is not None:
1866 ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};')
1867 else:
1868 ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};')
1869 ri.cw.p(f"yds.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1870 ri.cw.nl()
1871 ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1872
1873 if ri.fixed_hdr:
1874 ri.cw.p("hdr_len = sizeof(req->_hdr);")
1875 ri.cw.p("hdr = mnl_nlmsg_put_extra_header(nlh, hdr_len);")
1876 ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);")
1877 ri.cw.nl()
1878
1879 if "request" in ri.op[ri.op_mode]:
1880 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1881 ri.cw.nl()
1882 for _, attr in ri.struct["request"].member_list():
1883 attr.attr_put(ri, "req")
1884 ri.cw.nl()
1885
1886 ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);')
1887 ri.cw.p('if (err < 0)')
1888 ri.cw.p('goto free_list;')
1889 ri.cw.nl()
1890
1891 ri.cw.p('return yds.first;')
1892 ri.cw.nl()
1893 ri.cw.p('free_list:')
1894 ri.cw.p(call_free(ri, rdir(direction), 'yds.first'))
1895 ri.cw.p('return NULL;')
1896 ri.cw.block_end()
1897
1898
1899def call_free(ri, direction, var):
1900 return f"{op_prefix(ri, direction)}_free({var});"
1901
1902
1903def free_arg_name(direction):
1904 if direction:
1905 return direction_to_suffix[direction][1:]
1906 return 'obj'
1907
1908
1909def print_alloc_wrapper(ri, direction):
1910 name = op_prefix(ri, direction)
1911 ri.cw.write_func_prot(f'static inline struct {name} *', f"{name}_alloc", [f"void"])
1912 ri.cw.block_start()
1913 ri.cw.p(f'return calloc(1, sizeof(struct {name}));')
1914 ri.cw.block_end()
1915
1916
1917def print_free_prototype(ri, direction, suffix=';'):
1918 name = op_prefix(ri, direction)
1919 struct_name = name
1920 if ri.type_name_conflict:
1921 struct_name += '_'
1922 arg = free_arg_name(direction)
1923 ri.cw.write_func_prot('void', f"{name}_free", [f"struct {struct_name} *{arg}"], suffix=suffix)
1924
1925
1926def _print_type(ri, direction, struct):
1927 suffix = f'_{ri.type_name}{direction_to_suffix[direction]}'
1928 if not direction and ri.type_name_conflict:
1929 suffix += '_'
1930
1931 if ri.op_mode == 'dump':
1932 suffix += '_dump'
1933
1934 ri.cw.block_start(line=f"struct {ri.family.c_name}{suffix}")
1935
1936 if ri.fixed_hdr:
1937 ri.cw.p(ri.fixed_hdr + ' _hdr;')
1938 ri.cw.nl()
1939
1940 meta_started = False
1941 for _, attr in struct.member_list():
1942 for type_filter in ['len', 'bit']:
1943 line = attr.presence_member(ri.ku_space, type_filter)
1944 if line:
1945 if not meta_started:
1946 ri.cw.block_start(line=f"struct")
1947 meta_started = True
1948 ri.cw.p(line)
1949 if meta_started:
1950 ri.cw.block_end(line='_present;')
1951 ri.cw.nl()
1952
1953 for arg in struct.inherited:
1954 ri.cw.p(f"__u32 {arg};")
1955
1956 for _, attr in struct.member_list():
1957 attr.struct_member(ri)
1958
1959 ri.cw.block_end(line=';')
1960 ri.cw.nl()
1961
1962
1963def print_type(ri, direction):
1964 _print_type(ri, direction, ri.struct[direction])
1965
1966
1967def print_type_full(ri, struct):
1968 _print_type(ri, "", struct)
1969
1970
1971def print_type_helpers(ri, direction, deref=False):
1972 print_free_prototype(ri, direction)
1973 ri.cw.nl()
1974
1975 if ri.ku_space == 'user' and direction == 'request':
1976 for _, attr in ri.struct[direction].member_list():
1977 attr.setter(ri, ri.attr_set, direction, deref=deref)
1978 ri.cw.nl()
1979
1980
1981def print_req_type_helpers(ri):
1982 if len(ri.struct["request"].attr_list) == 0:
1983 return
1984 print_alloc_wrapper(ri, "request")
1985 print_type_helpers(ri, "request")
1986
1987
1988def print_rsp_type_helpers(ri):
1989 if 'reply' not in ri.op[ri.op_mode]:
1990 return
1991 print_type_helpers(ri, "reply")
1992
1993
1994def print_parse_prototype(ri, direction, terminate=True):
1995 suffix = "_rsp" if direction == "reply" else "_req"
1996 term = ';' if terminate else ''
1997
1998 ri.cw.write_func_prot('void', f"{ri.op.render_name}{suffix}_parse",
1999 ['const struct nlattr **tb',
2000 f"struct {ri.op.render_name}{suffix} *req"],
2001 suffix=term)
2002
2003
2004def print_req_type(ri):
2005 if len(ri.struct["request"].attr_list) == 0:
2006 return
2007 print_type(ri, "request")
2008
2009
2010def print_req_free(ri):
2011 if 'request' not in ri.op[ri.op_mode]:
2012 return
2013 _free_type(ri, 'request', ri.struct['request'])
2014
2015
2016def print_rsp_type(ri):
2017 if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]:
2018 direction = 'reply'
2019 elif ri.op_mode == 'event':
2020 direction = 'reply'
2021 else:
2022 return
2023 print_type(ri, direction)
2024
2025
2026def print_wrapped_type(ri):
2027 ri.cw.block_start(line=f"{type_name(ri, 'reply')}")
2028 if ri.op_mode == 'dump':
2029 ri.cw.p(f"{type_name(ri, 'reply')} *next;")
2030 elif ri.op_mode == 'notify' or ri.op_mode == 'event':
2031 ri.cw.p('__u16 family;')
2032 ri.cw.p('__u8 cmd;')
2033 ri.cw.p('struct ynl_ntf_base_type *next;')
2034 ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);")
2035 ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__((aligned(8)));")
2036 ri.cw.block_end(line=';')
2037 ri.cw.nl()
2038 print_free_prototype(ri, 'reply')
2039 ri.cw.nl()
2040
2041
2042def _free_type_members_iter(ri, struct):
2043 for _, attr in struct.member_list():
2044 if attr.free_needs_iter():
2045 ri.cw.p('unsigned int i;')
2046 ri.cw.nl()
2047 break
2048
2049
2050def _free_type_members(ri, var, struct, ref=''):
2051 for _, attr in struct.member_list():
2052 attr.free(ri, var, ref)
2053
2054
2055def _free_type(ri, direction, struct):
2056 var = free_arg_name(direction)
2057
2058 print_free_prototype(ri, direction, suffix='')
2059 ri.cw.block_start()
2060 _free_type_members_iter(ri, struct)
2061 _free_type_members(ri, var, struct)
2062 if direction:
2063 ri.cw.p(f'free({var});')
2064 ri.cw.block_end()
2065 ri.cw.nl()
2066
2067
2068def free_rsp_nested_prototype(ri):
2069 print_free_prototype(ri, "")
2070
2071
2072def free_rsp_nested(ri, struct):
2073 _free_type(ri, "", struct)
2074
2075
2076def print_rsp_free(ri):
2077 if 'reply' not in ri.op[ri.op_mode]:
2078 return
2079 _free_type(ri, 'reply', ri.struct['reply'])
2080
2081
2082def print_dump_type_free(ri):
2083 sub_type = type_name(ri, 'reply')
2084
2085 print_free_prototype(ri, 'reply', suffix='')
2086 ri.cw.block_start()
2087 ri.cw.p(f"{sub_type} *next = rsp;")
2088 ri.cw.nl()
2089 ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)')
2090 _free_type_members_iter(ri, ri.struct['reply'])
2091 ri.cw.p('rsp = next;')
2092 ri.cw.p('next = rsp->next;')
2093 ri.cw.nl()
2094
2095 _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2096 ri.cw.p(f'free(rsp);')
2097 ri.cw.block_end()
2098 ri.cw.block_end()
2099 ri.cw.nl()
2100
2101
2102def print_ntf_type_free(ri):
2103 print_free_prototype(ri, 'reply', suffix='')
2104 ri.cw.block_start()
2105 _free_type_members_iter(ri, ri.struct['reply'])
2106 _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2107 ri.cw.p(f'free(rsp);')
2108 ri.cw.block_end()
2109 ri.cw.nl()
2110
2111
2112def print_req_policy_fwd(cw, struct, ri=None, terminate=True):
2113 if terminate and ri and policy_should_be_static(struct.family):
2114 return
2115
2116 if terminate:
2117 prefix = 'extern '
2118 else:
2119 if ri and policy_should_be_static(struct.family):
2120 prefix = 'static '
2121 else:
2122 prefix = ''
2123
2124 suffix = ';' if terminate else ' = {'
2125
2126 max_attr = struct.attr_max_val
2127 if ri:
2128 name = ri.op.render_name
2129 if ri.op.dual_policy:
2130 name += '_' + ri.op_mode
2131 else:
2132 name = struct.render_name
2133 cw.p(f"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}")
2134
2135
2136def print_req_policy(cw, struct, ri=None):
2137 if ri and ri.op:
2138 cw.ifdef_block(ri.op.get('config-cond', None))
2139 print_req_policy_fwd(cw, struct, ri=ri, terminate=False)
2140 for _, arg in struct.member_list():
2141 arg.attr_policy(cw)
2142 cw.p("};")
2143 cw.ifdef_block(None)
2144 cw.nl()
2145
2146
2147def kernel_can_gen_family_struct(family):
2148 return family.proto == 'genetlink'
2149
2150
2151def policy_should_be_static(family):
2152 return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family)
2153
2154
2155def print_kernel_policy_ranges(family, cw):
2156 first = True
2157 for _, attr_set in family.attr_sets.items():
2158 if attr_set.subset_of:
2159 continue
2160
2161 for _, attr in attr_set.items():
2162 if not attr.request:
2163 continue
2164 if 'full-range' not in attr.checks:
2165 continue
2166
2167 if first:
2168 cw.p('/* Integer value ranges */')
2169 first = False
2170
2171 sign = '' if attr.type[0] == 'u' else '_signed'
2172 suffix = 'ULL' if attr.type[0] == 'u' else 'LL'
2173 cw.block_start(line=f'static const struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =')
2174 members = []
2175 if 'min' in attr.checks:
2176 members.append(('min', str(attr.get_limit('min')) + suffix))
2177 if 'max' in attr.checks:
2178 members.append(('max', str(attr.get_limit('max')) + suffix))
2179 cw.write_struct_init(members)
2180 cw.block_end(line=';')
2181 cw.nl()
2182
2183
2184def print_kernel_op_table_fwd(family, cw, terminate):
2185 exported = not kernel_can_gen_family_struct(family)
2186
2187 if not terminate or exported:
2188 cw.p(f"/* Ops table for {family.name} */")
2189
2190 pol_to_struct = {'global': 'genl_small_ops',
2191 'per-op': 'genl_ops',
2192 'split': 'genl_split_ops'}
2193 struct_type = pol_to_struct[family.kernel_policy]
2194
2195 if not exported:
2196 cnt = ""
2197 elif family.kernel_policy == 'split':
2198 cnt = 0
2199 for op in family.ops.values():
2200 if 'do' in op:
2201 cnt += 1
2202 if 'dump' in op:
2203 cnt += 1
2204 else:
2205 cnt = len(family.ops)
2206
2207 qual = 'static const' if not exported else 'const'
2208 line = f"{qual} struct {struct_type} {family.c_name}_nl_ops[{cnt}]"
2209 if terminate:
2210 cw.p(f"extern {line};")
2211 else:
2212 cw.block_start(line=line + ' =')
2213
2214 if not terminate:
2215 return
2216
2217 cw.nl()
2218 for name in family.hooks['pre']['do']['list']:
2219 cw.write_func_prot('int', c_lower(name),
2220 ['const struct genl_split_ops *ops',
2221 'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2222 for name in family.hooks['post']['do']['list']:
2223 cw.write_func_prot('void', c_lower(name),
2224 ['const struct genl_split_ops *ops',
2225 'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2226 for name in family.hooks['pre']['dump']['list']:
2227 cw.write_func_prot('int', c_lower(name),
2228 ['struct netlink_callback *cb'], suffix=';')
2229 for name in family.hooks['post']['dump']['list']:
2230 cw.write_func_prot('int', c_lower(name),
2231 ['struct netlink_callback *cb'], suffix=';')
2232
2233 cw.nl()
2234
2235 for op_name, op in family.ops.items():
2236 if op.is_async:
2237 continue
2238
2239 if 'do' in op:
2240 name = c_lower(f"{family.name}-nl-{op_name}-doit")
2241 cw.write_func_prot('int', name,
2242 ['struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2243
2244 if 'dump' in op:
2245 name = c_lower(f"{family.name}-nl-{op_name}-dumpit")
2246 cw.write_func_prot('int', name,
2247 ['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix=';')
2248 cw.nl()
2249
2250
2251def print_kernel_op_table_hdr(family, cw):
2252 print_kernel_op_table_fwd(family, cw, terminate=True)
2253
2254
2255def print_kernel_op_table(family, cw):
2256 print_kernel_op_table_fwd(family, cw, terminate=False)
2257 if family.kernel_policy == 'global' or family.kernel_policy == 'per-op':
2258 for op_name, op in family.ops.items():
2259 if op.is_async:
2260 continue
2261
2262 cw.ifdef_block(op.get('config-cond', None))
2263 cw.block_start()
2264 members = [('cmd', op.enum_name)]
2265 if 'dont-validate' in op:
2266 members.append(('validate',
2267 ' | '.join([c_upper('genl-dont-validate-' + x)
2268 for x in op['dont-validate']])), )
2269 for op_mode in ['do', 'dump']:
2270 if op_mode in op:
2271 name = c_lower(f"{family.name}-nl-{op_name}-{op_mode}it")
2272 members.append((op_mode + 'it', name))
2273 if family.kernel_policy == 'per-op':
2274 struct = Struct(family, op['attribute-set'],
2275 type_list=op['do']['request']['attributes'])
2276
2277 name = c_lower(f"{family.name}-{op_name}-nl-policy")
2278 members.append(('policy', name))
2279 members.append(('maxattr', struct.attr_max_val.enum_name))
2280 if 'flags' in op:
2281 members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in op['flags']])))
2282 cw.write_struct_init(members)
2283 cw.block_end(line=',')
2284 elif family.kernel_policy == 'split':
2285 cb_names = {'do': {'pre': 'pre_doit', 'post': 'post_doit'},
2286 'dump': {'pre': 'start', 'post': 'done'}}
2287
2288 for op_name, op in family.ops.items():
2289 for op_mode in ['do', 'dump']:
2290 if op.is_async or op_mode not in op:
2291 continue
2292
2293 cw.ifdef_block(op.get('config-cond', None))
2294 cw.block_start()
2295 members = [('cmd', op.enum_name)]
2296 if 'dont-validate' in op:
2297 dont_validate = []
2298 for x in op['dont-validate']:
2299 if op_mode == 'do' and x in ['dump', 'dump-strict']:
2300 continue
2301 if op_mode == "dump" and x == 'strict':
2302 continue
2303 dont_validate.append(x)
2304
2305 if dont_validate:
2306 members.append(('validate',
2307 ' | '.join([c_upper('genl-dont-validate-' + x)
2308 for x in dont_validate])), )
2309 name = c_lower(f"{family.name}-nl-{op_name}-{op_mode}it")
2310 if 'pre' in op[op_mode]:
2311 members.append((cb_names[op_mode]['pre'], c_lower(op[op_mode]['pre'])))
2312 members.append((op_mode + 'it', name))
2313 if 'post' in op[op_mode]:
2314 members.append((cb_names[op_mode]['post'], c_lower(op[op_mode]['post'])))
2315 if 'request' in op[op_mode]:
2316 struct = Struct(family, op['attribute-set'],
2317 type_list=op[op_mode]['request']['attributes'])
2318
2319 if op.dual_policy:
2320 name = c_lower(f"{family.name}-{op_name}-{op_mode}-nl-policy")
2321 else:
2322 name = c_lower(f"{family.name}-{op_name}-nl-policy")
2323 members.append(('policy', name))
2324 members.append(('maxattr', struct.attr_max_val.enum_name))
2325 flags = (op['flags'] if 'flags' in op else []) + ['cmd-cap-' + op_mode]
2326 members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in flags])))
2327 cw.write_struct_init(members)
2328 cw.block_end(line=',')
2329 cw.ifdef_block(None)
2330
2331 cw.block_end(line=';')
2332 cw.nl()
2333
2334
2335def print_kernel_mcgrp_hdr(family, cw):
2336 if not family.mcgrps['list']:
2337 return
2338
2339 cw.block_start('enum')
2340 for grp in family.mcgrps['list']:
2341 grp_id = c_upper(f"{family.name}-nlgrp-{grp['name']},")
2342 cw.p(grp_id)
2343 cw.block_end(';')
2344 cw.nl()
2345
2346
2347def print_kernel_mcgrp_src(family, cw):
2348 if not family.mcgrps['list']:
2349 return
2350
2351 cw.block_start('static const struct genl_multicast_group ' + family.c_name + '_nl_mcgrps[] =')
2352 for grp in family.mcgrps['list']:
2353 name = grp['name']
2354 grp_id = c_upper(f"{family.name}-nlgrp-{name}")
2355 cw.p('[' + grp_id + '] = { "' + name + '", },')
2356 cw.block_end(';')
2357 cw.nl()
2358
2359
2360def print_kernel_family_struct_hdr(family, cw):
2361 if not kernel_can_gen_family_struct(family):
2362 return
2363
2364 cw.p(f"extern struct genl_family {family.c_name}_nl_family;")
2365 cw.nl()
2366
2367
2368def print_kernel_family_struct_src(family, cw):
2369 if not kernel_can_gen_family_struct(family):
2370 return
2371
2372 cw.block_start(f"struct genl_family {family.name}_nl_family __ro_after_init =")
2373 cw.p('.name\t\t= ' + family.fam_key + ',')
2374 cw.p('.version\t= ' + family.ver_key + ',')
2375 cw.p('.netnsok\t= true,')
2376 cw.p('.parallel_ops\t= true,')
2377 cw.p('.module\t\t= THIS_MODULE,')
2378 if family.kernel_policy == 'per-op':
2379 cw.p(f'.ops\t\t= {family.c_name}_nl_ops,')
2380 cw.p(f'.n_ops\t\t= ARRAY_SIZE({family.c_name}_nl_ops),')
2381 elif family.kernel_policy == 'split':
2382 cw.p(f'.split_ops\t= {family.c_name}_nl_ops,')
2383 cw.p(f'.n_split_ops\t= ARRAY_SIZE({family.c_name}_nl_ops),')
2384 if family.mcgrps['list']:
2385 cw.p(f'.mcgrps\t\t= {family.c_name}_nl_mcgrps,')
2386 cw.p(f'.n_mcgrps\t= ARRAY_SIZE({family.c_name}_nl_mcgrps),')
2387 cw.block_end(';')
2388
2389
2390def uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'):
2391 start_line = 'enum'
2392 if enum_name in obj:
2393 if obj[enum_name]:
2394 start_line = 'enum ' + c_lower(obj[enum_name])
2395 elif ckey and ckey in obj:
2396 start_line = 'enum ' + family.c_name + '_' + c_lower(obj[ckey])
2397 cw.block_start(line=start_line)
2398
2399
2400def render_uapi(family, cw):
2401 hdr_prot = f"_UAPI_LINUX_{c_upper(family.uapi_header_name)}_H"
2402 cw.p('#ifndef ' + hdr_prot)
2403 cw.p('#define ' + hdr_prot)
2404 cw.nl()
2405
2406 defines = [(family.fam_key, family["name"]),
2407 (family.ver_key, family.get('version', 1))]
2408 cw.writes_defines(defines)
2409 cw.nl()
2410
2411 defines = []
2412 for const in family['definitions']:
2413 if const['type'] != 'const':
2414 cw.writes_defines(defines)
2415 defines = []
2416 cw.nl()
2417
2418 # Write kdoc for enum and flags (one day maybe also structs)
2419 if const['type'] == 'enum' or const['type'] == 'flags':
2420 enum = family.consts[const['name']]
2421
2422 if enum.has_doc():
2423 cw.p('/**')
2424 doc = ''
2425 if 'doc' in enum:
2426 doc = ' - ' + enum['doc']
2427 cw.write_doc_line(enum.enum_name + doc)
2428 for entry in enum.entries.values():
2429 if entry.has_doc():
2430 doc = '@' + entry.c_name + ': ' + entry['doc']
2431 cw.write_doc_line(doc)
2432 cw.p(' */')
2433
2434 uapi_enum_start(family, cw, const, 'name')
2435 name_pfx = const.get('name-prefix', f"{family.name}-{const['name']}-")
2436 for entry in enum.entries.values():
2437 suffix = ','
2438 if entry.value_change:
2439 suffix = f" = {entry.user_value()}" + suffix
2440 cw.p(entry.c_name + suffix)
2441
2442 if const.get('render-max', False):
2443 cw.nl()
2444 cw.p('/* private: */')
2445 if const['type'] == 'flags':
2446 max_name = c_upper(name_pfx + 'mask')
2447 max_val = f' = {enum.get_mask()},'
2448 cw.p(max_name + max_val)
2449 else:
2450 max_name = c_upper(name_pfx + 'max')
2451 cw.p('__' + max_name + ',')
2452 cw.p(max_name + ' = (__' + max_name + ' - 1)')
2453 cw.block_end(line=';')
2454 cw.nl()
2455 elif const['type'] == 'const':
2456 defines.append([c_upper(family.get('c-define-name',
2457 f"{family.name}-{const['name']}")),
2458 const['value']])
2459
2460 if defines:
2461 cw.writes_defines(defines)
2462 cw.nl()
2463
2464 max_by_define = family.get('max-by-define', False)
2465
2466 for _, attr_set in family.attr_sets.items():
2467 if attr_set.subset_of:
2468 continue
2469
2470 max_value = f"({attr_set.cnt_name} - 1)"
2471
2472 val = 0
2473 uapi_enum_start(family, cw, attr_set.yaml, 'enum-name')
2474 for _, attr in attr_set.items():
2475 suffix = ','
2476 if attr.value != val:
2477 suffix = f" = {attr.value},"
2478 val = attr.value
2479 val += 1
2480 cw.p(attr.enum_name + suffix)
2481 cw.nl()
2482 cw.p(attr_set.cnt_name + ('' if max_by_define else ','))
2483 if not max_by_define:
2484 cw.p(f"{attr_set.max_name} = {max_value}")
2485 cw.block_end(line=';')
2486 if max_by_define:
2487 cw.p(f"#define {attr_set.max_name} {max_value}")
2488 cw.nl()
2489
2490 # Commands
2491 separate_ntf = 'async-prefix' in family['operations']
2492
2493 max_name = c_upper(family.get('cmd-max-name', f"{family.op_prefix}MAX"))
2494 cnt_name = c_upper(family.get('cmd-cnt-name', f"__{family.op_prefix}MAX"))
2495 max_value = f"({cnt_name} - 1)"
2496
2497 uapi_enum_start(family, cw, family['operations'], 'enum-name')
2498 val = 0
2499 for op in family.msgs.values():
2500 if separate_ntf and ('notify' in op or 'event' in op):
2501 continue
2502
2503 suffix = ','
2504 if op.value != val:
2505 suffix = f" = {op.value},"
2506 val = op.value
2507 cw.p(op.enum_name + suffix)
2508 val += 1
2509 cw.nl()
2510 cw.p(cnt_name + ('' if max_by_define else ','))
2511 if not max_by_define:
2512 cw.p(f"{max_name} = {max_value}")
2513 cw.block_end(line=';')
2514 if max_by_define:
2515 cw.p(f"#define {max_name} {max_value}")
2516 cw.nl()
2517
2518 if separate_ntf:
2519 uapi_enum_start(family, cw, family['operations'], enum_name='async-enum')
2520 for op in family.msgs.values():
2521 if separate_ntf and not ('notify' in op or 'event' in op):
2522 continue
2523
2524 suffix = ','
2525 if 'value' in op:
2526 suffix = f" = {op['value']},"
2527 cw.p(op.enum_name + suffix)
2528 cw.block_end(line=';')
2529 cw.nl()
2530
2531 # Multicast
2532 defines = []
2533 for grp in family.mcgrps['list']:
2534 name = grp['name']
2535 defines.append([c_upper(grp.get('c-define-name', f"{family.name}-mcgrp-{name}")),
2536 f'{name}'])
2537 cw.nl()
2538 if defines:
2539 cw.writes_defines(defines)
2540 cw.nl()
2541
2542 cw.p(f'#endif /* {hdr_prot} */')
2543
2544
2545def _render_user_ntf_entry(ri, op):
2546 ri.cw.block_start(line=f"[{op.enum_name}] = ")
2547 ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),")
2548 ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,")
2549 ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,")
2550 ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,")
2551 ri.cw.block_end(line=',')
2552
2553
2554def render_user_family(family, cw, prototype):
2555 symbol = f'const struct ynl_family ynl_{family.c_name}_family'
2556 if prototype:
2557 cw.p(f'extern {symbol};')
2558 return
2559
2560 if family.ntfs:
2561 cw.block_start(line=f"static const struct ynl_ntf_info {family['name']}_ntf_info[] = ")
2562 for ntf_op_name, ntf_op in family.ntfs.items():
2563 if 'notify' in ntf_op:
2564 op = family.ops[ntf_op['notify']]
2565 ri = RenderInfo(cw, family, "user", op, "notify")
2566 elif 'event' in ntf_op:
2567 ri = RenderInfo(cw, family, "user", ntf_op, "event")
2568 else:
2569 raise Exception('Invalid notification ' + ntf_op_name)
2570 _render_user_ntf_entry(ri, ntf_op)
2571 for op_name, op in family.ops.items():
2572 if 'event' not in op:
2573 continue
2574 ri = RenderInfo(cw, family, "user", op, "event")
2575 _render_user_ntf_entry(ri, op)
2576 cw.block_end(line=";")
2577 cw.nl()
2578
2579 cw.block_start(f'{symbol} = ')
2580 cw.p(f'.name\t\t= "{family.c_name}",')
2581 if family.fixed_header:
2582 cw.p(f'.hdr_len\t= sizeof(struct genlmsghdr) + sizeof(struct {c_lower(family.fixed_header)}),')
2583 else:
2584 cw.p('.hdr_len\t= sizeof(struct genlmsghdr),')
2585 if family.ntfs:
2586 cw.p(f".ntf_info\t= {family['name']}_ntf_info,")
2587 cw.p(f".ntf_info_size\t= MNL_ARRAY_SIZE({family['name']}_ntf_info),")
2588 cw.block_end(line=';')
2589
2590
2591def family_contains_bitfield32(family):
2592 for _, attr_set in family.attr_sets.items():
2593 if attr_set.subset_of:
2594 continue
2595 for _, attr in attr_set.items():
2596 if attr.type == "bitfield32":
2597 return True
2598 return False
2599
2600
2601def find_kernel_root(full_path):
2602 sub_path = ''
2603 while True:
2604 sub_path = os.path.join(os.path.basename(full_path), sub_path)
2605 full_path = os.path.dirname(full_path)
2606 maintainers = os.path.join(full_path, "MAINTAINERS")
2607 if os.path.exists(maintainers):
2608 return full_path, sub_path[:-1]
2609
2610
2611def main():
2612 parser = argparse.ArgumentParser(description='Netlink simple parsing generator')
2613 parser.add_argument('--mode', dest='mode', type=str, required=True)
2614 parser.add_argument('--spec', dest='spec', type=str, required=True)
2615 parser.add_argument('--header', dest='header', action='store_true', default=None)
2616 parser.add_argument('--source', dest='header', action='store_false')
2617 parser.add_argument('--user-header', nargs='+', default=[])
2618 parser.add_argument('--cmp-out', action='store_true', default=None,
2619 help='Do not overwrite the output file if the new output is identical to the old')
2620 parser.add_argument('--exclude-op', action='append', default=[])
2621 parser.add_argument('-o', dest='out_file', type=str, default=None)
2622 args = parser.parse_args()
2623
2624 if args.header is None:
2625 parser.error("--header or --source is required")
2626
2627 exclude_ops = [re.compile(expr) for expr in args.exclude_op]
2628
2629 try:
2630 parsed = Family(args.spec, exclude_ops)
2631 if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
2632 print('Spec license:', parsed.license)
2633 print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
2634 os.sys.exit(1)
2635 except yaml.YAMLError as exc:
2636 print(exc)
2637 os.sys.exit(1)
2638 return
2639
2640 supported_models = ['unified']
2641 if args.mode in ['user', 'kernel']:
2642 supported_models += ['directional']
2643 if parsed.msg_id_model not in supported_models:
2644 print(f'Message enum-model {parsed.msg_id_model} not supported for {args.mode} generation')
2645 os.sys.exit(1)
2646
2647 cw = CodeWriter(BaseNlLib(), args.out_file, overwrite=(not args.cmp_out))
2648
2649 _, spec_kernel = find_kernel_root(args.spec)
2650 if args.mode == 'uapi' or args.header:
2651 cw.p(f'/* SPDX-License-Identifier: {parsed.license} */')
2652 else:
2653 cw.p(f'// SPDX-License-Identifier: {parsed.license}')
2654 cw.p("/* Do not edit directly, auto-generated from: */")
2655 cw.p(f"/*\t{spec_kernel} */")
2656 cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
2657 if args.exclude_op or args.user_header:
2658 line = ''
2659 line += ' --user-header '.join([''] + args.user_header)
2660 line += ' --exclude-op '.join([''] + args.exclude_op)
2661 cw.p(f'/* YNL-ARG{line} */')
2662 cw.nl()
2663
2664 if args.mode == 'uapi':
2665 render_uapi(parsed, cw)
2666 return
2667
2668 hdr_prot = f"_LINUX_{parsed.c_name.upper()}_GEN_H"
2669 if args.header:
2670 cw.p('#ifndef ' + hdr_prot)
2671 cw.p('#define ' + hdr_prot)
2672 cw.nl()
2673
2674 if args.mode == 'kernel':
2675 cw.p('#include <net/netlink.h>')
2676 cw.p('#include <net/genetlink.h>')
2677 cw.nl()
2678 if not args.header:
2679 if args.out_file:
2680 cw.p(f'#include "{os.path.basename(args.out_file[:-2])}.h"')
2681 cw.nl()
2682 headers = ['uapi/' + parsed.uapi_header]
2683 else:
2684 cw.p('#include <stdlib.h>')
2685 cw.p('#include <string.h>')
2686 if args.header:
2687 cw.p('#include <linux/types.h>')
2688 if family_contains_bitfield32(parsed):
2689 cw.p('#include <linux/netlink.h>')
2690 else:
2691 cw.p(f'#include "{parsed.name}-user.h"')
2692 cw.p('#include "ynl.h"')
2693 headers = [parsed.uapi_header]
2694 for definition in parsed['definitions']:
2695 if 'header' in definition:
2696 headers.append(definition['header'])
2697 for one in headers:
2698 cw.p(f"#include <{one}>")
2699 cw.nl()
2700
2701 if args.mode == "user":
2702 if not args.header:
2703 cw.p("#include <libmnl/libmnl.h>")
2704 cw.p("#include <linux/genetlink.h>")
2705 cw.nl()
2706 for one in args.user_header:
2707 cw.p(f'#include "{one}"')
2708 else:
2709 cw.p('struct ynl_sock;')
2710 cw.nl()
2711 render_user_family(parsed, cw, True)
2712 cw.nl()
2713
2714 if args.mode == "kernel":
2715 if args.header:
2716 for _, struct in sorted(parsed.pure_nested_structs.items()):
2717 if struct.request:
2718 cw.p('/* Common nested types */')
2719 break
2720 for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2721 if struct.request:
2722 print_req_policy_fwd(cw, struct)
2723 cw.nl()
2724
2725 if parsed.kernel_policy == 'global':
2726 cw.p(f"/* Global operation policy for {parsed.name} */")
2727
2728 struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2729 print_req_policy_fwd(cw, struct)
2730 cw.nl()
2731
2732 if parsed.kernel_policy in {'per-op', 'split'}:
2733 for op_name, op in parsed.ops.items():
2734 if 'do' in op and 'event' not in op:
2735 ri = RenderInfo(cw, parsed, args.mode, op, "do")
2736 print_req_policy_fwd(cw, ri.struct['request'], ri=ri)
2737 cw.nl()
2738
2739 print_kernel_op_table_hdr(parsed, cw)
2740 print_kernel_mcgrp_hdr(parsed, cw)
2741 print_kernel_family_struct_hdr(parsed, cw)
2742 else:
2743 print_kernel_policy_ranges(parsed, cw)
2744
2745 for _, struct in sorted(parsed.pure_nested_structs.items()):
2746 if struct.request:
2747 cw.p('/* Common nested types */')
2748 break
2749 for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2750 if struct.request:
2751 print_req_policy(cw, struct)
2752 cw.nl()
2753
2754 if parsed.kernel_policy == 'global':
2755 cw.p(f"/* Global operation policy for {parsed.name} */")
2756
2757 struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2758 print_req_policy(cw, struct)
2759 cw.nl()
2760
2761 for op_name, op in parsed.ops.items():
2762 if parsed.kernel_policy in {'per-op', 'split'}:
2763 for op_mode in ['do', 'dump']:
2764 if op_mode in op and 'request' in op[op_mode]:
2765 cw.p(f"/* {op.enum_name} - {op_mode} */")
2766 ri = RenderInfo(cw, parsed, args.mode, op, op_mode)
2767 print_req_policy(cw, ri.struct['request'], ri=ri)
2768 cw.nl()
2769
2770 print_kernel_op_table(parsed, cw)
2771 print_kernel_mcgrp_src(parsed, cw)
2772 print_kernel_family_struct_src(parsed, cw)
2773
2774 if args.mode == "user":
2775 if args.header:
2776 cw.p('/* Enums */')
2777 put_op_name_fwd(parsed, cw)
2778
2779 for name, const in parsed.consts.items():
2780 if isinstance(const, EnumSet):
2781 put_enum_to_str_fwd(parsed, cw, const)
2782 cw.nl()
2783
2784 cw.p('/* Common nested types */')
2785 for attr_set, struct in parsed.pure_nested_structs.items():
2786 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2787 print_type_full(ri, struct)
2788
2789 for op_name, op in parsed.ops.items():
2790 cw.p(f"/* ============== {op.enum_name} ============== */")
2791
2792 if 'do' in op and 'event' not in op:
2793 cw.p(f"/* {op.enum_name} - do */")
2794 ri = RenderInfo(cw, parsed, args.mode, op, "do")
2795 print_req_type(ri)
2796 print_req_type_helpers(ri)
2797 cw.nl()
2798 print_rsp_type(ri)
2799 print_rsp_type_helpers(ri)
2800 cw.nl()
2801 print_req_prototype(ri)
2802 cw.nl()
2803
2804 if 'dump' in op:
2805 cw.p(f"/* {op.enum_name} - dump */")
2806 ri = RenderInfo(cw, parsed, args.mode, op, 'dump')
2807 print_req_type(ri)
2808 print_req_type_helpers(ri)
2809 if not ri.type_consistent:
2810 print_rsp_type(ri)
2811 print_wrapped_type(ri)
2812 print_dump_prototype(ri)
2813 cw.nl()
2814
2815 if op.has_ntf:
2816 cw.p(f"/* {op.enum_name} - notify */")
2817 ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
2818 if not ri.type_consistent:
2819 raise Exception(f'Only notifications with consistent types supported ({op.name})')
2820 print_wrapped_type(ri)
2821
2822 for op_name, op in parsed.ntfs.items():
2823 if 'event' in op:
2824 ri = RenderInfo(cw, parsed, args.mode, op, 'event')
2825 cw.p(f"/* {op.enum_name} - event */")
2826 print_rsp_type(ri)
2827 cw.nl()
2828 print_wrapped_type(ri)
2829 cw.nl()
2830 else:
2831 cw.p('/* Enums */')
2832 put_op_name(parsed, cw)
2833
2834 for name, const in parsed.consts.items():
2835 if isinstance(const, EnumSet):
2836 put_enum_to_str(parsed, cw, const)
2837 cw.nl()
2838
2839 has_recursive_nests = False
2840 cw.p('/* Policies */')
2841 for struct in parsed.pure_nested_structs.values():
2842 if struct.recursive:
2843 put_typol_fwd(cw, struct)
2844 has_recursive_nests = True
2845 if has_recursive_nests:
2846 cw.nl()
2847 for name in parsed.pure_nested_structs:
2848 struct = Struct(parsed, name)
2849 put_typol(cw, struct)
2850 for name in parsed.root_sets:
2851 struct = Struct(parsed, name)
2852 put_typol(cw, struct)
2853
2854 cw.p('/* Common nested types */')
2855 if has_recursive_nests:
2856 for attr_set, struct in parsed.pure_nested_structs.items():
2857 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2858 free_rsp_nested_prototype(ri)
2859 if struct.request:
2860 put_req_nested_prototype(ri, struct)
2861 if struct.reply:
2862 parse_rsp_nested_prototype(ri, struct)
2863 cw.nl()
2864 for attr_set, struct in parsed.pure_nested_structs.items():
2865 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2866
2867 free_rsp_nested(ri, struct)
2868 if struct.request:
2869 put_req_nested(ri, struct)
2870 if struct.reply:
2871 parse_rsp_nested(ri, struct)
2872
2873 for op_name, op in parsed.ops.items():
2874 cw.p(f"/* ============== {op.enum_name} ============== */")
2875 if 'do' in op and 'event' not in op:
2876 cw.p(f"/* {op.enum_name} - do */")
2877 ri = RenderInfo(cw, parsed, args.mode, op, "do")
2878 print_req_free(ri)
2879 print_rsp_free(ri)
2880 parse_rsp_msg(ri)
2881 print_req(ri)
2882 cw.nl()
2883
2884 if 'dump' in op:
2885 cw.p(f"/* {op.enum_name} - dump */")
2886 ri = RenderInfo(cw, parsed, args.mode, op, "dump")
2887 if not ri.type_consistent:
2888 parse_rsp_msg(ri, deref=True)
2889 print_req_free(ri)
2890 print_dump_type_free(ri)
2891 print_dump(ri)
2892 cw.nl()
2893
2894 if op.has_ntf:
2895 cw.p(f"/* {op.enum_name} - notify */")
2896 ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
2897 if not ri.type_consistent:
2898 raise Exception(f'Only notifications with consistent types supported ({op.name})')
2899 print_ntf_type_free(ri)
2900
2901 for op_name, op in parsed.ntfs.items():
2902 if 'event' in op:
2903 cw.p(f"/* {op.enum_name} - event */")
2904
2905 ri = RenderInfo(cw, parsed, args.mode, op, "do")
2906 parse_rsp_msg(ri)
2907
2908 ri = RenderInfo(cw, parsed, args.mode, op, "event")
2909 print_ntf_type_free(ri)
2910 cw.nl()
2911 render_user_family(parsed, cw, False)
2912
2913 if args.header:
2914 cw.p(f'#endif /* {hdr_prot} */')
2915
2916
2917if __name__ == "__main__":
2918 main()
1#!/usr/bin/env python3
2# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
3
4import argparse
5import collections
6import filecmp
7import pathlib
8import os
9import re
10import shutil
11import sys
12import tempfile
13import yaml
14
15sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix())
16from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
17
18
19def c_upper(name):
20 return name.upper().replace('-', '_')
21
22
23def c_lower(name):
24 return name.lower().replace('-', '_')
25
26
27def limit_to_number(name):
28 """
29 Turn a string limit like u32-max or s64-min into its numerical value
30 """
31 if name[0] == 'u' and name.endswith('-min'):
32 return 0
33 width = int(name[1:-4])
34 if name[0] == 's':
35 width -= 1
36 value = (1 << width) - 1
37 if name[0] == 's' and name.endswith('-min'):
38 value = -value - 1
39 return value
40
41
42class BaseNlLib:
43 def get_family_id(self):
44 return 'ys->family_id'
45
46
47class Type(SpecAttr):
48 def __init__(self, family, attr_set, attr, value):
49 super().__init__(family, attr_set, attr, value)
50
51 self.attr = attr
52 self.attr_set = attr_set
53 self.type = attr['type']
54 self.checks = attr.get('checks', {})
55
56 self.request = False
57 self.reply = False
58
59 if 'len' in attr:
60 self.len = attr['len']
61
62 if 'nested-attributes' in attr:
63 self.nested_attrs = attr['nested-attributes']
64 if self.nested_attrs == family.name:
65 self.nested_render_name = c_lower(f"{family.ident_name}")
66 else:
67 self.nested_render_name = c_lower(f"{family.ident_name}_{self.nested_attrs}")
68
69 if self.nested_attrs in self.family.consts:
70 self.nested_struct_type = 'struct ' + self.nested_render_name + '_'
71 else:
72 self.nested_struct_type = 'struct ' + self.nested_render_name
73
74 self.c_name = c_lower(self.name)
75 if self.c_name in _C_KW:
76 self.c_name += '_'
77
78 # Added by resolve():
79 self.enum_name = None
80 delattr(self, "enum_name")
81
82 def get_limit(self, limit, default=None):
83 value = self.checks.get(limit, default)
84 if value is None:
85 return value
86 if isinstance(value, int):
87 return value
88 if value in self.family.consts:
89 raise Exception("Resolving family constants not implemented, yet")
90 return limit_to_number(value)
91
92 def get_limit_str(self, limit, default=None, suffix=''):
93 value = self.checks.get(limit, default)
94 if value is None:
95 return ''
96 if isinstance(value, int):
97 return str(value) + suffix
98 if value in self.family.consts:
99 return c_upper(f"{self.family['name']}-{value}")
100 return c_upper(value)
101
102 def resolve(self):
103 if 'name-prefix' in self.attr:
104 enum_name = f"{self.attr['name-prefix']}{self.name}"
105 else:
106 enum_name = f"{self.attr_set.name_prefix}{self.name}"
107 self.enum_name = c_upper(enum_name)
108
109 def is_multi_val(self):
110 return None
111
112 def is_scalar(self):
113 return self.type in {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
114
115 def is_recursive(self):
116 return False
117
118 def is_recursive_for_op(self, ri):
119 return self.is_recursive() and not ri.op
120
121 def presence_type(self):
122 return 'bit'
123
124 def presence_member(self, space, type_filter):
125 if self.presence_type() != type_filter:
126 return
127
128 if self.presence_type() == 'bit':
129 pfx = '__' if space == 'user' else ''
130 return f"{pfx}u32 {self.c_name}:1;"
131
132 if self.presence_type() == 'len':
133 pfx = '__' if space == 'user' else ''
134 return f"{pfx}u32 {self.c_name}_len;"
135
136 def _complex_member_type(self, ri):
137 return None
138
139 def free_needs_iter(self):
140 return False
141
142 def free(self, ri, var, ref):
143 if self.is_multi_val() or self.presence_type() == 'len':
144 ri.cw.p(f'free({var}->{ref}{self.c_name});')
145
146 def arg_member(self, ri):
147 member = self._complex_member_type(ri)
148 if member:
149 arg = [member + ' *' + self.c_name]
150 if self.presence_type() == 'count':
151 arg += ['unsigned int n_' + self.c_name]
152 return arg
153 raise Exception(f"Struct member not implemented for class type {self.type}")
154
155 def struct_member(self, ri):
156 if self.is_multi_val():
157 ri.cw.p(f"unsigned int n_{self.c_name};")
158 member = self._complex_member_type(ri)
159 if member:
160 ptr = '*' if self.is_multi_val() else ''
161 if self.is_recursive_for_op(ri):
162 ptr = '*'
163 ri.cw.p(f"{member} {ptr}{self.c_name};")
164 return
165 members = self.arg_member(ri)
166 for one in members:
167 ri.cw.p(one + ';')
168
169 def _attr_policy(self, policy):
170 return '{ .type = ' + policy + ', }'
171
172 def attr_policy(self, cw):
173 policy = f'NLA_{c_upper(self.type)}'
174 if self.attr.get('byte-order') == 'big-endian':
175 if self.type in {'u16', 'u32'}:
176 policy = f'NLA_BE{self.type[1:]}'
177
178 spec = self._attr_policy(policy)
179 cw.p(f"\t[{self.enum_name}] = {spec},")
180
181 def _attr_typol(self):
182 raise Exception(f"Type policy not implemented for class type {self.type}")
183
184 def attr_typol(self, cw):
185 typol = self._attr_typol()
186 cw.p(f'[{self.enum_name}] = {"{"} .name = "{self.name}", {typol}{"}"},')
187
188 def _attr_put_line(self, ri, var, line):
189 if self.presence_type() == 'bit':
190 ri.cw.p(f"if ({var}->_present.{self.c_name})")
191 elif self.presence_type() == 'len':
192 ri.cw.p(f"if ({var}->_present.{self.c_name}_len)")
193 ri.cw.p(f"{line};")
194
195 def _attr_put_simple(self, ri, var, put_type):
196 line = f"ynl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name})"
197 self._attr_put_line(ri, var, line)
198
199 def attr_put(self, ri, var):
200 raise Exception(f"Put not implemented for class type {self.type}")
201
202 def _attr_get(self, ri, var):
203 raise Exception(f"Attr get not implemented for class type {self.type}")
204
205 def attr_get(self, ri, var, first):
206 lines, init_lines, local_vars = self._attr_get(ri, var)
207 if type(lines) is str:
208 lines = [lines]
209 if type(init_lines) is str:
210 init_lines = [init_lines]
211
212 kw = 'if' if first else 'else if'
213 ri.cw.block_start(line=f"{kw} (type == {self.enum_name})")
214 if local_vars:
215 for local in local_vars:
216 ri.cw.p(local)
217 ri.cw.nl()
218
219 if not self.is_multi_val():
220 ri.cw.p("if (ynl_attr_validate(yarg, attr))")
221 ri.cw.p("return YNL_PARSE_CB_ERROR;")
222 if self.presence_type() == 'bit':
223 ri.cw.p(f"{var}->_present.{self.c_name} = 1;")
224
225 if init_lines:
226 ri.cw.nl()
227 for line in init_lines:
228 ri.cw.p(line)
229
230 for line in lines:
231 ri.cw.p(line)
232 ri.cw.block_end()
233 return True
234
235 def _setter_lines(self, ri, member, presence):
236 raise Exception(f"Setter not implemented for class type {self.type}")
237
238 def setter(self, ri, space, direction, deref=False, ref=None):
239 ref = (ref if ref else []) + [self.c_name]
240 var = "req"
241 member = f"{var}->{'.'.join(ref)}"
242
243 code = []
244 presence = ''
245 for i in range(0, len(ref)):
246 presence = f"{var}->{'.'.join(ref[:i] + [''])}_present.{ref[i]}"
247 # Every layer below last is a nest, so we know it uses bit presence
248 # last layer is "self" and may be a complex type
249 if i == len(ref) - 1 and self.presence_type() != 'bit':
250 continue
251 code.append(presence + ' = 1;')
252 code += self._setter_lines(ri, member, presence)
253
254 func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
255 free = bool([x for x in code if 'free(' in x])
256 alloc = bool([x for x in code if 'alloc(' in x])
257 if free and not alloc:
258 func_name = '__' + func_name
259 ri.cw.write_func('static inline void', func_name, body=code,
260 args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
261
262
263class TypeUnused(Type):
264 def presence_type(self):
265 return ''
266
267 def arg_member(self, ri):
268 return []
269
270 def _attr_get(self, ri, var):
271 return ['return YNL_PARSE_CB_ERROR;'], None, None
272
273 def _attr_typol(self):
274 return '.type = YNL_PT_REJECT, '
275
276 def attr_policy(self, cw):
277 pass
278
279 def attr_put(self, ri, var):
280 pass
281
282 def attr_get(self, ri, var, first):
283 pass
284
285 def setter(self, ri, space, direction, deref=False, ref=None):
286 pass
287
288
289class TypePad(Type):
290 def presence_type(self):
291 return ''
292
293 def arg_member(self, ri):
294 return []
295
296 def _attr_typol(self):
297 return '.type = YNL_PT_IGNORE, '
298
299 def attr_put(self, ri, var):
300 pass
301
302 def attr_get(self, ri, var, first):
303 pass
304
305 def attr_policy(self, cw):
306 pass
307
308 def setter(self, ri, space, direction, deref=False, ref=None):
309 pass
310
311
312class TypeScalar(Type):
313 def __init__(self, family, attr_set, attr, value):
314 super().__init__(family, attr_set, attr, value)
315
316 self.byte_order_comment = ''
317 if 'byte-order' in attr:
318 self.byte_order_comment = f" /* {attr['byte-order']} */"
319
320 if 'enum' in self.attr:
321 enum = self.family.consts[self.attr['enum']]
322 low, high = enum.value_range()
323 if 'min' not in self.checks:
324 if low != 0 or self.type[0] == 's':
325 self.checks['min'] = low
326 if 'max' not in self.checks:
327 self.checks['max'] = high
328
329 if 'min' in self.checks and 'max' in self.checks:
330 if self.get_limit('min') > self.get_limit('max'):
331 raise Exception(f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}')
332 self.checks['range'] = True
333
334 low = min(self.get_limit('min', 0), self.get_limit('max', 0))
335 high = max(self.get_limit('min', 0), self.get_limit('max', 0))
336 if low < 0 and self.type[0] == 'u':
337 raise Exception(f'Invalid limit for "{self.name}" negative limit for unsigned type')
338 if low < -32768 or high > 32767:
339 self.checks['full-range'] = True
340
341 # Added by resolve():
342 self.is_bitfield = None
343 delattr(self, "is_bitfield")
344 self.type_name = None
345 delattr(self, "type_name")
346
347 def resolve(self):
348 self.resolve_up(super())
349
350 if 'enum-as-flags' in self.attr and self.attr['enum-as-flags']:
351 self.is_bitfield = True
352 elif 'enum' in self.attr:
353 self.is_bitfield = self.family.consts[self.attr['enum']]['type'] == 'flags'
354 else:
355 self.is_bitfield = False
356
357 if not self.is_bitfield and 'enum' in self.attr:
358 self.type_name = self.family.consts[self.attr['enum']].user_type
359 elif self.is_auto_scalar:
360 self.type_name = '__' + self.type[0] + '64'
361 else:
362 self.type_name = '__' + self.type
363
364 def _attr_policy(self, policy):
365 if 'flags-mask' in self.checks or self.is_bitfield:
366 if self.is_bitfield:
367 enum = self.family.consts[self.attr['enum']]
368 mask = enum.get_mask(as_flags=True)
369 else:
370 flags = self.family.consts[self.checks['flags-mask']]
371 flag_cnt = len(flags['entries'])
372 mask = (1 << flag_cnt) - 1
373 return f"NLA_POLICY_MASK({policy}, 0x{mask:x})"
374 elif 'full-range' in self.checks:
375 return f"NLA_POLICY_FULL_RANGE({policy}, &{c_lower(self.enum_name)}_range)"
376 elif 'range' in self.checks:
377 return f"NLA_POLICY_RANGE({policy}, {self.get_limit_str('min')}, {self.get_limit_str('max')})"
378 elif 'min' in self.checks:
379 return f"NLA_POLICY_MIN({policy}, {self.get_limit_str('min')})"
380 elif 'max' in self.checks:
381 return f"NLA_POLICY_MAX({policy}, {self.get_limit_str('max')})"
382 return super()._attr_policy(policy)
383
384 def _attr_typol(self):
385 return f'.type = YNL_PT_U{c_upper(self.type[1:])}, '
386
387 def arg_member(self, ri):
388 return [f'{self.type_name} {self.c_name}{self.byte_order_comment}']
389
390 def attr_put(self, ri, var):
391 self._attr_put_simple(ri, var, self.type)
392
393 def _attr_get(self, ri, var):
394 return f"{var}->{self.c_name} = ynl_attr_get_{self.type}(attr);", None, None
395
396 def _setter_lines(self, ri, member, presence):
397 return [f"{member} = {self.c_name};"]
398
399
400class TypeFlag(Type):
401 def arg_member(self, ri):
402 return []
403
404 def _attr_typol(self):
405 return '.type = YNL_PT_FLAG, '
406
407 def attr_put(self, ri, var):
408 self._attr_put_line(ri, var, f"ynl_attr_put(nlh, {self.enum_name}, NULL, 0)")
409
410 def _attr_get(self, ri, var):
411 return [], None, None
412
413 def _setter_lines(self, ri, member, presence):
414 return []
415
416
417class TypeString(Type):
418 def arg_member(self, ri):
419 return [f"const char *{self.c_name}"]
420
421 def presence_type(self):
422 return 'len'
423
424 def struct_member(self, ri):
425 ri.cw.p(f"char *{self.c_name};")
426
427 def _attr_typol(self):
428 return f'.type = YNL_PT_NUL_STR, '
429
430 def _attr_policy(self, policy):
431 if 'exact-len' in self.checks:
432 mem = 'NLA_POLICY_EXACT_LEN(' + self.get_limit_str('exact-len') + ')'
433 else:
434 mem = '{ .type = ' + policy
435 if 'max-len' in self.checks:
436 mem += ', .len = ' + self.get_limit_str('max-len')
437 mem += ', }'
438 return mem
439
440 def attr_policy(self, cw):
441 if self.checks.get('unterminated-ok', False):
442 policy = 'NLA_STRING'
443 else:
444 policy = 'NLA_NUL_STRING'
445
446 spec = self._attr_policy(policy)
447 cw.p(f"\t[{self.enum_name}] = {spec},")
448
449 def attr_put(self, ri, var):
450 self._attr_put_simple(ri, var, 'str')
451
452 def _attr_get(self, ri, var):
453 len_mem = var + '->_present.' + self.c_name + '_len'
454 return [f"{len_mem} = len;",
455 f"{var}->{self.c_name} = malloc(len + 1);",
456 f"memcpy({var}->{self.c_name}, ynl_attr_get_str(attr), len);",
457 f"{var}->{self.c_name}[len] = 0;"], \
458 ['len = strnlen(ynl_attr_get_str(attr), ynl_attr_data_len(attr));'], \
459 ['unsigned int len;']
460
461 def _setter_lines(self, ri, member, presence):
462 return [f"free({member});",
463 f"{presence}_len = strlen({self.c_name});",
464 f"{member} = malloc({presence}_len + 1);",
465 f'memcpy({member}, {self.c_name}, {presence}_len);',
466 f'{member}[{presence}_len] = 0;']
467
468
469class TypeBinary(Type):
470 def arg_member(self, ri):
471 return [f"const void *{self.c_name}", 'size_t len']
472
473 def presence_type(self):
474 return 'len'
475
476 def struct_member(self, ri):
477 ri.cw.p(f"void *{self.c_name};")
478
479 def _attr_typol(self):
480 return f'.type = YNL_PT_BINARY,'
481
482 def _attr_policy(self, policy):
483 if len(self.checks) == 0:
484 pass
485 elif len(self.checks) == 1:
486 check_name = list(self.checks)[0]
487 if check_name not in {'exact-len', 'min-len', 'max-len'}:
488 raise Exception('Unsupported check for binary type: ' + check_name)
489 else:
490 raise Exception('More than one check for binary type not implemented, yet')
491
492 if len(self.checks) == 0:
493 mem = '{ .type = NLA_BINARY, }'
494 elif 'exact-len' in self.checks:
495 mem = 'NLA_POLICY_EXACT_LEN(' + self.get_limit_str('exact-len') + ')'
496 elif 'min-len' in self.checks:
497 mem = '{ .len = ' + self.get_limit_str('min-len') + ', }'
498 elif 'max-len' in self.checks:
499 mem = 'NLA_POLICY_MAX_LEN(' + self.get_limit_str('max-len') + ')'
500
501 return mem
502
503 def attr_put(self, ri, var):
504 self._attr_put_line(ri, var, f"ynl_attr_put(nlh, {self.enum_name}, " +
505 f"{var}->{self.c_name}, {var}->_present.{self.c_name}_len)")
506
507 def _attr_get(self, ri, var):
508 len_mem = var + '->_present.' + self.c_name + '_len'
509 return [f"{len_mem} = len;",
510 f"{var}->{self.c_name} = malloc(len);",
511 f"memcpy({var}->{self.c_name}, ynl_attr_data(attr), len);"], \
512 ['len = ynl_attr_data_len(attr);'], \
513 ['unsigned int len;']
514
515 def _setter_lines(self, ri, member, presence):
516 return [f"free({member});",
517 f"{presence}_len = len;",
518 f"{member} = malloc({presence}_len);",
519 f'memcpy({member}, {self.c_name}, {presence}_len);']
520
521
522class TypeBitfield32(Type):
523 def _complex_member_type(self, ri):
524 return "struct nla_bitfield32"
525
526 def _attr_typol(self):
527 return f'.type = YNL_PT_BITFIELD32, '
528
529 def _attr_policy(self, policy):
530 if not 'enum' in self.attr:
531 raise Exception('Enum required for bitfield32 attr')
532 enum = self.family.consts[self.attr['enum']]
533 mask = enum.get_mask(as_flags=True)
534 return f"NLA_POLICY_BITFIELD32({mask})"
535
536 def attr_put(self, ri, var):
537 line = f"ynl_attr_put(nlh, {self.enum_name}, &{var}->{self.c_name}, sizeof(struct nla_bitfield32))"
538 self._attr_put_line(ri, var, line)
539
540 def _attr_get(self, ri, var):
541 return f"memcpy(&{var}->{self.c_name}, ynl_attr_data(attr), sizeof(struct nla_bitfield32));", None, None
542
543 def _setter_lines(self, ri, member, presence):
544 return [f"memcpy(&{member}, {self.c_name}, sizeof(struct nla_bitfield32));"]
545
546
547class TypeNest(Type):
548 def is_recursive(self):
549 return self.family.pure_nested_structs[self.nested_attrs].recursive
550
551 def _complex_member_type(self, ri):
552 return self.nested_struct_type
553
554 def free(self, ri, var, ref):
555 at = '&'
556 if self.is_recursive_for_op(ri):
557 at = ''
558 ri.cw.p(f'if ({var}->{ref}{self.c_name})')
559 ri.cw.p(f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});')
560
561 def _attr_typol(self):
562 return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
563
564 def _attr_policy(self, policy):
565 return 'NLA_POLICY_NESTED(' + self.nested_render_name + '_nl_policy)'
566
567 def attr_put(self, ri, var):
568 at = '' if self.is_recursive_for_op(ri) else '&'
569 self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
570 f"{self.enum_name}, {at}{var}->{self.c_name})")
571
572 def _attr_get(self, ri, var):
573 get_lines = [f"if ({self.nested_render_name}_parse(&parg, attr))",
574 "return YNL_PARSE_CB_ERROR;"]
575 init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
576 f"parg.data = &{var}->{self.c_name};"]
577 return get_lines, init_lines, None
578
579 def setter(self, ri, space, direction, deref=False, ref=None):
580 ref = (ref if ref else []) + [self.c_name]
581
582 for _, attr in ri.family.pure_nested_structs[self.nested_attrs].member_list():
583 if attr.is_recursive():
584 continue
585 attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref)
586
587
588class TypeMultiAttr(Type):
589 def __init__(self, family, attr_set, attr, value, base_type):
590 super().__init__(family, attr_set, attr, value)
591
592 self.base_type = base_type
593
594 def is_multi_val(self):
595 return True
596
597 def presence_type(self):
598 return 'count'
599
600 def _complex_member_type(self, ri):
601 if 'type' not in self.attr or self.attr['type'] == 'nest':
602 return self.nested_struct_type
603 elif self.attr['type'] in scalars:
604 scalar_pfx = '__' if ri.ku_space == 'user' else ''
605 return scalar_pfx + self.attr['type']
606 else:
607 raise Exception(f"Sub-type {self.attr['type']} not supported yet")
608
609 def free_needs_iter(self):
610 return 'type' not in self.attr or self.attr['type'] == 'nest'
611
612 def free(self, ri, var, ref):
613 if self.attr['type'] in scalars:
614 ri.cw.p(f"free({var}->{ref}{self.c_name});")
615 elif 'type' not in self.attr or self.attr['type'] == 'nest':
616 ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
617 ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
618 ri.cw.p(f"free({var}->{ref}{self.c_name});")
619 else:
620 raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
621
622 def _attr_policy(self, policy):
623 return self.base_type._attr_policy(policy)
624
625 def _attr_typol(self):
626 return self.base_type._attr_typol()
627
628 def _attr_get(self, ri, var):
629 return f'n_{self.c_name}++;', None, None
630
631 def attr_put(self, ri, var):
632 if self.attr['type'] in scalars:
633 put_type = self.type
634 ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
635 ri.cw.p(f"ynl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name}[i]);")
636 elif 'type' not in self.attr or self.attr['type'] == 'nest':
637 ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
638 self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
639 f"{self.enum_name}, &{var}->{self.c_name}[i])")
640 else:
641 raise Exception(f"Put of MultiAttr sub-type {self.attr['type']} not supported yet")
642
643 def _setter_lines(self, ri, member, presence):
644 # For multi-attr we have a count, not presence, hack up the presence
645 presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name
646 return [f"free({member});",
647 f"{member} = {self.c_name};",
648 f"{presence} = n_{self.c_name};"]
649
650
651class TypeArrayNest(Type):
652 def is_multi_val(self):
653 return True
654
655 def presence_type(self):
656 return 'count'
657
658 def _complex_member_type(self, ri):
659 if 'sub-type' not in self.attr or self.attr['sub-type'] == 'nest':
660 return self.nested_struct_type
661 elif self.attr['sub-type'] in scalars:
662 scalar_pfx = '__' if ri.ku_space == 'user' else ''
663 return scalar_pfx + self.attr['sub-type']
664 else:
665 raise Exception(f"Sub-type {self.attr['sub-type']} not supported yet")
666
667 def _attr_typol(self):
668 return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
669
670 def _attr_get(self, ri, var):
671 local_vars = ['const struct nlattr *attr2;']
672 get_lines = [f'attr_{self.c_name} = attr;',
673 'ynl_attr_for_each_nested(attr2, attr)',
674 f'\t{var}->n_{self.c_name}++;']
675 return get_lines, None, local_vars
676
677
678class TypeNestTypeValue(Type):
679 def _complex_member_type(self, ri):
680 return self.nested_struct_type
681
682 def _attr_typol(self):
683 return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
684
685 def _attr_get(self, ri, var):
686 prev = 'attr'
687 tv_args = ''
688 get_lines = []
689 local_vars = []
690 init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
691 f"parg.data = &{var}->{self.c_name};"]
692 if 'type-value' in self.attr:
693 tv_names = [c_lower(x) for x in self.attr["type-value"]]
694 local_vars += [f'const struct nlattr *attr_{", *attr_".join(tv_names)};']
695 local_vars += [f'__u32 {", ".join(tv_names)};']
696 for level in self.attr["type-value"]:
697 level = c_lower(level)
698 get_lines += [f'attr_{level} = ynl_attr_data({prev});']
699 get_lines += [f'{level} = ynl_attr_type(attr_{level});']
700 prev = 'attr_' + level
701
702 tv_args = f", {', '.join(tv_names)}"
703
704 get_lines += [f"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"]
705 return get_lines, init_lines, local_vars
706
707
708class Struct:
709 def __init__(self, family, space_name, type_list=None, inherited=None):
710 self.family = family
711 self.space_name = space_name
712 self.attr_set = family.attr_sets[space_name]
713 # Use list to catch comparisons with empty sets
714 self._inherited = inherited if inherited is not None else []
715 self.inherited = []
716
717 self.nested = type_list is None
718 if family.name == c_lower(space_name):
719 self.render_name = c_lower(family.ident_name)
720 else:
721 self.render_name = c_lower(family.ident_name + '-' + space_name)
722 self.struct_name = 'struct ' + self.render_name
723 if self.nested and space_name in family.consts:
724 self.struct_name += '_'
725 self.ptr_name = self.struct_name + ' *'
726 # All attr sets this one contains, directly or multiple levels down
727 self.child_nests = set()
728
729 self.request = False
730 self.reply = False
731 self.recursive = False
732
733 self.attr_list = []
734 self.attrs = dict()
735 if type_list is not None:
736 for t in type_list:
737 self.attr_list.append((t, self.attr_set[t]),)
738 else:
739 for t in self.attr_set:
740 self.attr_list.append((t, self.attr_set[t]),)
741
742 max_val = 0
743 self.attr_max_val = None
744 for name, attr in self.attr_list:
745 if attr.value >= max_val:
746 max_val = attr.value
747 self.attr_max_val = attr
748 self.attrs[name] = attr
749
750 def __iter__(self):
751 yield from self.attrs
752
753 def __getitem__(self, key):
754 return self.attrs[key]
755
756 def member_list(self):
757 return self.attr_list
758
759 def set_inherited(self, new_inherited):
760 if self._inherited != new_inherited:
761 raise Exception("Inheriting different members not supported")
762 self.inherited = [c_lower(x) for x in sorted(self._inherited)]
763
764
765class EnumEntry(SpecEnumEntry):
766 def __init__(self, enum_set, yaml, prev, value_start):
767 super().__init__(enum_set, yaml, prev, value_start)
768
769 if prev:
770 self.value_change = (self.value != prev.value + 1)
771 else:
772 self.value_change = (self.value != 0)
773 self.value_change = self.value_change or self.enum_set['type'] == 'flags'
774
775 # Added by resolve:
776 self.c_name = None
777 delattr(self, "c_name")
778
779 def resolve(self):
780 self.resolve_up(super())
781
782 self.c_name = c_upper(self.enum_set.value_pfx + self.name)
783
784
785class EnumSet(SpecEnumSet):
786 def __init__(self, family, yaml):
787 self.render_name = c_lower(family.ident_name + '-' + yaml['name'])
788
789 if 'enum-name' in yaml:
790 if yaml['enum-name']:
791 self.enum_name = 'enum ' + c_lower(yaml['enum-name'])
792 self.user_type = self.enum_name
793 else:
794 self.enum_name = None
795 else:
796 self.enum_name = 'enum ' + self.render_name
797
798 if self.enum_name:
799 self.user_type = self.enum_name
800 else:
801 self.user_type = 'int'
802
803 self.value_pfx = yaml.get('name-prefix', f"{family.ident_name}-{yaml['name']}-")
804
805 super().__init__(family, yaml)
806
807 def new_entry(self, entry, prev_entry, value_start):
808 return EnumEntry(self, entry, prev_entry, value_start)
809
810 def value_range(self):
811 low = min([x.value for x in self.entries.values()])
812 high = max([x.value for x in self.entries.values()])
813
814 if high - low + 1 != len(self.entries):
815 raise Exception("Can't get value range for a noncontiguous enum")
816
817 return low, high
818
819
820class AttrSet(SpecAttrSet):
821 def __init__(self, family, yaml):
822 super().__init__(family, yaml)
823
824 if self.subset_of is None:
825 if 'name-prefix' in yaml:
826 pfx = yaml['name-prefix']
827 elif self.name == family.name:
828 pfx = family.ident_name + '-a-'
829 else:
830 pfx = f"{family.ident_name}-a-{self.name}-"
831 self.name_prefix = c_upper(pfx)
832 self.max_name = c_upper(self.yaml.get('attr-max-name', f"{self.name_prefix}max"))
833 self.cnt_name = c_upper(self.yaml.get('attr-cnt-name', f"__{self.name_prefix}max"))
834 else:
835 self.name_prefix = family.attr_sets[self.subset_of].name_prefix
836 self.max_name = family.attr_sets[self.subset_of].max_name
837 self.cnt_name = family.attr_sets[self.subset_of].cnt_name
838
839 # Added by resolve:
840 self.c_name = None
841 delattr(self, "c_name")
842
843 def resolve(self):
844 self.c_name = c_lower(self.name)
845 if self.c_name in _C_KW:
846 self.c_name += '_'
847 if self.c_name == self.family.c_name:
848 self.c_name = ''
849
850 def new_attr(self, elem, value):
851 if elem['type'] in scalars:
852 t = TypeScalar(self.family, self, elem, value)
853 elif elem['type'] == 'unused':
854 t = TypeUnused(self.family, self, elem, value)
855 elif elem['type'] == 'pad':
856 t = TypePad(self.family, self, elem, value)
857 elif elem['type'] == 'flag':
858 t = TypeFlag(self.family, self, elem, value)
859 elif elem['type'] == 'string':
860 t = TypeString(self.family, self, elem, value)
861 elif elem['type'] == 'binary':
862 t = TypeBinary(self.family, self, elem, value)
863 elif elem['type'] == 'bitfield32':
864 t = TypeBitfield32(self.family, self, elem, value)
865 elif elem['type'] == 'nest':
866 t = TypeNest(self.family, self, elem, value)
867 elif elem['type'] == 'indexed-array' and 'sub-type' in elem:
868 if elem["sub-type"] == 'nest':
869 t = TypeArrayNest(self.family, self, elem, value)
870 else:
871 raise Exception(f'new_attr: unsupported sub-type {elem["sub-type"]}')
872 elif elem['type'] == 'nest-type-value':
873 t = TypeNestTypeValue(self.family, self, elem, value)
874 else:
875 raise Exception(f"No typed class for type {elem['type']}")
876
877 if 'multi-attr' in elem and elem['multi-attr']:
878 t = TypeMultiAttr(self.family, self, elem, value, t)
879
880 return t
881
882
883class Operation(SpecOperation):
884 def __init__(self, family, yaml, req_value, rsp_value):
885 super().__init__(family, yaml, req_value, rsp_value)
886
887 self.render_name = c_lower(family.ident_name + '_' + self.name)
888
889 self.dual_policy = ('do' in yaml and 'request' in yaml['do']) and \
890 ('dump' in yaml and 'request' in yaml['dump'])
891
892 self.has_ntf = False
893
894 # Added by resolve:
895 self.enum_name = None
896 delattr(self, "enum_name")
897
898 def resolve(self):
899 self.resolve_up(super())
900
901 if not self.is_async:
902 self.enum_name = self.family.op_prefix + c_upper(self.name)
903 else:
904 self.enum_name = self.family.async_op_prefix + c_upper(self.name)
905
906 def mark_has_ntf(self):
907 self.has_ntf = True
908
909
910class Family(SpecFamily):
911 def __init__(self, file_name, exclude_ops):
912 # Added by resolve:
913 self.c_name = None
914 delattr(self, "c_name")
915 self.op_prefix = None
916 delattr(self, "op_prefix")
917 self.async_op_prefix = None
918 delattr(self, "async_op_prefix")
919 self.mcgrps = None
920 delattr(self, "mcgrps")
921 self.consts = None
922 delattr(self, "consts")
923 self.hooks = None
924 delattr(self, "hooks")
925
926 super().__init__(file_name, exclude_ops=exclude_ops)
927
928 self.fam_key = c_upper(self.yaml.get('c-family-name', self.yaml["name"] + '_FAMILY_NAME'))
929 self.ver_key = c_upper(self.yaml.get('c-version-name', self.yaml["name"] + '_FAMILY_VERSION'))
930
931 if 'definitions' not in self.yaml:
932 self.yaml['definitions'] = []
933
934 if 'uapi-header' in self.yaml:
935 self.uapi_header = self.yaml['uapi-header']
936 else:
937 self.uapi_header = f"linux/{self.ident_name}.h"
938 if self.uapi_header.startswith("linux/") and self.uapi_header.endswith('.h'):
939 self.uapi_header_name = self.uapi_header[6:-2]
940 else:
941 self.uapi_header_name = self.ident_name
942
943 def resolve(self):
944 self.resolve_up(super())
945
946 if self.yaml.get('protocol', 'genetlink') not in {'genetlink', 'genetlink-c', 'genetlink-legacy'}:
947 raise Exception("Codegen only supported for genetlink")
948
949 self.c_name = c_lower(self.ident_name)
950 if 'name-prefix' in self.yaml['operations']:
951 self.op_prefix = c_upper(self.yaml['operations']['name-prefix'])
952 else:
953 self.op_prefix = c_upper(self.yaml['name'] + '-cmd-')
954 if 'async-prefix' in self.yaml['operations']:
955 self.async_op_prefix = c_upper(self.yaml['operations']['async-prefix'])
956 else:
957 self.async_op_prefix = self.op_prefix
958
959 self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
960
961 self.hooks = dict()
962 for when in ['pre', 'post']:
963 self.hooks[when] = dict()
964 for op_mode in ['do', 'dump']:
965 self.hooks[when][op_mode] = dict()
966 self.hooks[when][op_mode]['set'] = set()
967 self.hooks[when][op_mode]['list'] = []
968
969 # dict space-name -> 'request': set(attrs), 'reply': set(attrs)
970 self.root_sets = dict()
971 # dict space-name -> set('request', 'reply')
972 self.pure_nested_structs = dict()
973
974 self._mark_notify()
975 self._mock_up_events()
976
977 self._load_root_sets()
978 self._load_nested_sets()
979 self._load_attr_use()
980 self._load_hooks()
981
982 self.kernel_policy = self.yaml.get('kernel-policy', 'split')
983 if self.kernel_policy == 'global':
984 self._load_global_policy()
985
986 def new_enum(self, elem):
987 return EnumSet(self, elem)
988
989 def new_attr_set(self, elem):
990 return AttrSet(self, elem)
991
992 def new_operation(self, elem, req_value, rsp_value):
993 return Operation(self, elem, req_value, rsp_value)
994
995 def _mark_notify(self):
996 for op in self.msgs.values():
997 if 'notify' in op:
998 self.ops[op['notify']].mark_has_ntf()
999
1000 # Fake a 'do' equivalent of all events, so that we can render their response parsing
1001 def _mock_up_events(self):
1002 for op in self.yaml['operations']['list']:
1003 if 'event' in op:
1004 op['do'] = {
1005 'reply': {
1006 'attributes': op['event']['attributes']
1007 }
1008 }
1009
1010 def _load_root_sets(self):
1011 for op_name, op in self.msgs.items():
1012 if 'attribute-set' not in op:
1013 continue
1014
1015 req_attrs = set()
1016 rsp_attrs = set()
1017 for op_mode in ['do', 'dump']:
1018 if op_mode in op and 'request' in op[op_mode]:
1019 req_attrs.update(set(op[op_mode]['request']['attributes']))
1020 if op_mode in op and 'reply' in op[op_mode]:
1021 rsp_attrs.update(set(op[op_mode]['reply']['attributes']))
1022 if 'event' in op:
1023 rsp_attrs.update(set(op['event']['attributes']))
1024
1025 if op['attribute-set'] not in self.root_sets:
1026 self.root_sets[op['attribute-set']] = {'request': req_attrs, 'reply': rsp_attrs}
1027 else:
1028 self.root_sets[op['attribute-set']]['request'].update(req_attrs)
1029 self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs)
1030
1031 def _sort_pure_types(self):
1032 # Try to reorder according to dependencies
1033 pns_key_list = list(self.pure_nested_structs.keys())
1034 pns_key_seen = set()
1035 rounds = len(pns_key_list) ** 2 # it's basically bubble sort
1036 for _ in range(rounds):
1037 if len(pns_key_list) == 0:
1038 break
1039 name = pns_key_list.pop(0)
1040 finished = True
1041 for _, spec in self.attr_sets[name].items():
1042 if 'nested-attributes' in spec:
1043 nested = spec['nested-attributes']
1044 # If the unknown nest we hit is recursive it's fine, it'll be a pointer
1045 if self.pure_nested_structs[nested].recursive:
1046 continue
1047 if nested not in pns_key_seen:
1048 # Dicts are sorted, this will make struct last
1049 struct = self.pure_nested_structs.pop(name)
1050 self.pure_nested_structs[name] = struct
1051 finished = False
1052 break
1053 if finished:
1054 pns_key_seen.add(name)
1055 else:
1056 pns_key_list.append(name)
1057
1058 def _load_nested_sets(self):
1059 attr_set_queue = list(self.root_sets.keys())
1060 attr_set_seen = set(self.root_sets.keys())
1061
1062 while len(attr_set_queue):
1063 a_set = attr_set_queue.pop(0)
1064 for attr, spec in self.attr_sets[a_set].items():
1065 if 'nested-attributes' not in spec:
1066 continue
1067
1068 nested = spec['nested-attributes']
1069 if nested not in attr_set_seen:
1070 attr_set_queue.append(nested)
1071 attr_set_seen.add(nested)
1072
1073 inherit = set()
1074 if nested not in self.root_sets:
1075 if nested not in self.pure_nested_structs:
1076 self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit)
1077 else:
1078 raise Exception(f'Using attr set as root and nested not supported - {nested}')
1079
1080 if 'type-value' in spec:
1081 if nested in self.root_sets:
1082 raise Exception("Inheriting members to a space used as root not supported")
1083 inherit.update(set(spec['type-value']))
1084 elif spec['type'] == 'indexed-array':
1085 inherit.add('idx')
1086 self.pure_nested_structs[nested].set_inherited(inherit)
1087
1088 for root_set, rs_members in self.root_sets.items():
1089 for attr, spec in self.attr_sets[root_set].items():
1090 if 'nested-attributes' in spec:
1091 nested = spec['nested-attributes']
1092 if attr in rs_members['request']:
1093 self.pure_nested_structs[nested].request = True
1094 if attr in rs_members['reply']:
1095 self.pure_nested_structs[nested].reply = True
1096
1097 self._sort_pure_types()
1098
1099 # Propagate the request / reply / recursive
1100 for attr_set, struct in reversed(self.pure_nested_structs.items()):
1101 for _, spec in self.attr_sets[attr_set].items():
1102 if 'nested-attributes' in spec:
1103 child_name = spec['nested-attributes']
1104 struct.child_nests.add(child_name)
1105 child = self.pure_nested_structs.get(child_name)
1106 if child:
1107 if not child.recursive:
1108 struct.child_nests.update(child.child_nests)
1109 child.request |= struct.request
1110 child.reply |= struct.reply
1111 if attr_set in struct.child_nests:
1112 struct.recursive = True
1113
1114 self._sort_pure_types()
1115
1116 def _load_attr_use(self):
1117 for _, struct in self.pure_nested_structs.items():
1118 if struct.request:
1119 for _, arg in struct.member_list():
1120 arg.request = True
1121 if struct.reply:
1122 for _, arg in struct.member_list():
1123 arg.reply = True
1124
1125 for root_set, rs_members in self.root_sets.items():
1126 for attr, spec in self.attr_sets[root_set].items():
1127 if attr in rs_members['request']:
1128 spec.request = True
1129 if attr in rs_members['reply']:
1130 spec.reply = True
1131
1132 def _load_global_policy(self):
1133 global_set = set()
1134 attr_set_name = None
1135 for op_name, op in self.ops.items():
1136 if not op:
1137 continue
1138 if 'attribute-set' not in op:
1139 continue
1140
1141 if attr_set_name is None:
1142 attr_set_name = op['attribute-set']
1143 if attr_set_name != op['attribute-set']:
1144 raise Exception('For a global policy all ops must use the same set')
1145
1146 for op_mode in ['do', 'dump']:
1147 if op_mode in op:
1148 req = op[op_mode].get('request')
1149 if req:
1150 global_set.update(req.get('attributes', []))
1151
1152 self.global_policy = []
1153 self.global_policy_set = attr_set_name
1154 for attr in self.attr_sets[attr_set_name]:
1155 if attr in global_set:
1156 self.global_policy.append(attr)
1157
1158 def _load_hooks(self):
1159 for op in self.ops.values():
1160 for op_mode in ['do', 'dump']:
1161 if op_mode not in op:
1162 continue
1163 for when in ['pre', 'post']:
1164 if when not in op[op_mode]:
1165 continue
1166 name = op[op_mode][when]
1167 if name in self.hooks[when][op_mode]['set']:
1168 continue
1169 self.hooks[when][op_mode]['set'].add(name)
1170 self.hooks[when][op_mode]['list'].append(name)
1171
1172
1173class RenderInfo:
1174 def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None):
1175 self.family = family
1176 self.nl = cw.nlib
1177 self.ku_space = ku_space
1178 self.op_mode = op_mode
1179 self.op = op
1180
1181 self.fixed_hdr = None
1182 if op and op.fixed_header:
1183 self.fixed_hdr = 'struct ' + c_lower(op.fixed_header)
1184
1185 # 'do' and 'dump' response parsing is identical
1186 self.type_consistent = True
1187 if op_mode != 'do' and 'dump' in op:
1188 if 'do' in op:
1189 if ('reply' in op['do']) != ('reply' in op["dump"]):
1190 self.type_consistent = False
1191 elif 'reply' in op['do'] and op["do"]["reply"] != op["dump"]["reply"]:
1192 self.type_consistent = False
1193 else:
1194 self.type_consistent = False
1195
1196 self.attr_set = attr_set
1197 if not self.attr_set:
1198 self.attr_set = op['attribute-set']
1199
1200 self.type_name_conflict = False
1201 if op:
1202 self.type_name = c_lower(op.name)
1203 else:
1204 self.type_name = c_lower(attr_set)
1205 if attr_set in family.consts:
1206 self.type_name_conflict = True
1207
1208 self.cw = cw
1209
1210 self.struct = dict()
1211 if op_mode == 'notify':
1212 op_mode = 'do'
1213 for op_dir in ['request', 'reply']:
1214 if op:
1215 type_list = []
1216 if op_dir in op[op_mode]:
1217 type_list = op[op_mode][op_dir]['attributes']
1218 self.struct[op_dir] = Struct(family, self.attr_set, type_list=type_list)
1219 if op_mode == 'event':
1220 self.struct['reply'] = Struct(family, self.attr_set, type_list=op['event']['attributes'])
1221
1222
1223class CodeWriter:
1224 def __init__(self, nlib, out_file=None, overwrite=True):
1225 self.nlib = nlib
1226 self._overwrite = overwrite
1227
1228 self._nl = False
1229 self._block_end = False
1230 self._silent_block = False
1231 self._ind = 0
1232 self._ifdef_block = None
1233 if out_file is None:
1234 self._out = os.sys.stdout
1235 else:
1236 self._out = tempfile.NamedTemporaryFile('w+')
1237 self._out_file = out_file
1238
1239 def __del__(self):
1240 self.close_out_file()
1241
1242 def close_out_file(self):
1243 if self._out == os.sys.stdout:
1244 return
1245 # Avoid modifying the file if contents didn't change
1246 self._out.flush()
1247 if not self._overwrite and os.path.isfile(self._out_file):
1248 if filecmp.cmp(self._out.name, self._out_file, shallow=False):
1249 return
1250 with open(self._out_file, 'w+') as out_file:
1251 self._out.seek(0)
1252 shutil.copyfileobj(self._out, out_file)
1253 self._out.close()
1254 self._out = os.sys.stdout
1255
1256 @classmethod
1257 def _is_cond(cls, line):
1258 return line.startswith('if') or line.startswith('while') or line.startswith('for')
1259
1260 def p(self, line, add_ind=0):
1261 if self._block_end:
1262 self._block_end = False
1263 if line.startswith('else'):
1264 line = '} ' + line
1265 else:
1266 self._out.write('\t' * self._ind + '}\n')
1267
1268 if self._nl:
1269 self._out.write('\n')
1270 self._nl = False
1271
1272 ind = self._ind
1273 if line[-1] == ':':
1274 ind -= 1
1275 if self._silent_block:
1276 ind += 1
1277 self._silent_block = line.endswith(')') and CodeWriter._is_cond(line)
1278 if line[0] == '#':
1279 ind = 0
1280 if add_ind:
1281 ind += add_ind
1282 self._out.write('\t' * ind + line + '\n')
1283
1284 def nl(self):
1285 self._nl = True
1286
1287 def block_start(self, line=''):
1288 if line:
1289 line = line + ' '
1290 self.p(line + '{')
1291 self._ind += 1
1292
1293 def block_end(self, line=''):
1294 if line and line[0] not in {';', ','}:
1295 line = ' ' + line
1296 self._ind -= 1
1297 self._nl = False
1298 if not line:
1299 # Delay printing closing bracket in case "else" comes next
1300 if self._block_end:
1301 self._out.write('\t' * (self._ind + 1) + '}\n')
1302 self._block_end = True
1303 else:
1304 self.p('}' + line)
1305
1306 def write_doc_line(self, doc, indent=True):
1307 words = doc.split()
1308 line = ' *'
1309 for word in words:
1310 if len(line) + len(word) >= 79:
1311 self.p(line)
1312 line = ' *'
1313 if indent:
1314 line += ' '
1315 line += ' ' + word
1316 self.p(line)
1317
1318 def write_func_prot(self, qual_ret, name, args=None, doc=None, suffix=''):
1319 if not args:
1320 args = ['void']
1321
1322 if doc:
1323 self.p('/*')
1324 self.p(' * ' + doc)
1325 self.p(' */')
1326
1327 oneline = qual_ret
1328 if qual_ret[-1] != '*':
1329 oneline += ' '
1330 oneline += f"{name}({', '.join(args)}){suffix}"
1331
1332 if len(oneline) < 80:
1333 self.p(oneline)
1334 return
1335
1336 v = qual_ret
1337 if len(v) > 3:
1338 self.p(v)
1339 v = ''
1340 elif qual_ret[-1] != '*':
1341 v += ' '
1342 v += name + '('
1343 ind = '\t' * (len(v) // 8) + ' ' * (len(v) % 8)
1344 delta_ind = len(v) - len(ind)
1345 v += args[0]
1346 i = 1
1347 while i < len(args):
1348 next_len = len(v) + len(args[i])
1349 if v[0] == '\t':
1350 next_len += delta_ind
1351 if next_len > 76:
1352 self.p(v + ',')
1353 v = ind
1354 else:
1355 v += ', '
1356 v += args[i]
1357 i += 1
1358 self.p(v + ')' + suffix)
1359
1360 def write_func_lvar(self, local_vars):
1361 if not local_vars:
1362 return
1363
1364 if type(local_vars) is str:
1365 local_vars = [local_vars]
1366
1367 local_vars.sort(key=len, reverse=True)
1368 for var in local_vars:
1369 self.p(var)
1370 self.nl()
1371
1372 def write_func(self, qual_ret, name, body, args=None, local_vars=None):
1373 self.write_func_prot(qual_ret=qual_ret, name=name, args=args)
1374 self.write_func_lvar(local_vars=local_vars)
1375
1376 self.block_start()
1377 for line in body:
1378 self.p(line)
1379 self.block_end()
1380
1381 def writes_defines(self, defines):
1382 longest = 0
1383 for define in defines:
1384 if len(define[0]) > longest:
1385 longest = len(define[0])
1386 longest = ((longest + 8) // 8) * 8
1387 for define in defines:
1388 line = '#define ' + define[0]
1389 line += '\t' * ((longest - len(define[0]) + 7) // 8)
1390 if type(define[1]) is int:
1391 line += str(define[1])
1392 elif type(define[1]) is str:
1393 line += '"' + define[1] + '"'
1394 self.p(line)
1395
1396 def write_struct_init(self, members):
1397 longest = max([len(x[0]) for x in members])
1398 longest += 1 # because we prepend a .
1399 longest = ((longest + 8) // 8) * 8
1400 for one in members:
1401 line = '.' + one[0]
1402 line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8)
1403 line += '= ' + str(one[1]) + ','
1404 self.p(line)
1405
1406 def ifdef_block(self, config):
1407 config_option = None
1408 if config:
1409 config_option = 'CONFIG_' + c_upper(config)
1410 if self._ifdef_block == config_option:
1411 return
1412
1413 if self._ifdef_block:
1414 self.p('#endif /* ' + self._ifdef_block + ' */')
1415 if config_option:
1416 self.p('#ifdef ' + config_option)
1417 self._ifdef_block = config_option
1418
1419
1420scalars = {'u8', 'u16', 'u32', 'u64', 's32', 's64', 'uint', 'sint'}
1421
1422direction_to_suffix = {
1423 'reply': '_rsp',
1424 'request': '_req',
1425 '': ''
1426}
1427
1428op_mode_to_wrapper = {
1429 'do': '',
1430 'dump': '_list',
1431 'notify': '_ntf',
1432 'event': '',
1433}
1434
1435_C_KW = {
1436 'auto',
1437 'bool',
1438 'break',
1439 'case',
1440 'char',
1441 'const',
1442 'continue',
1443 'default',
1444 'do',
1445 'double',
1446 'else',
1447 'enum',
1448 'extern',
1449 'float',
1450 'for',
1451 'goto',
1452 'if',
1453 'inline',
1454 'int',
1455 'long',
1456 'register',
1457 'return',
1458 'short',
1459 'signed',
1460 'sizeof',
1461 'static',
1462 'struct',
1463 'switch',
1464 'typedef',
1465 'union',
1466 'unsigned',
1467 'void',
1468 'volatile',
1469 'while'
1470}
1471
1472
1473def rdir(direction):
1474 if direction == 'reply':
1475 return 'request'
1476 if direction == 'request':
1477 return 'reply'
1478 return direction
1479
1480
1481def op_prefix(ri, direction, deref=False):
1482 suffix = f"_{ri.type_name}"
1483
1484 if not ri.op_mode or ri.op_mode == 'do':
1485 suffix += f"{direction_to_suffix[direction]}"
1486 else:
1487 if direction == 'request':
1488 suffix += '_req_dump'
1489 else:
1490 if ri.type_consistent:
1491 if deref:
1492 suffix += f"{direction_to_suffix[direction]}"
1493 else:
1494 suffix += op_mode_to_wrapper[ri.op_mode]
1495 else:
1496 suffix += '_rsp'
1497 suffix += '_dump' if deref else '_list'
1498
1499 return f"{ri.family.c_name}{suffix}"
1500
1501
1502def type_name(ri, direction, deref=False):
1503 return f"struct {op_prefix(ri, direction, deref=deref)}"
1504
1505
1506def print_prototype(ri, direction, terminate=True, doc=None):
1507 suffix = ';' if terminate else ''
1508
1509 fname = ri.op.render_name
1510 if ri.op_mode == 'dump':
1511 fname += '_dump'
1512
1513 args = ['struct ynl_sock *ys']
1514 if 'request' in ri.op[ri.op_mode]:
1515 args.append(f"{type_name(ri, direction)} *" + f"{direction_to_suffix[direction][1:]}")
1516
1517 ret = 'int'
1518 if 'reply' in ri.op[ri.op_mode]:
1519 ret = f"{type_name(ri, rdir(direction))} *"
1520
1521 ri.cw.write_func_prot(ret, fname, args, doc=doc, suffix=suffix)
1522
1523
1524def print_req_prototype(ri):
1525 print_prototype(ri, "request", doc=ri.op['doc'])
1526
1527
1528def print_dump_prototype(ri):
1529 print_prototype(ri, "request")
1530
1531
1532def put_typol_fwd(cw, struct):
1533 cw.p(f'extern const struct ynl_policy_nest {struct.render_name}_nest;')
1534
1535
1536def put_typol(cw, struct):
1537 type_max = struct.attr_set.max_name
1538 cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
1539
1540 for _, arg in struct.member_list():
1541 arg.attr_typol(cw)
1542
1543 cw.block_end(line=';')
1544 cw.nl()
1545
1546 cw.block_start(line=f'const struct ynl_policy_nest {struct.render_name}_nest =')
1547 cw.p(f'.max_attr = {type_max},')
1548 cw.p(f'.table = {struct.render_name}_policy,')
1549 cw.block_end(line=';')
1550 cw.nl()
1551
1552
1553def _put_enum_to_str_helper(cw, render_name, map_name, arg_name, enum=None):
1554 args = [f'int {arg_name}']
1555 if enum:
1556 args = [enum.user_type + ' ' + arg_name]
1557 cw.write_func_prot('const char *', f'{render_name}_str', args)
1558 cw.block_start()
1559 if enum and enum.type == 'flags':
1560 cw.p(f'{arg_name} = ffs({arg_name}) - 1;')
1561 cw.p(f'if ({arg_name} < 0 || {arg_name} >= (int)YNL_ARRAY_SIZE({map_name}))')
1562 cw.p('return NULL;')
1563 cw.p(f'return {map_name}[{arg_name}];')
1564 cw.block_end()
1565 cw.nl()
1566
1567
1568def put_op_name_fwd(family, cw):
1569 cw.write_func_prot('const char *', f'{family.c_name}_op_str', ['int op'], suffix=';')
1570
1571
1572def put_op_name(family, cw):
1573 map_name = f'{family.c_name}_op_strmap'
1574 cw.block_start(line=f"static const char * const {map_name}[] =")
1575 for op_name, op in family.msgs.items():
1576 if op.rsp_value:
1577 # Make sure we don't add duplicated entries, if multiple commands
1578 # produce the same response in legacy families.
1579 if family.rsp_by_value[op.rsp_value] != op:
1580 cw.p(f'// skip "{op_name}", duplicate reply value')
1581 continue
1582
1583 if op.req_value == op.rsp_value:
1584 cw.p(f'[{op.enum_name}] = "{op_name}",')
1585 else:
1586 cw.p(f'[{op.rsp_value}] = "{op_name}",')
1587 cw.block_end(line=';')
1588 cw.nl()
1589
1590 _put_enum_to_str_helper(cw, family.c_name + '_op', map_name, 'op')
1591
1592
1593def put_enum_to_str_fwd(family, cw, enum):
1594 args = [enum.user_type + ' value']
1595 cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';')
1596
1597
1598def put_enum_to_str(family, cw, enum):
1599 map_name = f'{enum.render_name}_strmap'
1600 cw.block_start(line=f"static const char * const {map_name}[] =")
1601 for entry in enum.entries.values():
1602 cw.p(f'[{entry.value}] = "{entry.name}",')
1603 cw.block_end(line=';')
1604 cw.nl()
1605
1606 _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
1607
1608
1609def put_req_nested_prototype(ri, struct, suffix=';'):
1610 func_args = ['struct nlmsghdr *nlh',
1611 'unsigned int attr_type',
1612 f'{struct.ptr_name}obj']
1613
1614 ri.cw.write_func_prot('int', f'{struct.render_name}_put', func_args,
1615 suffix=suffix)
1616
1617
1618def put_req_nested(ri, struct):
1619 put_req_nested_prototype(ri, struct, suffix='')
1620 ri.cw.block_start()
1621 ri.cw.write_func_lvar('struct nlattr *nest;')
1622
1623 ri.cw.p("nest = ynl_attr_nest_start(nlh, attr_type);")
1624
1625 for _, arg in struct.member_list():
1626 arg.attr_put(ri, "obj")
1627
1628 ri.cw.p("ynl_attr_nest_end(nlh, nest);")
1629
1630 ri.cw.nl()
1631 ri.cw.p('return 0;')
1632 ri.cw.block_end()
1633 ri.cw.nl()
1634
1635
1636def _multi_parse(ri, struct, init_lines, local_vars):
1637 if struct.nested:
1638 iter_line = "ynl_attr_for_each_nested(attr, nested)"
1639 else:
1640 if ri.fixed_hdr:
1641 local_vars += ['void *hdr;']
1642 iter_line = "ynl_attr_for_each(attr, nlh, yarg->ys->family->hdr_len)"
1643
1644 array_nests = set()
1645 multi_attrs = set()
1646 needs_parg = False
1647 for arg, aspec in struct.member_list():
1648 if aspec['type'] == 'indexed-array' and 'sub-type' in aspec:
1649 if aspec["sub-type"] == 'nest':
1650 local_vars.append(f'const struct nlattr *attr_{aspec.c_name};')
1651 array_nests.add(arg)
1652 else:
1653 raise Exception(f'Not supported sub-type {aspec["sub-type"]}')
1654 if 'multi-attr' in aspec:
1655 multi_attrs.add(arg)
1656 needs_parg |= 'nested-attributes' in aspec
1657 if array_nests or multi_attrs:
1658 local_vars.append('int i;')
1659 if needs_parg:
1660 local_vars.append('struct ynl_parse_arg parg;')
1661 init_lines.append('parg.ys = yarg->ys;')
1662
1663 all_multi = array_nests | multi_attrs
1664
1665 for anest in sorted(all_multi):
1666 local_vars.append(f"unsigned int n_{struct[anest].c_name} = 0;")
1667
1668 ri.cw.block_start()
1669 ri.cw.write_func_lvar(local_vars)
1670
1671 for line in init_lines:
1672 ri.cw.p(line)
1673 ri.cw.nl()
1674
1675 for arg in struct.inherited:
1676 ri.cw.p(f'dst->{arg} = {arg};')
1677
1678 if ri.fixed_hdr:
1679 ri.cw.p('hdr = ynl_nlmsg_data_offset(nlh, sizeof(struct genlmsghdr));')
1680 ri.cw.p(f"memcpy(&dst->_hdr, hdr, sizeof({ri.fixed_hdr}));")
1681 for anest in sorted(all_multi):
1682 aspec = struct[anest]
1683 ri.cw.p(f"if (dst->{aspec.c_name})")
1684 ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
1685
1686 ri.cw.nl()
1687 ri.cw.block_start(line=iter_line)
1688 ri.cw.p('unsigned int type = ynl_attr_type(attr);')
1689 ri.cw.nl()
1690
1691 first = True
1692 for _, arg in struct.member_list():
1693 good = arg.attr_get(ri, 'dst', first=first)
1694 # First may be 'unused' or 'pad', ignore those
1695 first &= not good
1696
1697 ri.cw.block_end()
1698 ri.cw.nl()
1699
1700 for anest in sorted(array_nests):
1701 aspec = struct[anest]
1702
1703 ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1704 ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1705 ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1706 ri.cw.p('i = 0;')
1707 ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1708 ri.cw.block_start(line=f"ynl_attr_for_each_nested(attr, attr_{aspec.c_name})")
1709 ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1710 ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr, ynl_attr_type(attr)))")
1711 ri.cw.p('return YNL_PARSE_CB_ERROR;')
1712 ri.cw.p('i++;')
1713 ri.cw.block_end()
1714 ri.cw.block_end()
1715 ri.cw.nl()
1716
1717 for anest in sorted(multi_attrs):
1718 aspec = struct[anest]
1719 ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1720 ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1721 ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1722 ri.cw.p('i = 0;')
1723 if 'nested-attributes' in aspec:
1724 ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1725 ri.cw.block_start(line=iter_line)
1726 ri.cw.block_start(line=f"if (ynl_attr_type(attr) == {aspec.enum_name})")
1727 if 'nested-attributes' in aspec:
1728 ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1729 ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))")
1730 ri.cw.p('return YNL_PARSE_CB_ERROR;')
1731 elif aspec.type in scalars:
1732 ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.type}(attr);")
1733 else:
1734 raise Exception('Nest parsing type not supported yet')
1735 ri.cw.p('i++;')
1736 ri.cw.block_end()
1737 ri.cw.block_end()
1738 ri.cw.block_end()
1739 ri.cw.nl()
1740
1741 if struct.nested:
1742 ri.cw.p('return 0;')
1743 else:
1744 ri.cw.p('return YNL_PARSE_CB_OK;')
1745 ri.cw.block_end()
1746 ri.cw.nl()
1747
1748
1749def parse_rsp_nested_prototype(ri, struct, suffix=';'):
1750 func_args = ['struct ynl_parse_arg *yarg',
1751 'const struct nlattr *nested']
1752 for arg in struct.inherited:
1753 func_args.append('__u32 ' + arg)
1754
1755 ri.cw.write_func_prot('int', f'{struct.render_name}_parse', func_args,
1756 suffix=suffix)
1757
1758
1759def parse_rsp_nested(ri, struct):
1760 parse_rsp_nested_prototype(ri, struct, suffix='')
1761
1762 local_vars = ['const struct nlattr *attr;',
1763 f'{struct.ptr_name}dst = yarg->data;']
1764 init_lines = []
1765
1766 _multi_parse(ri, struct, init_lines, local_vars)
1767
1768
1769def parse_rsp_msg(ri, deref=False):
1770 if 'reply' not in ri.op[ri.op_mode] and ri.op_mode != 'event':
1771 return
1772
1773 func_args = ['const struct nlmsghdr *nlh',
1774 'struct ynl_parse_arg *yarg']
1775
1776 local_vars = [f'{type_name(ri, "reply", deref=deref)} *dst;',
1777 'const struct nlattr *attr;']
1778 init_lines = ['dst = yarg->data;']
1779
1780 ri.cw.write_func_prot('int', f'{op_prefix(ri, "reply", deref=deref)}_parse', func_args)
1781
1782 if ri.struct["reply"].member_list():
1783 _multi_parse(ri, ri.struct["reply"], init_lines, local_vars)
1784 else:
1785 # Empty reply
1786 ri.cw.block_start()
1787 ri.cw.p('return YNL_PARSE_CB_OK;')
1788 ri.cw.block_end()
1789 ri.cw.nl()
1790
1791
1792def print_req(ri):
1793 ret_ok = '0'
1794 ret_err = '-1'
1795 direction = "request"
1796 local_vars = ['struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };',
1797 'struct nlmsghdr *nlh;',
1798 'int err;']
1799
1800 if 'reply' in ri.op[ri.op_mode]:
1801 ret_ok = 'rsp'
1802 ret_err = 'NULL'
1803 local_vars += [f'{type_name(ri, rdir(direction))} *rsp;']
1804
1805 if ri.fixed_hdr:
1806 local_vars += ['size_t hdr_len;',
1807 'void *hdr;']
1808
1809 print_prototype(ri, direction, terminate=False)
1810 ri.cw.block_start()
1811 ri.cw.write_func_lvar(local_vars)
1812
1813 ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1814
1815 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1816 if 'reply' in ri.op[ri.op_mode]:
1817 ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1818 ri.cw.nl()
1819
1820 if ri.fixed_hdr:
1821 ri.cw.p("hdr_len = sizeof(req->_hdr);")
1822 ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);")
1823 ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);")
1824 ri.cw.nl()
1825
1826 for _, attr in ri.struct["request"].member_list():
1827 attr.attr_put(ri, "req")
1828 ri.cw.nl()
1829
1830 if 'reply' in ri.op[ri.op_mode]:
1831 ri.cw.p('rsp = calloc(1, sizeof(*rsp));')
1832 ri.cw.p('yrs.yarg.data = rsp;')
1833 ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;")
1834 if ri.op.value is not None:
1835 ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};')
1836 else:
1837 ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};')
1838 ri.cw.nl()
1839 ri.cw.p("err = ynl_exec(ys, nlh, &yrs);")
1840 ri.cw.p('if (err < 0)')
1841 if 'reply' in ri.op[ri.op_mode]:
1842 ri.cw.p('goto err_free;')
1843 else:
1844 ri.cw.p('return -1;')
1845 ri.cw.nl()
1846
1847 ri.cw.p(f"return {ret_ok};")
1848 ri.cw.nl()
1849
1850 if 'reply' in ri.op[ri.op_mode]:
1851 ri.cw.p('err_free:')
1852 ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}")
1853 ri.cw.p(f"return {ret_err};")
1854
1855 ri.cw.block_end()
1856
1857
1858def print_dump(ri):
1859 direction = "request"
1860 print_prototype(ri, direction, terminate=False)
1861 ri.cw.block_start()
1862 local_vars = ['struct ynl_dump_state yds = {};',
1863 'struct nlmsghdr *nlh;',
1864 'int err;']
1865
1866 if ri.fixed_hdr:
1867 local_vars += ['size_t hdr_len;',
1868 'void *hdr;']
1869
1870 ri.cw.write_func_lvar(local_vars)
1871
1872 ri.cw.p('yds.yarg.ys = ys;')
1873 ri.cw.p(f"yds.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1874 ri.cw.p("yds.yarg.data = NULL;")
1875 ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});")
1876 ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;")
1877 if ri.op.value is not None:
1878 ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};')
1879 else:
1880 ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};')
1881 ri.cw.nl()
1882 ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1883
1884 if ri.fixed_hdr:
1885 ri.cw.p("hdr_len = sizeof(req->_hdr);")
1886 ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);")
1887 ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);")
1888 ri.cw.nl()
1889
1890 if "request" in ri.op[ri.op_mode]:
1891 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1892 ri.cw.nl()
1893 for _, attr in ri.struct["request"].member_list():
1894 attr.attr_put(ri, "req")
1895 ri.cw.nl()
1896
1897 ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);')
1898 ri.cw.p('if (err < 0)')
1899 ri.cw.p('goto free_list;')
1900 ri.cw.nl()
1901
1902 ri.cw.p('return yds.first;')
1903 ri.cw.nl()
1904 ri.cw.p('free_list:')
1905 ri.cw.p(call_free(ri, rdir(direction), 'yds.first'))
1906 ri.cw.p('return NULL;')
1907 ri.cw.block_end()
1908
1909
1910def call_free(ri, direction, var):
1911 return f"{op_prefix(ri, direction)}_free({var});"
1912
1913
1914def free_arg_name(direction):
1915 if direction:
1916 return direction_to_suffix[direction][1:]
1917 return 'obj'
1918
1919
1920def print_alloc_wrapper(ri, direction):
1921 name = op_prefix(ri, direction)
1922 ri.cw.write_func_prot(f'static inline struct {name} *', f"{name}_alloc", [f"void"])
1923 ri.cw.block_start()
1924 ri.cw.p(f'return calloc(1, sizeof(struct {name}));')
1925 ri.cw.block_end()
1926
1927
1928def print_free_prototype(ri, direction, suffix=';'):
1929 name = op_prefix(ri, direction)
1930 struct_name = name
1931 if ri.type_name_conflict:
1932 struct_name += '_'
1933 arg = free_arg_name(direction)
1934 ri.cw.write_func_prot('void', f"{name}_free", [f"struct {struct_name} *{arg}"], suffix=suffix)
1935
1936
1937def _print_type(ri, direction, struct):
1938 suffix = f'_{ri.type_name}{direction_to_suffix[direction]}'
1939 if not direction and ri.type_name_conflict:
1940 suffix += '_'
1941
1942 if ri.op_mode == 'dump':
1943 suffix += '_dump'
1944
1945 ri.cw.block_start(line=f"struct {ri.family.c_name}{suffix}")
1946
1947 if ri.fixed_hdr:
1948 ri.cw.p(ri.fixed_hdr + ' _hdr;')
1949 ri.cw.nl()
1950
1951 meta_started = False
1952 for _, attr in struct.member_list():
1953 for type_filter in ['len', 'bit']:
1954 line = attr.presence_member(ri.ku_space, type_filter)
1955 if line:
1956 if not meta_started:
1957 ri.cw.block_start(line=f"struct")
1958 meta_started = True
1959 ri.cw.p(line)
1960 if meta_started:
1961 ri.cw.block_end(line='_present;')
1962 ri.cw.nl()
1963
1964 for arg in struct.inherited:
1965 ri.cw.p(f"__u32 {arg};")
1966
1967 for _, attr in struct.member_list():
1968 attr.struct_member(ri)
1969
1970 ri.cw.block_end(line=';')
1971 ri.cw.nl()
1972
1973
1974def print_type(ri, direction):
1975 _print_type(ri, direction, ri.struct[direction])
1976
1977
1978def print_type_full(ri, struct):
1979 _print_type(ri, "", struct)
1980
1981
1982def print_type_helpers(ri, direction, deref=False):
1983 print_free_prototype(ri, direction)
1984 ri.cw.nl()
1985
1986 if ri.ku_space == 'user' and direction == 'request':
1987 for _, attr in ri.struct[direction].member_list():
1988 attr.setter(ri, ri.attr_set, direction, deref=deref)
1989 ri.cw.nl()
1990
1991
1992def print_req_type_helpers(ri):
1993 if len(ri.struct["request"].attr_list) == 0:
1994 return
1995 print_alloc_wrapper(ri, "request")
1996 print_type_helpers(ri, "request")
1997
1998
1999def print_rsp_type_helpers(ri):
2000 if 'reply' not in ri.op[ri.op_mode]:
2001 return
2002 print_type_helpers(ri, "reply")
2003
2004
2005def print_parse_prototype(ri, direction, terminate=True):
2006 suffix = "_rsp" if direction == "reply" else "_req"
2007 term = ';' if terminate else ''
2008
2009 ri.cw.write_func_prot('void', f"{ri.op.render_name}{suffix}_parse",
2010 ['const struct nlattr **tb',
2011 f"struct {ri.op.render_name}{suffix} *req"],
2012 suffix=term)
2013
2014
2015def print_req_type(ri):
2016 if len(ri.struct["request"].attr_list) == 0:
2017 return
2018 print_type(ri, "request")
2019
2020
2021def print_req_free(ri):
2022 if 'request' not in ri.op[ri.op_mode]:
2023 return
2024 _free_type(ri, 'request', ri.struct['request'])
2025
2026
2027def print_rsp_type(ri):
2028 if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]:
2029 direction = 'reply'
2030 elif ri.op_mode == 'event':
2031 direction = 'reply'
2032 else:
2033 return
2034 print_type(ri, direction)
2035
2036
2037def print_wrapped_type(ri):
2038 ri.cw.block_start(line=f"{type_name(ri, 'reply')}")
2039 if ri.op_mode == 'dump':
2040 ri.cw.p(f"{type_name(ri, 'reply')} *next;")
2041 elif ri.op_mode == 'notify' or ri.op_mode == 'event':
2042 ri.cw.p('__u16 family;')
2043 ri.cw.p('__u8 cmd;')
2044 ri.cw.p('struct ynl_ntf_base_type *next;')
2045 ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);")
2046 ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__((aligned(8)));")
2047 ri.cw.block_end(line=';')
2048 ri.cw.nl()
2049 print_free_prototype(ri, 'reply')
2050 ri.cw.nl()
2051
2052
2053def _free_type_members_iter(ri, struct):
2054 for _, attr in struct.member_list():
2055 if attr.free_needs_iter():
2056 ri.cw.p('unsigned int i;')
2057 ri.cw.nl()
2058 break
2059
2060
2061def _free_type_members(ri, var, struct, ref=''):
2062 for _, attr in struct.member_list():
2063 attr.free(ri, var, ref)
2064
2065
2066def _free_type(ri, direction, struct):
2067 var = free_arg_name(direction)
2068
2069 print_free_prototype(ri, direction, suffix='')
2070 ri.cw.block_start()
2071 _free_type_members_iter(ri, struct)
2072 _free_type_members(ri, var, struct)
2073 if direction:
2074 ri.cw.p(f'free({var});')
2075 ri.cw.block_end()
2076 ri.cw.nl()
2077
2078
2079def free_rsp_nested_prototype(ri):
2080 print_free_prototype(ri, "")
2081
2082
2083def free_rsp_nested(ri, struct):
2084 _free_type(ri, "", struct)
2085
2086
2087def print_rsp_free(ri):
2088 if 'reply' not in ri.op[ri.op_mode]:
2089 return
2090 _free_type(ri, 'reply', ri.struct['reply'])
2091
2092
2093def print_dump_type_free(ri):
2094 sub_type = type_name(ri, 'reply')
2095
2096 print_free_prototype(ri, 'reply', suffix='')
2097 ri.cw.block_start()
2098 ri.cw.p(f"{sub_type} *next = rsp;")
2099 ri.cw.nl()
2100 ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)')
2101 _free_type_members_iter(ri, ri.struct['reply'])
2102 ri.cw.p('rsp = next;')
2103 ri.cw.p('next = rsp->next;')
2104 ri.cw.nl()
2105
2106 _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2107 ri.cw.p(f'free(rsp);')
2108 ri.cw.block_end()
2109 ri.cw.block_end()
2110 ri.cw.nl()
2111
2112
2113def print_ntf_type_free(ri):
2114 print_free_prototype(ri, 'reply', suffix='')
2115 ri.cw.block_start()
2116 _free_type_members_iter(ri, ri.struct['reply'])
2117 _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2118 ri.cw.p(f'free(rsp);')
2119 ri.cw.block_end()
2120 ri.cw.nl()
2121
2122
2123def print_req_policy_fwd(cw, struct, ri=None, terminate=True):
2124 if terminate and ri and policy_should_be_static(struct.family):
2125 return
2126
2127 if terminate:
2128 prefix = 'extern '
2129 else:
2130 if ri and policy_should_be_static(struct.family):
2131 prefix = 'static '
2132 else:
2133 prefix = ''
2134
2135 suffix = ';' if terminate else ' = {'
2136
2137 max_attr = struct.attr_max_val
2138 if ri:
2139 name = ri.op.render_name
2140 if ri.op.dual_policy:
2141 name += '_' + ri.op_mode
2142 else:
2143 name = struct.render_name
2144 cw.p(f"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}")
2145
2146
2147def print_req_policy(cw, struct, ri=None):
2148 if ri and ri.op:
2149 cw.ifdef_block(ri.op.get('config-cond', None))
2150 print_req_policy_fwd(cw, struct, ri=ri, terminate=False)
2151 for _, arg in struct.member_list():
2152 arg.attr_policy(cw)
2153 cw.p("};")
2154 cw.ifdef_block(None)
2155 cw.nl()
2156
2157
2158def kernel_can_gen_family_struct(family):
2159 return family.proto == 'genetlink'
2160
2161
2162def policy_should_be_static(family):
2163 return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family)
2164
2165
2166def print_kernel_policy_ranges(family, cw):
2167 first = True
2168 for _, attr_set in family.attr_sets.items():
2169 if attr_set.subset_of:
2170 continue
2171
2172 for _, attr in attr_set.items():
2173 if not attr.request:
2174 continue
2175 if 'full-range' not in attr.checks:
2176 continue
2177
2178 if first:
2179 cw.p('/* Integer value ranges */')
2180 first = False
2181
2182 sign = '' if attr.type[0] == 'u' else '_signed'
2183 suffix = 'ULL' if attr.type[0] == 'u' else 'LL'
2184 cw.block_start(line=f'static const struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =')
2185 members = []
2186 if 'min' in attr.checks:
2187 members.append(('min', attr.get_limit_str('min', suffix=suffix)))
2188 if 'max' in attr.checks:
2189 members.append(('max', attr.get_limit_str('max', suffix=suffix)))
2190 cw.write_struct_init(members)
2191 cw.block_end(line=';')
2192 cw.nl()
2193
2194
2195def print_kernel_op_table_fwd(family, cw, terminate):
2196 exported = not kernel_can_gen_family_struct(family)
2197
2198 if not terminate or exported:
2199 cw.p(f"/* Ops table for {family.ident_name} */")
2200
2201 pol_to_struct = {'global': 'genl_small_ops',
2202 'per-op': 'genl_ops',
2203 'split': 'genl_split_ops'}
2204 struct_type = pol_to_struct[family.kernel_policy]
2205
2206 if not exported:
2207 cnt = ""
2208 elif family.kernel_policy == 'split':
2209 cnt = 0
2210 for op in family.ops.values():
2211 if 'do' in op:
2212 cnt += 1
2213 if 'dump' in op:
2214 cnt += 1
2215 else:
2216 cnt = len(family.ops)
2217
2218 qual = 'static const' if not exported else 'const'
2219 line = f"{qual} struct {struct_type} {family.c_name}_nl_ops[{cnt}]"
2220 if terminate:
2221 cw.p(f"extern {line};")
2222 else:
2223 cw.block_start(line=line + ' =')
2224
2225 if not terminate:
2226 return
2227
2228 cw.nl()
2229 for name in family.hooks['pre']['do']['list']:
2230 cw.write_func_prot('int', c_lower(name),
2231 ['const struct genl_split_ops *ops',
2232 'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2233 for name in family.hooks['post']['do']['list']:
2234 cw.write_func_prot('void', c_lower(name),
2235 ['const struct genl_split_ops *ops',
2236 'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2237 for name in family.hooks['pre']['dump']['list']:
2238 cw.write_func_prot('int', c_lower(name),
2239 ['struct netlink_callback *cb'], suffix=';')
2240 for name in family.hooks['post']['dump']['list']:
2241 cw.write_func_prot('int', c_lower(name),
2242 ['struct netlink_callback *cb'], suffix=';')
2243
2244 cw.nl()
2245
2246 for op_name, op in family.ops.items():
2247 if op.is_async:
2248 continue
2249
2250 if 'do' in op:
2251 name = c_lower(f"{family.ident_name}-nl-{op_name}-doit")
2252 cw.write_func_prot('int', name,
2253 ['struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2254
2255 if 'dump' in op:
2256 name = c_lower(f"{family.ident_name}-nl-{op_name}-dumpit")
2257 cw.write_func_prot('int', name,
2258 ['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix=';')
2259 cw.nl()
2260
2261
2262def print_kernel_op_table_hdr(family, cw):
2263 print_kernel_op_table_fwd(family, cw, terminate=True)
2264
2265
2266def print_kernel_op_table(family, cw):
2267 print_kernel_op_table_fwd(family, cw, terminate=False)
2268 if family.kernel_policy == 'global' or family.kernel_policy == 'per-op':
2269 for op_name, op in family.ops.items():
2270 if op.is_async:
2271 continue
2272
2273 cw.ifdef_block(op.get('config-cond', None))
2274 cw.block_start()
2275 members = [('cmd', op.enum_name)]
2276 if 'dont-validate' in op:
2277 members.append(('validate',
2278 ' | '.join([c_upper('genl-dont-validate-' + x)
2279 for x in op['dont-validate']])), )
2280 for op_mode in ['do', 'dump']:
2281 if op_mode in op:
2282 name = c_lower(f"{family.ident_name}-nl-{op_name}-{op_mode}it")
2283 members.append((op_mode + 'it', name))
2284 if family.kernel_policy == 'per-op':
2285 struct = Struct(family, op['attribute-set'],
2286 type_list=op['do']['request']['attributes'])
2287
2288 name = c_lower(f"{family.ident_name}-{op_name}-nl-policy")
2289 members.append(('policy', name))
2290 members.append(('maxattr', struct.attr_max_val.enum_name))
2291 if 'flags' in op:
2292 members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in op['flags']])))
2293 cw.write_struct_init(members)
2294 cw.block_end(line=',')
2295 elif family.kernel_policy == 'split':
2296 cb_names = {'do': {'pre': 'pre_doit', 'post': 'post_doit'},
2297 'dump': {'pre': 'start', 'post': 'done'}}
2298
2299 for op_name, op in family.ops.items():
2300 for op_mode in ['do', 'dump']:
2301 if op.is_async or op_mode not in op:
2302 continue
2303
2304 cw.ifdef_block(op.get('config-cond', None))
2305 cw.block_start()
2306 members = [('cmd', op.enum_name)]
2307 if 'dont-validate' in op:
2308 dont_validate = []
2309 for x in op['dont-validate']:
2310 if op_mode == 'do' and x in ['dump', 'dump-strict']:
2311 continue
2312 if op_mode == "dump" and x == 'strict':
2313 continue
2314 dont_validate.append(x)
2315
2316 if dont_validate:
2317 members.append(('validate',
2318 ' | '.join([c_upper('genl-dont-validate-' + x)
2319 for x in dont_validate])), )
2320 name = c_lower(f"{family.ident_name}-nl-{op_name}-{op_mode}it")
2321 if 'pre' in op[op_mode]:
2322 members.append((cb_names[op_mode]['pre'], c_lower(op[op_mode]['pre'])))
2323 members.append((op_mode + 'it', name))
2324 if 'post' in op[op_mode]:
2325 members.append((cb_names[op_mode]['post'], c_lower(op[op_mode]['post'])))
2326 if 'request' in op[op_mode]:
2327 struct = Struct(family, op['attribute-set'],
2328 type_list=op[op_mode]['request']['attributes'])
2329
2330 if op.dual_policy:
2331 name = c_lower(f"{family.ident_name}-{op_name}-{op_mode}-nl-policy")
2332 else:
2333 name = c_lower(f"{family.ident_name}-{op_name}-nl-policy")
2334 members.append(('policy', name))
2335 members.append(('maxattr', struct.attr_max_val.enum_name))
2336 flags = (op['flags'] if 'flags' in op else []) + ['cmd-cap-' + op_mode]
2337 members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in flags])))
2338 cw.write_struct_init(members)
2339 cw.block_end(line=',')
2340 cw.ifdef_block(None)
2341
2342 cw.block_end(line=';')
2343 cw.nl()
2344
2345
2346def print_kernel_mcgrp_hdr(family, cw):
2347 if not family.mcgrps['list']:
2348 return
2349
2350 cw.block_start('enum')
2351 for grp in family.mcgrps['list']:
2352 grp_id = c_upper(f"{family.ident_name}-nlgrp-{grp['name']},")
2353 cw.p(grp_id)
2354 cw.block_end(';')
2355 cw.nl()
2356
2357
2358def print_kernel_mcgrp_src(family, cw):
2359 if not family.mcgrps['list']:
2360 return
2361
2362 cw.block_start('static const struct genl_multicast_group ' + family.c_name + '_nl_mcgrps[] =')
2363 for grp in family.mcgrps['list']:
2364 name = grp['name']
2365 grp_id = c_upper(f"{family.ident_name}-nlgrp-{name}")
2366 cw.p('[' + grp_id + '] = { "' + name + '", },')
2367 cw.block_end(';')
2368 cw.nl()
2369
2370
2371def print_kernel_family_struct_hdr(family, cw):
2372 if not kernel_can_gen_family_struct(family):
2373 return
2374
2375 cw.p(f"extern struct genl_family {family.c_name}_nl_family;")
2376 cw.nl()
2377 if 'sock-priv' in family.kernel_family:
2378 cw.p(f'void {family.c_name}_nl_sock_priv_init({family.kernel_family["sock-priv"]} *priv);')
2379 cw.p(f'void {family.c_name}_nl_sock_priv_destroy({family.kernel_family["sock-priv"]} *priv);')
2380 cw.nl()
2381
2382
2383def print_kernel_family_struct_src(family, cw):
2384 if not kernel_can_gen_family_struct(family):
2385 return
2386
2387 if 'sock-priv' in family.kernel_family:
2388 # Generate "trampolines" to make CFI happy
2389 cw.write_func("static void", f"__{family.c_name}_nl_sock_priv_init",
2390 [f"{family.c_name}_nl_sock_priv_init(priv);"],
2391 ["void *priv"])
2392 cw.nl()
2393 cw.write_func("static void", f"__{family.c_name}_nl_sock_priv_destroy",
2394 [f"{family.c_name}_nl_sock_priv_destroy(priv);"],
2395 ["void *priv"])
2396 cw.nl()
2397
2398 cw.block_start(f"struct genl_family {family.ident_name}_nl_family __ro_after_init =")
2399 cw.p('.name\t\t= ' + family.fam_key + ',')
2400 cw.p('.version\t= ' + family.ver_key + ',')
2401 cw.p('.netnsok\t= true,')
2402 cw.p('.parallel_ops\t= true,')
2403 cw.p('.module\t\t= THIS_MODULE,')
2404 if family.kernel_policy == 'per-op':
2405 cw.p(f'.ops\t\t= {family.c_name}_nl_ops,')
2406 cw.p(f'.n_ops\t\t= ARRAY_SIZE({family.c_name}_nl_ops),')
2407 elif family.kernel_policy == 'split':
2408 cw.p(f'.split_ops\t= {family.c_name}_nl_ops,')
2409 cw.p(f'.n_split_ops\t= ARRAY_SIZE({family.c_name}_nl_ops),')
2410 if family.mcgrps['list']:
2411 cw.p(f'.mcgrps\t\t= {family.c_name}_nl_mcgrps,')
2412 cw.p(f'.n_mcgrps\t= ARRAY_SIZE({family.c_name}_nl_mcgrps),')
2413 if 'sock-priv' in family.kernel_family:
2414 cw.p(f'.sock_priv_size\t= sizeof({family.kernel_family["sock-priv"]}),')
2415 cw.p(f'.sock_priv_init\t= __{family.c_name}_nl_sock_priv_init,')
2416 cw.p(f'.sock_priv_destroy = __{family.c_name}_nl_sock_priv_destroy,')
2417 cw.block_end(';')
2418
2419
2420def uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'):
2421 start_line = 'enum'
2422 if enum_name in obj:
2423 if obj[enum_name]:
2424 start_line = 'enum ' + c_lower(obj[enum_name])
2425 elif ckey and ckey in obj:
2426 start_line = 'enum ' + family.c_name + '_' + c_lower(obj[ckey])
2427 cw.block_start(line=start_line)
2428
2429
2430def render_uapi(family, cw):
2431 hdr_prot = f"_UAPI_LINUX_{c_upper(family.uapi_header_name)}_H"
2432 hdr_prot = hdr_prot.replace('/', '_')
2433 cw.p('#ifndef ' + hdr_prot)
2434 cw.p('#define ' + hdr_prot)
2435 cw.nl()
2436
2437 defines = [(family.fam_key, family["name"]),
2438 (family.ver_key, family.get('version', 1))]
2439 cw.writes_defines(defines)
2440 cw.nl()
2441
2442 defines = []
2443 for const in family['definitions']:
2444 if const['type'] != 'const':
2445 cw.writes_defines(defines)
2446 defines = []
2447 cw.nl()
2448
2449 # Write kdoc for enum and flags (one day maybe also structs)
2450 if const['type'] == 'enum' or const['type'] == 'flags':
2451 enum = family.consts[const['name']]
2452
2453 if enum.has_doc():
2454 if enum.has_entry_doc():
2455 cw.p('/**')
2456 doc = ''
2457 if 'doc' in enum:
2458 doc = ' - ' + enum['doc']
2459 cw.write_doc_line(enum.enum_name + doc)
2460 else:
2461 cw.p('/*')
2462 cw.write_doc_line(enum['doc'], indent=False)
2463 for entry in enum.entries.values():
2464 if entry.has_doc():
2465 doc = '@' + entry.c_name + ': ' + entry['doc']
2466 cw.write_doc_line(doc)
2467 cw.p(' */')
2468
2469 uapi_enum_start(family, cw, const, 'name')
2470 name_pfx = const.get('name-prefix', f"{family.ident_name}-{const['name']}-")
2471 for entry in enum.entries.values():
2472 suffix = ','
2473 if entry.value_change:
2474 suffix = f" = {entry.user_value()}" + suffix
2475 cw.p(entry.c_name + suffix)
2476
2477 if const.get('render-max', False):
2478 cw.nl()
2479 cw.p('/* private: */')
2480 if const['type'] == 'flags':
2481 max_name = c_upper(name_pfx + 'mask')
2482 max_val = f' = {enum.get_mask()},'
2483 cw.p(max_name + max_val)
2484 else:
2485 max_name = c_upper(name_pfx + 'max')
2486 cw.p('__' + max_name + ',')
2487 cw.p(max_name + ' = (__' + max_name + ' - 1)')
2488 cw.block_end(line=';')
2489 cw.nl()
2490 elif const['type'] == 'const':
2491 defines.append([c_upper(family.get('c-define-name',
2492 f"{family.ident_name}-{const['name']}")),
2493 const['value']])
2494
2495 if defines:
2496 cw.writes_defines(defines)
2497 cw.nl()
2498
2499 max_by_define = family.get('max-by-define', False)
2500
2501 for _, attr_set in family.attr_sets.items():
2502 if attr_set.subset_of:
2503 continue
2504
2505 max_value = f"({attr_set.cnt_name} - 1)"
2506
2507 val = 0
2508 uapi_enum_start(family, cw, attr_set.yaml, 'enum-name')
2509 for _, attr in attr_set.items():
2510 suffix = ','
2511 if attr.value != val:
2512 suffix = f" = {attr.value},"
2513 val = attr.value
2514 val += 1
2515 cw.p(attr.enum_name + suffix)
2516 cw.nl()
2517 cw.p(attr_set.cnt_name + ('' if max_by_define else ','))
2518 if not max_by_define:
2519 cw.p(f"{attr_set.max_name} = {max_value}")
2520 cw.block_end(line=';')
2521 if max_by_define:
2522 cw.p(f"#define {attr_set.max_name} {max_value}")
2523 cw.nl()
2524
2525 # Commands
2526 separate_ntf = 'async-prefix' in family['operations']
2527
2528 max_name = c_upper(family.get('cmd-max-name', f"{family.op_prefix}MAX"))
2529 cnt_name = c_upper(family.get('cmd-cnt-name', f"__{family.op_prefix}MAX"))
2530 max_value = f"({cnt_name} - 1)"
2531
2532 uapi_enum_start(family, cw, family['operations'], 'enum-name')
2533 val = 0
2534 for op in family.msgs.values():
2535 if separate_ntf and ('notify' in op or 'event' in op):
2536 continue
2537
2538 suffix = ','
2539 if op.value != val:
2540 suffix = f" = {op.value},"
2541 val = op.value
2542 cw.p(op.enum_name + suffix)
2543 val += 1
2544 cw.nl()
2545 cw.p(cnt_name + ('' if max_by_define else ','))
2546 if not max_by_define:
2547 cw.p(f"{max_name} = {max_value}")
2548 cw.block_end(line=';')
2549 if max_by_define:
2550 cw.p(f"#define {max_name} {max_value}")
2551 cw.nl()
2552
2553 if separate_ntf:
2554 uapi_enum_start(family, cw, family['operations'], enum_name='async-enum')
2555 for op in family.msgs.values():
2556 if separate_ntf and not ('notify' in op or 'event' in op):
2557 continue
2558
2559 suffix = ','
2560 if 'value' in op:
2561 suffix = f" = {op['value']},"
2562 cw.p(op.enum_name + suffix)
2563 cw.block_end(line=';')
2564 cw.nl()
2565
2566 # Multicast
2567 defines = []
2568 for grp in family.mcgrps['list']:
2569 name = grp['name']
2570 defines.append([c_upper(grp.get('c-define-name', f"{family.ident_name}-mcgrp-{name}")),
2571 f'{name}'])
2572 cw.nl()
2573 if defines:
2574 cw.writes_defines(defines)
2575 cw.nl()
2576
2577 cw.p(f'#endif /* {hdr_prot} */')
2578
2579
2580def _render_user_ntf_entry(ri, op):
2581 ri.cw.block_start(line=f"[{op.enum_name}] = ")
2582 ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),")
2583 ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,")
2584 ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,")
2585 ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,")
2586 ri.cw.block_end(line=',')
2587
2588
2589def render_user_family(family, cw, prototype):
2590 symbol = f'const struct ynl_family ynl_{family.c_name}_family'
2591 if prototype:
2592 cw.p(f'extern {symbol};')
2593 return
2594
2595 if family.ntfs:
2596 cw.block_start(line=f"static const struct ynl_ntf_info {family['name']}_ntf_info[] = ")
2597 for ntf_op_name, ntf_op in family.ntfs.items():
2598 if 'notify' in ntf_op:
2599 op = family.ops[ntf_op['notify']]
2600 ri = RenderInfo(cw, family, "user", op, "notify")
2601 elif 'event' in ntf_op:
2602 ri = RenderInfo(cw, family, "user", ntf_op, "event")
2603 else:
2604 raise Exception('Invalid notification ' + ntf_op_name)
2605 _render_user_ntf_entry(ri, ntf_op)
2606 for op_name, op in family.ops.items():
2607 if 'event' not in op:
2608 continue
2609 ri = RenderInfo(cw, family, "user", op, "event")
2610 _render_user_ntf_entry(ri, op)
2611 cw.block_end(line=";")
2612 cw.nl()
2613
2614 cw.block_start(f'{symbol} = ')
2615 cw.p(f'.name\t\t= "{family.c_name}",')
2616 if family.fixed_header:
2617 cw.p(f'.hdr_len\t= sizeof(struct genlmsghdr) + sizeof(struct {c_lower(family.fixed_header)}),')
2618 else:
2619 cw.p('.hdr_len\t= sizeof(struct genlmsghdr),')
2620 if family.ntfs:
2621 cw.p(f".ntf_info\t= {family['name']}_ntf_info,")
2622 cw.p(f".ntf_info_size\t= YNL_ARRAY_SIZE({family['name']}_ntf_info),")
2623 cw.block_end(line=';')
2624
2625
2626def family_contains_bitfield32(family):
2627 for _, attr_set in family.attr_sets.items():
2628 if attr_set.subset_of:
2629 continue
2630 for _, attr in attr_set.items():
2631 if attr.type == "bitfield32":
2632 return True
2633 return False
2634
2635
2636def find_kernel_root(full_path):
2637 sub_path = ''
2638 while True:
2639 sub_path = os.path.join(os.path.basename(full_path), sub_path)
2640 full_path = os.path.dirname(full_path)
2641 maintainers = os.path.join(full_path, "MAINTAINERS")
2642 if os.path.exists(maintainers):
2643 return full_path, sub_path[:-1]
2644
2645
2646def main():
2647 parser = argparse.ArgumentParser(description='Netlink simple parsing generator')
2648 parser.add_argument('--mode', dest='mode', type=str, required=True)
2649 parser.add_argument('--spec', dest='spec', type=str, required=True)
2650 parser.add_argument('--header', dest='header', action='store_true', default=None)
2651 parser.add_argument('--source', dest='header', action='store_false')
2652 parser.add_argument('--user-header', nargs='+', default=[])
2653 parser.add_argument('--cmp-out', action='store_true', default=None,
2654 help='Do not overwrite the output file if the new output is identical to the old')
2655 parser.add_argument('--exclude-op', action='append', default=[])
2656 parser.add_argument('-o', dest='out_file', type=str, default=None)
2657 args = parser.parse_args()
2658
2659 if args.header is None:
2660 parser.error("--header or --source is required")
2661
2662 exclude_ops = [re.compile(expr) for expr in args.exclude_op]
2663
2664 try:
2665 parsed = Family(args.spec, exclude_ops)
2666 if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
2667 print('Spec license:', parsed.license)
2668 print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
2669 os.sys.exit(1)
2670 except yaml.YAMLError as exc:
2671 print(exc)
2672 os.sys.exit(1)
2673 return
2674
2675 supported_models = ['unified']
2676 if args.mode in ['user', 'kernel']:
2677 supported_models += ['directional']
2678 if parsed.msg_id_model not in supported_models:
2679 print(f'Message enum-model {parsed.msg_id_model} not supported for {args.mode} generation')
2680 os.sys.exit(1)
2681
2682 cw = CodeWriter(BaseNlLib(), args.out_file, overwrite=(not args.cmp_out))
2683
2684 _, spec_kernel = find_kernel_root(args.spec)
2685 if args.mode == 'uapi' or args.header:
2686 cw.p(f'/* SPDX-License-Identifier: {parsed.license} */')
2687 else:
2688 cw.p(f'// SPDX-License-Identifier: {parsed.license}')
2689 cw.p("/* Do not edit directly, auto-generated from: */")
2690 cw.p(f"/*\t{spec_kernel} */")
2691 cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
2692 if args.exclude_op or args.user_header:
2693 line = ''
2694 line += ' --user-header '.join([''] + args.user_header)
2695 line += ' --exclude-op '.join([''] + args.exclude_op)
2696 cw.p(f'/* YNL-ARG{line} */')
2697 cw.nl()
2698
2699 if args.mode == 'uapi':
2700 render_uapi(parsed, cw)
2701 return
2702
2703 hdr_prot = f"_LINUX_{parsed.c_name.upper()}_GEN_H"
2704 if args.header:
2705 cw.p('#ifndef ' + hdr_prot)
2706 cw.p('#define ' + hdr_prot)
2707 cw.nl()
2708
2709 hdr_file=os.path.basename(args.out_file[:-2]) + ".h"
2710
2711 if args.mode == 'kernel':
2712 cw.p('#include <net/netlink.h>')
2713 cw.p('#include <net/genetlink.h>')
2714 cw.nl()
2715 if not args.header:
2716 if args.out_file:
2717 cw.p(f'#include "{hdr_file}"')
2718 cw.nl()
2719 headers = ['uapi/' + parsed.uapi_header]
2720 headers += parsed.kernel_family.get('headers', [])
2721 else:
2722 cw.p('#include <stdlib.h>')
2723 cw.p('#include <string.h>')
2724 if args.header:
2725 cw.p('#include <linux/types.h>')
2726 if family_contains_bitfield32(parsed):
2727 cw.p('#include <linux/netlink.h>')
2728 else:
2729 cw.p(f'#include "{hdr_file}"')
2730 cw.p('#include "ynl.h"')
2731 headers = [parsed.uapi_header]
2732 for definition in parsed['definitions']:
2733 if 'header' in definition:
2734 headers.append(definition['header'])
2735 for one in headers:
2736 cw.p(f"#include <{one}>")
2737 cw.nl()
2738
2739 if args.mode == "user":
2740 if not args.header:
2741 cw.p("#include <linux/genetlink.h>")
2742 cw.nl()
2743 for one in args.user_header:
2744 cw.p(f'#include "{one}"')
2745 else:
2746 cw.p('struct ynl_sock;')
2747 cw.nl()
2748 render_user_family(parsed, cw, True)
2749 cw.nl()
2750
2751 if args.mode == "kernel":
2752 if args.header:
2753 for _, struct in sorted(parsed.pure_nested_structs.items()):
2754 if struct.request:
2755 cw.p('/* Common nested types */')
2756 break
2757 for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2758 if struct.request:
2759 print_req_policy_fwd(cw, struct)
2760 cw.nl()
2761
2762 if parsed.kernel_policy == 'global':
2763 cw.p(f"/* Global operation policy for {parsed.name} */")
2764
2765 struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2766 print_req_policy_fwd(cw, struct)
2767 cw.nl()
2768
2769 if parsed.kernel_policy in {'per-op', 'split'}:
2770 for op_name, op in parsed.ops.items():
2771 if 'do' in op and 'event' not in op:
2772 ri = RenderInfo(cw, parsed, args.mode, op, "do")
2773 print_req_policy_fwd(cw, ri.struct['request'], ri=ri)
2774 cw.nl()
2775
2776 print_kernel_op_table_hdr(parsed, cw)
2777 print_kernel_mcgrp_hdr(parsed, cw)
2778 print_kernel_family_struct_hdr(parsed, cw)
2779 else:
2780 print_kernel_policy_ranges(parsed, cw)
2781
2782 for _, struct in sorted(parsed.pure_nested_structs.items()):
2783 if struct.request:
2784 cw.p('/* Common nested types */')
2785 break
2786 for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2787 if struct.request:
2788 print_req_policy(cw, struct)
2789 cw.nl()
2790
2791 if parsed.kernel_policy == 'global':
2792 cw.p(f"/* Global operation policy for {parsed.name} */")
2793
2794 struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2795 print_req_policy(cw, struct)
2796 cw.nl()
2797
2798 for op_name, op in parsed.ops.items():
2799 if parsed.kernel_policy in {'per-op', 'split'}:
2800 for op_mode in ['do', 'dump']:
2801 if op_mode in op and 'request' in op[op_mode]:
2802 cw.p(f"/* {op.enum_name} - {op_mode} */")
2803 ri = RenderInfo(cw, parsed, args.mode, op, op_mode)
2804 print_req_policy(cw, ri.struct['request'], ri=ri)
2805 cw.nl()
2806
2807 print_kernel_op_table(parsed, cw)
2808 print_kernel_mcgrp_src(parsed, cw)
2809 print_kernel_family_struct_src(parsed, cw)
2810
2811 if args.mode == "user":
2812 if args.header:
2813 cw.p('/* Enums */')
2814 put_op_name_fwd(parsed, cw)
2815
2816 for name, const in parsed.consts.items():
2817 if isinstance(const, EnumSet):
2818 put_enum_to_str_fwd(parsed, cw, const)
2819 cw.nl()
2820
2821 cw.p('/* Common nested types */')
2822 for attr_set, struct in parsed.pure_nested_structs.items():
2823 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2824 print_type_full(ri, struct)
2825
2826 for op_name, op in parsed.ops.items():
2827 cw.p(f"/* ============== {op.enum_name} ============== */")
2828
2829 if 'do' in op and 'event' not in op:
2830 cw.p(f"/* {op.enum_name} - do */")
2831 ri = RenderInfo(cw, parsed, args.mode, op, "do")
2832 print_req_type(ri)
2833 print_req_type_helpers(ri)
2834 cw.nl()
2835 print_rsp_type(ri)
2836 print_rsp_type_helpers(ri)
2837 cw.nl()
2838 print_req_prototype(ri)
2839 cw.nl()
2840
2841 if 'dump' in op:
2842 cw.p(f"/* {op.enum_name} - dump */")
2843 ri = RenderInfo(cw, parsed, args.mode, op, 'dump')
2844 print_req_type(ri)
2845 print_req_type_helpers(ri)
2846 if not ri.type_consistent:
2847 print_rsp_type(ri)
2848 print_wrapped_type(ri)
2849 print_dump_prototype(ri)
2850 cw.nl()
2851
2852 if op.has_ntf:
2853 cw.p(f"/* {op.enum_name} - notify */")
2854 ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
2855 if not ri.type_consistent:
2856 raise Exception(f'Only notifications with consistent types supported ({op.name})')
2857 print_wrapped_type(ri)
2858
2859 for op_name, op in parsed.ntfs.items():
2860 if 'event' in op:
2861 ri = RenderInfo(cw, parsed, args.mode, op, 'event')
2862 cw.p(f"/* {op.enum_name} - event */")
2863 print_rsp_type(ri)
2864 cw.nl()
2865 print_wrapped_type(ri)
2866 cw.nl()
2867 else:
2868 cw.p('/* Enums */')
2869 put_op_name(parsed, cw)
2870
2871 for name, const in parsed.consts.items():
2872 if isinstance(const, EnumSet):
2873 put_enum_to_str(parsed, cw, const)
2874 cw.nl()
2875
2876 has_recursive_nests = False
2877 cw.p('/* Policies */')
2878 for struct in parsed.pure_nested_structs.values():
2879 if struct.recursive:
2880 put_typol_fwd(cw, struct)
2881 has_recursive_nests = True
2882 if has_recursive_nests:
2883 cw.nl()
2884 for name in parsed.pure_nested_structs:
2885 struct = Struct(parsed, name)
2886 put_typol(cw, struct)
2887 for name in parsed.root_sets:
2888 struct = Struct(parsed, name)
2889 put_typol(cw, struct)
2890
2891 cw.p('/* Common nested types */')
2892 if has_recursive_nests:
2893 for attr_set, struct in parsed.pure_nested_structs.items():
2894 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2895 free_rsp_nested_prototype(ri)
2896 if struct.request:
2897 put_req_nested_prototype(ri, struct)
2898 if struct.reply:
2899 parse_rsp_nested_prototype(ri, struct)
2900 cw.nl()
2901 for attr_set, struct in parsed.pure_nested_structs.items():
2902 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2903
2904 free_rsp_nested(ri, struct)
2905 if struct.request:
2906 put_req_nested(ri, struct)
2907 if struct.reply:
2908 parse_rsp_nested(ri, struct)
2909
2910 for op_name, op in parsed.ops.items():
2911 cw.p(f"/* ============== {op.enum_name} ============== */")
2912 if 'do' in op and 'event' not in op:
2913 cw.p(f"/* {op.enum_name} - do */")
2914 ri = RenderInfo(cw, parsed, args.mode, op, "do")
2915 print_req_free(ri)
2916 print_rsp_free(ri)
2917 parse_rsp_msg(ri)
2918 print_req(ri)
2919 cw.nl()
2920
2921 if 'dump' in op:
2922 cw.p(f"/* {op.enum_name} - dump */")
2923 ri = RenderInfo(cw, parsed, args.mode, op, "dump")
2924 if not ri.type_consistent:
2925 parse_rsp_msg(ri, deref=True)
2926 print_req_free(ri)
2927 print_dump_type_free(ri)
2928 print_dump(ri)
2929 cw.nl()
2930
2931 if op.has_ntf:
2932 cw.p(f"/* {op.enum_name} - notify */")
2933 ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
2934 if not ri.type_consistent:
2935 raise Exception(f'Only notifications with consistent types supported ({op.name})')
2936 print_ntf_type_free(ri)
2937
2938 for op_name, op in parsed.ntfs.items():
2939 if 'event' in op:
2940 cw.p(f"/* {op.enum_name} - event */")
2941
2942 ri = RenderInfo(cw, parsed, args.mode, op, "do")
2943 parse_rsp_msg(ri)
2944
2945 ri = RenderInfo(cw, parsed, args.mode, op, "event")
2946 print_ntf_type_free(ri)
2947 cw.nl()
2948 render_user_family(parsed, cw, False)
2949
2950 if args.header:
2951 cw.p(f'#endif /* {hdr_prot} */')
2952
2953
2954if __name__ == "__main__":
2955 main()