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