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