xref: /linux/tools/net/ynl/pyynl/ynl_gen_c.py (revision 87948df5af4bb27effb0a187abaf54f41a54fe35)
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        elif self.sub_type == 'nest':
829            ri.cw.p(f'for (i = 0; i < {var}->_count.{self.c_name}; i++)')
830            ri.cw.p(f"{self.nested_render_name}_put(nlh, i, &{var}->{self.c_name}[i]);")
831        else:
832            raise Exception(f"Put for ArrayNest sub-type {self.attr['sub-type']} not supported, yet")
833        ri.cw.p('ynl_attr_nest_end(nlh, array);')
834
835    def _setter_lines(self, ri, member, presence):
836        return [f"{member} = {self.c_name};",
837                f"{presence} = n_{self.c_name};"]
838
839
840class TypeNestTypeValue(Type):
841    def _complex_member_type(self, ri):
842        return self.nested_struct_type
843
844    def _attr_typol(self):
845        return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
846
847    def _attr_get(self, ri, var):
848        prev = 'attr'
849        tv_args = ''
850        get_lines = []
851        local_vars = []
852        init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
853                      f"parg.data = &{var}->{self.c_name};"]
854        if 'type-value' in self.attr:
855            tv_names = [c_lower(x) for x in self.attr["type-value"]]
856            local_vars += [f'const struct nlattr *attr_{", *attr_".join(tv_names)};']
857            local_vars += [f'__u32 {", ".join(tv_names)};']
858            for level in self.attr["type-value"]:
859                level = c_lower(level)
860                get_lines += [f'attr_{level} = ynl_attr_data({prev});']
861                get_lines += [f'{level} = ynl_attr_type(attr_{level});']
862                prev = 'attr_' + level
863
864            tv_args = f", {', '.join(tv_names)}"
865
866        get_lines += [f"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"]
867        return get_lines, init_lines, local_vars
868
869
870class Struct:
871    def __init__(self, family, space_name, type_list=None, inherited=None):
872        self.family = family
873        self.space_name = space_name
874        self.attr_set = family.attr_sets[space_name]
875        # Use list to catch comparisons with empty sets
876        self._inherited = inherited if inherited is not None else []
877        self.inherited = []
878
879        self.nested = type_list is None
880        if family.name == c_lower(space_name):
881            self.render_name = c_lower(family.ident_name)
882        else:
883            self.render_name = c_lower(family.ident_name + '-' + space_name)
884        self.struct_name = 'struct ' + self.render_name
885        if self.nested and space_name in family.consts:
886            self.struct_name += '_'
887        self.ptr_name = self.struct_name + ' *'
888        # All attr sets this one contains, directly or multiple levels down
889        self.child_nests = set()
890
891        self.request = False
892        self.reply = False
893        self.recursive = False
894        self.in_multi_val = False  # used by a MultiAttr or and legacy arrays
895
896        self.attr_list = []
897        self.attrs = dict()
898        if type_list is not None:
899            for t in type_list:
900                self.attr_list.append((t, self.attr_set[t]),)
901        else:
902            for t in self.attr_set:
903                self.attr_list.append((t, self.attr_set[t]),)
904
905        max_val = 0
906        self.attr_max_val = None
907        for name, attr in self.attr_list:
908            if attr.value >= max_val:
909                max_val = attr.value
910                self.attr_max_val = attr
911            self.attrs[name] = attr
912
913    def __iter__(self):
914        yield from self.attrs
915
916    def __getitem__(self, key):
917        return self.attrs[key]
918
919    def member_list(self):
920        return self.attr_list
921
922    def set_inherited(self, new_inherited):
923        if self._inherited != new_inherited:
924            raise Exception("Inheriting different members not supported")
925        self.inherited = [c_lower(x) for x in sorted(self._inherited)]
926
927    def free_needs_iter(self):
928        for _, attr in self.attr_list:
929            if attr.free_needs_iter():
930                return True
931        return False
932
933
934class EnumEntry(SpecEnumEntry):
935    def __init__(self, enum_set, yaml, prev, value_start):
936        super().__init__(enum_set, yaml, prev, value_start)
937
938        if prev:
939            self.value_change = (self.value != prev.value + 1)
940        else:
941            self.value_change = (self.value != 0)
942        self.value_change = self.value_change or self.enum_set['type'] == 'flags'
943
944        # Added by resolve:
945        self.c_name = None
946        delattr(self, "c_name")
947
948    def resolve(self):
949        self.resolve_up(super())
950
951        self.c_name = c_upper(self.enum_set.value_pfx + self.name)
952
953
954class EnumSet(SpecEnumSet):
955    def __init__(self, family, yaml):
956        self.render_name = c_lower(family.ident_name + '-' + yaml['name'])
957
958        if 'enum-name' in yaml:
959            if yaml['enum-name']:
960                self.enum_name = 'enum ' + c_lower(yaml['enum-name'])
961                self.user_type = self.enum_name
962            else:
963                self.enum_name = None
964        else:
965            self.enum_name = 'enum ' + self.render_name
966
967        if self.enum_name:
968            self.user_type = self.enum_name
969        else:
970            self.user_type = 'int'
971
972        self.value_pfx = yaml.get('name-prefix', f"{family.ident_name}-{yaml['name']}-")
973        self.header = yaml.get('header', None)
974        self.enum_cnt_name = yaml.get('enum-cnt-name', None)
975
976        super().__init__(family, yaml)
977
978    def new_entry(self, entry, prev_entry, value_start):
979        return EnumEntry(self, entry, prev_entry, value_start)
980
981    def value_range(self):
982        low = min([x.value for x in self.entries.values()])
983        high = max([x.value for x in self.entries.values()])
984
985        if high - low + 1 != len(self.entries):
986            return None, None
987
988        return low, high
989
990
991class AttrSet(SpecAttrSet):
992    def __init__(self, family, yaml):
993        super().__init__(family, yaml)
994
995        if self.subset_of is None:
996            if 'name-prefix' in yaml:
997                pfx = yaml['name-prefix']
998            elif self.name == family.name:
999                pfx = family.ident_name + '-a-'
1000            else:
1001                pfx = f"{family.ident_name}-a-{self.name}-"
1002            self.name_prefix = c_upper(pfx)
1003            self.max_name = c_upper(self.yaml.get('attr-max-name', f"{self.name_prefix}max"))
1004            self.cnt_name = c_upper(self.yaml.get('attr-cnt-name', f"__{self.name_prefix}max"))
1005        else:
1006            self.name_prefix = family.attr_sets[self.subset_of].name_prefix
1007            self.max_name = family.attr_sets[self.subset_of].max_name
1008            self.cnt_name = family.attr_sets[self.subset_of].cnt_name
1009
1010        # Added by resolve:
1011        self.c_name = None
1012        delattr(self, "c_name")
1013
1014    def resolve(self):
1015        self.c_name = c_lower(self.name)
1016        if self.c_name in _C_KW:
1017            self.c_name += '_'
1018        if self.c_name == self.family.c_name:
1019            self.c_name = ''
1020
1021    def new_attr(self, elem, value):
1022        if elem['type'] in scalars:
1023            t = TypeScalar(self.family, self, elem, value)
1024        elif elem['type'] == 'unused':
1025            t = TypeUnused(self.family, self, elem, value)
1026        elif elem['type'] == 'pad':
1027            t = TypePad(self.family, self, elem, value)
1028        elif elem['type'] == 'flag':
1029            t = TypeFlag(self.family, self, elem, value)
1030        elif elem['type'] == 'string':
1031            t = TypeString(self.family, self, elem, value)
1032        elif elem['type'] == 'binary':
1033            if 'struct' in elem:
1034                t = TypeBinaryStruct(self.family, self, elem, value)
1035            elif elem.get('sub-type') in scalars:
1036                t = TypeBinaryScalarArray(self.family, self, elem, value)
1037            else:
1038                t = TypeBinary(self.family, self, elem, value)
1039        elif elem['type'] == 'bitfield32':
1040            t = TypeBitfield32(self.family, self, elem, value)
1041        elif elem['type'] == 'nest':
1042            t = TypeNest(self.family, self, elem, value)
1043        elif elem['type'] == 'indexed-array' and 'sub-type' in elem:
1044            if elem["sub-type"] in ['binary', 'nest', 'u32']:
1045                t = TypeArrayNest(self.family, self, elem, value)
1046            else:
1047                raise Exception(f'new_attr: unsupported sub-type {elem["sub-type"]}')
1048        elif elem['type'] == 'nest-type-value':
1049            t = TypeNestTypeValue(self.family, self, elem, value)
1050        else:
1051            raise Exception(f"No typed class for type {elem['type']}")
1052
1053        if 'multi-attr' in elem and elem['multi-attr']:
1054            t = TypeMultiAttr(self.family, self, elem, value, t)
1055
1056        return t
1057
1058
1059class Operation(SpecOperation):
1060    def __init__(self, family, yaml, req_value, rsp_value):
1061        # Fill in missing operation properties (for fixed hdr-only msgs)
1062        for mode in ['do', 'dump', 'event']:
1063            for direction in ['request', 'reply']:
1064                try:
1065                    yaml[mode][direction].setdefault('attributes', [])
1066                except KeyError:
1067                    pass
1068
1069        super().__init__(family, yaml, req_value, rsp_value)
1070
1071        self.render_name = c_lower(family.ident_name + '_' + self.name)
1072
1073        self.dual_policy = ('do' in yaml and 'request' in yaml['do']) and \
1074                         ('dump' in yaml and 'request' in yaml['dump'])
1075
1076        self.has_ntf = False
1077
1078        # Added by resolve:
1079        self.enum_name = None
1080        delattr(self, "enum_name")
1081
1082    def resolve(self):
1083        self.resolve_up(super())
1084
1085        if not self.is_async:
1086            self.enum_name = self.family.op_prefix + c_upper(self.name)
1087        else:
1088            self.enum_name = self.family.async_op_prefix + c_upper(self.name)
1089
1090    def mark_has_ntf(self):
1091        self.has_ntf = True
1092
1093
1094class Family(SpecFamily):
1095    def __init__(self, file_name, exclude_ops):
1096        # Added by resolve:
1097        self.c_name = None
1098        delattr(self, "c_name")
1099        self.op_prefix = None
1100        delattr(self, "op_prefix")
1101        self.async_op_prefix = None
1102        delattr(self, "async_op_prefix")
1103        self.mcgrps = None
1104        delattr(self, "mcgrps")
1105        self.consts = None
1106        delattr(self, "consts")
1107        self.hooks = None
1108        delattr(self, "hooks")
1109
1110        super().__init__(file_name, exclude_ops=exclude_ops)
1111
1112        self.fam_key = c_upper(self.yaml.get('c-family-name', self.yaml["name"] + '_FAMILY_NAME'))
1113        self.ver_key = c_upper(self.yaml.get('c-version-name', self.yaml["name"] + '_FAMILY_VERSION'))
1114
1115        if 'definitions' not in self.yaml:
1116            self.yaml['definitions'] = []
1117
1118        if 'uapi-header' in self.yaml:
1119            self.uapi_header = self.yaml['uapi-header']
1120        else:
1121            self.uapi_header = f"linux/{self.ident_name}.h"
1122        if self.uapi_header.startswith("linux/") and self.uapi_header.endswith('.h'):
1123            self.uapi_header_name = self.uapi_header[6:-2]
1124        else:
1125            self.uapi_header_name = self.ident_name
1126
1127    def resolve(self):
1128        self.resolve_up(super())
1129
1130        self.c_name = c_lower(self.ident_name)
1131        if 'name-prefix' in self.yaml['operations']:
1132            self.op_prefix = c_upper(self.yaml['operations']['name-prefix'])
1133        else:
1134            self.op_prefix = c_upper(self.yaml['name'] + '-cmd-')
1135        if 'async-prefix' in self.yaml['operations']:
1136            self.async_op_prefix = c_upper(self.yaml['operations']['async-prefix'])
1137        else:
1138            self.async_op_prefix = self.op_prefix
1139
1140        self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
1141
1142        self.hooks = dict()
1143        for when in ['pre', 'post']:
1144            self.hooks[when] = dict()
1145            for op_mode in ['do', 'dump']:
1146                self.hooks[when][op_mode] = dict()
1147                self.hooks[when][op_mode]['set'] = set()
1148                self.hooks[when][op_mode]['list'] = []
1149
1150        # dict space-name -> 'request': set(attrs), 'reply': set(attrs)
1151        self.root_sets = dict()
1152        # dict space-name -> Struct
1153        self.pure_nested_structs = dict()
1154
1155        self._mark_notify()
1156        self._mock_up_events()
1157
1158        self._load_root_sets()
1159        self._load_nested_sets()
1160        self._load_attr_use()
1161        self._load_hooks()
1162
1163        self.kernel_policy = self.yaml.get('kernel-policy', 'split')
1164        if self.kernel_policy == 'global':
1165            self._load_global_policy()
1166
1167    def new_enum(self, elem):
1168        return EnumSet(self, elem)
1169
1170    def new_attr_set(self, elem):
1171        return AttrSet(self, elem)
1172
1173    def new_operation(self, elem, req_value, rsp_value):
1174        return Operation(self, elem, req_value, rsp_value)
1175
1176    def is_classic(self):
1177        return self.proto == 'netlink-raw'
1178
1179    def _mark_notify(self):
1180        for op in self.msgs.values():
1181            if 'notify' in op:
1182                self.ops[op['notify']].mark_has_ntf()
1183
1184    # Fake a 'do' equivalent of all events, so that we can render their response parsing
1185    def _mock_up_events(self):
1186        for op in self.yaml['operations']['list']:
1187            if 'event' in op:
1188                op['do'] = {
1189                    'reply': {
1190                        'attributes': op['event']['attributes']
1191                    }
1192                }
1193
1194    def _load_root_sets(self):
1195        for op_name, op in self.msgs.items():
1196            if 'attribute-set' not in op:
1197                continue
1198
1199            req_attrs = set()
1200            rsp_attrs = set()
1201            for op_mode in ['do', 'dump']:
1202                if op_mode in op and 'request' in op[op_mode]:
1203                    req_attrs.update(set(op[op_mode]['request']['attributes']))
1204                if op_mode in op and 'reply' in op[op_mode]:
1205                    rsp_attrs.update(set(op[op_mode]['reply']['attributes']))
1206            if 'event' in op:
1207                rsp_attrs.update(set(op['event']['attributes']))
1208
1209            if op['attribute-set'] not in self.root_sets:
1210                self.root_sets[op['attribute-set']] = {'request': req_attrs, 'reply': rsp_attrs}
1211            else:
1212                self.root_sets[op['attribute-set']]['request'].update(req_attrs)
1213                self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs)
1214
1215    def _sort_pure_types(self):
1216        # Try to reorder according to dependencies
1217        pns_key_list = list(self.pure_nested_structs.keys())
1218        pns_key_seen = set()
1219        rounds = len(pns_key_list) ** 2  # it's basically bubble sort
1220        for _ in range(rounds):
1221            if len(pns_key_list) == 0:
1222                break
1223            name = pns_key_list.pop(0)
1224            finished = True
1225            for _, spec in self.attr_sets[name].items():
1226                if 'nested-attributes' in spec:
1227                    nested = spec['nested-attributes']
1228                    # If the unknown nest we hit is recursive it's fine, it'll be a pointer
1229                    if self.pure_nested_structs[nested].recursive:
1230                        continue
1231                    if nested not in pns_key_seen:
1232                        # Dicts are sorted, this will make struct last
1233                        struct = self.pure_nested_structs.pop(name)
1234                        self.pure_nested_structs[name] = struct
1235                        finished = False
1236                        break
1237            if finished:
1238                pns_key_seen.add(name)
1239            else:
1240                pns_key_list.append(name)
1241
1242    def _load_nested_sets(self):
1243        attr_set_queue = list(self.root_sets.keys())
1244        attr_set_seen = set(self.root_sets.keys())
1245
1246        while len(attr_set_queue):
1247            a_set = attr_set_queue.pop(0)
1248            for attr, spec in self.attr_sets[a_set].items():
1249                if 'nested-attributes' not in spec:
1250                    continue
1251
1252                nested = spec['nested-attributes']
1253                if nested not in attr_set_seen:
1254                    attr_set_queue.append(nested)
1255                    attr_set_seen.add(nested)
1256
1257                inherit = set()
1258                if nested not in self.root_sets:
1259                    if nested not in self.pure_nested_structs:
1260                        self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit)
1261                else:
1262                    raise Exception(f'Using attr set as root and nested not supported - {nested}')
1263
1264                if 'type-value' in spec:
1265                    if nested in self.root_sets:
1266                        raise Exception("Inheriting members to a space used as root not supported")
1267                    inherit.update(set(spec['type-value']))
1268                elif spec['type'] == 'indexed-array':
1269                    inherit.add('idx')
1270                self.pure_nested_structs[nested].set_inherited(inherit)
1271
1272        for root_set, rs_members in self.root_sets.items():
1273            for attr, spec in self.attr_sets[root_set].items():
1274                if 'nested-attributes' in spec:
1275                    nested = spec['nested-attributes']
1276                    if attr in rs_members['request']:
1277                        self.pure_nested_structs[nested].request = True
1278                    if attr in rs_members['reply']:
1279                        self.pure_nested_structs[nested].reply = True
1280                    if spec.is_multi_val():
1281                        child = self.pure_nested_structs.get(nested)
1282                        child.in_multi_val = True
1283
1284        self._sort_pure_types()
1285
1286        # Propagate the request / reply / recursive
1287        for attr_set, struct in reversed(self.pure_nested_structs.items()):
1288            for _, spec in self.attr_sets[attr_set].items():
1289                if 'nested-attributes' in spec:
1290                    child_name = spec['nested-attributes']
1291                    struct.child_nests.add(child_name)
1292                    child = self.pure_nested_structs.get(child_name)
1293                    if child:
1294                        if not child.recursive:
1295                            struct.child_nests.update(child.child_nests)
1296                        child.request |= struct.request
1297                        child.reply |= struct.reply
1298                        if spec.is_multi_val():
1299                            child.in_multi_val = True
1300                if attr_set in struct.child_nests:
1301                    struct.recursive = True
1302
1303        self._sort_pure_types()
1304
1305    def _load_attr_use(self):
1306        for _, struct in self.pure_nested_structs.items():
1307            if struct.request:
1308                for _, arg in struct.member_list():
1309                    arg.set_request()
1310            if struct.reply:
1311                for _, arg in struct.member_list():
1312                    arg.set_reply()
1313
1314        for root_set, rs_members in self.root_sets.items():
1315            for attr, spec in self.attr_sets[root_set].items():
1316                if attr in rs_members['request']:
1317                    spec.set_request()
1318                if attr in rs_members['reply']:
1319                    spec.set_reply()
1320
1321    def _load_global_policy(self):
1322        global_set = set()
1323        attr_set_name = None
1324        for op_name, op in self.ops.items():
1325            if not op:
1326                continue
1327            if 'attribute-set' not in op:
1328                continue
1329
1330            if attr_set_name is None:
1331                attr_set_name = op['attribute-set']
1332            if attr_set_name != op['attribute-set']:
1333                raise Exception('For a global policy all ops must use the same set')
1334
1335            for op_mode in ['do', 'dump']:
1336                if op_mode in op:
1337                    req = op[op_mode].get('request')
1338                    if req:
1339                        global_set.update(req.get('attributes', []))
1340
1341        self.global_policy = []
1342        self.global_policy_set = attr_set_name
1343        for attr in self.attr_sets[attr_set_name]:
1344            if attr in global_set:
1345                self.global_policy.append(attr)
1346
1347    def _load_hooks(self):
1348        for op in self.ops.values():
1349            for op_mode in ['do', 'dump']:
1350                if op_mode not in op:
1351                    continue
1352                for when in ['pre', 'post']:
1353                    if when not in op[op_mode]:
1354                        continue
1355                    name = op[op_mode][when]
1356                    if name in self.hooks[when][op_mode]['set']:
1357                        continue
1358                    self.hooks[when][op_mode]['set'].add(name)
1359                    self.hooks[when][op_mode]['list'].append(name)
1360
1361
1362class RenderInfo:
1363    def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None):
1364        self.family = family
1365        self.nl = cw.nlib
1366        self.ku_space = ku_space
1367        self.op_mode = op_mode
1368        self.op = op
1369
1370        self.fixed_hdr = None
1371        self.fixed_hdr_len = 'ys->family->hdr_len'
1372        if op and op.fixed_header:
1373            self.fixed_hdr = 'struct ' + c_lower(op.fixed_header)
1374            if op.fixed_header != family.fixed_header:
1375                if family.is_classic():
1376                    self.fixed_hdr_len = f"sizeof({self.fixed_hdr})"
1377                else:
1378                    raise Exception(f"Per-op fixed header not supported, yet")
1379
1380
1381        # 'do' and 'dump' response parsing is identical
1382        self.type_consistent = True
1383        self.type_oneside = False
1384        if op_mode != 'do' and 'dump' in op:
1385            if 'do' in op:
1386                if ('reply' in op['do']) != ('reply' in op["dump"]):
1387                    self.type_consistent = False
1388                elif 'reply' in op['do'] and op["do"]["reply"] != op["dump"]["reply"]:
1389                    self.type_consistent = False
1390            else:
1391                self.type_consistent = True
1392                self.type_oneside = True
1393
1394        self.attr_set = attr_set
1395        if not self.attr_set:
1396            self.attr_set = op['attribute-set']
1397
1398        self.type_name_conflict = False
1399        if op:
1400            self.type_name = c_lower(op.name)
1401        else:
1402            self.type_name = c_lower(attr_set)
1403            if attr_set in family.consts:
1404                self.type_name_conflict = True
1405
1406        self.cw = cw
1407
1408        self.struct = dict()
1409        if op_mode == 'notify':
1410            op_mode = 'do' if 'do' in op else 'dump'
1411        for op_dir in ['request', 'reply']:
1412            if op:
1413                type_list = []
1414                if op_dir in op[op_mode]:
1415                    type_list = op[op_mode][op_dir]['attributes']
1416                self.struct[op_dir] = Struct(family, self.attr_set, type_list=type_list)
1417        if op_mode == 'event':
1418            self.struct['reply'] = Struct(family, self.attr_set, type_list=op['event']['attributes'])
1419
1420    def type_empty(self, key):
1421        return len(self.struct[key].attr_list) == 0 and self.fixed_hdr is None
1422
1423    def needs_nlflags(self, direction):
1424        return self.op_mode == 'do' and direction == 'request' and self.family.is_classic()
1425
1426
1427class CodeWriter:
1428    def __init__(self, nlib, out_file=None, overwrite=True):
1429        self.nlib = nlib
1430        self._overwrite = overwrite
1431
1432        self._nl = False
1433        self._block_end = False
1434        self._silent_block = False
1435        self._ind = 0
1436        self._ifdef_block = None
1437        if out_file is None:
1438            self._out = os.sys.stdout
1439        else:
1440            self._out = tempfile.NamedTemporaryFile('w+')
1441            self._out_file = out_file
1442
1443    def __del__(self):
1444        self.close_out_file()
1445
1446    def close_out_file(self):
1447        if self._out == os.sys.stdout:
1448            return
1449        # Avoid modifying the file if contents didn't change
1450        self._out.flush()
1451        if not self._overwrite and os.path.isfile(self._out_file):
1452            if filecmp.cmp(self._out.name, self._out_file, shallow=False):
1453                return
1454        with open(self._out_file, 'w+') as out_file:
1455            self._out.seek(0)
1456            shutil.copyfileobj(self._out, out_file)
1457            self._out.close()
1458        self._out = os.sys.stdout
1459
1460    @classmethod
1461    def _is_cond(cls, line):
1462        return line.startswith('if') or line.startswith('while') or line.startswith('for')
1463
1464    def p(self, line, add_ind=0):
1465        if self._block_end:
1466            self._block_end = False
1467            if line.startswith('else'):
1468                line = '} ' + line
1469            else:
1470                self._out.write('\t' * self._ind + '}\n')
1471
1472        if self._nl:
1473            self._out.write('\n')
1474            self._nl = False
1475
1476        ind = self._ind
1477        if line[-1] == ':':
1478            ind -= 1
1479        if self._silent_block:
1480            ind += 1
1481        self._silent_block = line.endswith(')') and CodeWriter._is_cond(line)
1482        self._silent_block |= line.strip() == 'else'
1483        if line[0] == '#':
1484            ind = 0
1485        if add_ind:
1486            ind += add_ind
1487        self._out.write('\t' * ind + line + '\n')
1488
1489    def nl(self):
1490        self._nl = True
1491
1492    def block_start(self, line=''):
1493        if line:
1494            line = line + ' '
1495        self.p(line + '{')
1496        self._ind += 1
1497
1498    def block_end(self, line=''):
1499        if line and line[0] not in {';', ','}:
1500            line = ' ' + line
1501        self._ind -= 1
1502        self._nl = False
1503        if not line:
1504            # Delay printing closing bracket in case "else" comes next
1505            if self._block_end:
1506                self._out.write('\t' * (self._ind + 1) + '}\n')
1507            self._block_end = True
1508        else:
1509            self.p('}' + line)
1510
1511    def write_doc_line(self, doc, indent=True):
1512        words = doc.split()
1513        line = ' *'
1514        for word in words:
1515            if len(line) + len(word) >= 79:
1516                self.p(line)
1517                line = ' *'
1518                if indent:
1519                    line += '  '
1520            line += ' ' + word
1521        self.p(line)
1522
1523    def write_func_prot(self, qual_ret, name, args=None, doc=None, suffix=''):
1524        if not args:
1525            args = ['void']
1526
1527        if doc:
1528            self.p('/*')
1529            self.p(' * ' + doc)
1530            self.p(' */')
1531
1532        oneline = qual_ret
1533        if qual_ret[-1] != '*':
1534            oneline += ' '
1535        oneline += f"{name}({', '.join(args)}){suffix}"
1536
1537        if len(oneline) < 80:
1538            self.p(oneline)
1539            return
1540
1541        v = qual_ret
1542        if len(v) > 3:
1543            self.p(v)
1544            v = ''
1545        elif qual_ret[-1] != '*':
1546            v += ' '
1547        v += name + '('
1548        ind = '\t' * (len(v) // 8) + ' ' * (len(v) % 8)
1549        delta_ind = len(v) - len(ind)
1550        v += args[0]
1551        i = 1
1552        while i < len(args):
1553            next_len = len(v) + len(args[i])
1554            if v[0] == '\t':
1555                next_len += delta_ind
1556            if next_len > 76:
1557                self.p(v + ',')
1558                v = ind
1559            else:
1560                v += ', '
1561            v += args[i]
1562            i += 1
1563        self.p(v + ')' + suffix)
1564
1565    def write_func_lvar(self, local_vars):
1566        if not local_vars:
1567            return
1568
1569        if type(local_vars) is str:
1570            local_vars = [local_vars]
1571
1572        local_vars.sort(key=len, reverse=True)
1573        for var in local_vars:
1574            self.p(var)
1575        self.nl()
1576
1577    def write_func(self, qual_ret, name, body, args=None, local_vars=None):
1578        self.write_func_prot(qual_ret=qual_ret, name=name, args=args)
1579        self.block_start()
1580        self.write_func_lvar(local_vars=local_vars)
1581
1582        for line in body:
1583            self.p(line)
1584        self.block_end()
1585
1586    def writes_defines(self, defines):
1587        longest = 0
1588        for define in defines:
1589            if len(define[0]) > longest:
1590                longest = len(define[0])
1591        longest = ((longest + 8) // 8) * 8
1592        for define in defines:
1593            line = '#define ' + define[0]
1594            line += '\t' * ((longest - len(define[0]) + 7) // 8)
1595            if type(define[1]) is int:
1596                line += str(define[1])
1597            elif type(define[1]) is str:
1598                line += '"' + define[1] + '"'
1599            self.p(line)
1600
1601    def write_struct_init(self, members):
1602        longest = max([len(x[0]) for x in members])
1603        longest += 1  # because we prepend a .
1604        longest = ((longest + 8) // 8) * 8
1605        for one in members:
1606            line = '.' + one[0]
1607            line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8)
1608            line += '= ' + str(one[1]) + ','
1609            self.p(line)
1610
1611    def ifdef_block(self, config):
1612        config_option = None
1613        if config:
1614            config_option = 'CONFIG_' + c_upper(config)
1615        if self._ifdef_block == config_option:
1616            return
1617
1618        if self._ifdef_block:
1619            self.p('#endif /* ' + self._ifdef_block + ' */')
1620        if config_option:
1621            self.p('#ifdef ' + config_option)
1622        self._ifdef_block = config_option
1623
1624
1625scalars = {'u8', 'u16', 'u32', 'u64', 's8', 's16', 's32', 's64', 'uint', 'sint'}
1626
1627direction_to_suffix = {
1628    'reply': '_rsp',
1629    'request': '_req',
1630    '': ''
1631}
1632
1633op_mode_to_wrapper = {
1634    'do': '',
1635    'dump': '_list',
1636    'notify': '_ntf',
1637    'event': '',
1638}
1639
1640_C_KW = {
1641    'auto',
1642    'bool',
1643    'break',
1644    'case',
1645    'char',
1646    'const',
1647    'continue',
1648    'default',
1649    'do',
1650    'double',
1651    'else',
1652    'enum',
1653    'extern',
1654    'float',
1655    'for',
1656    'goto',
1657    'if',
1658    'inline',
1659    'int',
1660    'long',
1661    'register',
1662    'return',
1663    'short',
1664    'signed',
1665    'sizeof',
1666    'static',
1667    'struct',
1668    'switch',
1669    'typedef',
1670    'union',
1671    'unsigned',
1672    'void',
1673    'volatile',
1674    'while'
1675}
1676
1677
1678def rdir(direction):
1679    if direction == 'reply':
1680        return 'request'
1681    if direction == 'request':
1682        return 'reply'
1683    return direction
1684
1685
1686def op_prefix(ri, direction, deref=False):
1687    suffix = f"_{ri.type_name}"
1688
1689    if not ri.op_mode or ri.op_mode == 'do':
1690        suffix += f"{direction_to_suffix[direction]}"
1691    else:
1692        if direction == 'request':
1693            suffix += '_req'
1694            if not ri.type_oneside:
1695                suffix += '_dump'
1696        else:
1697            if ri.type_consistent:
1698                if deref:
1699                    suffix += f"{direction_to_suffix[direction]}"
1700                else:
1701                    suffix += op_mode_to_wrapper[ri.op_mode]
1702            else:
1703                suffix += '_rsp'
1704                suffix += '_dump' if deref else '_list'
1705
1706    return f"{ri.family.c_name}{suffix}"
1707
1708
1709def type_name(ri, direction, deref=False):
1710    return f"struct {op_prefix(ri, direction, deref=deref)}"
1711
1712
1713def print_prototype(ri, direction, terminate=True, doc=None):
1714    suffix = ';' if terminate else ''
1715
1716    fname = ri.op.render_name
1717    if ri.op_mode == 'dump':
1718        fname += '_dump'
1719
1720    args = ['struct ynl_sock *ys']
1721    if 'request' in ri.op[ri.op_mode]:
1722        args.append(f"{type_name(ri, direction)} *" + f"{direction_to_suffix[direction][1:]}")
1723
1724    ret = 'int'
1725    if 'reply' in ri.op[ri.op_mode]:
1726        ret = f"{type_name(ri, rdir(direction))} *"
1727
1728    ri.cw.write_func_prot(ret, fname, args, doc=doc, suffix=suffix)
1729
1730
1731def print_req_prototype(ri):
1732    print_prototype(ri, "request", doc=ri.op['doc'])
1733
1734
1735def print_dump_prototype(ri):
1736    print_prototype(ri, "request")
1737
1738
1739def put_typol_fwd(cw, struct):
1740    cw.p(f'extern const struct ynl_policy_nest {struct.render_name}_nest;')
1741
1742
1743def put_typol(cw, struct):
1744    type_max = struct.attr_set.max_name
1745    cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
1746
1747    for _, arg in struct.member_list():
1748        arg.attr_typol(cw)
1749
1750    cw.block_end(line=';')
1751    cw.nl()
1752
1753    cw.block_start(line=f'const struct ynl_policy_nest {struct.render_name}_nest =')
1754    cw.p(f'.max_attr = {type_max},')
1755    cw.p(f'.table = {struct.render_name}_policy,')
1756    cw.block_end(line=';')
1757    cw.nl()
1758
1759
1760def _put_enum_to_str_helper(cw, render_name, map_name, arg_name, enum=None):
1761    args = [f'int {arg_name}']
1762    if enum:
1763        args = [enum.user_type + ' ' + arg_name]
1764    cw.write_func_prot('const char *', f'{render_name}_str', args)
1765    cw.block_start()
1766    if enum and enum.type == 'flags':
1767        cw.p(f'{arg_name} = ffs({arg_name}) - 1;')
1768    cw.p(f'if ({arg_name} < 0 || {arg_name} >= (int)YNL_ARRAY_SIZE({map_name}))')
1769    cw.p('return NULL;')
1770    cw.p(f'return {map_name}[{arg_name}];')
1771    cw.block_end()
1772    cw.nl()
1773
1774
1775def put_op_name_fwd(family, cw):
1776    cw.write_func_prot('const char *', f'{family.c_name}_op_str', ['int op'], suffix=';')
1777
1778
1779def put_op_name(family, cw):
1780    map_name = f'{family.c_name}_op_strmap'
1781    cw.block_start(line=f"static const char * const {map_name}[] =")
1782    for op_name, op in family.msgs.items():
1783        if op.rsp_value:
1784            # Make sure we don't add duplicated entries, if multiple commands
1785            # produce the same response in legacy families.
1786            if family.rsp_by_value[op.rsp_value] != op:
1787                cw.p(f'// skip "{op_name}", duplicate reply value')
1788                continue
1789
1790            if op.req_value == op.rsp_value:
1791                cw.p(f'[{op.enum_name}] = "{op_name}",')
1792            else:
1793                cw.p(f'[{op.rsp_value}] = "{op_name}",')
1794    cw.block_end(line=';')
1795    cw.nl()
1796
1797    _put_enum_to_str_helper(cw, family.c_name + '_op', map_name, 'op')
1798
1799
1800def put_enum_to_str_fwd(family, cw, enum):
1801    args = [enum.user_type + ' value']
1802    cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';')
1803
1804
1805def put_enum_to_str(family, cw, enum):
1806    map_name = f'{enum.render_name}_strmap'
1807    cw.block_start(line=f"static const char * const {map_name}[] =")
1808    for entry in enum.entries.values():
1809        cw.p(f'[{entry.value}] = "{entry.name}",')
1810    cw.block_end(line=';')
1811    cw.nl()
1812
1813    _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
1814
1815
1816def put_req_nested_prototype(ri, struct, suffix=';'):
1817    func_args = ['struct nlmsghdr *nlh',
1818                 'unsigned int attr_type',
1819                 f'{struct.ptr_name}obj']
1820
1821    ri.cw.write_func_prot('int', f'{struct.render_name}_put', func_args,
1822                          suffix=suffix)
1823
1824
1825def put_req_nested(ri, struct):
1826    local_vars = []
1827    init_lines = []
1828
1829    local_vars.append('struct nlattr *nest;')
1830    init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);")
1831
1832    has_anest = False
1833    has_count = False
1834    for _, arg in struct.member_list():
1835        has_anest |= arg.type == 'indexed-array'
1836        has_count |= arg.presence_type() == 'count'
1837    if has_anest:
1838        local_vars.append('struct nlattr *array;')
1839    if has_count:
1840        local_vars.append('unsigned int i;')
1841
1842    put_req_nested_prototype(ri, struct, suffix='')
1843    ri.cw.block_start()
1844    ri.cw.write_func_lvar(local_vars)
1845
1846    for line in init_lines:
1847        ri.cw.p(line)
1848
1849    for _, arg in struct.member_list():
1850        arg.attr_put(ri, "obj")
1851
1852    ri.cw.p("ynl_attr_nest_end(nlh, nest);")
1853
1854    ri.cw.nl()
1855    ri.cw.p('return 0;')
1856    ri.cw.block_end()
1857    ri.cw.nl()
1858
1859
1860def _multi_parse(ri, struct, init_lines, local_vars):
1861    if struct.nested:
1862        iter_line = "ynl_attr_for_each_nested(attr, nested)"
1863    else:
1864        if ri.fixed_hdr:
1865            local_vars += ['void *hdr;']
1866        iter_line = "ynl_attr_for_each(attr, nlh, yarg->ys->family->hdr_len)"
1867        if ri.op.fixed_header != ri.family.fixed_header:
1868            if ri.family.is_classic():
1869                iter_line = f"ynl_attr_for_each(attr, nlh, sizeof({ri.fixed_hdr}))"
1870            else:
1871                raise Exception(f"Per-op fixed header not supported, yet")
1872
1873    array_nests = set()
1874    multi_attrs = set()
1875    needs_parg = False
1876    for arg, aspec in struct.member_list():
1877        if aspec['type'] == 'indexed-array' and 'sub-type' in aspec:
1878            if aspec["sub-type"] in {'binary', 'nest'}:
1879                local_vars.append(f'const struct nlattr *attr_{aspec.c_name};')
1880                array_nests.add(arg)
1881            elif aspec['sub-type'] in scalars:
1882                local_vars.append(f'const struct nlattr *attr_{aspec.c_name};')
1883                array_nests.add(arg)
1884            else:
1885                raise Exception(f'Not supported sub-type {aspec["sub-type"]}')
1886        if 'multi-attr' in aspec:
1887            multi_attrs.add(arg)
1888        needs_parg |= 'nested-attributes' in aspec
1889    if array_nests or multi_attrs:
1890        local_vars.append('int i;')
1891    if needs_parg:
1892        local_vars.append('struct ynl_parse_arg parg;')
1893        init_lines.append('parg.ys = yarg->ys;')
1894
1895    all_multi = array_nests | multi_attrs
1896
1897    for anest in sorted(all_multi):
1898        local_vars.append(f"unsigned int n_{struct[anest].c_name} = 0;")
1899
1900    ri.cw.block_start()
1901    ri.cw.write_func_lvar(local_vars)
1902
1903    for line in init_lines:
1904        ri.cw.p(line)
1905    ri.cw.nl()
1906
1907    for arg in struct.inherited:
1908        ri.cw.p(f'dst->{arg} = {arg};')
1909
1910    if ri.fixed_hdr:
1911        if ri.family.is_classic():
1912            ri.cw.p('hdr = ynl_nlmsg_data(nlh);')
1913        else:
1914            ri.cw.p('hdr = ynl_nlmsg_data_offset(nlh, sizeof(struct genlmsghdr));')
1915        ri.cw.p(f"memcpy(&dst->_hdr, hdr, sizeof({ri.fixed_hdr}));")
1916    for anest in sorted(all_multi):
1917        aspec = struct[anest]
1918        ri.cw.p(f"if (dst->{aspec.c_name})")
1919        ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
1920
1921    ri.cw.nl()
1922    ri.cw.block_start(line=iter_line)
1923    ri.cw.p('unsigned int type = ynl_attr_type(attr);')
1924    ri.cw.nl()
1925
1926    first = True
1927    for _, arg in struct.member_list():
1928        good = arg.attr_get(ri, 'dst', first=first)
1929        # First may be 'unused' or 'pad', ignore those
1930        first &= not good
1931
1932    ri.cw.block_end()
1933    ri.cw.nl()
1934
1935    for anest in sorted(array_nests):
1936        aspec = struct[anest]
1937
1938        ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1939        ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1940        ri.cw.p(f"dst->_count.{aspec.c_name} = n_{aspec.c_name};")
1941        ri.cw.p('i = 0;')
1942        if 'nested-attributes' in aspec:
1943            ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1944        ri.cw.block_start(line=f"ynl_attr_for_each_nested(attr, attr_{aspec.c_name})")
1945        if 'nested-attributes' in aspec:
1946            ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1947            ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr, ynl_attr_type(attr)))")
1948            ri.cw.p('return YNL_PARSE_CB_ERROR;')
1949        elif aspec.sub_type in scalars:
1950            ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.sub_type}(attr);")
1951        elif aspec.sub_type == 'binary' and 'exact-len' in aspec.checks:
1952            # Length is validated by typol
1953            ri.cw.p(f'memcpy(dst->{aspec.c_name}[i], ynl_attr_data(attr), {aspec.checks["exact-len"]});')
1954        else:
1955            raise Exception(f"Nest parsing type not supported in {aspec['name']}")
1956        ri.cw.p('i++;')
1957        ri.cw.block_end()
1958        ri.cw.block_end()
1959    ri.cw.nl()
1960
1961    for anest in sorted(multi_attrs):
1962        aspec = struct[anest]
1963        ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1964        ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1965        ri.cw.p(f"dst->_count.{aspec.c_name} = n_{aspec.c_name};")
1966        ri.cw.p('i = 0;')
1967        if 'nested-attributes' in aspec:
1968            ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1969        ri.cw.block_start(line=iter_line)
1970        ri.cw.block_start(line=f"if (ynl_attr_type(attr) == {aspec.enum_name})")
1971        if 'nested-attributes' in aspec:
1972            ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1973            ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))")
1974            ri.cw.p('return YNL_PARSE_CB_ERROR;')
1975        elif aspec.type in scalars:
1976            ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.type}(attr);")
1977        elif aspec.type == 'binary' and 'struct' in aspec:
1978            ri.cw.p('size_t len = ynl_attr_data_len(attr);')
1979            ri.cw.nl()
1980            ri.cw.p(f'if (len > sizeof(dst->{aspec.c_name}[0]))')
1981            ri.cw.p(f'len = sizeof(dst->{aspec.c_name}[0]);')
1982            ri.cw.p(f"memcpy(&dst->{aspec.c_name}[i], ynl_attr_data(attr), len);")
1983        elif aspec.type == 'string':
1984            ri.cw.p('unsigned int len;')
1985            ri.cw.nl()
1986            ri.cw.p('len = strnlen(ynl_attr_get_str(attr), ynl_attr_data_len(attr));')
1987            ri.cw.p(f'dst->{aspec.c_name}[i] = malloc(sizeof(struct ynl_string) + len + 1);')
1988            ri.cw.p(f"dst->{aspec.c_name}[i]->len = len;")
1989            ri.cw.p(f"memcpy(dst->{aspec.c_name}[i]->str, ynl_attr_get_str(attr), len);")
1990            ri.cw.p(f"dst->{aspec.c_name}[i]->str[len] = 0;")
1991        else:
1992            raise Exception(f'Nest parsing of type {aspec.type} not supported yet')
1993        ri.cw.p('i++;')
1994        ri.cw.block_end()
1995        ri.cw.block_end()
1996        ri.cw.block_end()
1997    ri.cw.nl()
1998
1999    if struct.nested:
2000        ri.cw.p('return 0;')
2001    else:
2002        ri.cw.p('return YNL_PARSE_CB_OK;')
2003    ri.cw.block_end()
2004    ri.cw.nl()
2005
2006
2007def parse_rsp_nested_prototype(ri, struct, suffix=';'):
2008    func_args = ['struct ynl_parse_arg *yarg',
2009                 'const struct nlattr *nested']
2010    for arg in struct.inherited:
2011        func_args.append('__u32 ' + arg)
2012
2013    ri.cw.write_func_prot('int', f'{struct.render_name}_parse', func_args,
2014                          suffix=suffix)
2015
2016
2017def parse_rsp_nested(ri, struct):
2018    parse_rsp_nested_prototype(ri, struct, suffix='')
2019
2020    local_vars = ['const struct nlattr *attr;',
2021                  f'{struct.ptr_name}dst = yarg->data;']
2022    init_lines = []
2023
2024    if struct.member_list():
2025        _multi_parse(ri, struct, init_lines, local_vars)
2026    else:
2027        # Empty nest
2028        ri.cw.block_start()
2029        ri.cw.p('return 0;')
2030        ri.cw.block_end()
2031        ri.cw.nl()
2032
2033
2034def parse_rsp_msg(ri, deref=False):
2035    if 'reply' not in ri.op[ri.op_mode] and ri.op_mode != 'event':
2036        return
2037
2038    func_args = ['const struct nlmsghdr *nlh',
2039                 'struct ynl_parse_arg *yarg']
2040
2041    local_vars = [f'{type_name(ri, "reply", deref=deref)} *dst;',
2042                  'const struct nlattr *attr;']
2043    init_lines = ['dst = yarg->data;']
2044
2045    ri.cw.write_func_prot('int', f'{op_prefix(ri, "reply", deref=deref)}_parse', func_args)
2046
2047    if ri.struct["reply"].member_list():
2048        _multi_parse(ri, ri.struct["reply"], init_lines, local_vars)
2049    else:
2050        # Empty reply
2051        ri.cw.block_start()
2052        ri.cw.p('return YNL_PARSE_CB_OK;')
2053        ri.cw.block_end()
2054        ri.cw.nl()
2055
2056
2057def print_req(ri):
2058    ret_ok = '0'
2059    ret_err = '-1'
2060    direction = "request"
2061    local_vars = ['struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };',
2062                  'struct nlmsghdr *nlh;',
2063                  'int err;']
2064
2065    if 'reply' in ri.op[ri.op_mode]:
2066        ret_ok = 'rsp'
2067        ret_err = 'NULL'
2068        local_vars += [f'{type_name(ri, rdir(direction))} *rsp;']
2069
2070    if ri.fixed_hdr:
2071        local_vars += ['size_t hdr_len;',
2072                       'void *hdr;']
2073
2074    for _, attr in ri.struct["request"].member_list():
2075        if attr.presence_type() == 'count':
2076            local_vars += ['unsigned int i;']
2077            break
2078
2079    print_prototype(ri, direction, terminate=False)
2080    ri.cw.block_start()
2081    ri.cw.write_func_lvar(local_vars)
2082
2083    if ri.family.is_classic():
2084        ri.cw.p(f"nlh = ynl_msg_start_req(ys, {ri.op.enum_name}, req->_nlmsg_flags);")
2085    else:
2086        ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
2087
2088    ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
2089    ri.cw.p(f"ys->req_hdr_len = {ri.fixed_hdr_len};")
2090    if 'reply' in ri.op[ri.op_mode]:
2091        ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
2092    ri.cw.nl()
2093
2094    if ri.fixed_hdr:
2095        ri.cw.p("hdr_len = sizeof(req->_hdr);")
2096        ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);")
2097        ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);")
2098        ri.cw.nl()
2099
2100    for _, attr in ri.struct["request"].member_list():
2101        attr.attr_put(ri, "req")
2102    ri.cw.nl()
2103
2104    if 'reply' in ri.op[ri.op_mode]:
2105        ri.cw.p('rsp = calloc(1, sizeof(*rsp));')
2106        ri.cw.p('yrs.yarg.data = rsp;')
2107        ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;")
2108        if ri.op.value is not None:
2109            ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};')
2110        else:
2111            ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};')
2112        ri.cw.nl()
2113    ri.cw.p("err = ynl_exec(ys, nlh, &yrs);")
2114    ri.cw.p('if (err < 0)')
2115    if 'reply' in ri.op[ri.op_mode]:
2116        ri.cw.p('goto err_free;')
2117    else:
2118        ri.cw.p('return -1;')
2119    ri.cw.nl()
2120
2121    ri.cw.p(f"return {ret_ok};")
2122    ri.cw.nl()
2123
2124    if 'reply' in ri.op[ri.op_mode]:
2125        ri.cw.p('err_free:')
2126        ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}")
2127        ri.cw.p(f"return {ret_err};")
2128
2129    ri.cw.block_end()
2130
2131
2132def print_dump(ri):
2133    direction = "request"
2134    print_prototype(ri, direction, terminate=False)
2135    ri.cw.block_start()
2136    local_vars = ['struct ynl_dump_state yds = {};',
2137                  'struct nlmsghdr *nlh;',
2138                  'int err;']
2139
2140    if ri.fixed_hdr:
2141        local_vars += ['size_t hdr_len;',
2142                       'void *hdr;']
2143
2144    ri.cw.write_func_lvar(local_vars)
2145
2146    ri.cw.p('yds.yarg.ys = ys;')
2147    ri.cw.p(f"yds.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
2148    ri.cw.p("yds.yarg.data = NULL;")
2149    ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});")
2150    ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;")
2151    if ri.op.value is not None:
2152        ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};')
2153    else:
2154        ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};')
2155    ri.cw.nl()
2156    if ri.family.is_classic():
2157        ri.cw.p(f"nlh = ynl_msg_start_dump(ys, {ri.op.enum_name});")
2158    else:
2159        ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
2160
2161    if ri.fixed_hdr:
2162        ri.cw.p("hdr_len = sizeof(req->_hdr);")
2163        ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);")
2164        ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);")
2165        ri.cw.nl()
2166
2167    if "request" in ri.op[ri.op_mode]:
2168        ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
2169        ri.cw.p(f"ys->req_hdr_len = {ri.fixed_hdr_len};")
2170        ri.cw.nl()
2171        for _, attr in ri.struct["request"].member_list():
2172            attr.attr_put(ri, "req")
2173    ri.cw.nl()
2174
2175    ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);')
2176    ri.cw.p('if (err < 0)')
2177    ri.cw.p('goto free_list;')
2178    ri.cw.nl()
2179
2180    ri.cw.p('return yds.first;')
2181    ri.cw.nl()
2182    ri.cw.p('free_list:')
2183    ri.cw.p(call_free(ri, rdir(direction), 'yds.first'))
2184    ri.cw.p('return NULL;')
2185    ri.cw.block_end()
2186
2187
2188def call_free(ri, direction, var):
2189    return f"{op_prefix(ri, direction)}_free({var});"
2190
2191
2192def free_arg_name(direction):
2193    if direction:
2194        return direction_to_suffix[direction][1:]
2195    return 'obj'
2196
2197
2198def print_alloc_wrapper(ri, direction):
2199    name = op_prefix(ri, direction)
2200    ri.cw.write_func_prot(f'static inline struct {name} *', f"{name}_alloc", [f"void"])
2201    ri.cw.block_start()
2202    ri.cw.p(f'return calloc(1, sizeof(struct {name}));')
2203    ri.cw.block_end()
2204
2205
2206def print_free_prototype(ri, direction, suffix=';'):
2207    name = op_prefix(ri, direction)
2208    struct_name = name
2209    if ri.type_name_conflict:
2210        struct_name += '_'
2211    arg = free_arg_name(direction)
2212    ri.cw.write_func_prot('void', f"{name}_free", [f"struct {struct_name} *{arg}"], suffix=suffix)
2213
2214
2215def print_nlflags_set(ri, direction):
2216    name = op_prefix(ri, direction)
2217    ri.cw.write_func_prot(f'static inline void', f"{name}_set_nlflags",
2218                          [f"struct {name} *req", "__u16 nl_flags"])
2219    ri.cw.block_start()
2220    ri.cw.p('req->_nlmsg_flags = nl_flags;')
2221    ri.cw.block_end()
2222    ri.cw.nl()
2223
2224
2225def _print_type(ri, direction, struct):
2226    suffix = f'_{ri.type_name}{direction_to_suffix[direction]}'
2227    if not direction and ri.type_name_conflict:
2228        suffix += '_'
2229
2230    if ri.op_mode == 'dump' and not ri.type_oneside:
2231        suffix += '_dump'
2232
2233    ri.cw.block_start(line=f"struct {ri.family.c_name}{suffix}")
2234
2235    if ri.needs_nlflags(direction):
2236        ri.cw.p('__u16 _nlmsg_flags;')
2237        ri.cw.nl()
2238    if ri.fixed_hdr:
2239        ri.cw.p(ri.fixed_hdr + ' _hdr;')
2240        ri.cw.nl()
2241
2242    for type_filter in ['present', 'len', 'count']:
2243        meta_started = False
2244        for _, attr in struct.member_list():
2245            line = attr.presence_member(ri.ku_space, type_filter)
2246            if line:
2247                if not meta_started:
2248                    ri.cw.block_start(line=f"struct")
2249                    meta_started = True
2250                ri.cw.p(line)
2251        if meta_started:
2252            ri.cw.block_end(line=f'_{type_filter};')
2253    ri.cw.nl()
2254
2255    for arg in struct.inherited:
2256        ri.cw.p(f"__u32 {arg};")
2257
2258    for _, attr in struct.member_list():
2259        attr.struct_member(ri)
2260
2261    ri.cw.block_end(line=';')
2262    ri.cw.nl()
2263
2264
2265def print_type(ri, direction):
2266    _print_type(ri, direction, ri.struct[direction])
2267
2268
2269def print_type_full(ri, struct):
2270    _print_type(ri, "", struct)
2271
2272
2273def print_type_helpers(ri, direction, deref=False):
2274    print_free_prototype(ri, direction)
2275    ri.cw.nl()
2276
2277    if ri.needs_nlflags(direction):
2278        print_nlflags_set(ri, direction)
2279
2280    if ri.ku_space == 'user' and direction == 'request':
2281        for _, attr in ri.struct[direction].member_list():
2282            attr.setter(ri, ri.attr_set, direction, deref=deref)
2283    ri.cw.nl()
2284
2285
2286def print_req_type_helpers(ri):
2287    if ri.type_empty("request"):
2288        return
2289    print_alloc_wrapper(ri, "request")
2290    print_type_helpers(ri, "request")
2291
2292
2293def print_rsp_type_helpers(ri):
2294    if 'reply' not in ri.op[ri.op_mode]:
2295        return
2296    print_type_helpers(ri, "reply")
2297
2298
2299def print_parse_prototype(ri, direction, terminate=True):
2300    suffix = "_rsp" if direction == "reply" else "_req"
2301    term = ';' if terminate else ''
2302
2303    ri.cw.write_func_prot('void', f"{ri.op.render_name}{suffix}_parse",
2304                          ['const struct nlattr **tb',
2305                           f"struct {ri.op.render_name}{suffix} *req"],
2306                          suffix=term)
2307
2308
2309def print_req_type(ri):
2310    if ri.type_empty("request"):
2311        return
2312    print_type(ri, "request")
2313
2314
2315def print_req_free(ri):
2316    if 'request' not in ri.op[ri.op_mode]:
2317        return
2318    _free_type(ri, 'request', ri.struct['request'])
2319
2320
2321def print_rsp_type(ri):
2322    if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]:
2323        direction = 'reply'
2324    elif ri.op_mode == 'event':
2325        direction = 'reply'
2326    else:
2327        return
2328    print_type(ri, direction)
2329
2330
2331def print_wrapped_type(ri):
2332    ri.cw.block_start(line=f"{type_name(ri, 'reply')}")
2333    if ri.op_mode == 'dump':
2334        ri.cw.p(f"{type_name(ri, 'reply')} *next;")
2335    elif ri.op_mode == 'notify' or ri.op_mode == 'event':
2336        ri.cw.p('__u16 family;')
2337        ri.cw.p('__u8 cmd;')
2338        ri.cw.p('struct ynl_ntf_base_type *next;')
2339        ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);")
2340    ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__((aligned(8)));")
2341    ri.cw.block_end(line=';')
2342    ri.cw.nl()
2343    print_free_prototype(ri, 'reply')
2344    ri.cw.nl()
2345
2346
2347def _free_type_members_iter(ri, struct):
2348    if struct.free_needs_iter():
2349        ri.cw.p('unsigned int i;')
2350        ri.cw.nl()
2351
2352
2353def _free_type_members(ri, var, struct, ref=''):
2354    for _, attr in struct.member_list():
2355        attr.free(ri, var, ref)
2356
2357
2358def _free_type(ri, direction, struct):
2359    var = free_arg_name(direction)
2360
2361    print_free_prototype(ri, direction, suffix='')
2362    ri.cw.block_start()
2363    _free_type_members_iter(ri, struct)
2364    _free_type_members(ri, var, struct)
2365    if direction:
2366        ri.cw.p(f'free({var});')
2367    ri.cw.block_end()
2368    ri.cw.nl()
2369
2370
2371def free_rsp_nested_prototype(ri):
2372        print_free_prototype(ri, "")
2373
2374
2375def free_rsp_nested(ri, struct):
2376    _free_type(ri, "", struct)
2377
2378
2379def print_rsp_free(ri):
2380    if 'reply' not in ri.op[ri.op_mode]:
2381        return
2382    _free_type(ri, 'reply', ri.struct['reply'])
2383
2384
2385def print_dump_type_free(ri):
2386    sub_type = type_name(ri, 'reply')
2387
2388    print_free_prototype(ri, 'reply', suffix='')
2389    ri.cw.block_start()
2390    ri.cw.p(f"{sub_type} *next = rsp;")
2391    ri.cw.nl()
2392    ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)')
2393    _free_type_members_iter(ri, ri.struct['reply'])
2394    ri.cw.p('rsp = next;')
2395    ri.cw.p('next = rsp->next;')
2396    ri.cw.nl()
2397
2398    _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2399    ri.cw.p(f'free(rsp);')
2400    ri.cw.block_end()
2401    ri.cw.block_end()
2402    ri.cw.nl()
2403
2404
2405def print_ntf_type_free(ri):
2406    print_free_prototype(ri, 'reply', suffix='')
2407    ri.cw.block_start()
2408    _free_type_members_iter(ri, ri.struct['reply'])
2409    _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2410    ri.cw.p(f'free(rsp);')
2411    ri.cw.block_end()
2412    ri.cw.nl()
2413
2414
2415def print_req_policy_fwd(cw, struct, ri=None, terminate=True):
2416    if terminate and ri and policy_should_be_static(struct.family):
2417        return
2418
2419    if terminate:
2420        prefix = 'extern '
2421    else:
2422        if ri and policy_should_be_static(struct.family):
2423            prefix = 'static '
2424        else:
2425            prefix = ''
2426
2427    suffix = ';' if terminate else ' = {'
2428
2429    max_attr = struct.attr_max_val
2430    if ri:
2431        name = ri.op.render_name
2432        if ri.op.dual_policy:
2433            name += '_' + ri.op_mode
2434    else:
2435        name = struct.render_name
2436    cw.p(f"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}")
2437
2438
2439def print_req_policy(cw, struct, ri=None):
2440    if ri and ri.op:
2441        cw.ifdef_block(ri.op.get('config-cond', None))
2442    print_req_policy_fwd(cw, struct, ri=ri, terminate=False)
2443    for _, arg in struct.member_list():
2444        arg.attr_policy(cw)
2445    cw.p("};")
2446    cw.ifdef_block(None)
2447    cw.nl()
2448
2449
2450def kernel_can_gen_family_struct(family):
2451    return family.proto == 'genetlink'
2452
2453
2454def policy_should_be_static(family):
2455    return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family)
2456
2457
2458def print_kernel_policy_ranges(family, cw):
2459    first = True
2460    for _, attr_set in family.attr_sets.items():
2461        if attr_set.subset_of:
2462            continue
2463
2464        for _, attr in attr_set.items():
2465            if not attr.request:
2466                continue
2467            if 'full-range' not in attr.checks:
2468                continue
2469
2470            if first:
2471                cw.p('/* Integer value ranges */')
2472                first = False
2473
2474            sign = '' if attr.type[0] == 'u' else '_signed'
2475            suffix = 'ULL' if attr.type[0] == 'u' else 'LL'
2476            cw.block_start(line=f'static const struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =')
2477            members = []
2478            if 'min' in attr.checks:
2479                members.append(('min', attr.get_limit_str('min', suffix=suffix)))
2480            if 'max' in attr.checks:
2481                members.append(('max', attr.get_limit_str('max', suffix=suffix)))
2482            cw.write_struct_init(members)
2483            cw.block_end(line=';')
2484            cw.nl()
2485
2486
2487def print_kernel_policy_sparse_enum_validates(family, cw):
2488    first = True
2489    for _, attr_set in family.attr_sets.items():
2490        if attr_set.subset_of:
2491            continue
2492
2493        for _, attr in attr_set.items():
2494            if not attr.request:
2495                continue
2496            if not attr.enum_name:
2497                continue
2498            if 'sparse' not in attr.checks:
2499                continue
2500
2501            if first:
2502                cw.p('/* Sparse enums validation callbacks */')
2503                first = False
2504
2505            sign = '' if attr.type[0] == 'u' else '_signed'
2506            suffix = 'ULL' if attr.type[0] == 'u' else 'LL'
2507            cw.write_func_prot('static int', f'{c_lower(attr.enum_name)}_validate',
2508                               ['const struct nlattr *attr', 'struct netlink_ext_ack *extack'])
2509            cw.block_start()
2510            cw.block_start(line=f'switch (nla_get_{attr["type"]}(attr))')
2511            enum = family.consts[attr['enum']]
2512            first_entry = True
2513            for entry in enum.entries.values():
2514                if first_entry:
2515                    first_entry = False
2516                else:
2517                    cw.p('fallthrough;')
2518                cw.p(f'case {entry.c_name}:')
2519            cw.p('return 0;')
2520            cw.block_end()
2521            cw.p('NL_SET_ERR_MSG_ATTR(extack, attr, "invalid enum value");')
2522            cw.p('return -EINVAL;')
2523            cw.block_end()
2524            cw.nl()
2525
2526
2527def print_kernel_op_table_fwd(family, cw, terminate):
2528    exported = not kernel_can_gen_family_struct(family)
2529
2530    if not terminate or exported:
2531        cw.p(f"/* Ops table for {family.ident_name} */")
2532
2533        pol_to_struct = {'global': 'genl_small_ops',
2534                         'per-op': 'genl_ops',
2535                         'split': 'genl_split_ops'}
2536        struct_type = pol_to_struct[family.kernel_policy]
2537
2538        if not exported:
2539            cnt = ""
2540        elif family.kernel_policy == 'split':
2541            cnt = 0
2542            for op in family.ops.values():
2543                if 'do' in op:
2544                    cnt += 1
2545                if 'dump' in op:
2546                    cnt += 1
2547        else:
2548            cnt = len(family.ops)
2549
2550        qual = 'static const' if not exported else 'const'
2551        line = f"{qual} struct {struct_type} {family.c_name}_nl_ops[{cnt}]"
2552        if terminate:
2553            cw.p(f"extern {line};")
2554        else:
2555            cw.block_start(line=line + ' =')
2556
2557    if not terminate:
2558        return
2559
2560    cw.nl()
2561    for name in family.hooks['pre']['do']['list']:
2562        cw.write_func_prot('int', c_lower(name),
2563                           ['const struct genl_split_ops *ops',
2564                            'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2565    for name in family.hooks['post']['do']['list']:
2566        cw.write_func_prot('void', c_lower(name),
2567                           ['const struct genl_split_ops *ops',
2568                            'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2569    for name in family.hooks['pre']['dump']['list']:
2570        cw.write_func_prot('int', c_lower(name),
2571                           ['struct netlink_callback *cb'], suffix=';')
2572    for name in family.hooks['post']['dump']['list']:
2573        cw.write_func_prot('int', c_lower(name),
2574                           ['struct netlink_callback *cb'], suffix=';')
2575
2576    cw.nl()
2577
2578    for op_name, op in family.ops.items():
2579        if op.is_async:
2580            continue
2581
2582        if 'do' in op:
2583            name = c_lower(f"{family.ident_name}-nl-{op_name}-doit")
2584            cw.write_func_prot('int', name,
2585                               ['struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2586
2587        if 'dump' in op:
2588            name = c_lower(f"{family.ident_name}-nl-{op_name}-dumpit")
2589            cw.write_func_prot('int', name,
2590                               ['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix=';')
2591    cw.nl()
2592
2593
2594def print_kernel_op_table_hdr(family, cw):
2595    print_kernel_op_table_fwd(family, cw, terminate=True)
2596
2597
2598def print_kernel_op_table(family, cw):
2599    print_kernel_op_table_fwd(family, cw, terminate=False)
2600    if family.kernel_policy == 'global' or family.kernel_policy == 'per-op':
2601        for op_name, op in family.ops.items():
2602            if op.is_async:
2603                continue
2604
2605            cw.ifdef_block(op.get('config-cond', None))
2606            cw.block_start()
2607            members = [('cmd', op.enum_name)]
2608            if 'dont-validate' in op:
2609                members.append(('validate',
2610                                ' | '.join([c_upper('genl-dont-validate-' + x)
2611                                            for x in op['dont-validate']])), )
2612            for op_mode in ['do', 'dump']:
2613                if op_mode in op:
2614                    name = c_lower(f"{family.ident_name}-nl-{op_name}-{op_mode}it")
2615                    members.append((op_mode + 'it', name))
2616            if family.kernel_policy == 'per-op':
2617                struct = Struct(family, op['attribute-set'],
2618                                type_list=op['do']['request']['attributes'])
2619
2620                name = c_lower(f"{family.ident_name}-{op_name}-nl-policy")
2621                members.append(('policy', name))
2622                members.append(('maxattr', struct.attr_max_val.enum_name))
2623            if 'flags' in op:
2624                members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in op['flags']])))
2625            cw.write_struct_init(members)
2626            cw.block_end(line=',')
2627    elif family.kernel_policy == 'split':
2628        cb_names = {'do':   {'pre': 'pre_doit', 'post': 'post_doit'},
2629                    'dump': {'pre': 'start', 'post': 'done'}}
2630
2631        for op_name, op in family.ops.items():
2632            for op_mode in ['do', 'dump']:
2633                if op.is_async or op_mode not in op:
2634                    continue
2635
2636                cw.ifdef_block(op.get('config-cond', None))
2637                cw.block_start()
2638                members = [('cmd', op.enum_name)]
2639                if 'dont-validate' in op:
2640                    dont_validate = []
2641                    for x in op['dont-validate']:
2642                        if op_mode == 'do' and x in ['dump', 'dump-strict']:
2643                            continue
2644                        if op_mode == "dump" and x == 'strict':
2645                            continue
2646                        dont_validate.append(x)
2647
2648                    if dont_validate:
2649                        members.append(('validate',
2650                                        ' | '.join([c_upper('genl-dont-validate-' + x)
2651                                                    for x in dont_validate])), )
2652                name = c_lower(f"{family.ident_name}-nl-{op_name}-{op_mode}it")
2653                if 'pre' in op[op_mode]:
2654                    members.append((cb_names[op_mode]['pre'], c_lower(op[op_mode]['pre'])))
2655                members.append((op_mode + 'it', name))
2656                if 'post' in op[op_mode]:
2657                    members.append((cb_names[op_mode]['post'], c_lower(op[op_mode]['post'])))
2658                if 'request' in op[op_mode]:
2659                    struct = Struct(family, op['attribute-set'],
2660                                    type_list=op[op_mode]['request']['attributes'])
2661
2662                    if op.dual_policy:
2663                        name = c_lower(f"{family.ident_name}-{op_name}-{op_mode}-nl-policy")
2664                    else:
2665                        name = c_lower(f"{family.ident_name}-{op_name}-nl-policy")
2666                    members.append(('policy', name))
2667                    members.append(('maxattr', struct.attr_max_val.enum_name))
2668                flags = (op['flags'] if 'flags' in op else []) + ['cmd-cap-' + op_mode]
2669                members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in flags])))
2670                cw.write_struct_init(members)
2671                cw.block_end(line=',')
2672    cw.ifdef_block(None)
2673
2674    cw.block_end(line=';')
2675    cw.nl()
2676
2677
2678def print_kernel_mcgrp_hdr(family, cw):
2679    if not family.mcgrps['list']:
2680        return
2681
2682    cw.block_start('enum')
2683    for grp in family.mcgrps['list']:
2684        grp_id = c_upper(f"{family.ident_name}-nlgrp-{grp['name']},")
2685        cw.p(grp_id)
2686    cw.block_end(';')
2687    cw.nl()
2688
2689
2690def print_kernel_mcgrp_src(family, cw):
2691    if not family.mcgrps['list']:
2692        return
2693
2694    cw.block_start('static const struct genl_multicast_group ' + family.c_name + '_nl_mcgrps[] =')
2695    for grp in family.mcgrps['list']:
2696        name = grp['name']
2697        grp_id = c_upper(f"{family.ident_name}-nlgrp-{name}")
2698        cw.p('[' + grp_id + '] = { "' + name + '", },')
2699    cw.block_end(';')
2700    cw.nl()
2701
2702
2703def print_kernel_family_struct_hdr(family, cw):
2704    if not kernel_can_gen_family_struct(family):
2705        return
2706
2707    cw.p(f"extern struct genl_family {family.c_name}_nl_family;")
2708    cw.nl()
2709    if 'sock-priv' in family.kernel_family:
2710        cw.p(f'void {family.c_name}_nl_sock_priv_init({family.kernel_family["sock-priv"]} *priv);')
2711        cw.p(f'void {family.c_name}_nl_sock_priv_destroy({family.kernel_family["sock-priv"]} *priv);')
2712        cw.nl()
2713
2714
2715def print_kernel_family_struct_src(family, cw):
2716    if not kernel_can_gen_family_struct(family):
2717        return
2718
2719    if 'sock-priv' in family.kernel_family:
2720        # Generate "trampolines" to make CFI happy
2721        cw.write_func("static void", f"__{family.c_name}_nl_sock_priv_init",
2722                      [f"{family.c_name}_nl_sock_priv_init(priv);"],
2723                      ["void *priv"])
2724        cw.nl()
2725        cw.write_func("static void", f"__{family.c_name}_nl_sock_priv_destroy",
2726                      [f"{family.c_name}_nl_sock_priv_destroy(priv);"],
2727                      ["void *priv"])
2728        cw.nl()
2729
2730    cw.block_start(f"struct genl_family {family.ident_name}_nl_family __ro_after_init =")
2731    cw.p('.name\t\t= ' + family.fam_key + ',')
2732    cw.p('.version\t= ' + family.ver_key + ',')
2733    cw.p('.netnsok\t= true,')
2734    cw.p('.parallel_ops\t= true,')
2735    cw.p('.module\t\t= THIS_MODULE,')
2736    if family.kernel_policy == 'per-op':
2737        cw.p(f'.ops\t\t= {family.c_name}_nl_ops,')
2738        cw.p(f'.n_ops\t\t= ARRAY_SIZE({family.c_name}_nl_ops),')
2739    elif family.kernel_policy == 'split':
2740        cw.p(f'.split_ops\t= {family.c_name}_nl_ops,')
2741        cw.p(f'.n_split_ops\t= ARRAY_SIZE({family.c_name}_nl_ops),')
2742    if family.mcgrps['list']:
2743        cw.p(f'.mcgrps\t\t= {family.c_name}_nl_mcgrps,')
2744        cw.p(f'.n_mcgrps\t= ARRAY_SIZE({family.c_name}_nl_mcgrps),')
2745    if 'sock-priv' in family.kernel_family:
2746        cw.p(f'.sock_priv_size\t= sizeof({family.kernel_family["sock-priv"]}),')
2747        cw.p(f'.sock_priv_init\t= __{family.c_name}_nl_sock_priv_init,')
2748        cw.p(f'.sock_priv_destroy = __{family.c_name}_nl_sock_priv_destroy,')
2749    cw.block_end(';')
2750
2751
2752def uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'):
2753    start_line = 'enum'
2754    if enum_name in obj:
2755        if obj[enum_name]:
2756            start_line = 'enum ' + c_lower(obj[enum_name])
2757    elif ckey and ckey in obj:
2758        start_line = 'enum ' + family.c_name + '_' + c_lower(obj[ckey])
2759    cw.block_start(line=start_line)
2760
2761
2762def render_uapi_unified(family, cw, max_by_define, separate_ntf):
2763    max_name = c_upper(family.get('cmd-max-name', f"{family.op_prefix}MAX"))
2764    cnt_name = c_upper(family.get('cmd-cnt-name', f"__{family.op_prefix}MAX"))
2765    max_value = f"({cnt_name} - 1)"
2766
2767    uapi_enum_start(family, cw, family['operations'], 'enum-name')
2768    val = 0
2769    for op in family.msgs.values():
2770        if separate_ntf and ('notify' in op or 'event' in op):
2771            continue
2772
2773        suffix = ','
2774        if op.value != val:
2775            suffix = f" = {op.value},"
2776            val = op.value
2777        cw.p(op.enum_name + suffix)
2778        val += 1
2779    cw.nl()
2780    cw.p(cnt_name + ('' if max_by_define else ','))
2781    if not max_by_define:
2782        cw.p(f"{max_name} = {max_value}")
2783    cw.block_end(line=';')
2784    if max_by_define:
2785        cw.p(f"#define {max_name} {max_value}")
2786    cw.nl()
2787
2788
2789def render_uapi_directional(family, cw, max_by_define):
2790    max_name = f"{family.op_prefix}USER_MAX"
2791    cnt_name = f"__{family.op_prefix}USER_CNT"
2792    max_value = f"({cnt_name} - 1)"
2793
2794    cw.block_start(line='enum')
2795    cw.p(c_upper(f'{family.name}_MSG_USER_NONE = 0,'))
2796    val = 0
2797    for op in family.msgs.values():
2798        if 'do' in op and 'event' not in op:
2799            suffix = ','
2800            if op.value and op.value != val:
2801                suffix = f" = {op.value},"
2802                val = op.value
2803            cw.p(op.enum_name + suffix)
2804            val += 1
2805    cw.nl()
2806    cw.p(cnt_name + ('' if max_by_define else ','))
2807    if not max_by_define:
2808        cw.p(f"{max_name} = {max_value}")
2809    cw.block_end(line=';')
2810    if max_by_define:
2811        cw.p(f"#define {max_name} {max_value}")
2812    cw.nl()
2813
2814    max_name = f"{family.op_prefix}KERNEL_MAX"
2815    cnt_name = f"__{family.op_prefix}KERNEL_CNT"
2816    max_value = f"({cnt_name} - 1)"
2817
2818    cw.block_start(line='enum')
2819    cw.p(c_upper(f'{family.name}_MSG_KERNEL_NONE = 0,'))
2820    val = 0
2821    for op in family.msgs.values():
2822        if ('do' in op and 'reply' in op['do']) or 'notify' in op or 'event' in op:
2823            enum_name = op.enum_name
2824            if 'event' not in op and 'notify' not in op:
2825                enum_name = f'{enum_name}_REPLY'
2826
2827            suffix = ','
2828            if op.value and op.value != val:
2829                suffix = f" = {op.value},"
2830                val = op.value
2831            cw.p(enum_name + suffix)
2832            val += 1
2833    cw.nl()
2834    cw.p(cnt_name + ('' if max_by_define else ','))
2835    if not max_by_define:
2836        cw.p(f"{max_name} = {max_value}")
2837    cw.block_end(line=';')
2838    if max_by_define:
2839        cw.p(f"#define {max_name} {max_value}")
2840    cw.nl()
2841
2842
2843def render_uapi(family, cw):
2844    hdr_prot = f"_UAPI_LINUX_{c_upper(family.uapi_header_name)}_H"
2845    hdr_prot = hdr_prot.replace('/', '_')
2846    cw.p('#ifndef ' + hdr_prot)
2847    cw.p('#define ' + hdr_prot)
2848    cw.nl()
2849
2850    defines = [(family.fam_key, family["name"]),
2851               (family.ver_key, family.get('version', 1))]
2852    cw.writes_defines(defines)
2853    cw.nl()
2854
2855    defines = []
2856    for const in family['definitions']:
2857        if const.get('header'):
2858            continue
2859
2860        if const['type'] != 'const':
2861            cw.writes_defines(defines)
2862            defines = []
2863            cw.nl()
2864
2865        # Write kdoc for enum and flags (one day maybe also structs)
2866        if const['type'] == 'enum' or const['type'] == 'flags':
2867            enum = family.consts[const['name']]
2868
2869            if enum.header:
2870                continue
2871
2872            if enum.has_doc():
2873                if enum.has_entry_doc():
2874                    cw.p('/**')
2875                    doc = ''
2876                    if 'doc' in enum:
2877                        doc = ' - ' + enum['doc']
2878                    cw.write_doc_line(enum.enum_name + doc)
2879                else:
2880                    cw.p('/*')
2881                    cw.write_doc_line(enum['doc'], indent=False)
2882                for entry in enum.entries.values():
2883                    if entry.has_doc():
2884                        doc = '@' + entry.c_name + ': ' + entry['doc']
2885                        cw.write_doc_line(doc)
2886                cw.p(' */')
2887
2888            uapi_enum_start(family, cw, const, 'name')
2889            name_pfx = const.get('name-prefix', f"{family.ident_name}-{const['name']}-")
2890            for entry in enum.entries.values():
2891                suffix = ','
2892                if entry.value_change:
2893                    suffix = f" = {entry.user_value()}" + suffix
2894                cw.p(entry.c_name + suffix)
2895
2896            if const.get('render-max', False):
2897                cw.nl()
2898                cw.p('/* private: */')
2899                if const['type'] == 'flags':
2900                    max_name = c_upper(name_pfx + 'mask')
2901                    max_val = f' = {enum.get_mask()},'
2902                    cw.p(max_name + max_val)
2903                else:
2904                    cnt_name = enum.enum_cnt_name
2905                    max_name = c_upper(name_pfx + 'max')
2906                    if not cnt_name:
2907                        cnt_name = '__' + name_pfx + 'max'
2908                    cw.p(c_upper(cnt_name) + ',')
2909                    cw.p(max_name + ' = (' + c_upper(cnt_name) + ' - 1)')
2910            cw.block_end(line=';')
2911            cw.nl()
2912        elif const['type'] == 'const':
2913            defines.append([c_upper(family.get('c-define-name',
2914                                               f"{family.ident_name}-{const['name']}")),
2915                            const['value']])
2916
2917    if defines:
2918        cw.writes_defines(defines)
2919        cw.nl()
2920
2921    max_by_define = family.get('max-by-define', False)
2922
2923    for _, attr_set in family.attr_sets.items():
2924        if attr_set.subset_of:
2925            continue
2926
2927        max_value = f"({attr_set.cnt_name} - 1)"
2928
2929        val = 0
2930        uapi_enum_start(family, cw, attr_set.yaml, 'enum-name')
2931        for _, attr in attr_set.items():
2932            suffix = ','
2933            if attr.value != val:
2934                suffix = f" = {attr.value},"
2935                val = attr.value
2936            val += 1
2937            cw.p(attr.enum_name + suffix)
2938        if attr_set.items():
2939            cw.nl()
2940        cw.p(attr_set.cnt_name + ('' if max_by_define else ','))
2941        if not max_by_define:
2942            cw.p(f"{attr_set.max_name} = {max_value}")
2943        cw.block_end(line=';')
2944        if max_by_define:
2945            cw.p(f"#define {attr_set.max_name} {max_value}")
2946        cw.nl()
2947
2948    # Commands
2949    separate_ntf = 'async-prefix' in family['operations']
2950
2951    if family.msg_id_model == 'unified':
2952        render_uapi_unified(family, cw, max_by_define, separate_ntf)
2953    elif family.msg_id_model == 'directional':
2954        render_uapi_directional(family, cw, max_by_define)
2955    else:
2956        raise Exception(f'Unsupported message enum-model {family.msg_id_model}')
2957
2958    if separate_ntf:
2959        uapi_enum_start(family, cw, family['operations'], enum_name='async-enum')
2960        for op in family.msgs.values():
2961            if separate_ntf and not ('notify' in op or 'event' in op):
2962                continue
2963
2964            suffix = ','
2965            if 'value' in op:
2966                suffix = f" = {op['value']},"
2967            cw.p(op.enum_name + suffix)
2968        cw.block_end(line=';')
2969        cw.nl()
2970
2971    # Multicast
2972    defines = []
2973    for grp in family.mcgrps['list']:
2974        name = grp['name']
2975        defines.append([c_upper(grp.get('c-define-name', f"{family.ident_name}-mcgrp-{name}")),
2976                        f'{name}'])
2977    cw.nl()
2978    if defines:
2979        cw.writes_defines(defines)
2980        cw.nl()
2981
2982    cw.p(f'#endif /* {hdr_prot} */')
2983
2984
2985def _render_user_ntf_entry(ri, op):
2986    if not ri.family.is_classic():
2987        ri.cw.block_start(line=f"[{op.enum_name}] = ")
2988    else:
2989        crud_op = ri.family.req_by_value[op.rsp_value]
2990        ri.cw.block_start(line=f"[{crud_op.enum_name}] = ")
2991    ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),")
2992    ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,")
2993    ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,")
2994    ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,")
2995    ri.cw.block_end(line=',')
2996
2997
2998def render_user_family(family, cw, prototype):
2999    symbol = f'const struct ynl_family ynl_{family.c_name}_family'
3000    if prototype:
3001        cw.p(f'extern {symbol};')
3002        return
3003
3004    if family.ntfs:
3005        cw.block_start(line=f"static const struct ynl_ntf_info {family.c_name}_ntf_info[] = ")
3006        for ntf_op_name, ntf_op in family.ntfs.items():
3007            if 'notify' in ntf_op:
3008                op = family.ops[ntf_op['notify']]
3009                ri = RenderInfo(cw, family, "user", op, "notify")
3010            elif 'event' in ntf_op:
3011                ri = RenderInfo(cw, family, "user", ntf_op, "event")
3012            else:
3013                raise Exception('Invalid notification ' + ntf_op_name)
3014            _render_user_ntf_entry(ri, ntf_op)
3015        for op_name, op in family.ops.items():
3016            if 'event' not in op:
3017                continue
3018            ri = RenderInfo(cw, family, "user", op, "event")
3019            _render_user_ntf_entry(ri, op)
3020        cw.block_end(line=";")
3021        cw.nl()
3022
3023    cw.block_start(f'{symbol} = ')
3024    cw.p(f'.name\t\t= "{family.c_name}",')
3025    if family.is_classic():
3026        cw.p(f'.is_classic\t= true,')
3027        cw.p(f'.classic_id\t= {family.get("protonum")},')
3028    if family.is_classic():
3029        if family.fixed_header:
3030            cw.p(f'.hdr_len\t= sizeof(struct {c_lower(family.fixed_header)}),')
3031    elif family.fixed_header:
3032        cw.p(f'.hdr_len\t= sizeof(struct genlmsghdr) + sizeof(struct {c_lower(family.fixed_header)}),')
3033    else:
3034        cw.p('.hdr_len\t= sizeof(struct genlmsghdr),')
3035    if family.ntfs:
3036        cw.p(f".ntf_info\t= {family.c_name}_ntf_info,")
3037        cw.p(f".ntf_info_size\t= YNL_ARRAY_SIZE({family.c_name}_ntf_info),")
3038    cw.block_end(line=';')
3039
3040
3041def family_contains_bitfield32(family):
3042    for _, attr_set in family.attr_sets.items():
3043        if attr_set.subset_of:
3044            continue
3045        for _, attr in attr_set.items():
3046            if attr.type == "bitfield32":
3047                return True
3048    return False
3049
3050
3051def find_kernel_root(full_path):
3052    sub_path = ''
3053    while True:
3054        sub_path = os.path.join(os.path.basename(full_path), sub_path)
3055        full_path = os.path.dirname(full_path)
3056        maintainers = os.path.join(full_path, "MAINTAINERS")
3057        if os.path.exists(maintainers):
3058            return full_path, sub_path[:-1]
3059
3060
3061def main():
3062    parser = argparse.ArgumentParser(description='Netlink simple parsing generator')
3063    parser.add_argument('--mode', dest='mode', type=str, required=True,
3064                        choices=('user', 'kernel', 'uapi'))
3065    parser.add_argument('--spec', dest='spec', type=str, required=True)
3066    parser.add_argument('--header', dest='header', action='store_true', default=None)
3067    parser.add_argument('--source', dest='header', action='store_false')
3068    parser.add_argument('--user-header', nargs='+', default=[])
3069    parser.add_argument('--cmp-out', action='store_true', default=None,
3070                        help='Do not overwrite the output file if the new output is identical to the old')
3071    parser.add_argument('--exclude-op', action='append', default=[])
3072    parser.add_argument('-o', dest='out_file', type=str, default=None)
3073    args = parser.parse_args()
3074
3075    if args.header is None:
3076        parser.error("--header or --source is required")
3077
3078    exclude_ops = [re.compile(expr) for expr in args.exclude_op]
3079
3080    try:
3081        parsed = Family(args.spec, exclude_ops)
3082        if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
3083            print('Spec license:', parsed.license)
3084            print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
3085            os.sys.exit(1)
3086    except yaml.YAMLError as exc:
3087        print(exc)
3088        os.sys.exit(1)
3089        return
3090
3091    cw = CodeWriter(BaseNlLib(), args.out_file, overwrite=(not args.cmp_out))
3092
3093    _, spec_kernel = find_kernel_root(args.spec)
3094    if args.mode == 'uapi' or args.header:
3095        cw.p(f'/* SPDX-License-Identifier: {parsed.license} */')
3096    else:
3097        cw.p(f'// SPDX-License-Identifier: {parsed.license}')
3098    cw.p("/* Do not edit directly, auto-generated from: */")
3099    cw.p(f"/*\t{spec_kernel} */")
3100    cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
3101    if args.exclude_op or args.user_header:
3102        line = ''
3103        line += ' --user-header '.join([''] + args.user_header)
3104        line += ' --exclude-op '.join([''] + args.exclude_op)
3105        cw.p(f'/* YNL-ARG{line} */')
3106    cw.nl()
3107
3108    if args.mode == 'uapi':
3109        render_uapi(parsed, cw)
3110        return
3111
3112    hdr_prot = f"_LINUX_{parsed.c_name.upper()}_GEN_H"
3113    if args.header:
3114        cw.p('#ifndef ' + hdr_prot)
3115        cw.p('#define ' + hdr_prot)
3116        cw.nl()
3117
3118    if args.out_file:
3119        hdr_file = os.path.basename(args.out_file[:-2]) + ".h"
3120    else:
3121        hdr_file = "generated_header_file.h"
3122
3123    if args.mode == 'kernel':
3124        cw.p('#include <net/netlink.h>')
3125        cw.p('#include <net/genetlink.h>')
3126        cw.nl()
3127        if not args.header:
3128            if args.out_file:
3129                cw.p(f'#include "{hdr_file}"')
3130            cw.nl()
3131        headers = ['uapi/' + parsed.uapi_header]
3132        headers += parsed.kernel_family.get('headers', [])
3133    else:
3134        cw.p('#include <stdlib.h>')
3135        cw.p('#include <string.h>')
3136        if args.header:
3137            cw.p('#include <linux/types.h>')
3138            if family_contains_bitfield32(parsed):
3139                cw.p('#include <linux/netlink.h>')
3140        else:
3141            cw.p(f'#include "{hdr_file}"')
3142            cw.p('#include "ynl.h"')
3143        headers = []
3144    for definition in parsed['definitions'] + parsed['attribute-sets']:
3145        if 'header' in definition:
3146            headers.append(definition['header'])
3147    if args.mode == 'user':
3148        headers.append(parsed.uapi_header)
3149    seen_header = []
3150    for one in headers:
3151        if one not in seen_header:
3152            cw.p(f"#include <{one}>")
3153            seen_header.append(one)
3154    cw.nl()
3155
3156    if args.mode == "user":
3157        if not args.header:
3158            cw.p("#include <linux/genetlink.h>")
3159            cw.nl()
3160            for one in args.user_header:
3161                cw.p(f'#include "{one}"')
3162        else:
3163            cw.p('struct ynl_sock;')
3164            cw.nl()
3165            render_user_family(parsed, cw, True)
3166        cw.nl()
3167
3168    if args.mode == "kernel":
3169        if args.header:
3170            for _, struct in sorted(parsed.pure_nested_structs.items()):
3171                if struct.request:
3172                    cw.p('/* Common nested types */')
3173                    break
3174            for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
3175                if struct.request:
3176                    print_req_policy_fwd(cw, struct)
3177            cw.nl()
3178
3179            if parsed.kernel_policy == 'global':
3180                cw.p(f"/* Global operation policy for {parsed.name} */")
3181
3182                struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
3183                print_req_policy_fwd(cw, struct)
3184                cw.nl()
3185
3186            if parsed.kernel_policy in {'per-op', 'split'}:
3187                for op_name, op in parsed.ops.items():
3188                    if 'do' in op and 'event' not in op:
3189                        ri = RenderInfo(cw, parsed, args.mode, op, "do")
3190                        print_req_policy_fwd(cw, ri.struct['request'], ri=ri)
3191                        cw.nl()
3192
3193            print_kernel_op_table_hdr(parsed, cw)
3194            print_kernel_mcgrp_hdr(parsed, cw)
3195            print_kernel_family_struct_hdr(parsed, cw)
3196        else:
3197            print_kernel_policy_ranges(parsed, cw)
3198            print_kernel_policy_sparse_enum_validates(parsed, cw)
3199
3200            for _, struct in sorted(parsed.pure_nested_structs.items()):
3201                if struct.request:
3202                    cw.p('/* Common nested types */')
3203                    break
3204            for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
3205                if struct.request:
3206                    print_req_policy(cw, struct)
3207            cw.nl()
3208
3209            if parsed.kernel_policy == 'global':
3210                cw.p(f"/* Global operation policy for {parsed.name} */")
3211
3212                struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
3213                print_req_policy(cw, struct)
3214                cw.nl()
3215
3216            for op_name, op in parsed.ops.items():
3217                if parsed.kernel_policy in {'per-op', 'split'}:
3218                    for op_mode in ['do', 'dump']:
3219                        if op_mode in op and 'request' in op[op_mode]:
3220                            cw.p(f"/* {op.enum_name} - {op_mode} */")
3221                            ri = RenderInfo(cw, parsed, args.mode, op, op_mode)
3222                            print_req_policy(cw, ri.struct['request'], ri=ri)
3223                            cw.nl()
3224
3225            print_kernel_op_table(parsed, cw)
3226            print_kernel_mcgrp_src(parsed, cw)
3227            print_kernel_family_struct_src(parsed, cw)
3228
3229    if args.mode == "user":
3230        if args.header:
3231            cw.p('/* Enums */')
3232            put_op_name_fwd(parsed, cw)
3233
3234            for name, const in parsed.consts.items():
3235                if isinstance(const, EnumSet):
3236                    put_enum_to_str_fwd(parsed, cw, const)
3237            cw.nl()
3238
3239            cw.p('/* Common nested types */')
3240            for attr_set, struct in parsed.pure_nested_structs.items():
3241                ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
3242                print_type_full(ri, struct)
3243                if struct.request and struct.in_multi_val:
3244                    free_rsp_nested_prototype(ri)
3245                    cw.nl()
3246
3247            for op_name, op in parsed.ops.items():
3248                cw.p(f"/* ============== {op.enum_name} ============== */")
3249
3250                if 'do' in op and 'event' not in op:
3251                    cw.p(f"/* {op.enum_name} - do */")
3252                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
3253                    print_req_type(ri)
3254                    print_req_type_helpers(ri)
3255                    cw.nl()
3256                    print_rsp_type(ri)
3257                    print_rsp_type_helpers(ri)
3258                    cw.nl()
3259                    print_req_prototype(ri)
3260                    cw.nl()
3261
3262                if 'dump' in op:
3263                    cw.p(f"/* {op.enum_name} - dump */")
3264                    ri = RenderInfo(cw, parsed, args.mode, op, 'dump')
3265                    print_req_type(ri)
3266                    print_req_type_helpers(ri)
3267                    if not ri.type_consistent or ri.type_oneside:
3268                        print_rsp_type(ri)
3269                    print_wrapped_type(ri)
3270                    print_dump_prototype(ri)
3271                    cw.nl()
3272
3273                if op.has_ntf:
3274                    cw.p(f"/* {op.enum_name} - notify */")
3275                    ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
3276                    if not ri.type_consistent:
3277                        raise Exception(f'Only notifications with consistent types supported ({op.name})')
3278                    print_wrapped_type(ri)
3279
3280            for op_name, op in parsed.ntfs.items():
3281                if 'event' in op:
3282                    ri = RenderInfo(cw, parsed, args.mode, op, 'event')
3283                    cw.p(f"/* {op.enum_name} - event */")
3284                    print_rsp_type(ri)
3285                    cw.nl()
3286                    print_wrapped_type(ri)
3287            cw.nl()
3288        else:
3289            cw.p('/* Enums */')
3290            put_op_name(parsed, cw)
3291
3292            for name, const in parsed.consts.items():
3293                if isinstance(const, EnumSet):
3294                    put_enum_to_str(parsed, cw, const)
3295            cw.nl()
3296
3297            has_recursive_nests = False
3298            cw.p('/* Policies */')
3299            for struct in parsed.pure_nested_structs.values():
3300                if struct.recursive:
3301                    put_typol_fwd(cw, struct)
3302                    has_recursive_nests = True
3303            if has_recursive_nests:
3304                cw.nl()
3305            for name in parsed.pure_nested_structs:
3306                struct = Struct(parsed, name)
3307                put_typol(cw, struct)
3308            for name in parsed.root_sets:
3309                struct = Struct(parsed, name)
3310                put_typol(cw, struct)
3311
3312            cw.p('/* Common nested types */')
3313            if has_recursive_nests:
3314                for attr_set, struct in parsed.pure_nested_structs.items():
3315                    ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
3316                    free_rsp_nested_prototype(ri)
3317                    if struct.request:
3318                        put_req_nested_prototype(ri, struct)
3319                    if struct.reply:
3320                        parse_rsp_nested_prototype(ri, struct)
3321                cw.nl()
3322            for attr_set, struct in parsed.pure_nested_structs.items():
3323                ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
3324
3325                free_rsp_nested(ri, struct)
3326                if struct.request:
3327                    put_req_nested(ri, struct)
3328                if struct.reply:
3329                    parse_rsp_nested(ri, struct)
3330
3331            for op_name, op in parsed.ops.items():
3332                cw.p(f"/* ============== {op.enum_name} ============== */")
3333                if 'do' in op and 'event' not in op:
3334                    cw.p(f"/* {op.enum_name} - do */")
3335                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
3336                    print_req_free(ri)
3337                    print_rsp_free(ri)
3338                    parse_rsp_msg(ri)
3339                    print_req(ri)
3340                    cw.nl()
3341
3342                if 'dump' in op:
3343                    cw.p(f"/* {op.enum_name} - dump */")
3344                    ri = RenderInfo(cw, parsed, args.mode, op, "dump")
3345                    if not ri.type_consistent or ri.type_oneside:
3346                        parse_rsp_msg(ri, deref=True)
3347                    print_req_free(ri)
3348                    print_dump_type_free(ri)
3349                    print_dump(ri)
3350                    cw.nl()
3351
3352                if op.has_ntf:
3353                    cw.p(f"/* {op.enum_name} - notify */")
3354                    ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
3355                    if not ri.type_consistent:
3356                        raise Exception(f'Only notifications with consistent types supported ({op.name})')
3357                    print_ntf_type_free(ri)
3358
3359            for op_name, op in parsed.ntfs.items():
3360                if 'event' in op:
3361                    cw.p(f"/* {op.enum_name} - event */")
3362
3363                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
3364                    parse_rsp_msg(ri)
3365
3366                    ri = RenderInfo(cw, parsed, args.mode, op, "event")
3367                    print_ntf_type_free(ri)
3368            cw.nl()
3369            render_user_family(parsed, cw, False)
3370
3371    if args.header:
3372        cw.p(f'#endif /* {hdr_prot} */')
3373
3374
3375if __name__ == "__main__":
3376    main()
3377