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