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