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