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