Linux Audio

Check our new training course

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