2 # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
13 from lib
import SpecFamily
, SpecAttrSet
, SpecAttr
, SpecOperation
, SpecEnumSet
, SpecEnumEntry
17 return name
.upper().replace('-', '_')
21 return name
.lower().replace('-', '_')
24 def limit_to_number(name
):
26 Turn a string limit like u32-max or s64-min into its numerical value
28 if name
[0] == 'u' and name
.endswith('-min'):
30 width
= int(name
[1:-4])
33 value
= (1 << width
) - 1
34 if name
[0] == 's' and name
.endswith('-min'):
40 def get_family_id(self
):
41 return 'ys->family_id'
43 def parse_cb_run(self
, cb
, data
, is_dump
=False, indent
=1):
44 ind
= '\n\t\t' + '\t' * indent
+ ' '
46 return f
"mnl_cb_run2(ys->rx_buf, len, 0, 0, {cb}, {data},{ind}ynl_cb_array, NLMSG_MIN_TYPE)"
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)"
53 def __init__(self
, family
, attr_set
, attr
, value
):
54 super().__init
__(family
, attr_set
, attr
, value
)
57 self
.attr_set
= attr_set
58 self
.type = attr
['type']
59 self
.checks
= attr
.get('checks', {})
65 self
.len = attr
['len']
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
= f
"{family.name}"
72 self
.nested_render_name
= f
"{family.name}_{c_lower(self.nested_attrs)}"
74 if self
.nested_attrs
in self
.family
.consts
:
75 self
.nested_struct_type
= 'struct ' + self
.nested_render_name
+ '_'
77 self
.nested_struct_type
= 'struct ' + self
.nested_render_name
79 self
.c_name
= c_lower(self
.name
)
80 if self
.c_name
in _C_KW
:
85 delattr(self
, "enum_name")
87 def get_limit(self
, limit
, default
=None):
88 value
= self
.checks
.get(limit
, default
)
91 if not isinstance(value
, int):
92 value
= limit_to_number(value
)
96 if 'name-prefix' in self
.attr
:
97 enum_name
= f
"{self.attr['name-prefix']}{self.name}"
99 enum_name
= f
"{self.attr_set.name_prefix}{self.name}"
100 self
.enum_name
= c_upper(enum_name
)
102 def is_multi_val(self
):
106 return self
.type in {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
108 def presence_type(self
):
111 def presence_member(self
, space
, type_filter
):
112 if self
.presence_type() != type_filter
:
115 if self
.presence_type() == 'bit':
116 pfx
= '__' if space
== 'user' else ''
117 return f
"{pfx}u32 {self.c_name}:1;"
119 if self
.presence_type() == 'len':
120 pfx
= '__' if space
== 'user' else ''
121 return f
"{pfx}u32 {self.c_name}_len;"
123 def _complex_member_type(self
, ri
):
126 def free_needs_iter(self
):
129 def free(self
, ri
, var
, ref
):
130 if self
.is_multi_val() or self
.presence_type() == 'len':
131 ri
.cw
.p(f
'free({var}->{ref}{self.c_name});')
133 def arg_member(self
, ri
):
134 member
= self
._complex
_member
_type
(ri
)
136 arg
= [member
+ ' *' + self
.c_name
]
137 if self
.presence_type() == 'count':
138 arg
+= ['unsigned int n_' + self
.c_name
]
140 raise Exception(f
"Struct member not implemented for class type {self.type}")
142 def struct_member(self
, ri
):
143 if self
.is_multi_val():
144 ri
.cw
.p(f
"unsigned int n_{self.c_name};")
145 member
= self
._complex
_member
_type
(ri
)
147 ptr
= '*' if self
.is_multi_val() else ''
148 ri
.cw
.p(f
"{member} {ptr}{self.c_name};")
150 members
= self
.arg_member(ri
)
154 def _attr_policy(self
, policy
):
155 return '{ .type = ' + policy
+ ', }'
157 def attr_policy(self
, cw
):
158 policy
= c_upper('nla-' + self
.attr
['type'])
160 spec
= self
._attr
_policy
(policy
)
161 cw
.p(f
"\t[{self.enum_name}] = {spec},")
164 # mnl does not have helpers for signed integer types
165 # turn signed type into unsigned
166 # this only makes sense for scalar types
172 def _attr_typol(self
):
173 raise Exception(f
"Type policy not implemented for class type {self.type}")
175 def attr_typol(self
, cw
):
176 typol
= self
._attr
_typol
()
177 cw
.p(f
'[{self.enum_name}] = {"{"} .name = "{self.name}", {typol}{"}"},')
179 def _attr_put_line(self
, ri
, var
, line
):
180 if self
.presence_type() == 'bit':
181 ri
.cw
.p(f
"if ({var}->_present.{self.c_name})")
182 elif self
.presence_type() == 'len':
183 ri
.cw
.p(f
"if ({var}->_present.{self.c_name}_len)")
186 def _attr_put_simple(self
, ri
, var
, put_type
):
187 line
= f
"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name})"
188 self
._attr
_put
_line
(ri
, var
, line
)
190 def attr_put(self
, ri
, var
):
191 raise Exception(f
"Put not implemented for class type {self.type}")
193 def _attr_get(self
, ri
, var
):
194 raise Exception(f
"Attr get not implemented for class type {self.type}")
196 def attr_get(self
, ri
, var
, first
):
197 lines
, init_lines
, local_vars
= self
._attr
_get
(ri
, var
)
198 if type(lines
) is str:
200 if type(init_lines
) is str:
201 init_lines
= [init_lines
]
203 kw
= 'if' if first
else 'else if'
204 ri
.cw
.block_start(line
=f
"{kw} (type == {self.enum_name})")
206 for local
in local_vars
:
210 if not self
.is_multi_val():
211 ri
.cw
.p("if (ynl_attr_validate(yarg, attr))")
212 ri
.cw
.p("return MNL_CB_ERROR;")
213 if self
.presence_type() == 'bit':
214 ri
.cw
.p(f
"{var}->_present.{self.c_name} = 1;")
218 for line
in init_lines
:
226 def _setter_lines(self
, ri
, member
, presence
):
227 raise Exception(f
"Setter not implemented for class type {self.type}")
229 def setter(self
, ri
, space
, direction
, deref
=False, ref
=None):
230 ref
= (ref
if ref
else []) + [self
.c_name
]
232 member
= f
"{var}->{'.'.join(ref)}"
236 for i
in range(0, len(ref
)):
237 presence
= f
"{var}->{'.'.join(ref[:i] + [''])}_present.{ref[i]}"
238 if self
.presence_type() == 'bit':
239 code
.append(presence
+ ' = 1;')
240 code
+= self
._setter
_lines
(ri
, member
, presence
)
242 func_name
= f
"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
243 free
= bool([x
for x
in code
if 'free(' in x
])
244 alloc
= bool([x
for x
in code
if 'alloc(' in x
])
245 if free
and not alloc
:
246 func_name
= '__' + func_name
247 ri
.cw
.write_func('static inline void', func_name
, body
=code
,
248 args
=[f
'{type_name(ri, direction, deref=deref)} *{var}'] + self
.arg_member(ri
))
251 class TypeUnused(Type
):
252 def presence_type(self
):
255 def arg_member(self
, ri
):
258 def _attr_get(self
, ri
, var
):
259 return ['return MNL_CB_ERROR;'], None, None
261 def _attr_typol(self
):
262 return '.type = YNL_PT_REJECT, '
264 def attr_policy(self
, cw
):
269 def presence_type(self
):
272 def arg_member(self
, ri
):
275 def _attr_typol(self
):
276 return '.type = YNL_PT_IGNORE, '
278 def attr_put(self
, ri
, var
):
281 def attr_get(self
, ri
, var
, first
):
284 def attr_policy(self
, cw
):
287 def setter(self
, ri
, space
, direction
, deref
=False, ref
=None):
291 class TypeScalar(Type
):
292 def __init__(self
, family
, attr_set
, attr
, value
):
293 super().__init
__(family
, attr_set
, attr
, value
)
295 self
.byte_order_comment
= ''
296 if 'byte-order' in attr
:
297 self
.byte_order_comment
= f
" /* {attr['byte-order']} */"
299 if 'enum' in self
.attr
:
300 enum
= self
.family
.consts
[self
.attr
['enum']]
301 low
, high
= enum
.value_range()
302 if 'min' not in self
.checks
:
303 if low
!= 0 or self
.type[0] == 's':
304 self
.checks
['min'] = low
305 if 'max' not in self
.checks
:
306 self
.checks
['max'] = high
308 if 'min' in self
.checks
and 'max' in self
.checks
:
309 if self
.get_limit('min') > self
.get_limit('max'):
310 raise Exception(f
'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}')
311 self
.checks
['range'] = True
313 low
= min(self
.get_limit('min', 0), self
.get_limit('max', 0))
314 high
= max(self
.get_limit('min', 0), self
.get_limit('max', 0))
315 if low
< 0 and self
.type[0] == 'u':
316 raise Exception(f
'Invalid limit for "{self.name}" negative limit for unsigned type')
317 if low
< -32768 or high
> 32767:
318 self
.checks
['full-range'] = True
320 # Added by resolve():
321 self
.is_bitfield
= None
322 delattr(self
, "is_bitfield")
323 self
.type_name
= None
324 delattr(self
, "type_name")
327 self
.resolve_up(super())
329 if 'enum-as-flags' in self
.attr
and self
.attr
['enum-as-flags']:
330 self
.is_bitfield
= True
331 elif 'enum' in self
.attr
:
332 self
.is_bitfield
= self
.family
.consts
[self
.attr
['enum']]['type'] == 'flags'
334 self
.is_bitfield
= False
336 maybe_enum
= not self
.is_bitfield
and 'enum' in self
.attr
337 if maybe_enum
and self
.family
.consts
[self
.attr
['enum']].enum_name
:
338 self
.type_name
= f
"enum {self.family.name}_{c_lower(self.attr['enum'])}"
339 elif self
.is_auto_scalar
:
340 self
.type_name
= '__' + self
.type[0] + '64'
342 self
.type_name
= '__' + self
.type
345 return self
._mnl
_type
()
347 def _attr_policy(self
, policy
):
348 if 'flags-mask' in self
.checks
or self
.is_bitfield
:
350 enum
= self
.family
.consts
[self
.attr
['enum']]
351 mask
= enum
.get_mask(as_flags
=True)
353 flags
= self
.family
.consts
[self
.checks
['flags-mask']]
354 flag_cnt
= len(flags
['entries'])
355 mask
= (1 << flag_cnt
) - 1
356 return f
"NLA_POLICY_MASK({policy}, 0x{mask:x})"
357 elif 'full-range' in self
.checks
:
358 return f
"NLA_POLICY_FULL_RANGE({policy}, &{c_lower(self.enum_name)}_range)"
359 elif 'range' in self
.checks
:
360 return f
"NLA_POLICY_RANGE({policy}, {self.get_limit('min')}, {self.get_limit('max')})"
361 elif 'min' in self
.checks
:
362 return f
"NLA_POLICY_MIN({policy}, {self.get_limit('min')})"
363 elif 'max' in self
.checks
:
364 return f
"NLA_POLICY_MAX({policy}, {self.get_limit('max')})"
365 return super()._attr
_policy
(policy
)
367 def _attr_typol(self
):
368 return f
'.type = YNL_PT_U{c_upper(self.type[1:])}, '
370 def arg_member(self
, ri
):
371 return [f
'{self.type_name} {self.c_name}{self.byte_order_comment}']
373 def attr_put(self
, ri
, var
):
374 self
._attr
_put
_simple
(ri
, var
, self
.mnl_type())
376 def _attr_get(self
, ri
, var
):
377 return f
"{var}->{self.c_name} = mnl_attr_get_{self.mnl_type()}(attr);", None, None
379 def _setter_lines(self
, ri
, member
, presence
):
380 return [f
"{member} = {self.c_name};"]
383 class TypeFlag(Type
):
384 def arg_member(self
, ri
):
387 def _attr_typol(self
):
388 return '.type = YNL_PT_FLAG, '
390 def attr_put(self
, ri
, var
):
391 self
._attr
_put
_line
(ri
, var
, f
"mnl_attr_put(nlh, {self.enum_name}, 0, NULL)")
393 def _attr_get(self
, ri
, var
):
394 return [], None, None
396 def _setter_lines(self
, ri
, member
, presence
):
400 class TypeString(Type
):
401 def arg_member(self
, ri
):
402 return [f
"const char *{self.c_name}"]
404 def presence_type(self
):
407 def struct_member(self
, ri
):
408 ri
.cw
.p(f
"char *{self.c_name};")
410 def _attr_typol(self
):
411 return f
'.type = YNL_PT_NUL_STR, '
413 def _attr_policy(self
, policy
):
414 if 'exact-len' in self
.checks
:
415 mem
= 'NLA_POLICY_EXACT_LEN(' + str(self
.checks
['exact-len']) + ')'
417 mem
= '{ .type = ' + policy
418 if 'max-len' in self
.checks
:
419 mem
+= ', .len = ' + str(self
.get_limit('max-len'))
423 def attr_policy(self
, cw
):
424 if self
.checks
.get('unterminated-ok', False):
425 policy
= 'NLA_STRING'
427 policy
= 'NLA_NUL_STRING'
429 spec
= self
._attr
_policy
(policy
)
430 cw
.p(f
"\t[{self.enum_name}] = {spec},")
432 def attr_put(self
, ri
, var
):
433 self
._attr
_put
_simple
(ri
, var
, 'strz')
435 def _attr_get(self
, ri
, var
):
436 len_mem
= var
+ '->_present.' + self
.c_name
+ '_len'
437 return [f
"{len_mem} = len;",
438 f
"{var}->{self.c_name} = malloc(len + 1);",
439 f
"memcpy({var}->{self.c_name}, mnl_attr_get_str(attr), len);",
440 f
"{var}->{self.c_name}[len] = 0;"], \
441 ['len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr));'], \
442 ['unsigned int len;']
444 def _setter_lines(self
, ri
, member
, presence
):
445 return [f
"free({member});",
446 f
"{presence}_len = strlen({self.c_name});",
447 f
"{member} = malloc({presence}_len + 1);",
448 f
'memcpy({member}, {self.c_name}, {presence}_len);',
449 f
'{member}[{presence}_len] = 0;']
452 class TypeBinary(Type
):
453 def arg_member(self
, ri
):
454 return [f
"const void *{self.c_name}", 'size_t len']
456 def presence_type(self
):
459 def struct_member(self
, ri
):
460 ri
.cw
.p(f
"void *{self.c_name};")
462 def _attr_typol(self
):
463 return f
'.type = YNL_PT_BINARY,'
465 def _attr_policy(self
, policy
):
466 if 'exact-len' in self
.checks
:
467 mem
= 'NLA_POLICY_EXACT_LEN(' + str(self
.checks
['exact-len']) + ')'
470 if len(self
.checks
) == 1 and 'min-len' in self
.checks
:
471 mem
+= '.len = ' + str(self
.get_limit('min-len'))
472 elif len(self
.checks
) == 0:
473 mem
+= '.type = NLA_BINARY'
475 raise Exception('One or more of binary type checks not implemented, yet')
479 def attr_put(self
, ri
, var
):
480 self
._attr
_put
_line
(ri
, var
, f
"mnl_attr_put(nlh, {self.enum_name}, " +
481 f
"{var}->_present.{self.c_name}_len, {var}->{self.c_name})")
483 def _attr_get(self
, ri
, var
):
484 len_mem
= var
+ '->_present.' + self
.c_name
+ '_len'
485 return [f
"{len_mem} = len;",
486 f
"{var}->{self.c_name} = malloc(len);",
487 f
"memcpy({var}->{self.c_name}, mnl_attr_get_payload(attr), len);"], \
488 ['len = mnl_attr_get_payload_len(attr);'], \
489 ['unsigned int len;']
491 def _setter_lines(self
, ri
, member
, presence
):
492 return [f
"free({member});",
493 f
"{presence}_len = len;",
494 f
"{member} = malloc({presence}_len);",
495 f
'memcpy({member}, {self.c_name}, {presence}_len);']
498 class TypeBitfield32(Type
):
499 def _complex_member_type(self
, ri
):
500 return "struct nla_bitfield32"
502 def _attr_typol(self
):
503 return f
'.type = YNL_PT_BITFIELD32, '
505 def _attr_policy(self
, policy
):
506 if not 'enum' in self
.attr
:
507 raise Exception('Enum required for bitfield32 attr')
508 enum
= self
.family
.consts
[self
.attr
['enum']]
509 mask
= enum
.get_mask(as_flags
=True)
510 return f
"NLA_POLICY_BITFIELD32({mask})"
512 def attr_put(self
, ri
, var
):
513 line
= f
"mnl_attr_put(nlh, {self.enum_name}, sizeof(struct nla_bitfield32), &{var}->{self.c_name})"
514 self
._attr
_put
_line
(ri
, var
, line
)
516 def _attr_get(self
, ri
, var
):
517 return f
"memcpy(&{var}->{self.c_name}, mnl_attr_get_payload(attr), sizeof(struct nla_bitfield32));", None, None
519 def _setter_lines(self
, ri
, member
, presence
):
520 return [f
"memcpy(&{member}, {self.c_name}, sizeof(struct nla_bitfield32));"]
523 class TypeNest(Type
):
524 def _complex_member_type(self
, ri
):
525 return self
.nested_struct_type
527 def free(self
, ri
, var
, ref
):
528 ri
.cw
.p(f
'{self.nested_render_name}_free(&{var}->{ref}{self.c_name});')
530 def _attr_typol(self
):
531 return f
'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
533 def _attr_policy(self
, policy
):
534 return 'NLA_POLICY_NESTED(' + self
.nested_render_name
+ '_nl_policy)'
536 def attr_put(self
, ri
, var
):
537 self
._attr
_put
_line
(ri
, var
, f
"{self.nested_render_name}_put(nlh, " +
538 f
"{self.enum_name}, &{var}->{self.c_name})")
540 def _attr_get(self
, ri
, var
):
541 get_lines
= [f
"if ({self.nested_render_name}_parse(&parg, attr))",
542 "return MNL_CB_ERROR;"]
543 init_lines
= [f
"parg.rsp_policy = &{self.nested_render_name}_nest;",
544 f
"parg.data = &{var}->{self.c_name};"]
545 return get_lines
, init_lines
, None
547 def setter(self
, ri
, space
, direction
, deref
=False, ref
=None):
548 ref
= (ref
if ref
else []) + [self
.c_name
]
550 for _
, attr
in ri
.family
.pure_nested_structs
[self
.nested_attrs
].member_list():
551 attr
.setter(ri
, self
.nested_attrs
, direction
, deref
=deref
, ref
=ref
)
554 class TypeMultiAttr(Type
):
555 def __init__(self
, family
, attr_set
, attr
, value
, base_type
):
556 super().__init
__(family
, attr_set
, attr
, value
)
558 self
.base_type
= base_type
560 def is_multi_val(self
):
563 def presence_type(self
):
567 return self
._mnl
_type
()
569 def _complex_member_type(self
, ri
):
570 if 'type' not in self
.attr
or self
.attr
['type'] == 'nest':
571 return self
.nested_struct_type
572 elif self
.attr
['type'] in scalars
:
573 scalar_pfx
= '__' if ri
.ku_space
== 'user' else ''
574 return scalar_pfx
+ self
.attr
['type']
576 raise Exception(f
"Sub-type {self.attr['type']} not supported yet")
578 def free_needs_iter(self
):
579 return 'type' not in self
.attr
or self
.attr
['type'] == 'nest'
581 def free(self
, ri
, var
, ref
):
582 if self
.attr
['type'] in scalars
:
583 ri
.cw
.p(f
"free({var}->{ref}{self.c_name});")
584 elif 'type' not in self
.attr
or self
.attr
['type'] == 'nest':
585 ri
.cw
.p(f
"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
586 ri
.cw
.p(f
'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
587 ri
.cw
.p(f
"free({var}->{ref}{self.c_name});")
589 raise Exception(f
"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
591 def _attr_policy(self
, policy
):
592 return self
.base_type
._attr
_policy
(policy
)
594 def _attr_typol(self
):
595 return self
.base_type
._attr
_typol
()
597 def _attr_get(self
, ri
, var
):
598 return f
'n_{self.c_name}++;', None, None
600 def attr_put(self
, ri
, var
):
601 if self
.attr
['type'] in scalars
:
602 put_type
= self
.mnl_type()
603 ri
.cw
.p(f
"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
604 ri
.cw
.p(f
"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name}[i]);")
605 elif 'type' not in self
.attr
or self
.attr
['type'] == 'nest':
606 ri
.cw
.p(f
"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
607 self
._attr
_put
_line
(ri
, var
, f
"{self.nested_render_name}_put(nlh, " +
608 f
"{self.enum_name}, &{var}->{self.c_name}[i])")
610 raise Exception(f
"Put of MultiAttr sub-type {self.attr['type']} not supported yet")
612 def _setter_lines(self
, ri
, member
, presence
):
613 # For multi-attr we have a count, not presence, hack up the presence
614 presence
= presence
[:-(len('_present.') + len(self
.c_name
))] + "n_" + self
.c_name
615 return [f
"free({member});",
616 f
"{member} = {self.c_name};",
617 f
"{presence} = n_{self.c_name};"]
620 class TypeArrayNest(Type
):
621 def is_multi_val(self
):
624 def presence_type(self
):
627 def _complex_member_type(self
, ri
):
628 if 'sub-type' not in self
.attr
or self
.attr
['sub-type'] == 'nest':
629 return self
.nested_struct_type
630 elif self
.attr
['sub-type'] in scalars
:
631 scalar_pfx
= '__' if ri
.ku_space
== 'user' else ''
632 return scalar_pfx
+ self
.attr
['sub-type']
634 raise Exception(f
"Sub-type {self.attr['sub-type']} not supported yet")
636 def _attr_typol(self
):
637 return f
'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
639 def _attr_get(self
, ri
, var
):
640 local_vars
= ['const struct nlattr *attr2;']
641 get_lines
= [f
'attr_{self.c_name} = attr;',
642 'mnl_attr_for_each_nested(attr2, attr)',
643 f
'\t{var}->n_{self.c_name}++;']
644 return get_lines
, None, local_vars
647 class TypeNestTypeValue(Type
):
648 def _complex_member_type(self
, ri
):
649 return self
.nested_struct_type
651 def _attr_typol(self
):
652 return f
'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
654 def _attr_get(self
, ri
, var
):
659 init_lines
= [f
"parg.rsp_policy = &{self.nested_render_name}_nest;",
660 f
"parg.data = &{var}->{self.c_name};"]
661 if 'type-value' in self
.attr
:
662 tv_names
= [c_lower(x
) for x
in self
.attr
["type-value"]]
663 local_vars
+= [f
'const struct nlattr *attr_{", *attr_".join(tv_names)};']
664 local_vars
+= [f
'__u32 {", ".join(tv_names)};']
665 for level
in self
.attr
["type-value"]:
666 level
= c_lower(level
)
667 get_lines
+= [f
'attr_{level} = mnl_attr_get_payload({prev});']
668 get_lines
+= [f
'{level} = mnl_attr_get_type(attr_{level});']
669 prev
= 'attr_' + level
671 tv_args
= f
", {', '.join(tv_names)}"
673 get_lines
+= [f
"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"]
674 return get_lines
, init_lines
, local_vars
678 def __init__(self
, family
, space_name
, type_list
=None, inherited
=None):
680 self
.space_name
= space_name
681 self
.attr_set
= family
.attr_sets
[space_name
]
682 # Use list to catch comparisons with empty sets
683 self
._inherited
= inherited
if inherited
is not None else []
686 self
.nested
= type_list
is None
687 if family
.name
== c_lower(space_name
):
688 self
.render_name
= f
"{family.name}"
690 self
.render_name
= f
"{family.name}_{c_lower(space_name)}"
691 self
.struct_name
= 'struct ' + self
.render_name
692 if self
.nested
and space_name
in family
.consts
:
693 self
.struct_name
+= '_'
694 self
.ptr_name
= self
.struct_name
+ ' *'
701 if type_list
is not None:
703 self
.attr_list
.append((t
, self
.attr_set
[t
]),)
705 for t
in self
.attr_set
:
706 self
.attr_list
.append((t
, self
.attr_set
[t
]),)
709 self
.attr_max_val
= None
710 for name
, attr
in self
.attr_list
:
711 if attr
.value
>= max_val
:
713 self
.attr_max_val
= attr
714 self
.attrs
[name
] = attr
717 yield from self
.attrs
719 def __getitem__(self
, key
):
720 return self
.attrs
[key
]
722 def member_list(self
):
723 return self
.attr_list
725 def set_inherited(self
, new_inherited
):
726 if self
._inherited
!= new_inherited
:
727 raise Exception("Inheriting different members not supported")
728 self
.inherited
= [c_lower(x
) for x
in sorted(self
._inherited
)]
731 class EnumEntry(SpecEnumEntry
):
732 def __init__(self
, enum_set
, yaml
, prev
, value_start
):
733 super().__init
__(enum_set
, yaml
, prev
, value_start
)
736 self
.value_change
= (self
.value
!= prev
.value
+ 1)
738 self
.value_change
= (self
.value
!= 0)
739 self
.value_change
= self
.value_change
or self
.enum_set
['type'] == 'flags'
743 delattr(self
, "c_name")
746 self
.resolve_up(super())
748 self
.c_name
= c_upper(self
.enum_set
.value_pfx
+ self
.name
)
751 class EnumSet(SpecEnumSet
):
752 def __init__(self
, family
, yaml
):
753 self
.render_name
= c_lower(family
.name
+ '-' + yaml
['name'])
755 if 'enum-name' in yaml
:
756 if yaml
['enum-name']:
757 self
.enum_name
= 'enum ' + c_lower(yaml
['enum-name'])
759 self
.enum_name
= None
761 self
.enum_name
= 'enum ' + self
.render_name
763 self
.value_pfx
= yaml
.get('name-prefix', f
"{family.name}-{yaml['name']}-")
765 super().__init
__(family
, yaml
)
767 def new_entry(self
, entry
, prev_entry
, value_start
):
768 return EnumEntry(self
, entry
, prev_entry
, value_start
)
770 def value_range(self
):
771 low
= min([x
.value
for x
in self
.entries
.values()])
772 high
= max([x
.value
for x
in self
.entries
.values()])
774 if high
- low
+ 1 != len(self
.entries
):
775 raise Exception("Can't get value range for a noncontiguous enum")
780 class AttrSet(SpecAttrSet
):
781 def __init__(self
, family
, yaml
):
782 super().__init
__(family
, yaml
)
784 if self
.subset_of
is None:
785 if 'name-prefix' in yaml
:
786 pfx
= yaml
['name-prefix']
787 elif self
.name
== family
.name
:
788 pfx
= family
.name
+ '-a-'
790 pfx
= f
"{family.name}-a-{self.name}-"
791 self
.name_prefix
= c_upper(pfx
)
792 self
.max_name
= c_upper(self
.yaml
.get('attr-max-name', f
"{self.name_prefix}max"))
793 self
.cnt_name
= c_upper(self
.yaml
.get('attr-cnt-name', f
"__{self.name_prefix}max"))
795 self
.name_prefix
= family
.attr_sets
[self
.subset_of
].name_prefix
796 self
.max_name
= family
.attr_sets
[self
.subset_of
].max_name
797 self
.cnt_name
= family
.attr_sets
[self
.subset_of
].cnt_name
801 delattr(self
, "c_name")
804 self
.c_name
= c_lower(self
.name
)
805 if self
.c_name
in _C_KW
:
807 if self
.c_name
== self
.family
.c_name
:
810 def new_attr(self
, elem
, value
):
811 if elem
['type'] in scalars
:
812 t
= TypeScalar(self
.family
, self
, elem
, value
)
813 elif elem
['type'] == 'unused':
814 t
= TypeUnused(self
.family
, self
, elem
, value
)
815 elif elem
['type'] == 'pad':
816 t
= TypePad(self
.family
, self
, elem
, value
)
817 elif elem
['type'] == 'flag':
818 t
= TypeFlag(self
.family
, self
, elem
, value
)
819 elif elem
['type'] == 'string':
820 t
= TypeString(self
.family
, self
, elem
, value
)
821 elif elem
['type'] == 'binary':
822 t
= TypeBinary(self
.family
, self
, elem
, value
)
823 elif elem
['type'] == 'bitfield32':
824 t
= TypeBitfield32(self
.family
, self
, elem
, value
)
825 elif elem
['type'] == 'nest':
826 t
= TypeNest(self
.family
, self
, elem
, value
)
827 elif elem
['type'] == 'array-nest':
828 t
= TypeArrayNest(self
.family
, self
, elem
, value
)
829 elif elem
['type'] == 'nest-type-value':
830 t
= TypeNestTypeValue(self
.family
, self
, elem
, value
)
832 raise Exception(f
"No typed class for type {elem['type']}")
834 if 'multi-attr' in elem
and elem
['multi-attr']:
835 t
= TypeMultiAttr(self
.family
, self
, elem
, value
, t
)
840 class Operation(SpecOperation
):
841 def __init__(self
, family
, yaml
, req_value
, rsp_value
):
842 super().__init
__(family
, yaml
, req_value
, rsp_value
)
844 self
.render_name
= family
.name
+ '_' + c_lower(self
.name
)
846 self
.dual_policy
= ('do' in yaml
and 'request' in yaml
['do']) and \
847 ('dump' in yaml
and 'request' in yaml
['dump'])
852 self
.enum_name
= None
853 delattr(self
, "enum_name")
856 self
.resolve_up(super())
858 if not self
.is_async
:
859 self
.enum_name
= self
.family
.op_prefix
+ c_upper(self
.name
)
861 self
.enum_name
= self
.family
.async_op_prefix
+ c_upper(self
.name
)
863 def mark_has_ntf(self
):
867 class Family(SpecFamily
):
868 def __init__(self
, file_name
, exclude_ops
):
871 delattr(self
, "c_name")
872 self
.op_prefix
= None
873 delattr(self
, "op_prefix")
874 self
.async_op_prefix
= None
875 delattr(self
, "async_op_prefix")
877 delattr(self
, "mcgrps")
879 delattr(self
, "consts")
881 delattr(self
, "hooks")
883 super().__init
__(file_name
, exclude_ops
=exclude_ops
)
885 self
.fam_key
= c_upper(self
.yaml
.get('c-family-name', self
.yaml
["name"] + '_FAMILY_NAME'))
886 self
.ver_key
= c_upper(self
.yaml
.get('c-version-name', self
.yaml
["name"] + '_FAMILY_VERSION'))
888 if 'definitions' not in self
.yaml
:
889 self
.yaml
['definitions'] = []
891 if 'uapi-header' in self
.yaml
:
892 self
.uapi_header
= self
.yaml
['uapi-header']
894 self
.uapi_header
= f
"linux/{self.name}.h"
895 if self
.uapi_header
.startswith("linux/") and self
.uapi_header
.endswith('.h'):
896 self
.uapi_header_name
= self
.uapi_header
[6:-2]
898 self
.uapi_header_name
= self
.name
901 self
.resolve_up(super())
903 if self
.yaml
.get('protocol', 'genetlink') not in {'genetlink', 'genetlink-c', 'genetlink-legacy'}:
904 raise Exception("Codegen only supported for genetlink")
906 self
.c_name
= c_lower(self
.name
)
907 if 'name-prefix' in self
.yaml
['operations']:
908 self
.op_prefix
= c_upper(self
.yaml
['operations']['name-prefix'])
910 self
.op_prefix
= c_upper(self
.yaml
['name'] + '-cmd-')
911 if 'async-prefix' in self
.yaml
['operations']:
912 self
.async_op_prefix
= c_upper(self
.yaml
['operations']['async-prefix'])
914 self
.async_op_prefix
= self
.op_prefix
916 self
.mcgrps
= self
.yaml
.get('mcast-groups', {'list': []})
919 for when
in ['pre', 'post']:
920 self
.hooks
[when
] = dict()
921 for op_mode
in ['do', 'dump']:
922 self
.hooks
[when
][op_mode
] = dict()
923 self
.hooks
[when
][op_mode
]['set'] = set()
924 self
.hooks
[when
][op_mode
]['list'] = []
926 # dict space-name -> 'request': set(attrs), 'reply': set(attrs)
927 self
.root_sets
= dict()
928 # dict space-name -> set('request', 'reply')
929 self
.pure_nested_structs
= dict()
932 self
._mock
_up
_events
()
934 self
._load
_root
_sets
()
935 self
._load
_nested
_sets
()
936 self
._load
_attr
_use
()
939 self
.kernel_policy
= self
.yaml
.get('kernel-policy', 'split')
940 if self
.kernel_policy
== 'global':
941 self
._load
_global
_policy
()
943 def new_enum(self
, elem
):
944 return EnumSet(self
, elem
)
946 def new_attr_set(self
, elem
):
947 return AttrSet(self
, elem
)
949 def new_operation(self
, elem
, req_value
, rsp_value
):
950 return Operation(self
, elem
, req_value
, rsp_value
)
952 def _mark_notify(self
):
953 for op
in self
.msgs
.values():
955 self
.ops
[op
['notify']].mark_has_ntf()
957 # Fake a 'do' equivalent of all events, so that we can render their response parsing
958 def _mock_up_events(self
):
959 for op
in self
.yaml
['operations']['list']:
963 'attributes': op
['event']['attributes']
967 def _load_root_sets(self
):
968 for op_name
, op
in self
.msgs
.items():
969 if 'attribute-set' not in op
:
974 for op_mode
in ['do', 'dump']:
975 if op_mode
in op
and 'request' in op
[op_mode
]:
976 req_attrs
.update(set(op
[op_mode
]['request']['attributes']))
977 if op_mode
in op
and 'reply' in op
[op_mode
]:
978 rsp_attrs
.update(set(op
[op_mode
]['reply']['attributes']))
980 rsp_attrs
.update(set(op
['event']['attributes']))
982 if op
['attribute-set'] not in self
.root_sets
:
983 self
.root_sets
[op
['attribute-set']] = {'request': req_attrs
, 'reply': rsp_attrs
}
985 self
.root_sets
[op
['attribute-set']]['request'].update(req_attrs
)
986 self
.root_sets
[op
['attribute-set']]['reply'].update(rsp_attrs
)
988 def _load_nested_sets(self
):
989 attr_set_queue
= list(self
.root_sets
.keys())
990 attr_set_seen
= set(self
.root_sets
.keys())
992 while len(attr_set_queue
):
993 a_set
= attr_set_queue
.pop(0)
994 for attr
, spec
in self
.attr_sets
[a_set
].items():
995 if 'nested-attributes' not in spec
:
998 nested
= spec
['nested-attributes']
999 if nested
not in attr_set_seen
:
1000 attr_set_queue
.append(nested
)
1001 attr_set_seen
.add(nested
)
1004 if nested
not in self
.root_sets
:
1005 if nested
not in self
.pure_nested_structs
:
1006 self
.pure_nested_structs
[nested
] = Struct(self
, nested
, inherited
=inherit
)
1008 raise Exception(f
'Using attr set as root and nested not supported - {nested}')
1010 if 'type-value' in spec
:
1011 if nested
in self
.root_sets
:
1012 raise Exception("Inheriting members to a space used as root not supported")
1013 inherit
.update(set(spec
['type-value']))
1014 elif spec
['type'] == 'array-nest':
1016 self
.pure_nested_structs
[nested
].set_inherited(inherit
)
1018 for root_set
, rs_members
in self
.root_sets
.items():
1019 for attr
, spec
in self
.attr_sets
[root_set
].items():
1020 if 'nested-attributes' in spec
:
1021 nested
= spec
['nested-attributes']
1022 if attr
in rs_members
['request']:
1023 self
.pure_nested_structs
[nested
].request
= True
1024 if attr
in rs_members
['reply']:
1025 self
.pure_nested_structs
[nested
].reply
= True
1027 # Try to reorder according to dependencies
1028 pns_key_list
= list(self
.pure_nested_structs
.keys())
1029 pns_key_seen
= set()
1030 rounds
= len(pns_key_list
)**2 # it's basically bubble sort
1031 for _
in range(rounds
):
1032 if len(pns_key_list
) == 0:
1034 name
= pns_key_list
.pop(0)
1036 for _
, spec
in self
.attr_sets
[name
].items():
1037 if 'nested-attributes' in spec
:
1038 if spec
['nested-attributes'] not in pns_key_seen
:
1039 # Dicts are sorted, this will make struct last
1040 struct
= self
.pure_nested_structs
.pop(name
)
1041 self
.pure_nested_structs
[name
] = struct
1045 pns_key_seen
.add(name
)
1047 pns_key_list
.append(name
)
1048 # Propagate the request / reply
1049 for attr_set
, struct
in reversed(self
.pure_nested_structs
.items()):
1050 for _
, spec
in self
.attr_sets
[attr_set
].items():
1051 if 'nested-attributes' in spec
:
1052 child
= self
.pure_nested_structs
.get(spec
['nested-attributes'])
1054 child
.request |
= struct
.request
1055 child
.reply |
= struct
.reply
1057 def _load_attr_use(self
):
1058 for _
, struct
in self
.pure_nested_structs
.items():
1060 for _
, arg
in struct
.member_list():
1063 for _
, arg
in struct
.member_list():
1066 for root_set
, rs_members
in self
.root_sets
.items():
1067 for attr
, spec
in self
.attr_sets
[root_set
].items():
1068 if attr
in rs_members
['request']:
1070 if attr
in rs_members
['reply']:
1073 def _load_global_policy(self
):
1075 attr_set_name
= None
1076 for op_name
, op
in self
.ops
.items():
1079 if 'attribute-set' not in op
:
1082 if attr_set_name
is None:
1083 attr_set_name
= op
['attribute-set']
1084 if attr_set_name
!= op
['attribute-set']:
1085 raise Exception('For a global policy all ops must use the same set')
1087 for op_mode
in ['do', 'dump']:
1089 req
= op
[op_mode
].get('request')
1091 global_set
.update(req
.get('attributes', []))
1093 self
.global_policy
= []
1094 self
.global_policy_set
= attr_set_name
1095 for attr
in self
.attr_sets
[attr_set_name
]:
1096 if attr
in global_set
:
1097 self
.global_policy
.append(attr
)
1099 def _load_hooks(self
):
1100 for op
in self
.ops
.values():
1101 for op_mode
in ['do', 'dump']:
1102 if op_mode
not in op
:
1104 for when
in ['pre', 'post']:
1105 if when
not in op
[op_mode
]:
1107 name
= op
[op_mode
][when
]
1108 if name
in self
.hooks
[when
][op_mode
]['set']:
1110 self
.hooks
[when
][op_mode
]['set'].add(name
)
1111 self
.hooks
[when
][op_mode
]['list'].append(name
)
1115 def __init__(self
, cw
, family
, ku_space
, op
, op_mode
, attr_set
=None):
1116 self
.family
= family
1118 self
.ku_space
= ku_space
1119 self
.op_mode
= op_mode
1122 # 'do' and 'dump' response parsing is identical
1123 self
.type_consistent
= True
1124 if op_mode
!= 'do' and 'dump' in op
:
1126 if ('reply' in op
['do']) != ('reply' in op
["dump"]):
1127 self
.type_consistent
= False
1128 elif 'reply' in op
['do'] and op
["do"]["reply"] != op
["dump"]["reply"]:
1129 self
.type_consistent
= False
1131 self
.type_consistent
= False
1133 self
.attr_set
= attr_set
1134 if not self
.attr_set
:
1135 self
.attr_set
= op
['attribute-set']
1137 self
.type_name_conflict
= False
1139 self
.type_name
= c_lower(op
.name
)
1141 self
.type_name
= c_lower(attr_set
)
1142 if attr_set
in family
.consts
:
1143 self
.type_name_conflict
= True
1147 self
.struct
= dict()
1148 if op_mode
== 'notify':
1150 for op_dir
in ['request', 'reply']:
1153 if op_dir
in op
[op_mode
]:
1154 type_list
= op
[op_mode
][op_dir
]['attributes']
1155 self
.struct
[op_dir
] = Struct(family
, self
.attr_set
, type_list
=type_list
)
1156 if op_mode
== 'event':
1157 self
.struct
['reply'] = Struct(family
, self
.attr_set
, type_list
=op
['event']['attributes'])
1161 def __init__(self
, nlib
, out_file
=None):
1165 self
._block
_end
= False
1166 self
._silent
_block
= False
1168 self
._ifdef
_block
= None
1169 if out_file
is None:
1170 self
._out
= os
.sys
.stdout
1172 self
._out
= tempfile
.NamedTemporaryFile('w+')
1173 self
._out
_file
= out_file
1176 self
.close_out_file()
1178 def close_out_file(self
):
1179 if self
._out
== os
.sys
.stdout
:
1181 # Avoid modifying the file if contents didn't change
1183 if os
.path
.isfile(self
._out
_file
) and filecmp
.cmp(self
._out
.name
, self
._out
_file
, shallow
=False):
1185 with
open(self
._out
_file
, 'w+') as out_file
:
1187 shutil
.copyfileobj(self
._out
, out_file
)
1189 self
._out
= os
.sys
.stdout
1192 def _is_cond(cls
, line
):
1193 return line
.startswith('if') or line
.startswith('while') or line
.startswith('for')
1195 def p(self
, line
, add_ind
=0):
1197 self
._block
_end
= False
1198 if line
.startswith('else'):
1201 self
._out
.write('\t' * self
._ind
+ '}\n')
1204 self
._out
.write('\n')
1210 if self
._silent
_block
:
1212 self
._silent
_block
= line
.endswith(')') and CodeWriter
._is
_cond
(line
)
1217 self
._out
.write('\t' * ind
+ line
+ '\n')
1222 def block_start(self
, line
=''):
1228 def block_end(self
, line
=''):
1229 if line
and line
[0] not in {';', ','}:
1234 # Delay printing closing bracket in case "else" comes next
1236 self
._out
.write('\t' * (self
._ind
+ 1) + '}\n')
1237 self
._block
_end
= True
1241 def write_doc_line(self
, doc
, indent
=True):
1245 if len(line
) + len(word
) >= 79:
1253 def write_func_prot(self
, qual_ret
, name
, args
=None, doc
=None, suffix
=''):
1263 if qual_ret
[-1] != '*':
1265 oneline
+= f
"{name}({', '.join(args)}){suffix}"
1267 if len(oneline
) < 80:
1275 elif qual_ret
[-1] != '*':
1278 ind
= '\t' * (len(v
) // 8) + ' ' * (len(v
) % 8)
1279 delta_ind
= len(v
) - len(ind
)
1282 while i
< len(args
):
1283 next_len
= len(v
) + len(args
[i
])
1285 next_len
+= delta_ind
1293 self
.p(v
+ ')' + suffix
)
1295 def write_func_lvar(self
, local_vars
):
1299 if type(local_vars
) is str:
1300 local_vars
= [local_vars
]
1302 local_vars
.sort(key
=len, reverse
=True)
1303 for var
in local_vars
:
1307 def write_func(self
, qual_ret
, name
, body
, args
=None, local_vars
=None):
1308 self
.write_func_prot(qual_ret
=qual_ret
, name
=name
, args
=args
)
1309 self
.write_func_lvar(local_vars
=local_vars
)
1316 def writes_defines(self
, defines
):
1318 for define
in defines
:
1319 if len(define
[0]) > longest
:
1320 longest
= len(define
[0])
1321 longest
= ((longest
+ 8) // 8) * 8
1322 for define
in defines
:
1323 line
= '#define ' + define
[0]
1324 line
+= '\t' * ((longest
- len(define
[0]) + 7) // 8)
1325 if type(define
[1]) is int:
1326 line
+= str(define
[1])
1327 elif type(define
[1]) is str:
1328 line
+= '"' + define
[1] + '"'
1331 def write_struct_init(self
, members
):
1332 longest
= max([len(x
[0]) for x
in members
])
1333 longest
+= 1 # because we prepend a .
1334 longest
= ((longest
+ 8) // 8) * 8
1337 line
+= '\t' * ((longest
- len(one
[0]) - 1 + 7) // 8)
1338 line
+= '= ' + str(one
[1]) + ','
1341 def ifdef_block(self
, config
):
1342 config_option
= None
1344 config_option
= 'CONFIG_' + c_upper(config
)
1345 if self
._ifdef
_block
== config_option
:
1348 if self
._ifdef
_block
:
1349 self
.p('#endif /* ' + self
._ifdef
_block
+ ' */')
1351 self
.p('#ifdef ' + config_option
)
1352 self
._ifdef
_block
= config_option
1355 scalars
= {'u8', 'u16', 'u32', 'u64', 's32', 's64', 'uint', 'sint'}
1357 direction_to_suffix
= {
1363 op_mode_to_wrapper
= {
1408 def rdir(direction
):
1409 if direction
== 'reply':
1411 if direction
== 'request':
1416 def op_prefix(ri
, direction
, deref
=False):
1417 suffix
= f
"_{ri.type_name}"
1419 if not ri
.op_mode
or ri
.op_mode
== 'do':
1420 suffix
+= f
"{direction_to_suffix[direction]}"
1422 if direction
== 'request':
1423 suffix
+= '_req_dump'
1425 if ri
.type_consistent
:
1427 suffix
+= f
"{direction_to_suffix[direction]}"
1429 suffix
+= op_mode_to_wrapper
[ri
.op_mode
]
1432 suffix
+= '_dump' if deref
else '_list'
1434 return f
"{ri.family['name']}{suffix}"
1437 def type_name(ri
, direction
, deref
=False):
1438 return f
"struct {op_prefix(ri, direction, deref=deref)}"
1441 def print_prototype(ri
, direction
, terminate
=True, doc
=None):
1442 suffix
= ';' if terminate
else ''
1444 fname
= ri
.op
.render_name
1445 if ri
.op_mode
== 'dump':
1448 args
= ['struct ynl_sock *ys']
1449 if 'request' in ri
.op
[ri
.op_mode
]:
1450 args
.append(f
"{type_name(ri, direction)} *" + f
"{direction_to_suffix[direction][1:]}")
1453 if 'reply' in ri
.op
[ri
.op_mode
]:
1454 ret
= f
"{type_name(ri, rdir(direction))} *"
1456 ri
.cw
.write_func_prot(ret
, fname
, args
, doc
=doc
, suffix
=suffix
)
1459 def print_req_prototype(ri
):
1460 print_prototype(ri
, "request", doc
=ri
.op
['doc'])
1463 def print_dump_prototype(ri
):
1464 print_prototype(ri
, "request")
1467 def put_typol(cw
, struct
):
1468 type_max
= struct
.attr_set
.max_name
1469 cw
.block_start(line
=f
'struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
1471 for _
, arg
in struct
.member_list():
1474 cw
.block_end(line
=';')
1477 cw
.block_start(line
=f
'struct ynl_policy_nest {struct.render_name}_nest =')
1478 cw
.p(f
'.max_attr = {type_max},')
1479 cw
.p(f
'.table = {struct.render_name}_policy,')
1480 cw
.block_end(line
=';')
1484 def _put_enum_to_str_helper(cw
, render_name
, map_name
, arg_name
, enum
=None):
1485 args
= [f
'int {arg_name}']
1486 if enum
and not ('enum-name' in enum
and not enum
['enum-name']):
1487 args
= [f
'enum {render_name} {arg_name}']
1488 cw
.write_func_prot('const char *', f
'{render_name}_str', args
)
1490 if enum
and enum
.type == 'flags':
1491 cw
.p(f
'{arg_name} = ffs({arg_name}) - 1;')
1492 cw
.p(f
'if ({arg_name} < 0 || {arg_name} >= (int)MNL_ARRAY_SIZE({map_name}))')
1493 cw
.p('return NULL;')
1494 cw
.p(f
'return {map_name}[{arg_name}];')
1499 def put_op_name_fwd(family
, cw
):
1500 cw
.write_func_prot('const char *', f
'{family.name}_op_str', ['int op'], suffix
=';')
1503 def put_op_name(family
, cw
):
1504 map_name
= f
'{family.name}_op_strmap'
1505 cw
.block_start(line
=f
"static const char * const {map_name}[] =")
1506 for op_name
, op
in family
.msgs
.items():
1508 if op
.req_value
== op
.rsp_value
:
1509 cw
.p(f
'[{op.enum_name}] = "{op_name}",')
1511 cw
.p(f
'[{op.rsp_value}] = "{op_name}",')
1512 cw
.block_end(line
=';')
1515 _put_enum_to_str_helper(cw
, family
.name
+ '_op', map_name
, 'op')
1518 def put_enum_to_str_fwd(family
, cw
, enum
):
1519 args
= [f
'enum {enum.render_name} value']
1520 if 'enum-name' in enum
and not enum
['enum-name']:
1521 args
= ['int value']
1522 cw
.write_func_prot('const char *', f
'{enum.render_name}_str', args
, suffix
=';')
1525 def put_enum_to_str(family
, cw
, enum
):
1526 map_name
= f
'{enum.render_name}_strmap'
1527 cw
.block_start(line
=f
"static const char * const {map_name}[] =")
1528 for entry
in enum
.entries
.values():
1529 cw
.p(f
'[{entry.value}] = "{entry.name}",')
1530 cw
.block_end(line
=';')
1533 _put_enum_to_str_helper(cw
, enum
.render_name
, map_name
, 'value', enum
=enum
)
1536 def put_req_nested(ri
, struct
):
1537 func_args
= ['struct nlmsghdr *nlh',
1538 'unsigned int attr_type',
1539 f
'{struct.ptr_name}obj']
1541 ri
.cw
.write_func_prot('int', f
'{struct.render_name}_put', func_args
)
1543 ri
.cw
.write_func_lvar('struct nlattr *nest;')
1545 ri
.cw
.p("nest = mnl_attr_nest_start(nlh, attr_type);")
1547 for _
, arg
in struct
.member_list():
1548 arg
.attr_put(ri
, "obj")
1550 ri
.cw
.p("mnl_attr_nest_end(nlh, nest);")
1553 ri
.cw
.p('return 0;')
1558 def _multi_parse(ri
, struct
, init_lines
, local_vars
):
1560 iter_line
= "mnl_attr_for_each_nested(attr, nested)"
1562 iter_line
= "mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr))"
1567 for arg
, aspec
in struct
.member_list():
1568 if aspec
['type'] == 'array-nest':
1569 local_vars
.append(f
'const struct nlattr *attr_{aspec.c_name};')
1570 array_nests
.add(arg
)
1571 if 'multi-attr' in aspec
:
1572 multi_attrs
.add(arg
)
1573 needs_parg |
= 'nested-attributes' in aspec
1574 if array_nests
or multi_attrs
:
1575 local_vars
.append('int i;')
1577 local_vars
.append('struct ynl_parse_arg parg;')
1578 init_lines
.append('parg.ys = yarg->ys;')
1580 all_multi
= array_nests | multi_attrs
1582 for anest
in sorted(all_multi
):
1583 local_vars
.append(f
"unsigned int n_{struct[anest].c_name} = 0;")
1586 ri
.cw
.write_func_lvar(local_vars
)
1588 for line
in init_lines
:
1592 for arg
in struct
.inherited
:
1593 ri
.cw
.p(f
'dst->{arg} = {arg};')
1595 for anest
in sorted(all_multi
):
1596 aspec
= struct
[anest
]
1597 ri
.cw
.p(f
"if (dst->{aspec.c_name})")
1598 ri
.cw
.p(f
'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
1601 ri
.cw
.block_start(line
=iter_line
)
1602 ri
.cw
.p('unsigned int type = mnl_attr_get_type(attr);')
1606 for _
, arg
in struct
.member_list():
1607 good
= arg
.attr_get(ri
, 'dst', first
=first
)
1608 # First may be 'unused' or 'pad', ignore those
1614 for anest
in sorted(array_nests
):
1615 aspec
= struct
[anest
]
1617 ri
.cw
.block_start(line
=f
"if (n_{aspec.c_name})")
1618 ri
.cw
.p(f
"dst->{aspec.c_name} = calloc({aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1619 ri
.cw
.p(f
"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1621 ri
.cw
.p(f
"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1622 ri
.cw
.block_start(line
=f
"mnl_attr_for_each_nested(attr, attr_{aspec.c_name})")
1623 ri
.cw
.p(f
"parg.data = &dst->{aspec.c_name}[i];")
1624 ri
.cw
.p(f
"if ({aspec.nested_render_name}_parse(&parg, attr, mnl_attr_get_type(attr)))")
1625 ri
.cw
.p('return MNL_CB_ERROR;')
1631 for anest
in sorted(multi_attrs
):
1632 aspec
= struct
[anest
]
1633 ri
.cw
.block_start(line
=f
"if (n_{aspec.c_name})")
1634 ri
.cw
.p(f
"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1635 ri
.cw
.p(f
"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1637 if 'nested-attributes' in aspec
:
1638 ri
.cw
.p(f
"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1639 ri
.cw
.block_start(line
=iter_line
)
1640 ri
.cw
.block_start(line
=f
"if (mnl_attr_get_type(attr) == {aspec.enum_name})")
1641 if 'nested-attributes' in aspec
:
1642 ri
.cw
.p(f
"parg.data = &dst->{aspec.c_name}[i];")
1643 ri
.cw
.p(f
"if ({aspec.nested_render_name}_parse(&parg, attr))")
1644 ri
.cw
.p('return MNL_CB_ERROR;')
1645 elif aspec
.type in scalars
:
1646 ri
.cw
.p(f
"dst->{aspec.c_name}[i] = mnl_attr_get_{aspec.mnl_type()}(attr);")
1648 raise Exception('Nest parsing type not supported yet')
1656 ri
.cw
.p('return 0;')
1658 ri
.cw
.p('return MNL_CB_OK;')
1663 def parse_rsp_nested(ri
, struct
):
1664 func_args
= ['struct ynl_parse_arg *yarg',
1665 'const struct nlattr *nested']
1666 for arg
in struct
.inherited
:
1667 func_args
.append('__u32 ' + arg
)
1669 local_vars
= ['const struct nlattr *attr;',
1670 f
'{struct.ptr_name}dst = yarg->data;']
1673 ri
.cw
.write_func_prot('int', f
'{struct.render_name}_parse', func_args
)
1675 _multi_parse(ri
, struct
, init_lines
, local_vars
)
1678 def parse_rsp_msg(ri
, deref
=False):
1679 if 'reply' not in ri
.op
[ri
.op_mode
] and ri
.op_mode
!= 'event':
1682 func_args
= ['const struct nlmsghdr *nlh',
1685 local_vars
= [f
'{type_name(ri, "reply", deref=deref)} *dst;',
1686 'struct ynl_parse_arg *yarg = data;',
1687 'const struct nlattr *attr;']
1688 init_lines
= ['dst = yarg->data;']
1690 ri
.cw
.write_func_prot('int', f
'{op_prefix(ri, "reply", deref=deref)}_parse', func_args
)
1692 if ri
.struct
["reply"].member_list():
1693 _multi_parse(ri
, ri
.struct
["reply"], init_lines
, local_vars
)
1697 ri
.cw
.p('return MNL_CB_OK;')
1705 direction
= "request"
1706 local_vars
= ['struct nlmsghdr *nlh;',
1709 if 'reply' in ri
.op
[ri
.op_mode
]:
1712 local_vars
+= [f
'{type_name(ri, rdir(direction))} *rsp;',
1713 'struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };']
1715 print_prototype(ri
, direction
, terminate
=False)
1717 ri
.cw
.write_func_lvar(local_vars
)
1719 ri
.cw
.p(f
"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1721 ri
.cw
.p(f
"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1722 if 'reply' in ri
.op
[ri
.op_mode
]:
1723 ri
.cw
.p(f
"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1725 for _
, attr
in ri
.struct
["request"].member_list():
1726 attr
.attr_put(ri
, "req")
1730 if 'reply' in ri
.op
[ri
.op_mode
]:
1731 ri
.cw
.p('rsp = calloc(1, sizeof(*rsp));')
1732 ri
.cw
.p('yrs.yarg.data = rsp;')
1733 ri
.cw
.p(f
"yrs.cb = {op_prefix(ri, 'reply')}_parse;")
1734 if ri
.op
.value
is not None:
1735 ri
.cw
.p(f
'yrs.rsp_cmd = {ri.op.enum_name};')
1737 ri
.cw
.p(f
'yrs.rsp_cmd = {ri.op.rsp_value};')
1740 ri
.cw
.p(f
"err = ynl_exec(ys, nlh, {parse_arg});")
1741 ri
.cw
.p('if (err < 0)')
1742 if 'reply' in ri
.op
[ri
.op_mode
]:
1743 ri
.cw
.p('goto err_free;')
1745 ri
.cw
.p('return -1;')
1748 ri
.cw
.p(f
"return {ret_ok};")
1751 if 'reply' in ri
.op
[ri
.op_mode
]:
1752 ri
.cw
.p('err_free:')
1753 ri
.cw
.p(f
"{call_free(ri, rdir(direction), 'rsp')}")
1754 ri
.cw
.p(f
"return {ret_err};")
1760 direction
= "request"
1761 print_prototype(ri
, direction
, terminate
=False)
1763 local_vars
= ['struct ynl_dump_state yds = {};',
1764 'struct nlmsghdr *nlh;',
1767 for var
in local_vars
:
1771 ri
.cw
.p('yds.ys = ys;')
1772 ri
.cw
.p(f
"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});")
1773 ri
.cw
.p(f
"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;")
1774 if ri
.op
.value
is not None:
1775 ri
.cw
.p(f
'yds.rsp_cmd = {ri.op.enum_name};')
1777 ri
.cw
.p(f
'yds.rsp_cmd = {ri.op.rsp_value};')
1778 ri
.cw
.p(f
"yds.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1780 ri
.cw
.p(f
"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1782 if "request" in ri
.op
[ri
.op_mode
]:
1783 ri
.cw
.p(f
"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1785 for _
, attr
in ri
.struct
["request"].member_list():
1786 attr
.attr_put(ri
, "req")
1789 ri
.cw
.p('err = ynl_exec_dump(ys, nlh, &yds);')
1790 ri
.cw
.p('if (err < 0)')
1791 ri
.cw
.p('goto free_list;')
1794 ri
.cw
.p('return yds.first;')
1796 ri
.cw
.p('free_list:')
1797 ri
.cw
.p(call_free(ri
, rdir(direction
), 'yds.first'))
1798 ri
.cw
.p('return NULL;')
1802 def call_free(ri
, direction
, var
):
1803 return f
"{op_prefix(ri, direction)}_free({var});"
1806 def free_arg_name(direction
):
1808 return direction_to_suffix
[direction
][1:]
1812 def print_alloc_wrapper(ri
, direction
):
1813 name
= op_prefix(ri
, direction
)
1814 ri
.cw
.write_func_prot(f
'static inline struct {name} *', f
"{name}_alloc", [f
"void"])
1816 ri
.cw
.p(f
'return calloc(1, sizeof(struct {name}));')
1820 def print_free_prototype(ri
, direction
, suffix
=';'):
1821 name
= op_prefix(ri
, direction
)
1823 if ri
.type_name_conflict
:
1825 arg
= free_arg_name(direction
)
1826 ri
.cw
.write_func_prot('void', f
"{name}_free", [f
"struct {struct_name} *{arg}"], suffix
=suffix
)
1829 def _print_type(ri
, direction
, struct
):
1830 suffix
= f
'_{ri.type_name}{direction_to_suffix[direction]}'
1831 if not direction
and ri
.type_name_conflict
:
1834 if ri
.op_mode
== 'dump':
1837 ri
.cw
.block_start(line
=f
"struct {ri.family['name']}{suffix}")
1839 meta_started
= False
1840 for _
, attr
in struct
.member_list():
1841 for type_filter
in ['len', 'bit']:
1842 line
= attr
.presence_member(ri
.ku_space
, type_filter
)
1844 if not meta_started
:
1845 ri
.cw
.block_start(line
=f
"struct")
1849 ri
.cw
.block_end(line
='_present;')
1852 for arg
in struct
.inherited
:
1853 ri
.cw
.p(f
"__u32 {arg};")
1855 for _
, attr
in struct
.member_list():
1856 attr
.struct_member(ri
)
1858 ri
.cw
.block_end(line
=';')
1862 def print_type(ri
, direction
):
1863 _print_type(ri
, direction
, ri
.struct
[direction
])
1866 def print_type_full(ri
, struct
):
1867 _print_type(ri
, "", struct
)
1870 def print_type_helpers(ri
, direction
, deref
=False):
1871 print_free_prototype(ri
, direction
)
1874 if ri
.ku_space
== 'user' and direction
== 'request':
1875 for _
, attr
in ri
.struct
[direction
].member_list():
1876 attr
.setter(ri
, ri
.attr_set
, direction
, deref
=deref
)
1880 def print_req_type_helpers(ri
):
1881 if len(ri
.struct
["request"].attr_list
) == 0:
1883 print_alloc_wrapper(ri
, "request")
1884 print_type_helpers(ri
, "request")
1887 def print_rsp_type_helpers(ri
):
1888 if 'reply' not in ri
.op
[ri
.op_mode
]:
1890 print_type_helpers(ri
, "reply")
1893 def print_parse_prototype(ri
, direction
, terminate
=True):
1894 suffix
= "_rsp" if direction
== "reply" else "_req"
1895 term
= ';' if terminate
else ''
1897 ri
.cw
.write_func_prot('void', f
"{ri.op.render_name}{suffix}_parse",
1898 ['const struct nlattr **tb',
1899 f
"struct {ri.op.render_name}{suffix} *req"],
1903 def print_req_type(ri
):
1904 if len(ri
.struct
["request"].attr_list
) == 0:
1906 print_type(ri
, "request")
1909 def print_req_free(ri
):
1910 if 'request' not in ri
.op
[ri
.op_mode
]:
1912 _free_type(ri
, 'request', ri
.struct
['request'])
1915 def print_rsp_type(ri
):
1916 if (ri
.op_mode
== 'do' or ri
.op_mode
== 'dump') and 'reply' in ri
.op
[ri
.op_mode
]:
1918 elif ri
.op_mode
== 'event':
1922 print_type(ri
, direction
)
1925 def print_wrapped_type(ri
):
1926 ri
.cw
.block_start(line
=f
"{type_name(ri, 'reply')}")
1927 if ri
.op_mode
== 'dump':
1928 ri
.cw
.p(f
"{type_name(ri, 'reply')} *next;")
1929 elif ri
.op_mode
== 'notify' or ri
.op_mode
== 'event':
1930 ri
.cw
.p('__u16 family;')
1931 ri
.cw
.p('__u8 cmd;')
1932 ri
.cw
.p('struct ynl_ntf_base_type *next;')
1933 ri
.cw
.p(f
"void (*free)({type_name(ri, 'reply')} *ntf);")
1934 ri
.cw
.p(f
"{type_name(ri, 'reply', deref=True)} obj __attribute__((aligned(8)));")
1935 ri
.cw
.block_end(line
=';')
1937 print_free_prototype(ri
, 'reply')
1941 def _free_type_members_iter(ri
, struct
):
1942 for _
, attr
in struct
.member_list():
1943 if attr
.free_needs_iter():
1944 ri
.cw
.p('unsigned int i;')
1949 def _free_type_members(ri
, var
, struct
, ref
=''):
1950 for _
, attr
in struct
.member_list():
1951 attr
.free(ri
, var
, ref
)
1954 def _free_type(ri
, direction
, struct
):
1955 var
= free_arg_name(direction
)
1957 print_free_prototype(ri
, direction
, suffix
='')
1959 _free_type_members_iter(ri
, struct
)
1960 _free_type_members(ri
, var
, struct
)
1962 ri
.cw
.p(f
'free({var});')
1967 def free_rsp_nested(ri
, struct
):
1968 _free_type(ri
, "", struct
)
1971 def print_rsp_free(ri
):
1972 if 'reply' not in ri
.op
[ri
.op_mode
]:
1974 _free_type(ri
, 'reply', ri
.struct
['reply'])
1977 def print_dump_type_free(ri
):
1978 sub_type
= type_name(ri
, 'reply')
1980 print_free_prototype(ri
, 'reply', suffix
='')
1982 ri
.cw
.p(f
"{sub_type} *next = rsp;")
1984 ri
.cw
.block_start(line
='while ((void *)next != YNL_LIST_END)')
1985 _free_type_members_iter(ri
, ri
.struct
['reply'])
1986 ri
.cw
.p('rsp = next;')
1987 ri
.cw
.p('next = rsp->next;')
1990 _free_type_members(ri
, 'rsp', ri
.struct
['reply'], ref
='obj.')
1991 ri
.cw
.p(f
'free(rsp);')
1997 def print_ntf_type_free(ri
):
1998 print_free_prototype(ri
, 'reply', suffix
='')
2000 _free_type_members_iter(ri
, ri
.struct
['reply'])
2001 _free_type_members(ri
, 'rsp', ri
.struct
['reply'], ref
='obj.')
2002 ri
.cw
.p(f
'free(rsp);')
2007 def print_req_policy_fwd(cw
, struct
, ri
=None, terminate
=True):
2008 if terminate
and ri
and policy_should_be_static(struct
.family
):
2014 if ri
and policy_should_be_static(struct
.family
):
2019 suffix
= ';' if terminate
else ' = {'
2021 max_attr
= struct
.attr_max_val
2023 name
= ri
.op
.render_name
2024 if ri
.op
.dual_policy
:
2025 name
+= '_' + ri
.op_mode
2027 name
= struct
.render_name
2028 cw
.p(f
"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}")
2031 def print_req_policy(cw
, struct
, ri
=None):
2033 cw
.ifdef_block(ri
.op
.get('config-cond', None))
2034 print_req_policy_fwd(cw
, struct
, ri
=ri
, terminate
=False)
2035 for _
, arg
in struct
.member_list():
2038 cw
.ifdef_block(None)
2042 def kernel_can_gen_family_struct(family
):
2043 return family
.proto
== 'genetlink'
2046 def policy_should_be_static(family
):
2047 return family
.kernel_policy
== 'split' or kernel_can_gen_family_struct(family
)
2050 def print_kernel_policy_ranges(family
, cw
):
2052 for _
, attr_set
in family
.attr_sets
.items():
2053 if attr_set
.subset_of
:
2056 for _
, attr
in attr_set
.items():
2057 if not attr
.request
:
2059 if 'full-range' not in attr
.checks
:
2063 cw
.p('/* Integer value ranges */')
2066 sign
= '' if attr
.type[0] == 'u' else '_signed'
2067 cw
.block_start(line
=f
'static const struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =')
2069 if 'min' in attr
.checks
:
2070 members
.append(('min', attr
.get_limit('min')))
2071 if 'max' in attr
.checks
:
2072 members
.append(('max', attr
.get_limit('max')))
2073 cw
.write_struct_init(members
)
2074 cw
.block_end(line
=';')
2078 def print_kernel_op_table_fwd(family
, cw
, terminate
):
2079 exported
= not kernel_can_gen_family_struct(family
)
2081 if not terminate
or exported
:
2082 cw
.p(f
"/* Ops table for {family.name} */")
2084 pol_to_struct
= {'global': 'genl_small_ops',
2085 'per-op': 'genl_ops',
2086 'split': 'genl_split_ops'}
2087 struct_type
= pol_to_struct
[family
.kernel_policy
]
2091 elif family
.kernel_policy
== 'split':
2093 for op
in family
.ops
.values():
2099 cnt
= len(family
.ops
)
2101 qual
= 'static const' if not exported
else 'const'
2102 line
= f
"{qual} struct {struct_type} {family.name}_nl_ops[{cnt}]"
2104 cw
.p(f
"extern {line};")
2106 cw
.block_start(line
=line
+ ' =')
2112 for name
in family
.hooks
['pre']['do']['list']:
2113 cw
.write_func_prot('int', c_lower(name
),
2114 ['const struct genl_split_ops *ops',
2115 'struct sk_buff *skb', 'struct genl_info *info'], suffix
=';')
2116 for name
in family
.hooks
['post']['do']['list']:
2117 cw
.write_func_prot('void', c_lower(name
),
2118 ['const struct genl_split_ops *ops',
2119 'struct sk_buff *skb', 'struct genl_info *info'], suffix
=';')
2120 for name
in family
.hooks
['pre']['dump']['list']:
2121 cw
.write_func_prot('int', c_lower(name
),
2122 ['struct netlink_callback *cb'], suffix
=';')
2123 for name
in family
.hooks
['post']['dump']['list']:
2124 cw
.write_func_prot('int', c_lower(name
),
2125 ['struct netlink_callback *cb'], suffix
=';')
2129 for op_name
, op
in family
.ops
.items():
2134 name
= c_lower(f
"{family.name}-nl-{op_name}-doit")
2135 cw
.write_func_prot('int', name
,
2136 ['struct sk_buff *skb', 'struct genl_info *info'], suffix
=';')
2139 name
= c_lower(f
"{family.name}-nl-{op_name}-dumpit")
2140 cw
.write_func_prot('int', name
,
2141 ['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix
=';')
2145 def print_kernel_op_table_hdr(family
, cw
):
2146 print_kernel_op_table_fwd(family
, cw
, terminate
=True)
2149 def print_kernel_op_table(family
, cw
):
2150 print_kernel_op_table_fwd(family
, cw
, terminate
=False)
2151 if family
.kernel_policy
== 'global' or family
.kernel_policy
== 'per-op':
2152 for op_name
, op
in family
.ops
.items():
2156 cw
.ifdef_block(op
.get('config-cond', None))
2158 members
= [('cmd', op
.enum_name
)]
2159 if 'dont-validate' in op
:
2160 members
.append(('validate',
2161 ' | '.join([c_upper('genl-dont-validate-' + x
)
2162 for x
in op
['dont-validate']])), )
2163 for op_mode
in ['do', 'dump']:
2165 name
= c_lower(f
"{family.name}-nl-{op_name}-{op_mode}it")
2166 members
.append((op_mode
+ 'it', name
))
2167 if family
.kernel_policy
== 'per-op':
2168 struct
= Struct(family
, op
['attribute-set'],
2169 type_list
=op
['do']['request']['attributes'])
2171 name
= c_lower(f
"{family.name}-{op_name}-nl-policy")
2172 members
.append(('policy', name
))
2173 members
.append(('maxattr', struct
.attr_max_val
.enum_name
))
2175 members
.append(('flags', ' | '.join([c_upper('genl-' + x
) for x
in op
['flags']])))
2176 cw
.write_struct_init(members
)
2177 cw
.block_end(line
=',')
2178 elif family
.kernel_policy
== 'split':
2179 cb_names
= {'do': {'pre': 'pre_doit', 'post': 'post_doit'},
2180 'dump': {'pre': 'start', 'post': 'done'}}
2182 for op_name
, op
in family
.ops
.items():
2183 for op_mode
in ['do', 'dump']:
2184 if op
.is_async
or op_mode
not in op
:
2187 cw
.ifdef_block(op
.get('config-cond', None))
2189 members
= [('cmd', op
.enum_name
)]
2190 if 'dont-validate' in op
:
2192 for x
in op
['dont-validate']:
2193 if op_mode
== 'do' and x
in ['dump', 'dump-strict']:
2195 if op_mode
== "dump" and x
== 'strict':
2197 dont_validate
.append(x
)
2200 members
.append(('validate',
2201 ' | '.join([c_upper('genl-dont-validate-' + x
)
2202 for x
in dont_validate
])), )
2203 name
= c_lower(f
"{family.name}-nl-{op_name}-{op_mode}it")
2204 if 'pre' in op
[op_mode
]:
2205 members
.append((cb_names
[op_mode
]['pre'], c_lower(op
[op_mode
]['pre'])))
2206 members
.append((op_mode
+ 'it', name
))
2207 if 'post' in op
[op_mode
]:
2208 members
.append((cb_names
[op_mode
]['post'], c_lower(op
[op_mode
]['post'])))
2209 if 'request' in op
[op_mode
]:
2210 struct
= Struct(family
, op
['attribute-set'],
2211 type_list
=op
[op_mode
]['request']['attributes'])
2214 name
= c_lower(f
"{family.name}-{op_name}-{op_mode}-nl-policy")
2216 name
= c_lower(f
"{family.name}-{op_name}-nl-policy")
2217 members
.append(('policy', name
))
2218 members
.append(('maxattr', struct
.attr_max_val
.enum_name
))
2219 flags
= (op
['flags'] if 'flags' in op
else []) + ['cmd-cap-' + op_mode
]
2220 members
.append(('flags', ' | '.join([c_upper('genl-' + x
) for x
in flags
])))
2221 cw
.write_struct_init(members
)
2222 cw
.block_end(line
=',')
2223 cw
.ifdef_block(None)
2225 cw
.block_end(line
=';')
2229 def print_kernel_mcgrp_hdr(family
, cw
):
2230 if not family
.mcgrps
['list']:
2233 cw
.block_start('enum')
2234 for grp
in family
.mcgrps
['list']:
2235 grp_id
= c_upper(f
"{family.name}-nlgrp-{grp['name']},")
2241 def print_kernel_mcgrp_src(family
, cw
):
2242 if not family
.mcgrps
['list']:
2245 cw
.block_start('static const struct genl_multicast_group ' + family
.name
+ '_nl_mcgrps[] =')
2246 for grp
in family
.mcgrps
['list']:
2248 grp_id
= c_upper(f
"{family.name}-nlgrp-{name}")
2249 cw
.p('[' + grp_id
+ '] = { "' + name
+ '", },')
2254 def print_kernel_family_struct_hdr(family
, cw
):
2255 if not kernel_can_gen_family_struct(family
):
2258 cw
.p(f
"extern struct genl_family {family.name}_nl_family;")
2262 def print_kernel_family_struct_src(family
, cw
):
2263 if not kernel_can_gen_family_struct(family
):
2266 cw
.block_start(f
"struct genl_family {family.name}_nl_family __ro_after_init =")
2267 cw
.p('.name\t\t= ' + family
.fam_key
+ ',')
2268 cw
.p('.version\t= ' + family
.ver_key
+ ',')
2269 cw
.p('.netnsok\t= true,')
2270 cw
.p('.parallel_ops\t= true,')
2271 cw
.p('.module\t\t= THIS_MODULE,')
2272 if family
.kernel_policy
== 'per-op':
2273 cw
.p(f
'.ops\t\t= {family.name}_nl_ops,')
2274 cw
.p(f
'.n_ops\t\t= ARRAY_SIZE({family.name}_nl_ops),')
2275 elif family
.kernel_policy
== 'split':
2276 cw
.p(f
'.split_ops\t= {family.name}_nl_ops,')
2277 cw
.p(f
'.n_split_ops\t= ARRAY_SIZE({family.name}_nl_ops),')
2278 if family
.mcgrps
['list']:
2279 cw
.p(f
'.mcgrps\t\t= {family.name}_nl_mcgrps,')
2280 cw
.p(f
'.n_mcgrps\t= ARRAY_SIZE({family.name}_nl_mcgrps),')
2284 def uapi_enum_start(family
, cw
, obj
, ckey
='', enum_name
='enum-name'):
2286 if enum_name
in obj
:
2288 start_line
= 'enum ' + c_lower(obj
[enum_name
])
2289 elif ckey
and ckey
in obj
:
2290 start_line
= 'enum ' + family
.name
+ '_' + c_lower(obj
[ckey
])
2291 cw
.block_start(line
=start_line
)
2294 def render_uapi(family
, cw
):
2295 hdr_prot
= f
"_UAPI_LINUX_{c_upper(family.uapi_header_name)}_H"
2296 cw
.p('#ifndef ' + hdr_prot
)
2297 cw
.p('#define ' + hdr_prot
)
2300 defines
= [(family
.fam_key
, family
["name"]),
2301 (family
.ver_key
, family
.get('version', 1))]
2302 cw
.writes_defines(defines
)
2306 for const
in family
['definitions']:
2307 if const
['type'] != 'const':
2308 cw
.writes_defines(defines
)
2312 # Write kdoc for enum and flags (one day maybe also structs)
2313 if const
['type'] == 'enum' or const
['type'] == 'flags':
2314 enum
= family
.consts
[const
['name']]
2320 doc
= ' - ' + enum
['doc']
2321 cw
.write_doc_line(enum
.enum_name
+ doc
)
2322 for entry
in enum
.entries
.values():
2324 doc
= '@' + entry
.c_name
+ ': ' + entry
['doc']
2325 cw
.write_doc_line(doc
)
2328 uapi_enum_start(family
, cw
, const
, 'name')
2329 name_pfx
= const
.get('name-prefix', f
"{family.name}-{const['name']}-")
2330 for entry
in enum
.entries
.values():
2332 if entry
.value_change
:
2333 suffix
= f
" = {entry.user_value()}" + suffix
2334 cw
.p(entry
.c_name
+ suffix
)
2336 if const
.get('render-max', False):
2338 cw
.p('/* private: */')
2339 if const
['type'] == 'flags':
2340 max_name
= c_upper(name_pfx
+ 'mask')
2341 max_val
= f
' = {enum.get_mask()},'
2342 cw
.p(max_name
+ max_val
)
2344 max_name
= c_upper(name_pfx
+ 'max')
2345 cw
.p('__' + max_name
+ ',')
2346 cw
.p(max_name
+ ' = (__' + max_name
+ ' - 1)')
2347 cw
.block_end(line
=';')
2349 elif const
['type'] == 'const':
2350 defines
.append([c_upper(family
.get('c-define-name',
2351 f
"{family.name}-{const['name']}")),
2355 cw
.writes_defines(defines
)
2358 max_by_define
= family
.get('max-by-define', False)
2360 for _
, attr_set
in family
.attr_sets
.items():
2361 if attr_set
.subset_of
:
2364 max_value
= f
"({attr_set.cnt_name} - 1)"
2367 uapi_enum_start(family
, cw
, attr_set
.yaml
, 'enum-name')
2368 for _
, attr
in attr_set
.items():
2370 if attr
.value
!= val
:
2371 suffix
= f
" = {attr.value},"
2374 cw
.p(attr
.enum_name
+ suffix
)
2376 cw
.p(attr_set
.cnt_name
+ ('' if max_by_define
else ','))
2377 if not max_by_define
:
2378 cw
.p(f
"{attr_set.max_name} = {max_value}")
2379 cw
.block_end(line
=';')
2381 cw
.p(f
"#define {attr_set.max_name} {max_value}")
2385 separate_ntf
= 'async-prefix' in family
['operations']
2387 max_name
= c_upper(family
.get('cmd-max-name', f
"{family.op_prefix}MAX"))
2388 cnt_name
= c_upper(family
.get('cmd-cnt-name', f
"__{family.op_prefix}MAX"))
2389 max_value
= f
"({cnt_name} - 1)"
2391 uapi_enum_start(family
, cw
, family
['operations'], 'enum-name')
2393 for op
in family
.msgs
.values():
2394 if separate_ntf
and ('notify' in op
or 'event' in op
):
2399 suffix
= f
" = {op.value},"
2401 cw
.p(op
.enum_name
+ suffix
)
2404 cw
.p(cnt_name
+ ('' if max_by_define
else ','))
2405 if not max_by_define
:
2406 cw
.p(f
"{max_name} = {max_value}")
2407 cw
.block_end(line
=';')
2409 cw
.p(f
"#define {max_name} {max_value}")
2413 uapi_enum_start(family
, cw
, family
['operations'], enum_name
='async-enum')
2414 for op
in family
.msgs
.values():
2415 if separate_ntf
and not ('notify' in op
or 'event' in op
):
2420 suffix
= f
" = {op['value']},"
2421 cw
.p(op
.enum_name
+ suffix
)
2422 cw
.block_end(line
=';')
2427 for grp
in family
.mcgrps
['list']:
2429 defines
.append([c_upper(grp
.get('c-define-name', f
"{family.name}-mcgrp-{name}")),
2433 cw
.writes_defines(defines
)
2436 cw
.p(f
'#endif /* {hdr_prot} */')
2439 def _render_user_ntf_entry(ri
, op
):
2440 ri
.cw
.block_start(line
=f
"[{op.enum_name}] = ")
2441 ri
.cw
.p(f
".alloc_sz\t= sizeof({type_name(ri, 'event')}),")
2442 ri
.cw
.p(f
".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,")
2443 ri
.cw
.p(f
".policy\t\t= &{ri.struct['reply'].render_name}_nest,")
2444 ri
.cw
.p(f
".free\t\t= (void *){op_prefix(ri, 'notify')}_free,")
2445 ri
.cw
.block_end(line
=',')
2448 def render_user_family(family
, cw
, prototype
):
2449 symbol
= f
'const struct ynl_family ynl_{family.c_name}_family'
2451 cw
.p(f
'extern {symbol};')
2455 cw
.block_start(line
=f
"static const struct ynl_ntf_info {family['name']}_ntf_info[] = ")
2456 for ntf_op_name
, ntf_op
in family
.ntfs
.items():
2457 if 'notify' in ntf_op
:
2458 op
= family
.ops
[ntf_op
['notify']]
2459 ri
= RenderInfo(cw
, family
, "user", op
, "notify")
2460 elif 'event' in ntf_op
:
2461 ri
= RenderInfo(cw
, family
, "user", ntf_op
, "event")
2463 raise Exception('Invalid notification ' + ntf_op_name
)
2464 _render_user_ntf_entry(ri
, ntf_op
)
2465 for op_name
, op
in family
.ops
.items():
2466 if 'event' not in op
:
2468 ri
= RenderInfo(cw
, family
, "user", op
, "event")
2469 _render_user_ntf_entry(ri
, op
)
2470 cw
.block_end(line
=";")
2473 cw
.block_start(f
'{symbol} = ')
2474 cw
.p(f
'.name\t\t= "{family.name}",')
2476 cw
.p(f
".ntf_info\t= {family['name']}_ntf_info,")
2477 cw
.p(f
".ntf_info_size\t= MNL_ARRAY_SIZE({family['name']}_ntf_info),")
2478 cw
.block_end(line
=';')
2481 def family_contains_bitfield32(family
):
2482 for _
, attr_set
in family
.attr_sets
.items():
2483 if attr_set
.subset_of
:
2485 for _
, attr
in attr_set
.items():
2486 if attr
.type == "bitfield32":
2491 def find_kernel_root(full_path
):
2494 sub_path
= os
.path
.join(os
.path
.basename(full_path
), sub_path
)
2495 full_path
= os
.path
.dirname(full_path
)
2496 maintainers
= os
.path
.join(full_path
, "MAINTAINERS")
2497 if os
.path
.exists(maintainers
):
2498 return full_path
, sub_path
[:-1]
2502 parser
= argparse
.ArgumentParser(description
='Netlink simple parsing generator')
2503 parser
.add_argument('--mode', dest
='mode', type=str, required
=True)
2504 parser
.add_argument('--spec', dest
='spec', type=str, required
=True)
2505 parser
.add_argument('--header', dest
='header', action
='store_true', default
=None)
2506 parser
.add_argument('--source', dest
='header', action
='store_false')
2507 parser
.add_argument('--user-header', nargs
='+', default
=[])
2508 parser
.add_argument('--exclude-op', action
='append', default
=[])
2509 parser
.add_argument('-o', dest
='out_file', type=str, default
=None)
2510 args
= parser
.parse_args()
2512 if args
.header
is None:
2513 parser
.error("--header or --source is required")
2515 exclude_ops
= [re
.compile(expr
) for expr
in args
.exclude_op
]
2518 parsed
= Family(args
.spec
, exclude_ops
)
2519 if parsed
.license
!= '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
2520 print('Spec license:', parsed
.license
)
2521 print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
2523 except yaml
.YAMLError
as exc
:
2528 supported_models
= ['unified']
2529 if args
.mode
in ['user', 'kernel']:
2530 supported_models
+= ['directional']
2531 if parsed
.msg_id_model
not in supported_models
:
2532 print(f
'Message enum-model {parsed.msg_id_model} not supported for {args.mode} generation')
2535 cw
= CodeWriter(BaseNlLib(), args
.out_file
)
2537 _
, spec_kernel
= find_kernel_root(args
.spec
)
2538 if args
.mode
== 'uapi' or args
.header
:
2539 cw
.p(f
'/* SPDX-License-Identifier: {parsed.license} */')
2541 cw
.p(f
'// SPDX-License-Identifier: {parsed.license}')
2542 cw
.p("/* Do not edit directly, auto-generated from: */")
2543 cw
.p(f
"/*\t{spec_kernel} */")
2544 cw
.p(f
"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
2545 if args
.exclude_op
or args
.user_header
:
2547 line
+= ' --user-header '.join([''] + args
.user_header
)
2548 line
+= ' --exclude-op '.join([''] + args
.exclude_op
)
2549 cw
.p(f
'/* YNL-ARG{line} */')
2552 if args
.mode
== 'uapi':
2553 render_uapi(parsed
, cw
)
2556 hdr_prot
= f
"_LINUX_{parsed.name.upper()}_GEN_H"
2558 cw
.p('#ifndef ' + hdr_prot
)
2559 cw
.p('#define ' + hdr_prot
)
2562 if args
.mode
== 'kernel':
2563 cw
.p('#include <net/netlink.h>')
2564 cw
.p('#include <net/genetlink.h>')
2568 cw
.p(f
'#include "{os.path.basename(args.out_file[:-2])}.h"')
2570 headers
= ['uapi/' + parsed
.uapi_header
]
2572 cw
.p('#include <stdlib.h>')
2573 cw
.p('#include <string.h>')
2575 cw
.p('#include <linux/types.h>')
2576 if family_contains_bitfield32(parsed
):
2577 cw
.p('#include <linux/netlink.h>')
2579 cw
.p(f
'#include "{parsed.name}-user.h"')
2580 cw
.p('#include "ynl.h"')
2581 headers
= [parsed
.uapi_header
]
2582 for definition
in parsed
['definitions']:
2583 if 'header' in definition
:
2584 headers
.append(definition
['header'])
2586 cw
.p(f
"#include <{one}>")
2589 if args
.mode
== "user":
2591 cw
.p("#include <libmnl/libmnl.h>")
2592 cw
.p("#include <linux/genetlink.h>")
2594 for one
in args
.user_header
:
2595 cw
.p(f
'#include "{one}"')
2597 cw
.p('struct ynl_sock;')
2599 render_user_family(parsed
, cw
, True)
2602 if args
.mode
== "kernel":
2604 for _
, struct
in sorted(parsed
.pure_nested_structs
.items()):
2606 cw
.p('/* Common nested types */')
2608 for attr_set
, struct
in sorted(parsed
.pure_nested_structs
.items()):
2610 print_req_policy_fwd(cw
, struct
)
2613 if parsed
.kernel_policy
== 'global':
2614 cw
.p(f
"/* Global operation policy for {parsed.name} */")
2616 struct
= Struct(parsed
, parsed
.global_policy_set
, type_list
=parsed
.global_policy
)
2617 print_req_policy_fwd(cw
, struct
)
2620 if parsed
.kernel_policy
in {'per-op', 'split'}:
2621 for op_name
, op
in parsed
.ops
.items():
2622 if 'do' in op
and 'event' not in op
:
2623 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, "do")
2624 print_req_policy_fwd(cw
, ri
.struct
['request'], ri
=ri
)
2627 print_kernel_op_table_hdr(parsed
, cw
)
2628 print_kernel_mcgrp_hdr(parsed
, cw
)
2629 print_kernel_family_struct_hdr(parsed
, cw
)
2631 print_kernel_policy_ranges(parsed
, cw
)
2633 for _
, struct
in sorted(parsed
.pure_nested_structs
.items()):
2635 cw
.p('/* Common nested types */')
2637 for attr_set
, struct
in sorted(parsed
.pure_nested_structs
.items()):
2639 print_req_policy(cw
, struct
)
2642 if parsed
.kernel_policy
== 'global':
2643 cw
.p(f
"/* Global operation policy for {parsed.name} */")
2645 struct
= Struct(parsed
, parsed
.global_policy_set
, type_list
=parsed
.global_policy
)
2646 print_req_policy(cw
, struct
)
2649 for op_name
, op
in parsed
.ops
.items():
2650 if parsed
.kernel_policy
in {'per-op', 'split'}:
2651 for op_mode
in ['do', 'dump']:
2652 if op_mode
in op
and 'request' in op
[op_mode
]:
2653 cw
.p(f
"/* {op.enum_name} - {op_mode} */")
2654 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, op_mode
)
2655 print_req_policy(cw
, ri
.struct
['request'], ri
=ri
)
2658 print_kernel_op_table(parsed
, cw
)
2659 print_kernel_mcgrp_src(parsed
, cw
)
2660 print_kernel_family_struct_src(parsed
, cw
)
2662 if args
.mode
== "user":
2665 put_op_name_fwd(parsed
, cw
)
2667 for name
, const
in parsed
.consts
.items():
2668 if isinstance(const
, EnumSet
):
2669 put_enum_to_str_fwd(parsed
, cw
, const
)
2672 cw
.p('/* Common nested types */')
2673 for attr_set
, struct
in parsed
.pure_nested_structs
.items():
2674 ri
= RenderInfo(cw
, parsed
, args
.mode
, "", "", attr_set
)
2675 print_type_full(ri
, struct
)
2677 for op_name
, op
in parsed
.ops
.items():
2678 cw
.p(f
"/* ============== {op.enum_name} ============== */")
2680 if 'do' in op
and 'event' not in op
:
2681 cw
.p(f
"/* {op.enum_name} - do */")
2682 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, "do")
2684 print_req_type_helpers(ri
)
2687 print_rsp_type_helpers(ri
)
2689 print_req_prototype(ri
)
2693 cw
.p(f
"/* {op.enum_name} - dump */")
2694 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, 'dump')
2696 print_req_type_helpers(ri
)
2697 if not ri
.type_consistent
:
2699 print_wrapped_type(ri
)
2700 print_dump_prototype(ri
)
2704 cw
.p(f
"/* {op.enum_name} - notify */")
2705 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, 'notify')
2706 if not ri
.type_consistent
:
2707 raise Exception(f
'Only notifications with consistent types supported ({op.name})')
2708 print_wrapped_type(ri
)
2710 for op_name
, op
in parsed
.ntfs
.items():
2712 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, 'event')
2713 cw
.p(f
"/* {op.enum_name} - event */")
2716 print_wrapped_type(ri
)
2720 put_op_name(parsed
, cw
)
2722 for name
, const
in parsed
.consts
.items():
2723 if isinstance(const
, EnumSet
):
2724 put_enum_to_str(parsed
, cw
, const
)
2727 cw
.p('/* Policies */')
2728 for name
in parsed
.pure_nested_structs
:
2729 struct
= Struct(parsed
, name
)
2730 put_typol(cw
, struct
)
2731 for name
in parsed
.root_sets
:
2732 struct
= Struct(parsed
, name
)
2733 put_typol(cw
, struct
)
2735 cw
.p('/* Common nested types */')
2736 for attr_set
, struct
in parsed
.pure_nested_structs
.items():
2737 ri
= RenderInfo(cw
, parsed
, args
.mode
, "", "", attr_set
)
2739 free_rsp_nested(ri
, struct
)
2741 put_req_nested(ri
, struct
)
2743 parse_rsp_nested(ri
, struct
)
2745 for op_name
, op
in parsed
.ops
.items():
2746 cw
.p(f
"/* ============== {op.enum_name} ============== */")
2747 if 'do' in op
and 'event' not in op
:
2748 cw
.p(f
"/* {op.enum_name} - do */")
2749 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, "do")
2757 cw
.p(f
"/* {op.enum_name} - dump */")
2758 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, "dump")
2759 if not ri
.type_consistent
:
2760 parse_rsp_msg(ri
, deref
=True)
2761 print_dump_type_free(ri
)
2766 cw
.p(f
"/* {op.enum_name} - notify */")
2767 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, 'notify')
2768 if not ri
.type_consistent
:
2769 raise Exception(f
'Only notifications with consistent types supported ({op.name})')
2770 print_ntf_type_free(ri
)
2772 for op_name
, op
in parsed
.ntfs
.items():
2774 cw
.p(f
"/* {op.enum_name} - event */")
2776 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, "do")
2779 ri
= RenderInfo(cw
, parsed
, args
.mode
, op
, "event")
2780 print_ntf_type_free(ri
)
2782 render_user_family(parsed
, cw
, False)
2785 cw
.p(f
'#endif /* {hdr_prot} */')
2788 if __name__
== "__main__":