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