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