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 def free_needs_iter(self): 865 return self.sub_type == 'nest' 866 867 def _free_lines(self, ri, var, ref): 868 lines = [] 869 if self.sub_type == 'nest': 870 lines += [ 871 f"for (i = 0; i < {var}->{ref}_count.{self.c_name}; i++)", 872 f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);', 873 ] 874 lines += f"free({var}->{ref}{self.c_name});", 875 return lines 876 877class TypeNestTypeValue(Type): 878 def _complex_member_type(self, ri): 879 return self.nested_struct_type 880 881 def _attr_typol(self): 882 return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, ' 883 884 def _attr_get(self, ri, var): 885 prev = 'attr' 886 tv_args = '' 887 get_lines = [] 888 local_vars = [] 889 init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;", 890 f"parg.data = &{var}->{self.c_name};"] 891 if 'type-value' in self.attr: 892 tv_names = [c_lower(x) for x in self.attr["type-value"]] 893 local_vars += [f'const struct nlattr *attr_{", *attr_".join(tv_names)};'] 894 local_vars += [f'__u32 {", ".join(tv_names)};'] 895 for level in self.attr["type-value"]: 896 level = c_lower(level) 897 get_lines += [f'attr_{level} = ynl_attr_data({prev});'] 898 get_lines += [f'{level} = ynl_attr_type(attr_{level});'] 899 prev = 'attr_' + level 900 901 tv_args = f", {', '.join(tv_names)}" 902 903 get_lines += [f"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"] 904 return get_lines, init_lines, local_vars 905 906 907class TypeSubMessage(TypeNest): 908 def __init__(self, family, attr_set, attr, value): 909 super().__init__(family, attr_set, attr, value) 910 911 self.selector = Selector(attr, attr_set) 912 913 def _attr_typol(self): 914 typol = f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, ' 915 typol += '.is_submsg = 1, ' 916 # Reverse-parsing of the policy (ynl_err_walk() in ynl.c) does not 917 # support external selectors. No family uses sub-messages with external 918 # selector for requests so this is fine for now. 919 if not self.selector.is_external(): 920 typol += f'.selector_type = {self.attr_set[self["selector"]].value} ' 921 return typol 922 923 def _attr_get(self, ri, var): 924 sel = c_lower(self['selector']) 925 if self.selector.is_external(): 926 sel_var = f"_sel_{sel}" 927 else: 928 sel_var = f"{var}->{sel}" 929 get_lines = [f'if (!{sel_var})', 930 'return ynl_submsg_failed(yarg, "%s", "%s");' % 931 (self.name, self['selector']), 932 f"if ({self.nested_render_name}_parse(&parg, {sel_var}, attr))", 933 "return YNL_PARSE_CB_ERROR;"] 934 init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;", 935 f"parg.data = &{var}->{self.c_name};"] 936 return get_lines, init_lines, None 937 938 939class Selector: 940 def __init__(self, msg_attr, attr_set): 941 self.name = msg_attr["selector"] 942 943 if self.name in attr_set: 944 self.attr = attr_set[self.name] 945 self.attr.is_selector = True 946 self._external = False 947 else: 948 # The selector will need to get passed down thru the structs 949 self.attr = None 950 self._external = True 951 952 def set_attr(self, attr): 953 self.attr = attr 954 955 def is_external(self): 956 return self._external 957 958 959class Struct: 960 def __init__(self, family, space_name, type_list=None, fixed_header=None, 961 inherited=None, submsg=None): 962 self.family = family 963 self.space_name = space_name 964 self.attr_set = family.attr_sets[space_name] 965 # Use list to catch comparisons with empty sets 966 self._inherited = inherited if inherited is not None else [] 967 self.inherited = [] 968 self.fixed_header = None 969 if fixed_header: 970 self.fixed_header = 'struct ' + c_lower(fixed_header) 971 self.submsg = submsg 972 973 self.nested = type_list is None 974 if family.name == c_lower(space_name): 975 self.render_name = c_lower(family.ident_name) 976 else: 977 self.render_name = c_lower(family.ident_name + '-' + space_name) 978 self.struct_name = 'struct ' + self.render_name 979 if self.nested and space_name in family.consts: 980 self.struct_name += '_' 981 self.ptr_name = self.struct_name + ' *' 982 # All attr sets this one contains, directly or multiple levels down 983 self.child_nests = set() 984 985 self.request = False 986 self.reply = False 987 self.recursive = False 988 self.in_multi_val = False # used by a MultiAttr or and legacy arrays 989 990 self.attr_list = [] 991 self.attrs = dict() 992 if type_list is not None: 993 for t in type_list: 994 self.attr_list.append((t, self.attr_set[t]),) 995 else: 996 for t in self.attr_set: 997 self.attr_list.append((t, self.attr_set[t]),) 998 999 max_val = 0 1000 self.attr_max_val = None 1001 for name, attr in self.attr_list: 1002 if attr.value >= max_val: 1003 max_val = attr.value 1004 self.attr_max_val = attr 1005 self.attrs[name] = attr 1006 1007 def __iter__(self): 1008 yield from self.attrs 1009 1010 def __getitem__(self, key): 1011 return self.attrs[key] 1012 1013 def member_list(self): 1014 return self.attr_list 1015 1016 def set_inherited(self, new_inherited): 1017 if self._inherited != new_inherited: 1018 raise Exception("Inheriting different members not supported") 1019 self.inherited = [c_lower(x) for x in sorted(self._inherited)] 1020 1021 def external_selectors(self): 1022 sels = [] 1023 for name, attr in self.attr_list: 1024 if isinstance(attr, TypeSubMessage) and attr.selector.is_external(): 1025 sels.append(attr.selector) 1026 return sels 1027 1028 def free_needs_iter(self): 1029 for _, attr in self.attr_list: 1030 if attr.free_needs_iter(): 1031 return True 1032 return False 1033 1034 1035class EnumEntry(SpecEnumEntry): 1036 def __init__(self, enum_set, yaml, prev, value_start): 1037 super().__init__(enum_set, yaml, prev, value_start) 1038 1039 if prev: 1040 self.value_change = (self.value != prev.value + 1) 1041 else: 1042 self.value_change = (self.value != 0) 1043 self.value_change = self.value_change or self.enum_set['type'] == 'flags' 1044 1045 # Added by resolve: 1046 self.c_name = None 1047 delattr(self, "c_name") 1048 1049 def resolve(self): 1050 self.resolve_up(super()) 1051 1052 self.c_name = c_upper(self.enum_set.value_pfx + self.name) 1053 1054 1055class EnumSet(SpecEnumSet): 1056 def __init__(self, family, yaml): 1057 self.render_name = c_lower(family.ident_name + '-' + yaml['name']) 1058 1059 if 'enum-name' in yaml: 1060 if yaml['enum-name']: 1061 self.enum_name = 'enum ' + c_lower(yaml['enum-name']) 1062 self.user_type = self.enum_name 1063 else: 1064 self.enum_name = None 1065 else: 1066 self.enum_name = 'enum ' + self.render_name 1067 1068 if self.enum_name: 1069 self.user_type = self.enum_name 1070 else: 1071 self.user_type = 'int' 1072 1073 self.value_pfx = yaml.get('name-prefix', f"{family.ident_name}-{yaml['name']}-") 1074 self.header = yaml.get('header', None) 1075 self.enum_cnt_name = yaml.get('enum-cnt-name', None) 1076 1077 super().__init__(family, yaml) 1078 1079 def new_entry(self, entry, prev_entry, value_start): 1080 return EnumEntry(self, entry, prev_entry, value_start) 1081 1082 def value_range(self): 1083 low = min([x.value for x in self.entries.values()]) 1084 high = max([x.value for x in self.entries.values()]) 1085 1086 if high - low + 1 != len(self.entries): 1087 return None, None 1088 1089 return low, high 1090 1091 1092class AttrSet(SpecAttrSet): 1093 def __init__(self, family, yaml): 1094 super().__init__(family, yaml) 1095 1096 if self.subset_of is None: 1097 if 'name-prefix' in yaml: 1098 pfx = yaml['name-prefix'] 1099 elif self.name == family.name: 1100 pfx = family.ident_name + '-a-' 1101 else: 1102 pfx = f"{family.ident_name}-a-{self.name}-" 1103 self.name_prefix = c_upper(pfx) 1104 self.max_name = c_upper(self.yaml.get('attr-max-name', f"{self.name_prefix}max")) 1105 self.cnt_name = c_upper(self.yaml.get('attr-cnt-name', f"__{self.name_prefix}max")) 1106 else: 1107 self.name_prefix = family.attr_sets[self.subset_of].name_prefix 1108 self.max_name = family.attr_sets[self.subset_of].max_name 1109 self.cnt_name = family.attr_sets[self.subset_of].cnt_name 1110 1111 # Added by resolve: 1112 self.c_name = None 1113 delattr(self, "c_name") 1114 1115 def resolve(self): 1116 self.c_name = c_lower(self.name) 1117 if self.c_name in _C_KW: 1118 self.c_name += '_' 1119 if self.c_name == self.family.c_name: 1120 self.c_name = '' 1121 1122 def new_attr(self, elem, value): 1123 if elem['type'] in scalars: 1124 t = TypeScalar(self.family, self, elem, value) 1125 elif elem['type'] == 'unused': 1126 t = TypeUnused(self.family, self, elem, value) 1127 elif elem['type'] == 'pad': 1128 t = TypePad(self.family, self, elem, value) 1129 elif elem['type'] == 'flag': 1130 t = TypeFlag(self.family, self, elem, value) 1131 elif elem['type'] == 'string': 1132 t = TypeString(self.family, self, elem, value) 1133 elif elem['type'] == 'binary': 1134 if 'struct' in elem: 1135 t = TypeBinaryStruct(self.family, self, elem, value) 1136 elif elem.get('sub-type') in scalars: 1137 t = TypeBinaryScalarArray(self.family, self, elem, value) 1138 else: 1139 t = TypeBinary(self.family, self, elem, value) 1140 elif elem['type'] == 'bitfield32': 1141 t = TypeBitfield32(self.family, self, elem, value) 1142 elif elem['type'] == 'nest': 1143 t = TypeNest(self.family, self, elem, value) 1144 elif elem['type'] == 'indexed-array' and 'sub-type' in elem: 1145 if elem["sub-type"] in ['binary', 'nest', 'u32']: 1146 t = TypeIndexedArray(self.family, self, elem, value) 1147 else: 1148 raise Exception(f'new_attr: unsupported sub-type {elem["sub-type"]}') 1149 elif elem['type'] == 'nest-type-value': 1150 t = TypeNestTypeValue(self.family, self, elem, value) 1151 elif elem['type'] == 'sub-message': 1152 t = TypeSubMessage(self.family, self, elem, value) 1153 else: 1154 raise Exception(f"No typed class for type {elem['type']}") 1155 1156 if 'multi-attr' in elem and elem['multi-attr']: 1157 t = TypeMultiAttr(self.family, self, elem, value, t) 1158 1159 return t 1160 1161 1162class Operation(SpecOperation): 1163 def __init__(self, family, yaml, req_value, rsp_value): 1164 # Fill in missing operation properties (for fixed hdr-only msgs) 1165 for mode in ['do', 'dump', 'event']: 1166 for direction in ['request', 'reply']: 1167 try: 1168 yaml[mode][direction].setdefault('attributes', []) 1169 except KeyError: 1170 pass 1171 1172 super().__init__(family, yaml, req_value, rsp_value) 1173 1174 self.render_name = c_lower(family.ident_name + '_' + self.name) 1175 1176 self.dual_policy = ('do' in yaml and 'request' in yaml['do']) and \ 1177 ('dump' in yaml and 'request' in yaml['dump']) 1178 1179 self.has_ntf = False 1180 1181 # Added by resolve: 1182 self.enum_name = None 1183 delattr(self, "enum_name") 1184 1185 def resolve(self): 1186 self.resolve_up(super()) 1187 1188 if not self.is_async: 1189 self.enum_name = self.family.op_prefix + c_upper(self.name) 1190 else: 1191 self.enum_name = self.family.async_op_prefix + c_upper(self.name) 1192 1193 def mark_has_ntf(self): 1194 self.has_ntf = True 1195 1196 1197class SubMessage(SpecSubMessage): 1198 def __init__(self, family, yaml): 1199 super().__init__(family, yaml) 1200 1201 self.render_name = c_lower(family.ident_name + '-' + yaml['name']) 1202 1203 def resolve(self): 1204 self.resolve_up(super()) 1205 1206 1207class Family(SpecFamily): 1208 def __init__(self, file_name, exclude_ops, fn_prefix): 1209 # Added by resolve: 1210 self.c_name = None 1211 delattr(self, "c_name") 1212 self.op_prefix = None 1213 delattr(self, "op_prefix") 1214 self.async_op_prefix = None 1215 delattr(self, "async_op_prefix") 1216 self.mcgrps = None 1217 delattr(self, "mcgrps") 1218 self.consts = None 1219 delattr(self, "consts") 1220 self.hooks = None 1221 delattr(self, "hooks") 1222 1223 super().__init__(file_name, exclude_ops=exclude_ops) 1224 1225 self.fam_key = c_upper(self.yaml.get('c-family-name', self.yaml["name"] + '_FAMILY_NAME')) 1226 self.ver_key = c_upper(self.yaml.get('c-version-name', self.yaml["name"] + '_FAMILY_VERSION')) 1227 1228 if 'definitions' not in self.yaml: 1229 self.yaml['definitions'] = [] 1230 1231 if 'uapi-header' in self.yaml: 1232 self.uapi_header = self.yaml['uapi-header'] 1233 else: 1234 self.uapi_header = f"linux/{self.ident_name}.h" 1235 if self.uapi_header.startswith("linux/") and self.uapi_header.endswith('.h'): 1236 self.uapi_header_name = self.uapi_header[6:-2] 1237 else: 1238 self.uapi_header_name = self.ident_name 1239 1240 self.fn_prefix = fn_prefix if fn_prefix else f'{self.ident_name}-nl' 1241 1242 def resolve(self): 1243 self.resolve_up(super()) 1244 1245 self.c_name = c_lower(self.ident_name) 1246 if 'name-prefix' in self.yaml['operations']: 1247 self.op_prefix = c_upper(self.yaml['operations']['name-prefix']) 1248 else: 1249 self.op_prefix = c_upper(self.yaml['name'] + '-cmd-') 1250 if 'async-prefix' in self.yaml['operations']: 1251 self.async_op_prefix = c_upper(self.yaml['operations']['async-prefix']) 1252 else: 1253 self.async_op_prefix = self.op_prefix 1254 1255 self.mcgrps = self.yaml.get('mcast-groups', {'list': []}) 1256 1257 self.hooks = dict() 1258 for when in ['pre', 'post']: 1259 self.hooks[when] = dict() 1260 for op_mode in ['do', 'dump']: 1261 self.hooks[when][op_mode] = dict() 1262 self.hooks[when][op_mode]['set'] = set() 1263 self.hooks[when][op_mode]['list'] = [] 1264 1265 # dict space-name -> 'request': set(attrs), 'reply': set(attrs) 1266 self.root_sets = dict() 1267 # dict space-name -> Struct 1268 self.pure_nested_structs = dict() 1269 1270 self._mark_notify() 1271 self._mock_up_events() 1272 1273 self._load_root_sets() 1274 self._load_nested_sets() 1275 self._load_attr_use() 1276 self._load_selector_passing() 1277 self._load_hooks() 1278 1279 self.kernel_policy = self.yaml.get('kernel-policy', 'split') 1280 if self.kernel_policy == 'global': 1281 self._load_global_policy() 1282 1283 def new_enum(self, elem): 1284 return EnumSet(self, elem) 1285 1286 def new_attr_set(self, elem): 1287 return AttrSet(self, elem) 1288 1289 def new_operation(self, elem, req_value, rsp_value): 1290 return Operation(self, elem, req_value, rsp_value) 1291 1292 def new_sub_message(self, elem): 1293 return SubMessage(self, elem) 1294 1295 def is_classic(self): 1296 return self.proto == 'netlink-raw' 1297 1298 def _mark_notify(self): 1299 for op in self.msgs.values(): 1300 if 'notify' in op: 1301 self.ops[op['notify']].mark_has_ntf() 1302 1303 # Fake a 'do' equivalent of all events, so that we can render their response parsing 1304 def _mock_up_events(self): 1305 for op in self.yaml['operations']['list']: 1306 if 'event' in op: 1307 op['do'] = { 1308 'reply': { 1309 'attributes': op['event']['attributes'] 1310 } 1311 } 1312 1313 def _load_root_sets(self): 1314 for op_name, op in self.msgs.items(): 1315 if 'attribute-set' not in op: 1316 continue 1317 1318 req_attrs = set() 1319 rsp_attrs = set() 1320 for op_mode in ['do', 'dump']: 1321 if op_mode in op and 'request' in op[op_mode]: 1322 req_attrs.update(set(op[op_mode]['request']['attributes'])) 1323 if op_mode in op and 'reply' in op[op_mode]: 1324 rsp_attrs.update(set(op[op_mode]['reply']['attributes'])) 1325 if 'event' in op: 1326 rsp_attrs.update(set(op['event']['attributes'])) 1327 1328 if op['attribute-set'] not in self.root_sets: 1329 self.root_sets[op['attribute-set']] = {'request': req_attrs, 'reply': rsp_attrs} 1330 else: 1331 self.root_sets[op['attribute-set']]['request'].update(req_attrs) 1332 self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs) 1333 1334 def _sort_pure_types(self): 1335 # Try to reorder according to dependencies 1336 pns_key_list = list(self.pure_nested_structs.keys()) 1337 pns_key_seen = set() 1338 rounds = len(pns_key_list) ** 2 # it's basically bubble sort 1339 for _ in range(rounds): 1340 if len(pns_key_list) == 0: 1341 break 1342 name = pns_key_list.pop(0) 1343 finished = True 1344 for _, spec in self.attr_sets[name].items(): 1345 if 'nested-attributes' in spec: 1346 nested = spec['nested-attributes'] 1347 elif 'sub-message' in spec: 1348 nested = spec.sub_message 1349 else: 1350 continue 1351 1352 # If the unknown nest we hit is recursive it's fine, it'll be a pointer 1353 if self.pure_nested_structs[nested].recursive: 1354 continue 1355 if nested not in pns_key_seen: 1356 # Dicts are sorted, this will make struct last 1357 struct = self.pure_nested_structs.pop(name) 1358 self.pure_nested_structs[name] = struct 1359 finished = False 1360 break 1361 if finished: 1362 pns_key_seen.add(name) 1363 else: 1364 pns_key_list.append(name) 1365 1366 def _load_nested_set_nest(self, spec): 1367 inherit = set() 1368 nested = spec['nested-attributes'] 1369 if nested not in self.root_sets: 1370 if nested not in self.pure_nested_structs: 1371 self.pure_nested_structs[nested] = \ 1372 Struct(self, nested, inherited=inherit, 1373 fixed_header=spec.get('fixed-header')) 1374 else: 1375 raise Exception(f'Using attr set as root and nested not supported - {nested}') 1376 1377 if 'type-value' in spec: 1378 if nested in self.root_sets: 1379 raise Exception("Inheriting members to a space used as root not supported") 1380 inherit.update(set(spec['type-value'])) 1381 elif spec['type'] == 'indexed-array': 1382 inherit.add('idx') 1383 self.pure_nested_structs[nested].set_inherited(inherit) 1384 1385 return nested 1386 1387 def _load_nested_set_submsg(self, spec): 1388 # Fake the struct type for the sub-message itself 1389 # its not a attr_set but codegen wants attr_sets. 1390 submsg = self.sub_msgs[spec["sub-message"]] 1391 nested = submsg.name 1392 1393 attrs = [] 1394 for name, fmt in submsg.formats.items(): 1395 attr = { 1396 "name": name, 1397 "parent-sub-message": spec, 1398 } 1399 if 'attribute-set' in fmt: 1400 attr |= { 1401 "type": "nest", 1402 "nested-attributes": fmt['attribute-set'], 1403 } 1404 if 'fixed-header' in fmt: 1405 attr |= { "fixed-header": fmt["fixed-header"] } 1406 elif 'fixed-header' in fmt: 1407 attr |= { 1408 "type": "binary", 1409 "struct": fmt["fixed-header"], 1410 } 1411 else: 1412 attr["type"] = "flag" 1413 attrs.append(attr) 1414 1415 self.attr_sets[nested] = AttrSet(self, { 1416 "name": nested, 1417 "name-pfx": self.name + '-' + spec.name + '-', 1418 "attributes": attrs 1419 }) 1420 1421 if nested not in self.pure_nested_structs: 1422 self.pure_nested_structs[nested] = Struct(self, nested, submsg=submsg) 1423 1424 return nested 1425 1426 def _load_nested_sets(self): 1427 attr_set_queue = list(self.root_sets.keys()) 1428 attr_set_seen = set(self.root_sets.keys()) 1429 1430 while len(attr_set_queue): 1431 a_set = attr_set_queue.pop(0) 1432 for attr, spec in self.attr_sets[a_set].items(): 1433 if 'nested-attributes' in spec: 1434 nested = self._load_nested_set_nest(spec) 1435 elif 'sub-message' in spec: 1436 nested = self._load_nested_set_submsg(spec) 1437 else: 1438 continue 1439 1440 if nested not in attr_set_seen: 1441 attr_set_queue.append(nested) 1442 attr_set_seen.add(nested) 1443 1444 for root_set, rs_members in self.root_sets.items(): 1445 for attr, spec in self.attr_sets[root_set].items(): 1446 if 'nested-attributes' in spec: 1447 nested = spec['nested-attributes'] 1448 elif 'sub-message' in spec: 1449 nested = spec.sub_message 1450 else: 1451 nested = None 1452 1453 if nested: 1454 if attr in rs_members['request']: 1455 self.pure_nested_structs[nested].request = True 1456 if attr in rs_members['reply']: 1457 self.pure_nested_structs[nested].reply = True 1458 1459 if spec.is_multi_val(): 1460 child = self.pure_nested_structs.get(nested) 1461 child.in_multi_val = True 1462 1463 self._sort_pure_types() 1464 1465 # Propagate the request / reply / recursive 1466 for attr_set, struct in reversed(self.pure_nested_structs.items()): 1467 for _, spec in self.attr_sets[attr_set].items(): 1468 if attr_set in struct.child_nests: 1469 struct.recursive = True 1470 1471 if 'nested-attributes' in spec: 1472 child_name = spec['nested-attributes'] 1473 elif 'sub-message' in spec: 1474 child_name = spec.sub_message 1475 else: 1476 continue 1477 1478 struct.child_nests.add(child_name) 1479 child = self.pure_nested_structs.get(child_name) 1480 if child: 1481 if not child.recursive: 1482 struct.child_nests.update(child.child_nests) 1483 child.request |= struct.request 1484 child.reply |= struct.reply 1485 if spec.is_multi_val(): 1486 child.in_multi_val = True 1487 1488 self._sort_pure_types() 1489 1490 def _load_attr_use(self): 1491 for _, struct in self.pure_nested_structs.items(): 1492 if struct.request: 1493 for _, arg in struct.member_list(): 1494 arg.set_request() 1495 if struct.reply: 1496 for _, arg in struct.member_list(): 1497 arg.set_reply() 1498 1499 for root_set, rs_members in self.root_sets.items(): 1500 for attr, spec in self.attr_sets[root_set].items(): 1501 if attr in rs_members['request']: 1502 spec.set_request() 1503 if attr in rs_members['reply']: 1504 spec.set_reply() 1505 1506 def _load_selector_passing(self): 1507 def all_structs(): 1508 for k, v in reversed(self.pure_nested_structs.items()): 1509 yield k, v 1510 for k, _ in self.root_sets.items(): 1511 yield k, None # we don't have a struct, but it must be terminal 1512 1513 for attr_set, struct in all_structs(): 1514 for _, spec in self.attr_sets[attr_set].items(): 1515 if 'nested-attributes' in spec: 1516 child_name = spec['nested-attributes'] 1517 elif 'sub-message' in spec: 1518 child_name = spec.sub_message 1519 else: 1520 continue 1521 1522 child = self.pure_nested_structs.get(child_name) 1523 for selector in child.external_selectors(): 1524 if selector.name in self.attr_sets[attr_set]: 1525 sel_attr = self.attr_sets[attr_set][selector.name] 1526 selector.set_attr(sel_attr) 1527 else: 1528 raise Exception("Passing selector thru more than one layer not supported") 1529 1530 def _load_global_policy(self): 1531 global_set = set() 1532 attr_set_name = None 1533 for op_name, op in self.ops.items(): 1534 if not op: 1535 continue 1536 if 'attribute-set' not in op: 1537 continue 1538 1539 if attr_set_name is None: 1540 attr_set_name = op['attribute-set'] 1541 if attr_set_name != op['attribute-set']: 1542 raise Exception('For a global policy all ops must use the same set') 1543 1544 for op_mode in ['do', 'dump']: 1545 if op_mode in op: 1546 req = op[op_mode].get('request') 1547 if req: 1548 global_set.update(req.get('attributes', [])) 1549 1550 self.global_policy = [] 1551 self.global_policy_set = attr_set_name 1552 for attr in self.attr_sets[attr_set_name]: 1553 if attr in global_set: 1554 self.global_policy.append(attr) 1555 1556 def _load_hooks(self): 1557 for op in self.ops.values(): 1558 for op_mode in ['do', 'dump']: 1559 if op_mode not in op: 1560 continue 1561 for when in ['pre', 'post']: 1562 if when not in op[op_mode]: 1563 continue 1564 name = op[op_mode][when] 1565 if name in self.hooks[when][op_mode]['set']: 1566 continue 1567 self.hooks[when][op_mode]['set'].add(name) 1568 self.hooks[when][op_mode]['list'].append(name) 1569 1570 1571class RenderInfo: 1572 def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None): 1573 self.family = family 1574 self.nl = cw.nlib 1575 self.ku_space = ku_space 1576 self.op_mode = op_mode 1577 self.op = op 1578 1579 fixed_hdr = op.fixed_header if op else None 1580 self.fixed_hdr_len = 'ys->family->hdr_len' 1581 if op and op.fixed_header: 1582 if op.fixed_header != family.fixed_header: 1583 if family.is_classic(): 1584 self.fixed_hdr_len = f"sizeof(struct {c_lower(fixed_hdr)})" 1585 else: 1586 raise Exception("Per-op fixed header not supported, yet") 1587 1588 1589 # 'do' and 'dump' response parsing is identical 1590 self.type_consistent = True 1591 self.type_oneside = False 1592 if op_mode != 'do' and 'dump' in op: 1593 if 'do' in op: 1594 if ('reply' in op['do']) != ('reply' in op["dump"]): 1595 self.type_consistent = False 1596 elif 'reply' in op['do'] and op["do"]["reply"] != op["dump"]["reply"]: 1597 self.type_consistent = False 1598 else: 1599 self.type_consistent = True 1600 self.type_oneside = True 1601 1602 self.attr_set = attr_set 1603 if not self.attr_set: 1604 self.attr_set = op['attribute-set'] 1605 1606 self.type_name_conflict = False 1607 if op: 1608 self.type_name = c_lower(op.name) 1609 else: 1610 self.type_name = c_lower(attr_set) 1611 if attr_set in family.consts: 1612 self.type_name_conflict = True 1613 1614 self.cw = cw 1615 1616 self.struct = dict() 1617 if op_mode == 'notify': 1618 op_mode = 'do' if 'do' in op else 'dump' 1619 for op_dir in ['request', 'reply']: 1620 if op: 1621 type_list = [] 1622 if op_dir in op[op_mode]: 1623 type_list = op[op_mode][op_dir]['attributes'] 1624 self.struct[op_dir] = Struct(family, self.attr_set, 1625 fixed_header=fixed_hdr, 1626 type_list=type_list) 1627 if op_mode == 'event': 1628 self.struct['reply'] = Struct(family, self.attr_set, 1629 fixed_header=fixed_hdr, 1630 type_list=op['event']['attributes']) 1631 1632 def type_empty(self, key): 1633 return len(self.struct[key].attr_list) == 0 and \ 1634 self.struct['request'].fixed_header is None 1635 1636 def needs_nlflags(self, direction): 1637 return self.op_mode == 'do' and direction == 'request' and self.family.is_classic() 1638 1639 1640class CodeWriter: 1641 def __init__(self, nlib, out_file=None, overwrite=True): 1642 self.nlib = nlib 1643 self._overwrite = overwrite 1644 1645 self._nl = False 1646 self._block_end = False 1647 self._silent_block = False 1648 self._ind = 0 1649 self._ifdef_block = None 1650 if out_file is None: 1651 self._out = os.sys.stdout 1652 else: 1653 self._out = tempfile.NamedTemporaryFile('w+') 1654 self._out_file = out_file 1655 1656 def __del__(self): 1657 self.close_out_file() 1658 1659 def close_out_file(self): 1660 if self._out == os.sys.stdout: 1661 return 1662 # Avoid modifying the file if contents didn't change 1663 self._out.flush() 1664 if not self._overwrite and os.path.isfile(self._out_file): 1665 if filecmp.cmp(self._out.name, self._out_file, shallow=False): 1666 return 1667 with open(self._out_file, 'w+') as out_file: 1668 self._out.seek(0) 1669 shutil.copyfileobj(self._out, out_file) 1670 self._out.close() 1671 self._out = os.sys.stdout 1672 1673 @classmethod 1674 def _is_cond(cls, line): 1675 return line.startswith('if') or line.startswith('while') or line.startswith('for') 1676 1677 def p(self, line, add_ind=0): 1678 if self._block_end: 1679 self._block_end = False 1680 if line.startswith('else'): 1681 line = '} ' + line 1682 else: 1683 self._out.write('\t' * self._ind + '}\n') 1684 1685 if self._nl: 1686 self._out.write('\n') 1687 self._nl = False 1688 1689 ind = self._ind 1690 if line[-1] == ':': 1691 ind -= 1 1692 if self._silent_block: 1693 ind += 1 1694 self._silent_block = line.endswith(')') and CodeWriter._is_cond(line) 1695 self._silent_block |= line.strip() == 'else' 1696 if line[0] == '#': 1697 ind = 0 1698 if add_ind: 1699 ind += add_ind 1700 self._out.write('\t' * ind + line + '\n') 1701 1702 def nl(self): 1703 self._nl = True 1704 1705 def block_start(self, line=''): 1706 if line: 1707 line = line + ' ' 1708 self.p(line + '{') 1709 self._ind += 1 1710 1711 def block_end(self, line=''): 1712 if line and line[0] not in {';', ','}: 1713 line = ' ' + line 1714 self._ind -= 1 1715 self._nl = False 1716 if not line: 1717 # Delay printing closing bracket in case "else" comes next 1718 if self._block_end: 1719 self._out.write('\t' * (self._ind + 1) + '}\n') 1720 self._block_end = True 1721 else: 1722 self.p('}' + line) 1723 1724 def write_doc_line(self, doc, indent=True): 1725 words = doc.split() 1726 line = ' *' 1727 for word in words: 1728 if len(line) + len(word) >= 79: 1729 self.p(line) 1730 line = ' *' 1731 if indent: 1732 line += ' ' 1733 line += ' ' + word 1734 self.p(line) 1735 1736 def write_func_prot(self, qual_ret, name, args=None, doc=None, suffix=''): 1737 if not args: 1738 args = ['void'] 1739 1740 if doc: 1741 self.p('/*') 1742 self.p(' * ' + doc) 1743 self.p(' */') 1744 1745 oneline = qual_ret 1746 if qual_ret[-1] != '*': 1747 oneline += ' ' 1748 oneline += f"{name}({', '.join(args)}){suffix}" 1749 1750 if len(oneline) < 80: 1751 self.p(oneline) 1752 return 1753 1754 v = qual_ret 1755 if len(v) > 3: 1756 self.p(v) 1757 v = '' 1758 elif qual_ret[-1] != '*': 1759 v += ' ' 1760 v += name + '(' 1761 ind = '\t' * (len(v) // 8) + ' ' * (len(v) % 8) 1762 delta_ind = len(v) - len(ind) 1763 v += args[0] 1764 i = 1 1765 while i < len(args): 1766 next_len = len(v) + len(args[i]) 1767 if v[0] == '\t': 1768 next_len += delta_ind 1769 if next_len > 76: 1770 self.p(v + ',') 1771 v = ind 1772 else: 1773 v += ', ' 1774 v += args[i] 1775 i += 1 1776 self.p(v + ')' + suffix) 1777 1778 def write_func_lvar(self, local_vars): 1779 if not local_vars: 1780 return 1781 1782 if type(local_vars) is str: 1783 local_vars = [local_vars] 1784 1785 local_vars.sort(key=len, reverse=True) 1786 for var in local_vars: 1787 self.p(var) 1788 self.nl() 1789 1790 def write_func(self, qual_ret, name, body, args=None, local_vars=None): 1791 self.write_func_prot(qual_ret=qual_ret, name=name, args=args) 1792 self.block_start() 1793 self.write_func_lvar(local_vars=local_vars) 1794 1795 for line in body: 1796 self.p(line) 1797 self.block_end() 1798 1799 def writes_defines(self, defines): 1800 longest = 0 1801 for define in defines: 1802 if len(define[0]) > longest: 1803 longest = len(define[0]) 1804 longest = ((longest + 8) // 8) * 8 1805 for define in defines: 1806 line = '#define ' + define[0] 1807 line += '\t' * ((longest - len(define[0]) + 7) // 8) 1808 if type(define[1]) is int: 1809 line += str(define[1]) 1810 elif type(define[1]) is str: 1811 line += '"' + define[1] + '"' 1812 self.p(line) 1813 1814 def write_struct_init(self, members): 1815 longest = max([len(x[0]) for x in members]) 1816 longest += 1 # because we prepend a . 1817 longest = ((longest + 8) // 8) * 8 1818 for one in members: 1819 line = '.' + one[0] 1820 line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8) 1821 line += '= ' + str(one[1]) + ',' 1822 self.p(line) 1823 1824 def ifdef_block(self, config): 1825 config_option = None 1826 if config: 1827 config_option = 'CONFIG_' + c_upper(config) 1828 if self._ifdef_block == config_option: 1829 return 1830 1831 if self._ifdef_block: 1832 self.p('#endif /* ' + self._ifdef_block + ' */') 1833 if config_option: 1834 self.p('#ifdef ' + config_option) 1835 self._ifdef_block = config_option 1836 1837 1838scalars = {'u8', 'u16', 'u32', 'u64', 's8', 's16', 's32', 's64', 'uint', 'sint'} 1839 1840direction_to_suffix = { 1841 'reply': '_rsp', 1842 'request': '_req', 1843 '': '' 1844} 1845 1846op_mode_to_wrapper = { 1847 'do': '', 1848 'dump': '_list', 1849 'notify': '_ntf', 1850 'event': '', 1851} 1852 1853_C_KW = { 1854 'auto', 1855 'bool', 1856 'break', 1857 'case', 1858 'char', 1859 'const', 1860 'continue', 1861 'default', 1862 'do', 1863 'double', 1864 'else', 1865 'enum', 1866 'extern', 1867 'float', 1868 'for', 1869 'goto', 1870 'if', 1871 'inline', 1872 'int', 1873 'long', 1874 'register', 1875 'return', 1876 'short', 1877 'signed', 1878 'sizeof', 1879 'static', 1880 'struct', 1881 'switch', 1882 'typedef', 1883 'union', 1884 'unsigned', 1885 'void', 1886 'volatile', 1887 'while' 1888} 1889 1890 1891def rdir(direction): 1892 if direction == 'reply': 1893 return 'request' 1894 if direction == 'request': 1895 return 'reply' 1896 return direction 1897 1898 1899def op_prefix(ri, direction, deref=False): 1900 suffix = f"_{ri.type_name}" 1901 1902 if not ri.op_mode: 1903 pass 1904 elif ri.op_mode == 'do': 1905 suffix += f"{direction_to_suffix[direction]}" 1906 else: 1907 if direction == 'request': 1908 suffix += '_req' 1909 if not ri.type_oneside: 1910 suffix += '_dump' 1911 else: 1912 if ri.type_consistent: 1913 if deref: 1914 suffix += f"{direction_to_suffix[direction]}" 1915 else: 1916 suffix += op_mode_to_wrapper[ri.op_mode] 1917 else: 1918 suffix += '_rsp' 1919 suffix += '_dump' if deref else '_list' 1920 1921 return f"{ri.family.c_name}{suffix}" 1922 1923 1924def type_name(ri, direction, deref=False): 1925 return f"struct {op_prefix(ri, direction, deref=deref)}" 1926 1927 1928def print_prototype(ri, direction, terminate=True, doc=None): 1929 suffix = ';' if terminate else '' 1930 1931 fname = ri.op.render_name 1932 if ri.op_mode == 'dump': 1933 fname += '_dump' 1934 1935 args = ['struct ynl_sock *ys'] 1936 if 'request' in ri.op[ri.op_mode]: 1937 args.append(f"{type_name(ri, direction)} *" + f"{direction_to_suffix[direction][1:]}") 1938 1939 ret = 'int' 1940 if 'reply' in ri.op[ri.op_mode]: 1941 ret = f"{type_name(ri, rdir(direction))} *" 1942 1943 ri.cw.write_func_prot(ret, fname, args, doc=doc, suffix=suffix) 1944 1945 1946def print_req_prototype(ri): 1947 print_prototype(ri, "request", doc=ri.op['doc']) 1948 1949 1950def print_dump_prototype(ri): 1951 print_prototype(ri, "request") 1952 1953 1954def put_typol_submsg(cw, struct): 1955 cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[] =') 1956 1957 i = 0 1958 for name, arg in struct.member_list(): 1959 nest = "" 1960 if arg.type == 'nest': 1961 nest = f" .nest = &{arg.nested_render_name}_nest," 1962 cw.p('[%d] = { .type = YNL_PT_SUBMSG, .name = "%s",%s },' % 1963 (i, name, nest)) 1964 i += 1 1965 1966 cw.block_end(line=';') 1967 cw.nl() 1968 1969 cw.block_start(line=f'const struct ynl_policy_nest {struct.render_name}_nest =') 1970 cw.p(f'.max_attr = {i - 1},') 1971 cw.p(f'.table = {struct.render_name}_policy,') 1972 cw.block_end(line=';') 1973 cw.nl() 1974 1975 1976def put_typol_fwd(cw, struct): 1977 cw.p(f'extern const struct ynl_policy_nest {struct.render_name}_nest;') 1978 1979 1980def put_typol(cw, struct): 1981 if struct.submsg: 1982 put_typol_submsg(cw, struct) 1983 return 1984 1985 type_max = struct.attr_set.max_name 1986 cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =') 1987 1988 for _, arg in struct.member_list(): 1989 arg.attr_typol(cw) 1990 1991 cw.block_end(line=';') 1992 cw.nl() 1993 1994 cw.block_start(line=f'const struct ynl_policy_nest {struct.render_name}_nest =') 1995 cw.p(f'.max_attr = {type_max},') 1996 cw.p(f'.table = {struct.render_name}_policy,') 1997 cw.block_end(line=';') 1998 cw.nl() 1999 2000 2001def _put_enum_to_str_helper(cw, render_name, map_name, arg_name, enum=None): 2002 args = [f'int {arg_name}'] 2003 if enum: 2004 args = [enum.user_type + ' ' + arg_name] 2005 cw.write_func_prot('const char *', f'{render_name}_str', args) 2006 cw.block_start() 2007 if enum and enum.type == 'flags': 2008 cw.p(f'{arg_name} = ffs({arg_name}) - 1;') 2009 cw.p(f'if ({arg_name} < 0 || {arg_name} >= (int)YNL_ARRAY_SIZE({map_name}))') 2010 cw.p('return NULL;') 2011 cw.p(f'return {map_name}[{arg_name}];') 2012 cw.block_end() 2013 cw.nl() 2014 2015 2016def put_op_name_fwd(family, cw): 2017 cw.write_func_prot('const char *', f'{family.c_name}_op_str', ['int op'], suffix=';') 2018 2019 2020def put_op_name(family, cw): 2021 map_name = f'{family.c_name}_op_strmap' 2022 cw.block_start(line=f"static const char * const {map_name}[] =") 2023 for op_name, op in family.msgs.items(): 2024 if op.rsp_value: 2025 # Make sure we don't add duplicated entries, if multiple commands 2026 # produce the same response in legacy families. 2027 if family.rsp_by_value[op.rsp_value] != op: 2028 cw.p(f'// skip "{op_name}", duplicate reply value') 2029 continue 2030 2031 if op.req_value == op.rsp_value: 2032 cw.p(f'[{op.enum_name}] = "{op_name}",') 2033 else: 2034 cw.p(f'[{op.rsp_value}] = "{op_name}",') 2035 cw.block_end(line=';') 2036 cw.nl() 2037 2038 _put_enum_to_str_helper(cw, family.c_name + '_op', map_name, 'op') 2039 2040 2041def put_enum_to_str_fwd(family, cw, enum): 2042 args = [enum.user_type + ' value'] 2043 cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';') 2044 2045 2046def put_enum_to_str(family, cw, enum): 2047 map_name = f'{enum.render_name}_strmap' 2048 cw.block_start(line=f"static const char * const {map_name}[] =") 2049 for entry in enum.entries.values(): 2050 cw.p(f'[{entry.value}] = "{entry.name}",') 2051 cw.block_end(line=';') 2052 cw.nl() 2053 2054 _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum) 2055 2056 2057def put_local_vars(struct): 2058 local_vars = [] 2059 has_array = False 2060 has_count = False 2061 for _, arg in struct.member_list(): 2062 has_array |= arg.type == 'indexed-array' 2063 has_count |= arg.presence_type() == 'count' 2064 if has_array: 2065 local_vars.append('struct nlattr *array;') 2066 if has_count: 2067 local_vars.append('unsigned int i;') 2068 return local_vars 2069 2070 2071def put_req_nested_prototype(ri, struct, suffix=';'): 2072 func_args = ['struct nlmsghdr *nlh', 2073 'unsigned int attr_type', 2074 f'{struct.ptr_name}obj'] 2075 2076 ri.cw.write_func_prot('int', f'{struct.render_name}_put', func_args, 2077 suffix=suffix) 2078 2079 2080def put_req_nested(ri, struct): 2081 local_vars = [] 2082 init_lines = [] 2083 2084 if struct.submsg is None: 2085 local_vars.append('struct nlattr *nest;') 2086 init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);") 2087 if struct.fixed_header: 2088 local_vars.append('void *hdr;') 2089 struct_sz = f'sizeof({struct.fixed_header})' 2090 init_lines.append(f"hdr = ynl_nlmsg_put_extra_header(nlh, {struct_sz});") 2091 init_lines.append(f"memcpy(hdr, &obj->_hdr, {struct_sz});") 2092 2093 local_vars += put_local_vars(struct) 2094 2095 put_req_nested_prototype(ri, struct, suffix='') 2096 ri.cw.block_start() 2097 ri.cw.write_func_lvar(local_vars) 2098 2099 for line in init_lines: 2100 ri.cw.p(line) 2101 2102 for _, arg in struct.member_list(): 2103 arg.attr_put(ri, "obj") 2104 2105 if struct.submsg is None: 2106 ri.cw.p("ynl_attr_nest_end(nlh, nest);") 2107 2108 ri.cw.nl() 2109 ri.cw.p('return 0;') 2110 ri.cw.block_end() 2111 ri.cw.nl() 2112 2113 2114def _multi_parse(ri, struct, init_lines, local_vars): 2115 if struct.fixed_header: 2116 local_vars += ['void *hdr;'] 2117 if struct.nested: 2118 if struct.fixed_header: 2119 iter_line = f"ynl_attr_for_each_nested_off(attr, nested, sizeof({struct.fixed_header}))" 2120 else: 2121 iter_line = "ynl_attr_for_each_nested(attr, nested)" 2122 else: 2123 iter_line = "ynl_attr_for_each(attr, nlh, yarg->ys->family->hdr_len)" 2124 if ri.op.fixed_header != ri.family.fixed_header: 2125 if ri.family.is_classic(): 2126 iter_line = f"ynl_attr_for_each(attr, nlh, sizeof({struct.fixed_header}))" 2127 else: 2128 raise Exception("Per-op fixed header not supported, yet") 2129 2130 indexed_arrays = set() 2131 multi_attrs = set() 2132 needs_parg = False 2133 var_set = set() 2134 for arg, aspec in struct.member_list(): 2135 if aspec['type'] == 'indexed-array' and 'sub-type' in aspec: 2136 if aspec["sub-type"] in {'binary', 'nest'}: 2137 local_vars.append(f'const struct nlattr *attr_{aspec.c_name} = NULL;') 2138 indexed_arrays.add(arg) 2139 elif aspec['sub-type'] in scalars: 2140 local_vars.append(f'const struct nlattr *attr_{aspec.c_name} = NULL;') 2141 indexed_arrays.add(arg) 2142 else: 2143 raise Exception(f'Not supported sub-type {aspec["sub-type"]}') 2144 if 'multi-attr' in aspec: 2145 multi_attrs.add(arg) 2146 needs_parg |= 'nested-attributes' in aspec 2147 needs_parg |= 'sub-message' in aspec 2148 2149 try: 2150 _, _, l_vars = aspec._attr_get(ri, '') 2151 var_set |= set(l_vars) if l_vars else set() 2152 except Exception: 2153 pass # _attr_get() not implemented by simple types, ignore 2154 local_vars += list(var_set) 2155 if indexed_arrays or multi_attrs: 2156 local_vars.append('int i;') 2157 if needs_parg: 2158 local_vars.append('struct ynl_parse_arg parg;') 2159 init_lines.append('parg.ys = yarg->ys;') 2160 2161 all_multi = indexed_arrays | multi_attrs 2162 2163 for arg in sorted(all_multi): 2164 local_vars.append(f"unsigned int n_{struct[arg].c_name} = 0;") 2165 2166 ri.cw.block_start() 2167 ri.cw.write_func_lvar(local_vars) 2168 2169 for line in init_lines: 2170 ri.cw.p(line) 2171 ri.cw.nl() 2172 2173 for arg in struct.inherited: 2174 ri.cw.p(f'dst->{arg} = {arg};') 2175 2176 if struct.fixed_header: 2177 if struct.nested: 2178 ri.cw.p('hdr = ynl_attr_data(nested);') 2179 elif ri.family.is_classic(): 2180 ri.cw.p('hdr = ynl_nlmsg_data(nlh);') 2181 else: 2182 ri.cw.p('hdr = ynl_nlmsg_data_offset(nlh, sizeof(struct genlmsghdr));') 2183 ri.cw.p(f"memcpy(&dst->_hdr, hdr, sizeof({struct.fixed_header}));") 2184 for arg in sorted(all_multi): 2185 aspec = struct[arg] 2186 ri.cw.p(f"if (dst->{aspec.c_name})") 2187 ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");') 2188 2189 ri.cw.nl() 2190 ri.cw.block_start(line=iter_line) 2191 ri.cw.p('unsigned int type = ynl_attr_type(attr);') 2192 ri.cw.nl() 2193 2194 first = True 2195 for _, arg in struct.member_list(): 2196 good = arg.attr_get(ri, 'dst', first=first) 2197 # First may be 'unused' or 'pad', ignore those 2198 first &= not good 2199 2200 ri.cw.block_end() 2201 ri.cw.nl() 2202 2203 for arg in sorted(indexed_arrays): 2204 aspec = struct[arg] 2205 2206 ri.cw.block_start(line=f"if (n_{aspec.c_name})") 2207 ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));") 2208 ri.cw.p(f"dst->_count.{aspec.c_name} = n_{aspec.c_name};") 2209 ri.cw.p('i = 0;') 2210 if 'nested-attributes' in aspec: 2211 ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;") 2212 ri.cw.block_start(line=f"ynl_attr_for_each_nested(attr, attr_{aspec.c_name})") 2213 if 'nested-attributes' in aspec: 2214 ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];") 2215 ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr, ynl_attr_type(attr)))") 2216 ri.cw.p('return YNL_PARSE_CB_ERROR;') 2217 elif aspec.sub_type in scalars: 2218 ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.sub_type}(attr);") 2219 elif aspec.sub_type == 'binary' and 'exact-len' in aspec.checks: 2220 # Length is validated by typol 2221 ri.cw.p(f'memcpy(dst->{aspec.c_name}[i], ynl_attr_data(attr), {aspec.checks["exact-len"]});') 2222 else: 2223 raise Exception(f"Nest parsing type not supported in {aspec['name']}") 2224 ri.cw.p('i++;') 2225 ri.cw.block_end() 2226 ri.cw.block_end() 2227 ri.cw.nl() 2228 2229 for arg in sorted(multi_attrs): 2230 aspec = struct[arg] 2231 ri.cw.block_start(line=f"if (n_{aspec.c_name})") 2232 ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));") 2233 ri.cw.p(f"dst->_count.{aspec.c_name} = n_{aspec.c_name};") 2234 ri.cw.p('i = 0;') 2235 if 'nested-attributes' in aspec: 2236 ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;") 2237 ri.cw.block_start(line=iter_line) 2238 ri.cw.block_start(line=f"if (ynl_attr_type(attr) == {aspec.enum_name})") 2239 if 'nested-attributes' in aspec: 2240 ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];") 2241 ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))") 2242 ri.cw.p('return YNL_PARSE_CB_ERROR;') 2243 elif aspec.type in scalars: 2244 ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.type}(attr);") 2245 elif aspec.type == 'binary' and 'struct' in aspec: 2246 ri.cw.p('size_t len = ynl_attr_data_len(attr);') 2247 ri.cw.nl() 2248 ri.cw.p(f'if (len > sizeof(dst->{aspec.c_name}[0]))') 2249 ri.cw.p(f'len = sizeof(dst->{aspec.c_name}[0]);') 2250 ri.cw.p(f"memcpy(&dst->{aspec.c_name}[i], ynl_attr_data(attr), len);") 2251 elif aspec.type == 'string': 2252 ri.cw.p('unsigned int len;') 2253 ri.cw.nl() 2254 ri.cw.p('len = strnlen(ynl_attr_get_str(attr), ynl_attr_data_len(attr));') 2255 ri.cw.p(f'dst->{aspec.c_name}[i] = malloc(sizeof(struct ynl_string) + len + 1);') 2256 ri.cw.p(f"dst->{aspec.c_name}[i]->len = len;") 2257 ri.cw.p(f"memcpy(dst->{aspec.c_name}[i]->str, ynl_attr_get_str(attr), len);") 2258 ri.cw.p(f"dst->{aspec.c_name}[i]->str[len] = 0;") 2259 else: 2260 raise Exception(f'Nest parsing of type {aspec.type} not supported yet') 2261 ri.cw.p('i++;') 2262 ri.cw.block_end() 2263 ri.cw.block_end() 2264 ri.cw.block_end() 2265 ri.cw.nl() 2266 2267 if struct.nested: 2268 ri.cw.p('return 0;') 2269 else: 2270 ri.cw.p('return YNL_PARSE_CB_OK;') 2271 ri.cw.block_end() 2272 ri.cw.nl() 2273 2274 2275def parse_rsp_submsg(ri, struct): 2276 parse_rsp_nested_prototype(ri, struct, suffix='') 2277 2278 var = 'dst' 2279 local_vars = {'const struct nlattr *attr = nested;', 2280 f'{struct.ptr_name}{var} = yarg->data;', 2281 'struct ynl_parse_arg parg;'} 2282 2283 for _, arg in struct.member_list(): 2284 _, _, l_vars = arg._attr_get(ri, var) 2285 local_vars |= set(l_vars) if l_vars else set() 2286 2287 ri.cw.block_start() 2288 ri.cw.write_func_lvar(list(local_vars)) 2289 ri.cw.p('parg.ys = yarg->ys;') 2290 ri.cw.nl() 2291 2292 first = True 2293 for name, arg in struct.member_list(): 2294 kw = 'if' if first else 'else if' 2295 first = False 2296 2297 ri.cw.block_start(line=f'{kw} (!strcmp(sel, "{name}"))') 2298 get_lines, init_lines, _ = arg._attr_get(ri, var) 2299 for line in init_lines or []: 2300 ri.cw.p(line) 2301 for line in get_lines: 2302 ri.cw.p(line) 2303 if arg.presence_type() == 'present': 2304 ri.cw.p(f"{var}->_present.{arg.c_name} = 1;") 2305 ri.cw.block_end() 2306 ri.cw.p('return 0;') 2307 ri.cw.block_end() 2308 ri.cw.nl() 2309 2310 2311def parse_rsp_nested_prototype(ri, struct, suffix=';'): 2312 func_args = ['struct ynl_parse_arg *yarg', 2313 'const struct nlattr *nested'] 2314 for sel in struct.external_selectors(): 2315 func_args.append('const char *_sel_' + sel.name) 2316 if struct.submsg: 2317 func_args.insert(1, 'const char *sel') 2318 for arg in struct.inherited: 2319 func_args.append('__u32 ' + arg) 2320 2321 ri.cw.write_func_prot('int', f'{struct.render_name}_parse', func_args, 2322 suffix=suffix) 2323 2324 2325def parse_rsp_nested(ri, struct): 2326 if struct.submsg: 2327 return parse_rsp_submsg(ri, struct) 2328 2329 parse_rsp_nested_prototype(ri, struct, suffix='') 2330 2331 local_vars = ['const struct nlattr *attr;', 2332 f'{struct.ptr_name}dst = yarg->data;'] 2333 init_lines = [] 2334 2335 if struct.member_list(): 2336 _multi_parse(ri, struct, init_lines, local_vars) 2337 else: 2338 # Empty nest 2339 ri.cw.block_start() 2340 ri.cw.p('return 0;') 2341 ri.cw.block_end() 2342 ri.cw.nl() 2343 2344 2345def parse_rsp_msg(ri, deref=False): 2346 if 'reply' not in ri.op[ri.op_mode] and ri.op_mode != 'event': 2347 return 2348 2349 func_args = ['const struct nlmsghdr *nlh', 2350 'struct ynl_parse_arg *yarg'] 2351 2352 local_vars = [f'{type_name(ri, "reply", deref=deref)} *dst;', 2353 'const struct nlattr *attr;'] 2354 init_lines = ['dst = yarg->data;'] 2355 2356 ri.cw.write_func_prot('int', f'{op_prefix(ri, "reply", deref=deref)}_parse', func_args) 2357 2358 if ri.struct["reply"].member_list(): 2359 _multi_parse(ri, ri.struct["reply"], init_lines, local_vars) 2360 else: 2361 # Empty reply 2362 ri.cw.block_start() 2363 ri.cw.p('return YNL_PARSE_CB_OK;') 2364 ri.cw.block_end() 2365 ri.cw.nl() 2366 2367 2368def print_req(ri): 2369 ret_ok = '0' 2370 ret_err = '-1' 2371 direction = "request" 2372 local_vars = ['struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };', 2373 'struct nlmsghdr *nlh;', 2374 'int err;'] 2375 2376 if 'reply' in ri.op[ri.op_mode]: 2377 ret_ok = 'rsp' 2378 ret_err = 'NULL' 2379 local_vars += [f'{type_name(ri, rdir(direction))} *rsp;'] 2380 2381 if ri.struct["request"].fixed_header: 2382 local_vars += ['size_t hdr_len;', 2383 'void *hdr;'] 2384 2385 local_vars += put_local_vars(ri.struct['request']) 2386 2387 print_prototype(ri, direction, terminate=False) 2388 ri.cw.block_start() 2389 ri.cw.write_func_lvar(local_vars) 2390 2391 if ri.family.is_classic(): 2392 ri.cw.p(f"nlh = ynl_msg_start_req(ys, {ri.op.enum_name}, req->_nlmsg_flags);") 2393 else: 2394 ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);") 2395 2396 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;") 2397 ri.cw.p(f"ys->req_hdr_len = {ri.fixed_hdr_len};") 2398 if 'reply' in ri.op[ri.op_mode]: 2399 ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;") 2400 ri.cw.nl() 2401 2402 if ri.struct['request'].fixed_header: 2403 ri.cw.p("hdr_len = sizeof(req->_hdr);") 2404 ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);") 2405 ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);") 2406 ri.cw.nl() 2407 2408 for _, attr in ri.struct["request"].member_list(): 2409 attr.attr_put(ri, "req") 2410 ri.cw.nl() 2411 2412 if 'reply' in ri.op[ri.op_mode]: 2413 ri.cw.p('rsp = calloc(1, sizeof(*rsp));') 2414 ri.cw.p('yrs.yarg.data = rsp;') 2415 ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;") 2416 if ri.op.value is not None: 2417 ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};') 2418 else: 2419 ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};') 2420 ri.cw.nl() 2421 ri.cw.p("err = ynl_exec(ys, nlh, &yrs);") 2422 ri.cw.p('if (err < 0)') 2423 if 'reply' in ri.op[ri.op_mode]: 2424 ri.cw.p('goto err_free;') 2425 else: 2426 ri.cw.p('return -1;') 2427 ri.cw.nl() 2428 2429 ri.cw.p(f"return {ret_ok};") 2430 ri.cw.nl() 2431 2432 if 'reply' in ri.op[ri.op_mode]: 2433 ri.cw.p('err_free:') 2434 ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}") 2435 ri.cw.p(f"return {ret_err};") 2436 2437 ri.cw.block_end() 2438 2439 2440def print_dump(ri): 2441 direction = "request" 2442 print_prototype(ri, direction, terminate=False) 2443 ri.cw.block_start() 2444 local_vars = ['struct ynl_dump_state yds = {};', 2445 'struct nlmsghdr *nlh;', 2446 'int err;'] 2447 2448 if ri.struct['request'].fixed_header: 2449 local_vars += ['size_t hdr_len;', 2450 'void *hdr;'] 2451 2452 if 'request' in ri.op[ri.op_mode]: 2453 local_vars += put_local_vars(ri.struct['request']) 2454 2455 ri.cw.write_func_lvar(local_vars) 2456 2457 ri.cw.p('yds.yarg.ys = ys;') 2458 ri.cw.p(f"yds.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;") 2459 ri.cw.p("yds.yarg.data = NULL;") 2460 ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});") 2461 ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;") 2462 if ri.op.value is not None: 2463 ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};') 2464 else: 2465 ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};') 2466 ri.cw.nl() 2467 if ri.family.is_classic(): 2468 ri.cw.p(f"nlh = ynl_msg_start_dump(ys, {ri.op.enum_name});") 2469 else: 2470 ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);") 2471 2472 if ri.struct['request'].fixed_header: 2473 ri.cw.p("hdr_len = sizeof(req->_hdr);") 2474 ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);") 2475 ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);") 2476 ri.cw.nl() 2477 2478 if "request" in ri.op[ri.op_mode]: 2479 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;") 2480 ri.cw.p(f"ys->req_hdr_len = {ri.fixed_hdr_len};") 2481 ri.cw.nl() 2482 for _, attr in ri.struct["request"].member_list(): 2483 attr.attr_put(ri, "req") 2484 ri.cw.nl() 2485 2486 ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);') 2487 ri.cw.p('if (err < 0)') 2488 ri.cw.p('goto free_list;') 2489 ri.cw.nl() 2490 2491 ri.cw.p('return yds.first;') 2492 ri.cw.nl() 2493 ri.cw.p('free_list:') 2494 ri.cw.p(call_free(ri, rdir(direction), 'yds.first')) 2495 ri.cw.p('return NULL;') 2496 ri.cw.block_end() 2497 2498 2499def call_free(ri, direction, var): 2500 return f"{op_prefix(ri, direction)}_free({var});" 2501 2502 2503def free_arg_name(direction): 2504 if direction: 2505 return direction_to_suffix[direction][1:] 2506 return 'obj' 2507 2508 2509def print_alloc_wrapper(ri, direction, struct=None): 2510 name = op_prefix(ri, direction) 2511 struct_name = name 2512 if ri.type_name_conflict: 2513 struct_name += '_' 2514 2515 args = ["void"] 2516 cnt = "1" 2517 if struct and struct.in_multi_val: 2518 args = ["unsigned int n"] 2519 cnt = "n" 2520 2521 ri.cw.write_func_prot(f'static inline struct {struct_name} *', 2522 f"{name}_alloc", args) 2523 ri.cw.block_start() 2524 ri.cw.p(f'return calloc({cnt}, sizeof(struct {struct_name}));') 2525 ri.cw.block_end() 2526 2527 2528def print_free_prototype(ri, direction, suffix=';'): 2529 name = op_prefix(ri, direction) 2530 struct_name = name 2531 if ri.type_name_conflict: 2532 struct_name += '_' 2533 arg = free_arg_name(direction) 2534 ri.cw.write_func_prot('void', f"{name}_free", [f"struct {struct_name} *{arg}"], suffix=suffix) 2535 2536 2537def print_nlflags_set(ri, direction): 2538 name = op_prefix(ri, direction) 2539 ri.cw.write_func_prot('static inline void', f"{name}_set_nlflags", 2540 [f"struct {name} *req", "__u16 nl_flags"]) 2541 ri.cw.block_start() 2542 ri.cw.p('req->_nlmsg_flags = nl_flags;') 2543 ri.cw.block_end() 2544 ri.cw.nl() 2545 2546 2547def _print_type(ri, direction, struct): 2548 suffix = f'_{ri.type_name}{direction_to_suffix[direction]}' 2549 if not direction and ri.type_name_conflict: 2550 suffix += '_' 2551 2552 if ri.op_mode == 'dump' and not ri.type_oneside: 2553 suffix += '_dump' 2554 2555 ri.cw.block_start(line=f"struct {ri.family.c_name}{suffix}") 2556 2557 if ri.needs_nlflags(direction): 2558 ri.cw.p('__u16 _nlmsg_flags;') 2559 ri.cw.nl() 2560 if struct.fixed_header: 2561 ri.cw.p(struct.fixed_header + ' _hdr;') 2562 ri.cw.nl() 2563 2564 for type_filter in ['present', 'len', 'count']: 2565 meta_started = False 2566 for _, attr in struct.member_list(): 2567 line = attr.presence_member(ri.ku_space, type_filter) 2568 if line: 2569 if not meta_started: 2570 ri.cw.block_start(line="struct") 2571 meta_started = True 2572 ri.cw.p(line) 2573 if meta_started: 2574 ri.cw.block_end(line=f'_{type_filter};') 2575 ri.cw.nl() 2576 2577 for arg in struct.inherited: 2578 ri.cw.p(f"__u32 {arg};") 2579 2580 for _, attr in struct.member_list(): 2581 attr.struct_member(ri) 2582 2583 ri.cw.block_end(line=';') 2584 ri.cw.nl() 2585 2586 2587def print_type(ri, direction): 2588 _print_type(ri, direction, ri.struct[direction]) 2589 2590 2591def print_type_full(ri, struct): 2592 _print_type(ri, "", struct) 2593 2594 if struct.request and struct.in_multi_val: 2595 print_alloc_wrapper(ri, "", struct) 2596 ri.cw.nl() 2597 free_rsp_nested_prototype(ri) 2598 ri.cw.nl() 2599 2600 # Name conflicts are too hard to deal with with the current code base, 2601 # they are very rare so don't bother printing setters in that case. 2602 if ri.ku_space == 'user' and not ri.type_name_conflict: 2603 for _, attr in struct.member_list(): 2604 attr.setter(ri, ri.attr_set, "", var="obj") 2605 ri.cw.nl() 2606 2607 2608def print_type_helpers(ri, direction, deref=False): 2609 print_free_prototype(ri, direction) 2610 ri.cw.nl() 2611 2612 if ri.needs_nlflags(direction): 2613 print_nlflags_set(ri, direction) 2614 2615 if ri.ku_space == 'user' and direction == 'request': 2616 for _, attr in ri.struct[direction].member_list(): 2617 attr.setter(ri, ri.attr_set, direction, deref=deref) 2618 ri.cw.nl() 2619 2620 2621def print_req_type_helpers(ri): 2622 if ri.type_empty("request"): 2623 return 2624 print_alloc_wrapper(ri, "request") 2625 print_type_helpers(ri, "request") 2626 2627 2628def print_rsp_type_helpers(ri): 2629 if 'reply' not in ri.op[ri.op_mode]: 2630 return 2631 print_type_helpers(ri, "reply") 2632 2633 2634def print_parse_prototype(ri, direction, terminate=True): 2635 suffix = "_rsp" if direction == "reply" else "_req" 2636 term = ';' if terminate else '' 2637 2638 ri.cw.write_func_prot('void', f"{ri.op.render_name}{suffix}_parse", 2639 ['const struct nlattr **tb', 2640 f"struct {ri.op.render_name}{suffix} *req"], 2641 suffix=term) 2642 2643 2644def print_req_type(ri): 2645 if ri.type_empty("request"): 2646 return 2647 print_type(ri, "request") 2648 2649 2650def print_req_free(ri): 2651 if 'request' not in ri.op[ri.op_mode]: 2652 return 2653 _free_type(ri, 'request', ri.struct['request']) 2654 2655 2656def print_rsp_type(ri): 2657 if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]: 2658 direction = 'reply' 2659 elif ri.op_mode == 'event': 2660 direction = 'reply' 2661 else: 2662 return 2663 print_type(ri, direction) 2664 2665 2666def print_wrapped_type(ri): 2667 ri.cw.block_start(line=f"{type_name(ri, 'reply')}") 2668 if ri.op_mode == 'dump': 2669 ri.cw.p(f"{type_name(ri, 'reply')} *next;") 2670 elif ri.op_mode == 'notify' or ri.op_mode == 'event': 2671 ri.cw.p('__u16 family;') 2672 ri.cw.p('__u8 cmd;') 2673 ri.cw.p('struct ynl_ntf_base_type *next;') 2674 ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);") 2675 ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__((aligned(8)));") 2676 ri.cw.block_end(line=';') 2677 ri.cw.nl() 2678 print_free_prototype(ri, 'reply') 2679 ri.cw.nl() 2680 2681 2682def _free_type_members_iter(ri, struct): 2683 if struct.free_needs_iter(): 2684 ri.cw.p('unsigned int i;') 2685 ri.cw.nl() 2686 2687 2688def _free_type_members(ri, var, struct, ref=''): 2689 for _, attr in struct.member_list(): 2690 attr.free(ri, var, ref) 2691 2692 2693def _free_type(ri, direction, struct): 2694 var = free_arg_name(direction) 2695 2696 print_free_prototype(ri, direction, suffix='') 2697 ri.cw.block_start() 2698 _free_type_members_iter(ri, struct) 2699 _free_type_members(ri, var, struct) 2700 if direction: 2701 ri.cw.p(f'free({var});') 2702 ri.cw.block_end() 2703 ri.cw.nl() 2704 2705 2706def free_rsp_nested_prototype(ri): 2707 print_free_prototype(ri, "") 2708 2709 2710def free_rsp_nested(ri, struct): 2711 _free_type(ri, "", struct) 2712 2713 2714def print_rsp_free(ri): 2715 if 'reply' not in ri.op[ri.op_mode]: 2716 return 2717 _free_type(ri, 'reply', ri.struct['reply']) 2718 2719 2720def print_dump_type_free(ri): 2721 sub_type = type_name(ri, 'reply') 2722 2723 print_free_prototype(ri, 'reply', suffix='') 2724 ri.cw.block_start() 2725 ri.cw.p(f"{sub_type} *next = rsp;") 2726 ri.cw.nl() 2727 ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)') 2728 _free_type_members_iter(ri, ri.struct['reply']) 2729 ri.cw.p('rsp = next;') 2730 ri.cw.p('next = rsp->next;') 2731 ri.cw.nl() 2732 2733 _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.') 2734 ri.cw.p('free(rsp);') 2735 ri.cw.block_end() 2736 ri.cw.block_end() 2737 ri.cw.nl() 2738 2739 2740def print_ntf_type_free(ri): 2741 print_free_prototype(ri, 'reply', suffix='') 2742 ri.cw.block_start() 2743 _free_type_members_iter(ri, ri.struct['reply']) 2744 _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.') 2745 ri.cw.p('free(rsp);') 2746 ri.cw.block_end() 2747 ri.cw.nl() 2748 2749 2750def print_req_policy_fwd(cw, struct, ri=None, terminate=True): 2751 if terminate and ri and policy_should_be_static(struct.family): 2752 return 2753 2754 if terminate: 2755 prefix = 'extern ' 2756 else: 2757 if ri and policy_should_be_static(struct.family): 2758 prefix = 'static ' 2759 else: 2760 prefix = '' 2761 2762 suffix = ';' if terminate else ' = {' 2763 2764 max_attr = struct.attr_max_val 2765 if ri: 2766 name = ri.op.render_name 2767 if ri.op.dual_policy: 2768 name += '_' + ri.op_mode 2769 else: 2770 name = struct.render_name 2771 cw.p(f"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}") 2772 2773 2774def print_req_policy(cw, struct, ri=None): 2775 if ri and ri.op: 2776 cw.ifdef_block(ri.op.get('config-cond', None)) 2777 print_req_policy_fwd(cw, struct, ri=ri, terminate=False) 2778 for _, arg in struct.member_list(): 2779 arg.attr_policy(cw) 2780 cw.p("};") 2781 cw.ifdef_block(None) 2782 cw.nl() 2783 2784 2785def kernel_can_gen_family_struct(family): 2786 return family.proto == 'genetlink' 2787 2788 2789def policy_should_be_static(family): 2790 return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family) 2791 2792 2793def print_kernel_policy_ranges(family, cw): 2794 first = True 2795 for _, attr_set in family.attr_sets.items(): 2796 if attr_set.subset_of: 2797 continue 2798 2799 for _, attr in attr_set.items(): 2800 if not attr.request: 2801 continue 2802 if 'full-range' not in attr.checks: 2803 continue 2804 2805 if first: 2806 cw.p('/* Integer value ranges */') 2807 first = False 2808 2809 sign = '' if attr.type[0] == 'u' else '_signed' 2810 suffix = 'ULL' if attr.type[0] == 'u' else 'LL' 2811 cw.block_start(line=f'static const struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =') 2812 members = [] 2813 if 'min' in attr.checks: 2814 members.append(('min', attr.get_limit_str('min', suffix=suffix))) 2815 if 'max' in attr.checks: 2816 members.append(('max', attr.get_limit_str('max', suffix=suffix))) 2817 cw.write_struct_init(members) 2818 cw.block_end(line=';') 2819 cw.nl() 2820 2821 2822def print_kernel_policy_sparse_enum_validates(family, cw): 2823 first = True 2824 for _, attr_set in family.attr_sets.items(): 2825 if attr_set.subset_of: 2826 continue 2827 2828 for _, attr in attr_set.items(): 2829 if not attr.request: 2830 continue 2831 if not attr.enum_name: 2832 continue 2833 if 'sparse' not in attr.checks: 2834 continue 2835 2836 if first: 2837 cw.p('/* Sparse enums validation callbacks */') 2838 first = False 2839 2840 cw.write_func_prot('static int', f'{c_lower(attr.enum_name)}_validate', 2841 ['const struct nlattr *attr', 'struct netlink_ext_ack *extack']) 2842 cw.block_start() 2843 cw.block_start(line=f'switch (nla_get_{attr["type"]}(attr))') 2844 enum = family.consts[attr['enum']] 2845 first_entry = True 2846 for entry in enum.entries.values(): 2847 if first_entry: 2848 first_entry = False 2849 else: 2850 cw.p('fallthrough;') 2851 cw.p(f'case {entry.c_name}:') 2852 cw.p('return 0;') 2853 cw.block_end() 2854 cw.p('NL_SET_ERR_MSG_ATTR(extack, attr, "invalid enum value");') 2855 cw.p('return -EINVAL;') 2856 cw.block_end() 2857 cw.nl() 2858 2859 2860def print_kernel_op_table_fwd(family, cw, terminate): 2861 exported = not kernel_can_gen_family_struct(family) 2862 2863 if not terminate or exported: 2864 cw.p(f"/* Ops table for {family.ident_name} */") 2865 2866 pol_to_struct = {'global': 'genl_small_ops', 2867 'per-op': 'genl_ops', 2868 'split': 'genl_split_ops'} 2869 struct_type = pol_to_struct[family.kernel_policy] 2870 2871 if not exported: 2872 cnt = "" 2873 elif family.kernel_policy == 'split': 2874 cnt = 0 2875 for op in family.ops.values(): 2876 if 'do' in op: 2877 cnt += 1 2878 if 'dump' in op: 2879 cnt += 1 2880 else: 2881 cnt = len(family.ops) 2882 2883 qual = 'static const' if not exported else 'const' 2884 line = f"{qual} struct {struct_type} {family.c_name}_nl_ops[{cnt}]" 2885 if terminate: 2886 cw.p(f"extern {line};") 2887 else: 2888 cw.block_start(line=line + ' =') 2889 2890 if not terminate: 2891 return 2892 2893 cw.nl() 2894 for name in family.hooks['pre']['do']['list']: 2895 cw.write_func_prot('int', c_lower(name), 2896 ['const struct genl_split_ops *ops', 2897 'struct sk_buff *skb', 'struct genl_info *info'], suffix=';') 2898 for name in family.hooks['post']['do']['list']: 2899 cw.write_func_prot('void', c_lower(name), 2900 ['const struct genl_split_ops *ops', 2901 'struct sk_buff *skb', 'struct genl_info *info'], suffix=';') 2902 for name in family.hooks['pre']['dump']['list']: 2903 cw.write_func_prot('int', c_lower(name), 2904 ['struct netlink_callback *cb'], suffix=';') 2905 for name in family.hooks['post']['dump']['list']: 2906 cw.write_func_prot('int', c_lower(name), 2907 ['struct netlink_callback *cb'], suffix=';') 2908 2909 cw.nl() 2910 2911 for op_name, op in family.ops.items(): 2912 if op.is_async: 2913 continue 2914 2915 if 'do' in op: 2916 name = c_lower(f"{family.fn_prefix}-{op_name}-doit") 2917 cw.write_func_prot('int', name, 2918 ['struct sk_buff *skb', 'struct genl_info *info'], suffix=';') 2919 2920 if 'dump' in op: 2921 name = c_lower(f"{family.fn_prefix}-{op_name}-dumpit") 2922 cw.write_func_prot('int', name, 2923 ['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix=';') 2924 cw.nl() 2925 2926 2927def print_kernel_op_table_hdr(family, cw): 2928 print_kernel_op_table_fwd(family, cw, terminate=True) 2929 2930 2931def print_kernel_op_table(family, cw): 2932 print_kernel_op_table_fwd(family, cw, terminate=False) 2933 if family.kernel_policy == 'global' or family.kernel_policy == 'per-op': 2934 for op_name, op in family.ops.items(): 2935 if op.is_async: 2936 continue 2937 2938 cw.ifdef_block(op.get('config-cond', None)) 2939 cw.block_start() 2940 members = [('cmd', op.enum_name)] 2941 if 'dont-validate' in op: 2942 members.append(('validate', 2943 ' | '.join([c_upper('genl-dont-validate-' + x) 2944 for x in op['dont-validate']])), ) 2945 for op_mode in ['do', 'dump']: 2946 if op_mode in op: 2947 name = c_lower(f"{family.fn_prefix}-{op_name}-{op_mode}it") 2948 members.append((op_mode + 'it', name)) 2949 if family.kernel_policy == 'per-op': 2950 struct = Struct(family, op['attribute-set'], 2951 type_list=op['do']['request']['attributes']) 2952 2953 name = c_lower(f"{family.ident_name}-{op_name}-nl-policy") 2954 members.append(('policy', name)) 2955 members.append(('maxattr', struct.attr_max_val.enum_name)) 2956 if 'flags' in op: 2957 members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in op['flags']]))) 2958 cw.write_struct_init(members) 2959 cw.block_end(line=',') 2960 elif family.kernel_policy == 'split': 2961 cb_names = {'do': {'pre': 'pre_doit', 'post': 'post_doit'}, 2962 'dump': {'pre': 'start', 'post': 'done'}} 2963 2964 for op_name, op in family.ops.items(): 2965 for op_mode in ['do', 'dump']: 2966 if op.is_async or op_mode not in op: 2967 continue 2968 2969 cw.ifdef_block(op.get('config-cond', None)) 2970 cw.block_start() 2971 members = [('cmd', op.enum_name)] 2972 if 'dont-validate' in op: 2973 dont_validate = [] 2974 for x in op['dont-validate']: 2975 if op_mode == 'do' and x in ['dump', 'dump-strict']: 2976 continue 2977 if op_mode == "dump" and x == 'strict': 2978 continue 2979 dont_validate.append(x) 2980 2981 if dont_validate: 2982 members.append(('validate', 2983 ' | '.join([c_upper('genl-dont-validate-' + x) 2984 for x in dont_validate])), ) 2985 name = c_lower(f"{family.fn_prefix}-{op_name}-{op_mode}it") 2986 if 'pre' in op[op_mode]: 2987 members.append((cb_names[op_mode]['pre'], c_lower(op[op_mode]['pre']))) 2988 members.append((op_mode + 'it', name)) 2989 if 'post' in op[op_mode]: 2990 members.append((cb_names[op_mode]['post'], c_lower(op[op_mode]['post']))) 2991 if 'request' in op[op_mode]: 2992 struct = Struct(family, op['attribute-set'], 2993 type_list=op[op_mode]['request']['attributes']) 2994 2995 if op.dual_policy: 2996 name = c_lower(f"{family.ident_name}-{op_name}-{op_mode}-nl-policy") 2997 else: 2998 name = c_lower(f"{family.ident_name}-{op_name}-nl-policy") 2999 members.append(('policy', name)) 3000 members.append(('maxattr', struct.attr_max_val.enum_name)) 3001 flags = (op['flags'] if 'flags' in op else []) + ['cmd-cap-' + op_mode] 3002 members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in flags]))) 3003 cw.write_struct_init(members) 3004 cw.block_end(line=',') 3005 cw.ifdef_block(None) 3006 3007 cw.block_end(line=';') 3008 cw.nl() 3009 3010 3011def print_kernel_mcgrp_hdr(family, cw): 3012 if not family.mcgrps['list']: 3013 return 3014 3015 cw.block_start('enum') 3016 for grp in family.mcgrps['list']: 3017 grp_id = c_upper(f"{family.ident_name}-nlgrp-{grp['name']},") 3018 cw.p(grp_id) 3019 cw.block_end(';') 3020 cw.nl() 3021 3022 3023def print_kernel_mcgrp_src(family, cw): 3024 if not family.mcgrps['list']: 3025 return 3026 3027 cw.block_start('static const struct genl_multicast_group ' + family.c_name + '_nl_mcgrps[] =') 3028 for grp in family.mcgrps['list']: 3029 name = grp['name'] 3030 grp_id = c_upper(f"{family.ident_name}-nlgrp-{name}") 3031 cw.p('[' + grp_id + '] = { "' + name + '", },') 3032 cw.block_end(';') 3033 cw.nl() 3034 3035 3036def print_kernel_family_struct_hdr(family, cw): 3037 if not kernel_can_gen_family_struct(family): 3038 return 3039 3040 cw.p(f"extern struct genl_family {family.c_name}_nl_family;") 3041 cw.nl() 3042 if 'sock-priv' in family.kernel_family: 3043 cw.p(f'void {family.c_name}_nl_sock_priv_init({family.kernel_family["sock-priv"]} *priv);') 3044 cw.p(f'void {family.c_name}_nl_sock_priv_destroy({family.kernel_family["sock-priv"]} *priv);') 3045 cw.nl() 3046 3047 3048def print_kernel_family_struct_src(family, cw): 3049 if not kernel_can_gen_family_struct(family): 3050 return 3051 3052 if 'sock-priv' in family.kernel_family: 3053 # Generate "trampolines" to make CFI happy 3054 cw.write_func("static void", f"__{family.c_name}_nl_sock_priv_init", 3055 [f"{family.c_name}_nl_sock_priv_init(priv);"], 3056 ["void *priv"]) 3057 cw.nl() 3058 cw.write_func("static void", f"__{family.c_name}_nl_sock_priv_destroy", 3059 [f"{family.c_name}_nl_sock_priv_destroy(priv);"], 3060 ["void *priv"]) 3061 cw.nl() 3062 3063 cw.block_start(f"struct genl_family {family.ident_name}_nl_family __ro_after_init =") 3064 cw.p('.name\t\t= ' + family.fam_key + ',') 3065 cw.p('.version\t= ' + family.ver_key + ',') 3066 cw.p('.netnsok\t= true,') 3067 cw.p('.parallel_ops\t= true,') 3068 cw.p('.module\t\t= THIS_MODULE,') 3069 if family.kernel_policy == 'per-op': 3070 cw.p(f'.ops\t\t= {family.c_name}_nl_ops,') 3071 cw.p(f'.n_ops\t\t= ARRAY_SIZE({family.c_name}_nl_ops),') 3072 elif family.kernel_policy == 'split': 3073 cw.p(f'.split_ops\t= {family.c_name}_nl_ops,') 3074 cw.p(f'.n_split_ops\t= ARRAY_SIZE({family.c_name}_nl_ops),') 3075 if family.mcgrps['list']: 3076 cw.p(f'.mcgrps\t\t= {family.c_name}_nl_mcgrps,') 3077 cw.p(f'.n_mcgrps\t= ARRAY_SIZE({family.c_name}_nl_mcgrps),') 3078 if 'sock-priv' in family.kernel_family: 3079 cw.p(f'.sock_priv_size\t= sizeof({family.kernel_family["sock-priv"]}),') 3080 cw.p(f'.sock_priv_init\t= __{family.c_name}_nl_sock_priv_init,') 3081 cw.p(f'.sock_priv_destroy = __{family.c_name}_nl_sock_priv_destroy,') 3082 cw.block_end(';') 3083 3084 3085def uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'): 3086 start_line = 'enum' 3087 if enum_name in obj: 3088 if obj[enum_name]: 3089 start_line = 'enum ' + c_lower(obj[enum_name]) 3090 elif ckey and ckey in obj: 3091 start_line = 'enum ' + family.c_name + '_' + c_lower(obj[ckey]) 3092 cw.block_start(line=start_line) 3093 3094 3095def render_uapi_unified(family, cw, max_by_define, separate_ntf): 3096 max_name = c_upper(family.get('cmd-max-name', f"{family.op_prefix}MAX")) 3097 cnt_name = c_upper(family.get('cmd-cnt-name', f"__{family.op_prefix}MAX")) 3098 max_value = f"({cnt_name} - 1)" 3099 3100 uapi_enum_start(family, cw, family['operations'], 'enum-name') 3101 val = 0 3102 for op in family.msgs.values(): 3103 if separate_ntf and ('notify' in op or 'event' in op): 3104 continue 3105 3106 suffix = ',' 3107 if op.value != val: 3108 suffix = f" = {op.value}," 3109 val = op.value 3110 cw.p(op.enum_name + suffix) 3111 val += 1 3112 cw.nl() 3113 cw.p(cnt_name + ('' if max_by_define else ',')) 3114 if not max_by_define: 3115 cw.p(f"{max_name} = {max_value}") 3116 cw.block_end(line=';') 3117 if max_by_define: 3118 cw.p(f"#define {max_name} {max_value}") 3119 cw.nl() 3120 3121 3122def render_uapi_directional(family, cw, max_by_define): 3123 max_name = f"{family.op_prefix}USER_MAX" 3124 cnt_name = f"__{family.op_prefix}USER_CNT" 3125 max_value = f"({cnt_name} - 1)" 3126 3127 cw.block_start(line='enum') 3128 cw.p(c_upper(f'{family.name}_MSG_USER_NONE = 0,')) 3129 val = 0 3130 for op in family.msgs.values(): 3131 if 'do' in op and 'event' not in op: 3132 suffix = ',' 3133 if op.value and op.value != val: 3134 suffix = f" = {op.value}," 3135 val = op.value 3136 cw.p(op.enum_name + suffix) 3137 val += 1 3138 cw.nl() 3139 cw.p(cnt_name + ('' if max_by_define else ',')) 3140 if not max_by_define: 3141 cw.p(f"{max_name} = {max_value}") 3142 cw.block_end(line=';') 3143 if max_by_define: 3144 cw.p(f"#define {max_name} {max_value}") 3145 cw.nl() 3146 3147 max_name = f"{family.op_prefix}KERNEL_MAX" 3148 cnt_name = f"__{family.op_prefix}KERNEL_CNT" 3149 max_value = f"({cnt_name} - 1)" 3150 3151 cw.block_start(line='enum') 3152 cw.p(c_upper(f'{family.name}_MSG_KERNEL_NONE = 0,')) 3153 val = 0 3154 for op in family.msgs.values(): 3155 if ('do' in op and 'reply' in op['do']) or 'notify' in op or 'event' in op: 3156 enum_name = op.enum_name 3157 if 'event' not in op and 'notify' not in op: 3158 enum_name = f'{enum_name}_REPLY' 3159 3160 suffix = ',' 3161 if op.value and op.value != val: 3162 suffix = f" = {op.value}," 3163 val = op.value 3164 cw.p(enum_name + suffix) 3165 val += 1 3166 cw.nl() 3167 cw.p(cnt_name + ('' if max_by_define else ',')) 3168 if not max_by_define: 3169 cw.p(f"{max_name} = {max_value}") 3170 cw.block_end(line=';') 3171 if max_by_define: 3172 cw.p(f"#define {max_name} {max_value}") 3173 cw.nl() 3174 3175 3176def render_uapi(family, cw): 3177 hdr_prot = f"_UAPI_LINUX_{c_upper(family.uapi_header_name)}_H" 3178 hdr_prot = hdr_prot.replace('/', '_') 3179 cw.p('#ifndef ' + hdr_prot) 3180 cw.p('#define ' + hdr_prot) 3181 cw.nl() 3182 3183 defines = [(family.fam_key, family["name"]), 3184 (family.ver_key, family.get('version', 1))] 3185 cw.writes_defines(defines) 3186 cw.nl() 3187 3188 defines = [] 3189 for const in family['definitions']: 3190 if const.get('header'): 3191 continue 3192 3193 if const['type'] != 'const': 3194 cw.writes_defines(defines) 3195 defines = [] 3196 cw.nl() 3197 3198 # Write kdoc for enum and flags (one day maybe also structs) 3199 if const['type'] == 'enum' or const['type'] == 'flags': 3200 enum = family.consts[const['name']] 3201 3202 if enum.header: 3203 continue 3204 3205 if enum.has_doc(): 3206 if enum.has_entry_doc(): 3207 cw.p('/**') 3208 doc = '' 3209 if 'doc' in enum: 3210 doc = ' - ' + enum['doc'] 3211 cw.write_doc_line(enum.enum_name + doc) 3212 else: 3213 cw.p('/*') 3214 cw.write_doc_line(enum['doc'], indent=False) 3215 for entry in enum.entries.values(): 3216 if entry.has_doc(): 3217 doc = '@' + entry.c_name + ': ' + entry['doc'] 3218 cw.write_doc_line(doc) 3219 cw.p(' */') 3220 3221 uapi_enum_start(family, cw, const, 'name') 3222 name_pfx = const.get('name-prefix', f"{family.ident_name}-{const['name']}-") 3223 for entry in enum.entries.values(): 3224 suffix = ',' 3225 if entry.value_change: 3226 suffix = f" = {entry.user_value()}" + suffix 3227 cw.p(entry.c_name + suffix) 3228 3229 if const.get('render-max', False): 3230 cw.nl() 3231 cw.p('/* private: */') 3232 if const['type'] == 'flags': 3233 max_name = c_upper(name_pfx + 'mask') 3234 max_val = f' = {enum.get_mask()},' 3235 cw.p(max_name + max_val) 3236 else: 3237 cnt_name = enum.enum_cnt_name 3238 max_name = c_upper(name_pfx + 'max') 3239 if not cnt_name: 3240 cnt_name = '__' + name_pfx + 'max' 3241 cw.p(c_upper(cnt_name) + ',') 3242 cw.p(max_name + ' = (' + c_upper(cnt_name) + ' - 1)') 3243 cw.block_end(line=';') 3244 cw.nl() 3245 elif const['type'] == 'const': 3246 name_pfx = const.get('name-prefix', f"{family.ident_name}-") 3247 defines.append([c_upper(family.get('c-define-name', 3248 f"{name_pfx}{const['name']}")), 3249 const['value']]) 3250 3251 if defines: 3252 cw.writes_defines(defines) 3253 cw.nl() 3254 3255 max_by_define = family.get('max-by-define', False) 3256 3257 for _, attr_set in family.attr_sets.items(): 3258 if attr_set.subset_of: 3259 continue 3260 3261 max_value = f"({attr_set.cnt_name} - 1)" 3262 3263 val = 0 3264 uapi_enum_start(family, cw, attr_set.yaml, 'enum-name') 3265 for _, attr in attr_set.items(): 3266 suffix = ',' 3267 if attr.value != val: 3268 suffix = f" = {attr.value}," 3269 val = attr.value 3270 val += 1 3271 cw.p(attr.enum_name + suffix) 3272 if attr_set.items(): 3273 cw.nl() 3274 cw.p(attr_set.cnt_name + ('' if max_by_define else ',')) 3275 if not max_by_define: 3276 cw.p(f"{attr_set.max_name} = {max_value}") 3277 cw.block_end(line=';') 3278 if max_by_define: 3279 cw.p(f"#define {attr_set.max_name} {max_value}") 3280 cw.nl() 3281 3282 # Commands 3283 separate_ntf = 'async-prefix' in family['operations'] 3284 3285 if family.msg_id_model == 'unified': 3286 render_uapi_unified(family, cw, max_by_define, separate_ntf) 3287 elif family.msg_id_model == 'directional': 3288 render_uapi_directional(family, cw, max_by_define) 3289 else: 3290 raise Exception(f'Unsupported message enum-model {family.msg_id_model}') 3291 3292 if separate_ntf: 3293 uapi_enum_start(family, cw, family['operations'], enum_name='async-enum') 3294 for op in family.msgs.values(): 3295 if separate_ntf and not ('notify' in op or 'event' in op): 3296 continue 3297 3298 suffix = ',' 3299 if 'value' in op: 3300 suffix = f" = {op['value']}," 3301 cw.p(op.enum_name + suffix) 3302 cw.block_end(line=';') 3303 cw.nl() 3304 3305 # Multicast 3306 defines = [] 3307 for grp in family.mcgrps['list']: 3308 name = grp['name'] 3309 defines.append([c_upper(grp.get('c-define-name', f"{family.ident_name}-mcgrp-{name}")), 3310 f'{name}']) 3311 cw.nl() 3312 if defines: 3313 cw.writes_defines(defines) 3314 cw.nl() 3315 3316 cw.p(f'#endif /* {hdr_prot} */') 3317 3318 3319def _render_user_ntf_entry(ri, op): 3320 if not ri.family.is_classic(): 3321 ri.cw.block_start(line=f"[{op.enum_name}] = ") 3322 else: 3323 crud_op = ri.family.req_by_value[op.rsp_value] 3324 ri.cw.block_start(line=f"[{crud_op.enum_name}] = ") 3325 ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),") 3326 ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,") 3327 ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,") 3328 ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,") 3329 ri.cw.block_end(line=',') 3330 3331 3332def render_user_family(family, cw, prototype): 3333 symbol = f'const struct ynl_family ynl_{family.c_name}_family' 3334 if prototype: 3335 cw.p(f'extern {symbol};') 3336 return 3337 3338 if family.ntfs: 3339 cw.block_start(line=f"static const struct ynl_ntf_info {family.c_name}_ntf_info[] = ") 3340 for ntf_op_name, ntf_op in family.ntfs.items(): 3341 if 'notify' in ntf_op: 3342 op = family.ops[ntf_op['notify']] 3343 ri = RenderInfo(cw, family, "user", op, "notify") 3344 elif 'event' in ntf_op: 3345 ri = RenderInfo(cw, family, "user", ntf_op, "event") 3346 else: 3347 raise Exception('Invalid notification ' + ntf_op_name) 3348 _render_user_ntf_entry(ri, ntf_op) 3349 for op_name, op in family.ops.items(): 3350 if 'event' not in op: 3351 continue 3352 ri = RenderInfo(cw, family, "user", op, "event") 3353 _render_user_ntf_entry(ri, op) 3354 cw.block_end(line=";") 3355 cw.nl() 3356 3357 cw.block_start(f'{symbol} = ') 3358 cw.p(f'.name\t\t= "{family.c_name}",') 3359 if family.is_classic(): 3360 cw.p('.is_classic\t= true,') 3361 cw.p(f'.classic_id\t= {family.get("protonum")},') 3362 if family.is_classic(): 3363 if family.fixed_header: 3364 cw.p(f'.hdr_len\t= sizeof(struct {c_lower(family.fixed_header)}),') 3365 elif family.fixed_header: 3366 cw.p(f'.hdr_len\t= sizeof(struct genlmsghdr) + sizeof(struct {c_lower(family.fixed_header)}),') 3367 else: 3368 cw.p('.hdr_len\t= sizeof(struct genlmsghdr),') 3369 if family.ntfs: 3370 cw.p(f".ntf_info\t= {family.c_name}_ntf_info,") 3371 cw.p(f".ntf_info_size\t= YNL_ARRAY_SIZE({family.c_name}_ntf_info),") 3372 cw.block_end(line=';') 3373 3374 3375def family_contains_bitfield32(family): 3376 for _, attr_set in family.attr_sets.items(): 3377 if attr_set.subset_of: 3378 continue 3379 for _, attr in attr_set.items(): 3380 if attr.type == "bitfield32": 3381 return True 3382 return False 3383 3384 3385def find_kernel_root(full_path): 3386 sub_path = '' 3387 while True: 3388 sub_path = os.path.join(os.path.basename(full_path), sub_path) 3389 full_path = os.path.dirname(full_path) 3390 maintainers = os.path.join(full_path, "MAINTAINERS") 3391 if os.path.exists(maintainers): 3392 return full_path, sub_path[:-1] 3393 3394 3395def main(): 3396 parser = argparse.ArgumentParser(description='Netlink simple parsing generator') 3397 parser.add_argument('--mode', dest='mode', type=str, required=True, 3398 choices=('user', 'kernel', 'uapi')) 3399 parser.add_argument('--spec', dest='spec', type=str, required=True) 3400 parser.add_argument('--header', dest='header', action='store_true', default=None) 3401 parser.add_argument('--source', dest='header', action='store_false') 3402 parser.add_argument('--user-header', nargs='+', default=[]) 3403 parser.add_argument('--cmp-out', action='store_true', default=None, 3404 help='Do not overwrite the output file if the new output is identical to the old') 3405 parser.add_argument('--exclude-op', action='append', default=[]) 3406 parser.add_argument('-o', dest='out_file', type=str, default=None) 3407 parser.add_argument('--function-prefix', dest='fn_prefix', type=str) 3408 args = parser.parse_args() 3409 3410 if args.header is None: 3411 parser.error("--header or --source is required") 3412 3413 exclude_ops = [re.compile(expr) for expr in args.exclude_op] 3414 3415 try: 3416 parsed = Family(args.spec, exclude_ops, args.fn_prefix) 3417 if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)': 3418 print('Spec license:', parsed.license) 3419 print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)') 3420 os.sys.exit(1) 3421 except yaml.YAMLError as exc: 3422 print(exc) 3423 os.sys.exit(1) 3424 return 3425 3426 cw = CodeWriter(BaseNlLib(), args.out_file, overwrite=(not args.cmp_out)) 3427 3428 _, spec_kernel = find_kernel_root(args.spec) 3429 if args.mode == 'uapi' or args.header: 3430 cw.p(f'/* SPDX-License-Identifier: {parsed.license} */') 3431 else: 3432 cw.p(f'// SPDX-License-Identifier: {parsed.license}') 3433 cw.p("/* Do not edit directly, auto-generated from: */") 3434 cw.p(f"/*\t{spec_kernel} */") 3435 cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */") 3436 if args.exclude_op or args.user_header or args.fn_prefix: 3437 line = '' 3438 if args.user_header: 3439 line += ' --user-header '.join([''] + args.user_header) 3440 if args.exclude_op: 3441 line += ' --exclude-op '.join([''] + args.exclude_op) 3442 if args.fn_prefix: 3443 line += f' --function-prefix {args.fn_prefix}' 3444 cw.p(f'/* YNL-ARG{line} */') 3445 cw.p('/* To regenerate run: tools/net/ynl/ynl-regen.sh */') 3446 cw.nl() 3447 3448 if args.mode == 'uapi': 3449 render_uapi(parsed, cw) 3450 return 3451 3452 hdr_prot = f"_LINUX_{parsed.c_name.upper()}_GEN_H" 3453 if args.header: 3454 cw.p('#ifndef ' + hdr_prot) 3455 cw.p('#define ' + hdr_prot) 3456 cw.nl() 3457 3458 if args.out_file: 3459 hdr_file = os.path.basename(args.out_file[:-2]) + ".h" 3460 else: 3461 hdr_file = "generated_header_file.h" 3462 3463 if args.mode == 'kernel': 3464 cw.p('#include <net/netlink.h>') 3465 cw.p('#include <net/genetlink.h>') 3466 cw.nl() 3467 if not args.header: 3468 if args.out_file: 3469 cw.p(f'#include "{hdr_file}"') 3470 cw.nl() 3471 headers = ['uapi/' + parsed.uapi_header] 3472 headers += parsed.kernel_family.get('headers', []) 3473 else: 3474 cw.p('#include <stdlib.h>') 3475 cw.p('#include <string.h>') 3476 if args.header: 3477 cw.p('#include <linux/types.h>') 3478 if family_contains_bitfield32(parsed): 3479 cw.p('#include <linux/netlink.h>') 3480 else: 3481 cw.p(f'#include "{hdr_file}"') 3482 cw.p('#include "ynl.h"') 3483 headers = [] 3484 for definition in parsed['definitions'] + parsed['attribute-sets']: 3485 if 'header' in definition: 3486 headers.append(definition['header']) 3487 if args.mode == 'user': 3488 headers.append(parsed.uapi_header) 3489 seen_header = [] 3490 for one in headers: 3491 if one not in seen_header: 3492 cw.p(f"#include <{one}>") 3493 seen_header.append(one) 3494 cw.nl() 3495 3496 if args.mode == "user": 3497 if not args.header: 3498 cw.p("#include <linux/genetlink.h>") 3499 cw.nl() 3500 for one in args.user_header: 3501 cw.p(f'#include "{one}"') 3502 else: 3503 cw.p('struct ynl_sock;') 3504 cw.nl() 3505 render_user_family(parsed, cw, True) 3506 cw.nl() 3507 3508 if args.mode == "kernel": 3509 if args.header: 3510 for _, struct in sorted(parsed.pure_nested_structs.items()): 3511 if struct.request: 3512 cw.p('/* Common nested types */') 3513 break 3514 for attr_set, struct in sorted(parsed.pure_nested_structs.items()): 3515 if struct.request: 3516 print_req_policy_fwd(cw, struct) 3517 cw.nl() 3518 3519 if parsed.kernel_policy == 'global': 3520 cw.p(f"/* Global operation policy for {parsed.name} */") 3521 3522 struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy) 3523 print_req_policy_fwd(cw, struct) 3524 cw.nl() 3525 3526 if parsed.kernel_policy in {'per-op', 'split'}: 3527 for op_name, op in parsed.ops.items(): 3528 if 'do' in op and 'event' not in op: 3529 ri = RenderInfo(cw, parsed, args.mode, op, "do") 3530 print_req_policy_fwd(cw, ri.struct['request'], ri=ri) 3531 cw.nl() 3532 3533 print_kernel_op_table_hdr(parsed, cw) 3534 print_kernel_mcgrp_hdr(parsed, cw) 3535 print_kernel_family_struct_hdr(parsed, cw) 3536 else: 3537 print_kernel_policy_ranges(parsed, cw) 3538 print_kernel_policy_sparse_enum_validates(parsed, cw) 3539 3540 for _, struct in sorted(parsed.pure_nested_structs.items()): 3541 if struct.request: 3542 cw.p('/* Common nested types */') 3543 break 3544 for attr_set, struct in sorted(parsed.pure_nested_structs.items()): 3545 if struct.request: 3546 print_req_policy(cw, struct) 3547 cw.nl() 3548 3549 if parsed.kernel_policy == 'global': 3550 cw.p(f"/* Global operation policy for {parsed.name} */") 3551 3552 struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy) 3553 print_req_policy(cw, struct) 3554 cw.nl() 3555 3556 for op_name, op in parsed.ops.items(): 3557 if parsed.kernel_policy in {'per-op', 'split'}: 3558 for op_mode in ['do', 'dump']: 3559 if op_mode in op and 'request' in op[op_mode]: 3560 cw.p(f"/* {op.enum_name} - {op_mode} */") 3561 ri = RenderInfo(cw, parsed, args.mode, op, op_mode) 3562 print_req_policy(cw, ri.struct['request'], ri=ri) 3563 cw.nl() 3564 3565 print_kernel_op_table(parsed, cw) 3566 print_kernel_mcgrp_src(parsed, cw) 3567 print_kernel_family_struct_src(parsed, cw) 3568 3569 if args.mode == "user": 3570 if args.header: 3571 cw.p('/* Enums */') 3572 put_op_name_fwd(parsed, cw) 3573 3574 for name, const in parsed.consts.items(): 3575 if isinstance(const, EnumSet): 3576 put_enum_to_str_fwd(parsed, cw, const) 3577 cw.nl() 3578 3579 cw.p('/* Common nested types */') 3580 for attr_set, struct in parsed.pure_nested_structs.items(): 3581 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set) 3582 print_type_full(ri, struct) 3583 3584 for op_name, op in parsed.ops.items(): 3585 cw.p(f"/* ============== {op.enum_name} ============== */") 3586 3587 if 'do' in op and 'event' not in op: 3588 cw.p(f"/* {op.enum_name} - do */") 3589 ri = RenderInfo(cw, parsed, args.mode, op, "do") 3590 print_req_type(ri) 3591 print_req_type_helpers(ri) 3592 cw.nl() 3593 print_rsp_type(ri) 3594 print_rsp_type_helpers(ri) 3595 cw.nl() 3596 print_req_prototype(ri) 3597 cw.nl() 3598 3599 if 'dump' in op: 3600 cw.p(f"/* {op.enum_name} - dump */") 3601 ri = RenderInfo(cw, parsed, args.mode, op, 'dump') 3602 print_req_type(ri) 3603 print_req_type_helpers(ri) 3604 if not ri.type_consistent or ri.type_oneside: 3605 print_rsp_type(ri) 3606 print_wrapped_type(ri) 3607 print_dump_prototype(ri) 3608 cw.nl() 3609 3610 if op.has_ntf: 3611 cw.p(f"/* {op.enum_name} - notify */") 3612 ri = RenderInfo(cw, parsed, args.mode, op, 'notify') 3613 if not ri.type_consistent: 3614 raise Exception(f'Only notifications with consistent types supported ({op.name})') 3615 print_wrapped_type(ri) 3616 3617 for op_name, op in parsed.ntfs.items(): 3618 if 'event' in op: 3619 ri = RenderInfo(cw, parsed, args.mode, op, 'event') 3620 cw.p(f"/* {op.enum_name} - event */") 3621 print_rsp_type(ri) 3622 cw.nl() 3623 print_wrapped_type(ri) 3624 cw.nl() 3625 else: 3626 cw.p('/* Enums */') 3627 put_op_name(parsed, cw) 3628 3629 for name, const in parsed.consts.items(): 3630 if isinstance(const, EnumSet): 3631 put_enum_to_str(parsed, cw, const) 3632 cw.nl() 3633 3634 has_recursive_nests = False 3635 cw.p('/* Policies */') 3636 for struct in parsed.pure_nested_structs.values(): 3637 if struct.recursive: 3638 put_typol_fwd(cw, struct) 3639 has_recursive_nests = True 3640 if has_recursive_nests: 3641 cw.nl() 3642 for struct in parsed.pure_nested_structs.values(): 3643 put_typol(cw, struct) 3644 for name in parsed.root_sets: 3645 struct = Struct(parsed, name) 3646 put_typol(cw, struct) 3647 3648 cw.p('/* Common nested types */') 3649 if has_recursive_nests: 3650 for attr_set, struct in parsed.pure_nested_structs.items(): 3651 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set) 3652 free_rsp_nested_prototype(ri) 3653 if struct.request: 3654 put_req_nested_prototype(ri, struct) 3655 if struct.reply: 3656 parse_rsp_nested_prototype(ri, struct) 3657 cw.nl() 3658 for attr_set, struct in parsed.pure_nested_structs.items(): 3659 ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set) 3660 3661 free_rsp_nested(ri, struct) 3662 if struct.request: 3663 put_req_nested(ri, struct) 3664 if struct.reply: 3665 parse_rsp_nested(ri, struct) 3666 3667 for op_name, op in parsed.ops.items(): 3668 cw.p(f"/* ============== {op.enum_name} ============== */") 3669 if 'do' in op and 'event' not in op: 3670 cw.p(f"/* {op.enum_name} - do */") 3671 ri = RenderInfo(cw, parsed, args.mode, op, "do") 3672 print_req_free(ri) 3673 print_rsp_free(ri) 3674 parse_rsp_msg(ri) 3675 print_req(ri) 3676 cw.nl() 3677 3678 if 'dump' in op: 3679 cw.p(f"/* {op.enum_name} - dump */") 3680 ri = RenderInfo(cw, parsed, args.mode, op, "dump") 3681 if not ri.type_consistent or ri.type_oneside: 3682 parse_rsp_msg(ri, deref=True) 3683 print_req_free(ri) 3684 print_dump_type_free(ri) 3685 print_dump(ri) 3686 cw.nl() 3687 3688 if op.has_ntf: 3689 cw.p(f"/* {op.enum_name} - notify */") 3690 ri = RenderInfo(cw, parsed, args.mode, op, 'notify') 3691 if not ri.type_consistent: 3692 raise Exception(f'Only notifications with consistent types supported ({op.name})') 3693 print_ntf_type_free(ri) 3694 3695 for op_name, op in parsed.ntfs.items(): 3696 if 'event' in op: 3697 cw.p(f"/* {op.enum_name} - event */") 3698 3699 ri = RenderInfo(cw, parsed, args.mode, op, "do") 3700 parse_rsp_msg(ri) 3701 3702 ri = RenderInfo(cw, parsed, args.mode, op, "event") 3703 print_ntf_type_free(ri) 3704 cw.nl() 3705 render_user_family(parsed, cw, False) 3706 3707 if args.header: 3708 cw.p(f'#endif /* {hdr_prot} */') 3709 3710 3711if __name__ == "__main__": 3712 main() 3713