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