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