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