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