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