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