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