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