Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/net/ynl/pyynl/ynl_gen_c.py
29271 views
1
#!/usr/bin/env python3
2
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
3
4
import argparse
5
import filecmp
6
import pathlib
7
import os
8
import re
9
import shutil
10
import sys
11
import tempfile
12
import yaml
13
14
sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix())
15
from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
16
from lib import SpecSubMessage
17
18
19
def c_upper(name):
20
return name.upper().replace('-', '_')
21
22
23
def c_lower(name):
24
return name.lower().replace('-', '_')
25
26
27
def limit_to_number(name):
28
"""
29
Turn a string limit like u32-max or s64-min into its numerical value
30
"""
31
if name[0] == 'u' and name.endswith('-min'):
32
return 0
33
width = int(name[1:-4])
34
if name[0] == 's':
35
width -= 1
36
value = (1 << width) - 1
37
if name[0] == 's' and name.endswith('-min'):
38
value = -value - 1
39
return value
40
41
42
class BaseNlLib:
43
def get_family_id(self):
44
return 'ys->family_id'
45
46
47
class Type(SpecAttr):
48
def __init__(self, family, attr_set, attr, value):
49
super().__init__(family, attr_set, attr, value)
50
51
self.attr = attr
52
self.attr_set = attr_set
53
self.type = attr['type']
54
self.checks = attr.get('checks', {})
55
56
self.request = False
57
self.reply = False
58
59
self.is_selector = False
60
61
if 'len' in attr:
62
self.len = attr['len']
63
64
if 'nested-attributes' in attr:
65
nested = attr['nested-attributes']
66
elif 'sub-message' in attr:
67
nested = attr['sub-message']
68
else:
69
nested = None
70
71
if nested:
72
self.nested_attrs = nested
73
if self.nested_attrs == family.name:
74
self.nested_render_name = c_lower(f"{family.ident_name}")
75
else:
76
self.nested_render_name = c_lower(f"{family.ident_name}_{self.nested_attrs}")
77
78
if self.nested_attrs in self.family.consts:
79
self.nested_struct_type = 'struct ' + self.nested_render_name + '_'
80
else:
81
self.nested_struct_type = 'struct ' + self.nested_render_name
82
83
self.c_name = c_lower(self.name)
84
if self.c_name in _C_KW:
85
self.c_name += '_'
86
if self.c_name[0].isdigit():
87
self.c_name = '_' + self.c_name
88
89
# Added by resolve():
90
self.enum_name = None
91
delattr(self, "enum_name")
92
93
def _get_real_attr(self):
94
# if the attr is for a subset return the "real" attr (just one down, does not recurse)
95
return self.family.attr_sets[self.attr_set.subset_of][self.name]
96
97
def set_request(self):
98
self.request = True
99
if self.attr_set.subset_of:
100
self._get_real_attr().set_request()
101
102
def set_reply(self):
103
self.reply = True
104
if self.attr_set.subset_of:
105
self._get_real_attr().set_reply()
106
107
def get_limit(self, limit, default=None):
108
value = self.checks.get(limit, default)
109
if value is None:
110
return value
111
if isinstance(value, int):
112
return value
113
if value in self.family.consts:
114
return self.family.consts[value]["value"]
115
return limit_to_number(value)
116
117
def get_limit_str(self, limit, default=None, suffix=''):
118
value = self.checks.get(limit, default)
119
if value is None:
120
return ''
121
if isinstance(value, int):
122
return str(value) + suffix
123
if value in self.family.consts:
124
const = self.family.consts[value]
125
if const.get('header'):
126
return c_upper(value)
127
return c_upper(f"{self.family['name']}-{value}")
128
return c_upper(value)
129
130
def resolve(self):
131
if 'parent-sub-message' in self.attr:
132
enum_name = self.attr['parent-sub-message'].enum_name
133
elif 'name-prefix' in self.attr:
134
enum_name = f"{self.attr['name-prefix']}{self.name}"
135
else:
136
enum_name = f"{self.attr_set.name_prefix}{self.name}"
137
self.enum_name = c_upper(enum_name)
138
139
if self.attr_set.subset_of:
140
if self.checks != self._get_real_attr().checks:
141
raise Exception("Overriding checks not supported by codegen, yet")
142
143
def is_multi_val(self):
144
return None
145
146
def is_scalar(self):
147
return self.type in {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
148
149
def is_recursive(self):
150
return False
151
152
def is_recursive_for_op(self, ri):
153
return self.is_recursive() and not ri.op
154
155
def presence_type(self):
156
return 'present'
157
158
def presence_member(self, space, type_filter):
159
if self.presence_type() != type_filter:
160
return
161
162
if self.presence_type() == 'present':
163
pfx = '__' if space == 'user' else ''
164
return f"{pfx}u32 {self.c_name}:1;"
165
166
if self.presence_type() in {'len', 'count'}:
167
pfx = '__' if space == 'user' else ''
168
return f"{pfx}u32 {self.c_name};"
169
170
def _complex_member_type(self, ri):
171
return None
172
173
def free_needs_iter(self):
174
return False
175
176
def _free_lines(self, ri, var, ref):
177
if self.is_multi_val() or self.presence_type() in {'count', 'len'}:
178
return [f'free({var}->{ref}{self.c_name});']
179
return []
180
181
def free(self, ri, var, ref):
182
lines = self._free_lines(ri, var, ref)
183
for line in lines:
184
ri.cw.p(line)
185
186
def arg_member(self, ri):
187
member = self._complex_member_type(ri)
188
if member:
189
spc = ' ' if member[-1] != '*' else ''
190
arg = [member + spc + '*' + self.c_name]
191
if self.presence_type() == 'count':
192
arg += ['unsigned int n_' + self.c_name]
193
return arg
194
raise Exception(f"Struct member not implemented for class type {self.type}")
195
196
def struct_member(self, ri):
197
member = self._complex_member_type(ri)
198
if member:
199
ptr = '*' if self.is_multi_val() else ''
200
if self.is_recursive_for_op(ri):
201
ptr = '*'
202
spc = ' ' if member[-1] != '*' else ''
203
ri.cw.p(f"{member}{spc}{ptr}{self.c_name};")
204
return
205
members = self.arg_member(ri)
206
for one in members:
207
ri.cw.p(one + ';')
208
209
def _attr_policy(self, policy):
210
return '{ .type = ' + policy + ', }'
211
212
def attr_policy(self, cw):
213
policy = f'NLA_{c_upper(self.type)}'
214
if self.attr.get('byte-order') == 'big-endian':
215
if self.type in {'u16', 'u32'}:
216
policy = f'NLA_BE{self.type[1:]}'
217
218
spec = self._attr_policy(policy)
219
cw.p(f"\t[{self.enum_name}] = {spec},")
220
221
def _attr_typol(self):
222
raise Exception(f"Type policy not implemented for class type {self.type}")
223
224
def attr_typol(self, cw):
225
typol = self._attr_typol()
226
cw.p(f'[{self.enum_name}] = {"{"} .name = "{self.name}", {typol}{"}"},')
227
228
def _attr_put_line(self, ri, var, line):
229
presence = self.presence_type()
230
if presence in {'present', 'len'}:
231
ri.cw.p(f"if ({var}->_{presence}.{self.c_name})")
232
ri.cw.p(f"{line};")
233
234
def _attr_put_simple(self, ri, var, put_type):
235
line = f"ynl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name})"
236
self._attr_put_line(ri, var, line)
237
238
def attr_put(self, ri, var):
239
raise Exception(f"Put not implemented for class type {self.type}")
240
241
def _attr_get(self, ri, var):
242
raise Exception(f"Attr get not implemented for class type {self.type}")
243
244
def attr_get(self, ri, var, first):
245
lines, init_lines, _ = self._attr_get(ri, var)
246
if type(lines) is str:
247
lines = [lines]
248
if type(init_lines) is str:
249
init_lines = [init_lines]
250
251
kw = 'if' if first else 'else if'
252
ri.cw.block_start(line=f"{kw} (type == {self.enum_name})")
253
254
if not self.is_multi_val():
255
ri.cw.p("if (ynl_attr_validate(yarg, attr))")
256
ri.cw.p("return YNL_PARSE_CB_ERROR;")
257
if self.presence_type() == 'present':
258
ri.cw.p(f"{var}->_present.{self.c_name} = 1;")
259
260
if init_lines:
261
ri.cw.nl()
262
for line in init_lines:
263
ri.cw.p(line)
264
265
for line in lines:
266
ri.cw.p(line)
267
ri.cw.block_end()
268
return True
269
270
def _setter_lines(self, ri, member, presence):
271
raise Exception(f"Setter not implemented for class type {self.type}")
272
273
def setter(self, ri, space, direction, deref=False, ref=None, var="req"):
274
ref = (ref if ref else []) + [self.c_name]
275
member = f"{var}->{'.'.join(ref)}"
276
277
local_vars = []
278
if self.free_needs_iter():
279
local_vars += ['unsigned int i;']
280
281
code = []
282
presence = ''
283
for i in range(0, len(ref)):
284
presence = f"{var}->{'.'.join(ref[:i] + [''])}_present.{ref[i]}"
285
# Every layer below last is a nest, so we know it uses bit presence
286
# last layer is "self" and may be a complex type
287
if i == len(ref) - 1 and self.presence_type() != 'present':
288
presence = f"{var}->{'.'.join(ref[:i] + [''])}_{self.presence_type()}.{ref[i]}"
289
continue
290
code.append(presence + ' = 1;')
291
ref_path = '.'.join(ref[:-1])
292
if ref_path:
293
ref_path += '.'
294
code += self._free_lines(ri, var, ref_path)
295
code += self._setter_lines(ri, member, presence)
296
297
func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
298
free = bool([x for x in code if 'free(' in x])
299
alloc = bool([x for x in code if 'alloc(' in x])
300
if free and not alloc:
301
func_name = '__' + func_name
302
ri.cw.write_func('static inline void', func_name, local_vars=local_vars,
303
body=code,
304
args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
305
306
307
class TypeUnused(Type):
308
def presence_type(self):
309
return ''
310
311
def arg_member(self, ri):
312
return []
313
314
def _attr_get(self, ri, var):
315
return ['return YNL_PARSE_CB_ERROR;'], None, None
316
317
def _attr_typol(self):
318
return '.type = YNL_PT_REJECT, '
319
320
def attr_policy(self, cw):
321
pass
322
323
def attr_put(self, ri, var):
324
pass
325
326
def attr_get(self, ri, var, first):
327
pass
328
329
def setter(self, ri, space, direction, deref=False, ref=None, var=None):
330
pass
331
332
333
class TypePad(Type):
334
def presence_type(self):
335
return ''
336
337
def arg_member(self, ri):
338
return []
339
340
def _attr_typol(self):
341
return '.type = YNL_PT_IGNORE, '
342
343
def attr_put(self, ri, var):
344
pass
345
346
def attr_get(self, ri, var, first):
347
pass
348
349
def attr_policy(self, cw):
350
pass
351
352
def setter(self, ri, space, direction, deref=False, ref=None, var=None):
353
pass
354
355
356
class TypeScalar(Type):
357
def __init__(self, family, attr_set, attr, value):
358
super().__init__(family, attr_set, attr, value)
359
360
self.byte_order_comment = ''
361
if 'byte-order' in attr:
362
self.byte_order_comment = f" /* {attr['byte-order']} */"
363
364
# Classic families have some funny enums, don't bother
365
# computing checks, since we only need them for kernel policies
366
if not family.is_classic():
367
self._init_checks()
368
369
# Added by resolve():
370
self.is_bitfield = None
371
delattr(self, "is_bitfield")
372
self.type_name = None
373
delattr(self, "type_name")
374
375
def resolve(self):
376
self.resolve_up(super())
377
378
if 'enum-as-flags' in self.attr and self.attr['enum-as-flags']:
379
self.is_bitfield = True
380
elif 'enum' in self.attr:
381
self.is_bitfield = self.family.consts[self.attr['enum']]['type'] == 'flags'
382
else:
383
self.is_bitfield = False
384
385
if not self.is_bitfield and 'enum' in self.attr:
386
self.type_name = self.family.consts[self.attr['enum']].user_type
387
elif self.is_auto_scalar:
388
self.type_name = '__' + self.type[0] + '64'
389
else:
390
self.type_name = '__' + self.type
391
392
def _init_checks(self):
393
if 'enum' in self.attr:
394
enum = self.family.consts[self.attr['enum']]
395
low, high = enum.value_range()
396
if low is None and high is None:
397
self.checks['sparse'] = True
398
else:
399
if 'min' not in self.checks:
400
if low != 0 or self.type[0] == 's':
401
self.checks['min'] = low
402
if 'max' not in self.checks:
403
self.checks['max'] = high
404
405
if 'min' in self.checks and 'max' in self.checks:
406
if self.get_limit('min') > self.get_limit('max'):
407
raise Exception(f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}')
408
self.checks['range'] = True
409
410
low = min(self.get_limit('min', 0), self.get_limit('max', 0))
411
high = max(self.get_limit('min', 0), self.get_limit('max', 0))
412
if low < 0 and self.type[0] == 'u':
413
raise Exception(f'Invalid limit for "{self.name}" negative limit for unsigned type')
414
if low < -32768 or high > 32767:
415
self.checks['full-range'] = True
416
417
def _attr_policy(self, policy):
418
if 'flags-mask' in self.checks or self.is_bitfield:
419
if self.is_bitfield:
420
enum = self.family.consts[self.attr['enum']]
421
mask = enum.get_mask(as_flags=True)
422
else:
423
flags = self.family.consts[self.checks['flags-mask']]
424
flag_cnt = len(flags['entries'])
425
mask = (1 << flag_cnt) - 1
426
return f"NLA_POLICY_MASK({policy}, 0x{mask:x})"
427
elif 'full-range' in self.checks:
428
return f"NLA_POLICY_FULL_RANGE({policy}, &{c_lower(self.enum_name)}_range)"
429
elif 'range' in self.checks:
430
return f"NLA_POLICY_RANGE({policy}, {self.get_limit_str('min')}, {self.get_limit_str('max')})"
431
elif 'min' in self.checks:
432
return f"NLA_POLICY_MIN({policy}, {self.get_limit_str('min')})"
433
elif 'max' in self.checks:
434
return f"NLA_POLICY_MAX({policy}, {self.get_limit_str('max')})"
435
elif 'sparse' in self.checks:
436
return f"NLA_POLICY_VALIDATE_FN({policy}, &{c_lower(self.enum_name)}_validate)"
437
return super()._attr_policy(policy)
438
439
def _attr_typol(self):
440
return f'.type = YNL_PT_U{c_upper(self.type[1:])}, '
441
442
def arg_member(self, ri):
443
return [f'{self.type_name} {self.c_name}{self.byte_order_comment}']
444
445
def attr_put(self, ri, var):
446
self._attr_put_simple(ri, var, self.type)
447
448
def _attr_get(self, ri, var):
449
return f"{var}->{self.c_name} = ynl_attr_get_{self.type}(attr);", None, None
450
451
def _setter_lines(self, ri, member, presence):
452
return [f"{member} = {self.c_name};"]
453
454
455
class TypeFlag(Type):
456
def arg_member(self, ri):
457
return []
458
459
def _attr_typol(self):
460
return '.type = YNL_PT_FLAG, '
461
462
def attr_put(self, ri, var):
463
self._attr_put_line(ri, var, f"ynl_attr_put(nlh, {self.enum_name}, NULL, 0)")
464
465
def _attr_get(self, ri, var):
466
return [], None, None
467
468
def _setter_lines(self, ri, member, presence):
469
return []
470
471
472
class TypeString(Type):
473
def arg_member(self, ri):
474
return [f"const char *{self.c_name}"]
475
476
def presence_type(self):
477
return 'len'
478
479
def struct_member(self, ri):
480
ri.cw.p(f"char *{self.c_name};")
481
482
def _attr_typol(self):
483
typol = '.type = YNL_PT_NUL_STR, '
484
if self.is_selector:
485
typol += '.is_selector = 1, '
486
return typol
487
488
def _attr_policy(self, policy):
489
if 'exact-len' in self.checks:
490
mem = 'NLA_POLICY_EXACT_LEN(' + self.get_limit_str('exact-len') + ')'
491
else:
492
mem = '{ .type = ' + policy
493
if 'max-len' in self.checks:
494
mem += ', .len = ' + self.get_limit_str('max-len')
495
mem += ', }'
496
return mem
497
498
def attr_policy(self, cw):
499
if self.checks.get('unterminated-ok', False):
500
policy = 'NLA_STRING'
501
else:
502
policy = 'NLA_NUL_STRING'
503
504
spec = self._attr_policy(policy)
505
cw.p(f"\t[{self.enum_name}] = {spec},")
506
507
def attr_put(self, ri, var):
508
self._attr_put_simple(ri, var, 'str')
509
510
def _attr_get(self, ri, var):
511
len_mem = var + '->_len.' + self.c_name
512
return [f"{len_mem} = len;",
513
f"{var}->{self.c_name} = malloc(len + 1);",
514
f"memcpy({var}->{self.c_name}, ynl_attr_get_str(attr), len);",
515
f"{var}->{self.c_name}[len] = 0;"], \
516
['len = strnlen(ynl_attr_get_str(attr), ynl_attr_data_len(attr));'], \
517
['unsigned int len;']
518
519
def _setter_lines(self, ri, member, presence):
520
return [f"{presence} = strlen({self.c_name});",
521
f"{member} = malloc({presence} + 1);",
522
f'memcpy({member}, {self.c_name}, {presence});',
523
f'{member}[{presence}] = 0;']
524
525
526
class TypeBinary(Type):
527
def arg_member(self, ri):
528
return [f"const void *{self.c_name}", 'size_t len']
529
530
def presence_type(self):
531
return 'len'
532
533
def struct_member(self, ri):
534
ri.cw.p(f"void *{self.c_name};")
535
536
def _attr_typol(self):
537
return '.type = YNL_PT_BINARY,'
538
539
def _attr_policy(self, policy):
540
if len(self.checks) == 0:
541
pass
542
elif len(self.checks) == 1:
543
check_name = list(self.checks)[0]
544
if check_name not in {'exact-len', 'min-len', 'max-len'}:
545
raise Exception('Unsupported check for binary type: ' + check_name)
546
else:
547
raise Exception('More than one check for binary type not implemented, yet')
548
549
if len(self.checks) == 0:
550
mem = '{ .type = NLA_BINARY, }'
551
elif 'exact-len' in self.checks:
552
mem = 'NLA_POLICY_EXACT_LEN(' + self.get_limit_str('exact-len') + ')'
553
elif 'min-len' in self.checks:
554
mem = 'NLA_POLICY_MIN_LEN(' + self.get_limit_str('min-len') + ')'
555
elif 'max-len' in self.checks:
556
mem = 'NLA_POLICY_MAX_LEN(' + self.get_limit_str('max-len') + ')'
557
558
return mem
559
560
def attr_put(self, ri, var):
561
self._attr_put_line(ri, var, f"ynl_attr_put(nlh, {self.enum_name}, " +
562
f"{var}->{self.c_name}, {var}->_len.{self.c_name})")
563
564
def _attr_get(self, ri, var):
565
len_mem = var + '->_len.' + self.c_name
566
return [f"{len_mem} = len;",
567
f"{var}->{self.c_name} = malloc(len);",
568
f"memcpy({var}->{self.c_name}, ynl_attr_data(attr), len);"], \
569
['len = ynl_attr_data_len(attr);'], \
570
['unsigned int len;']
571
572
def _setter_lines(self, ri, member, presence):
573
return [f"{presence} = len;",
574
f"{member} = malloc({presence});",
575
f'memcpy({member}, {self.c_name}, {presence});']
576
577
578
class TypeBinaryStruct(TypeBinary):
579
def struct_member(self, ri):
580
ri.cw.p(f'struct {c_lower(self.get("struct"))} *{self.c_name};')
581
582
def _attr_get(self, ri, var):
583
struct_sz = 'sizeof(struct ' + c_lower(self.get("struct")) + ')'
584
len_mem = var + '->_' + self.presence_type() + '.' + self.c_name
585
return [f"{len_mem} = len;",
586
f"if (len < {struct_sz})",
587
f"{var}->{self.c_name} = calloc(1, {struct_sz});",
588
"else",
589
f"{var}->{self.c_name} = malloc(len);",
590
f"memcpy({var}->{self.c_name}, ynl_attr_data(attr), len);"], \
591
['len = ynl_attr_data_len(attr);'], \
592
['unsigned int len;']
593
594
595
class TypeBinaryScalarArray(TypeBinary):
596
def arg_member(self, ri):
597
return [f'__{self.get("sub-type")} *{self.c_name}', 'size_t count']
598
599
def presence_type(self):
600
return 'count'
601
602
def struct_member(self, ri):
603
ri.cw.p(f'__{self.get("sub-type")} *{self.c_name};')
604
605
def attr_put(self, ri, var):
606
presence = self.presence_type()
607
ri.cw.block_start(line=f"if ({var}->_{presence}.{self.c_name})")
608
ri.cw.p(f"i = {var}->_{presence}.{self.c_name} * sizeof(__{self.get('sub-type')});")
609
ri.cw.p(f"ynl_attr_put(nlh, {self.enum_name}, " +
610
f"{var}->{self.c_name}, i);")
611
ri.cw.block_end()
612
613
def _attr_get(self, ri, var):
614
len_mem = var + '->_count.' + self.c_name
615
return [f"{len_mem} = len / sizeof(__{self.get('sub-type')});",
616
f"len = {len_mem} * sizeof(__{self.get('sub-type')});",
617
f"{var}->{self.c_name} = malloc(len);",
618
f"memcpy({var}->{self.c_name}, ynl_attr_data(attr), len);"], \
619
['len = ynl_attr_data_len(attr);'], \
620
['unsigned int len;']
621
622
def _setter_lines(self, ri, member, presence):
623
return [f"{presence} = count;",
624
f"count *= sizeof(__{self.get('sub-type')});",
625
f"{member} = malloc(count);",
626
f'memcpy({member}, {self.c_name}, count);']
627
628
629
class TypeBitfield32(Type):
630
def _complex_member_type(self, ri):
631
return "struct nla_bitfield32"
632
633
def _attr_typol(self):
634
return '.type = YNL_PT_BITFIELD32, '
635
636
def _attr_policy(self, policy):
637
if 'enum' not in self.attr:
638
raise Exception('Enum required for bitfield32 attr')
639
enum = self.family.consts[self.attr['enum']]
640
mask = enum.get_mask(as_flags=True)
641
return f"NLA_POLICY_BITFIELD32({mask})"
642
643
def attr_put(self, ri, var):
644
line = f"ynl_attr_put(nlh, {self.enum_name}, &{var}->{self.c_name}, sizeof(struct nla_bitfield32))"
645
self._attr_put_line(ri, var, line)
646
647
def _attr_get(self, ri, var):
648
return f"memcpy(&{var}->{self.c_name}, ynl_attr_data(attr), sizeof(struct nla_bitfield32));", None, None
649
650
def _setter_lines(self, ri, member, presence):
651
return [f"memcpy(&{member}, {self.c_name}, sizeof(struct nla_bitfield32));"]
652
653
654
class TypeNest(Type):
655
def is_recursive(self):
656
return self.family.pure_nested_structs[self.nested_attrs].recursive
657
658
def _complex_member_type(self, ri):
659
return self.nested_struct_type
660
661
def _free_lines(self, ri, var, ref):
662
lines = []
663
at = '&'
664
if self.is_recursive_for_op(ri):
665
at = ''
666
lines += [f'if ({var}->{ref}{self.c_name})']
667
lines += [f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});']
668
return lines
669
670
def _attr_typol(self):
671
return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
672
673
def _attr_policy(self, policy):
674
return 'NLA_POLICY_NESTED(' + self.nested_render_name + '_nl_policy)'
675
676
def attr_put(self, ri, var):
677
at = '' if self.is_recursive_for_op(ri) else '&'
678
self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
679
f"{self.enum_name}, {at}{var}->{self.c_name})")
680
681
def _attr_get(self, ri, var):
682
pns = self.family.pure_nested_structs[self.nested_attrs]
683
args = ["&parg", "attr"]
684
for sel in pns.external_selectors():
685
args.append(f'{var}->{sel.name}')
686
get_lines = [f"if ({self.nested_render_name}_parse({', '.join(args)}))",
687
"return YNL_PARSE_CB_ERROR;"]
688
init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
689
f"parg.data = &{var}->{self.c_name};"]
690
return get_lines, init_lines, None
691
692
def setter(self, ri, space, direction, deref=False, ref=None, var="req"):
693
ref = (ref if ref else []) + [self.c_name]
694
695
for _, attr in ri.family.pure_nested_structs[self.nested_attrs].member_list():
696
if attr.is_recursive():
697
continue
698
attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref,
699
var=var)
700
701
702
class TypeMultiAttr(Type):
703
def __init__(self, family, attr_set, attr, value, base_type):
704
super().__init__(family, attr_set, attr, value)
705
706
self.base_type = base_type
707
708
def is_multi_val(self):
709
return True
710
711
def presence_type(self):
712
return 'count'
713
714
def _complex_member_type(self, ri):
715
if 'type' not in self.attr or self.attr['type'] == 'nest':
716
return self.nested_struct_type
717
elif self.attr['type'] == 'binary' and 'struct' in self.attr:
718
return None # use arg_member()
719
elif self.attr['type'] == 'string':
720
return 'struct ynl_string *'
721
elif self.attr['type'] in scalars:
722
scalar_pfx = '__' if ri.ku_space == 'user' else ''
723
if self.is_auto_scalar:
724
name = self.type[0] + '64'
725
else:
726
name = self.attr['type']
727
return scalar_pfx + name
728
else:
729
raise Exception(f"Sub-type {self.attr['type']} not supported yet")
730
731
def arg_member(self, ri):
732
if self.type == 'binary' and 'struct' in self.attr:
733
return [f'struct {c_lower(self.attr["struct"])} *{self.c_name}',
734
f'unsigned int n_{self.c_name}']
735
return super().arg_member(ri)
736
737
def free_needs_iter(self):
738
return self.attr['type'] in {'nest', 'string'}
739
740
def _free_lines(self, ri, var, ref):
741
lines = []
742
if self.attr['type'] in scalars:
743
lines += [f"free({var}->{ref}{self.c_name});"]
744
elif self.attr['type'] == 'binary':
745
lines += [f"free({var}->{ref}{self.c_name});"]
746
elif self.attr['type'] == 'string':
747
lines += [
748
f"for (i = 0; i < {var}->{ref}_count.{self.c_name}; i++)",
749
f"free({var}->{ref}{self.c_name}[i]);",
750
f"free({var}->{ref}{self.c_name});",
751
]
752
elif 'type' not in self.attr or self.attr['type'] == 'nest':
753
lines += [
754
f"for (i = 0; i < {var}->{ref}_count.{self.c_name}; i++)",
755
f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);',
756
f"free({var}->{ref}{self.c_name});",
757
]
758
else:
759
raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
760
return lines
761
762
def _attr_policy(self, policy):
763
return self.base_type._attr_policy(policy)
764
765
def _attr_typol(self):
766
return self.base_type._attr_typol()
767
768
def _attr_get(self, ri, var):
769
return f'n_{self.c_name}++;', None, None
770
771
def attr_put(self, ri, var):
772
if self.attr['type'] in scalars:
773
put_type = self.type
774
ri.cw.p(f"for (i = 0; i < {var}->_count.{self.c_name}; i++)")
775
ri.cw.p(f"ynl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name}[i]);")
776
elif self.attr['type'] == 'binary' and 'struct' in self.attr:
777
ri.cw.p(f"for (i = 0; i < {var}->_count.{self.c_name}; i++)")
778
ri.cw.p(f"ynl_attr_put(nlh, {self.enum_name}, &{var}->{self.c_name}[i], sizeof(struct {c_lower(self.attr['struct'])}));")
779
elif self.attr['type'] == 'string':
780
ri.cw.p(f"for (i = 0; i < {var}->_count.{self.c_name}; i++)")
781
ri.cw.p(f"ynl_attr_put_str(nlh, {self.enum_name}, {var}->{self.c_name}[i]->str);")
782
elif 'type' not in self.attr or self.attr['type'] == 'nest':
783
ri.cw.p(f"for (i = 0; i < {var}->_count.{self.c_name}; i++)")
784
self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
785
f"{self.enum_name}, &{var}->{self.c_name}[i])")
786
else:
787
raise Exception(f"Put of MultiAttr sub-type {self.attr['type']} not supported yet")
788
789
def _setter_lines(self, ri, member, presence):
790
return [f"{member} = {self.c_name};",
791
f"{presence} = n_{self.c_name};"]
792
793
794
class TypeIndexedArray(Type):
795
def is_multi_val(self):
796
return True
797
798
def presence_type(self):
799
return 'count'
800
801
def _complex_member_type(self, ri):
802
if 'sub-type' not in self.attr or self.attr['sub-type'] == 'nest':
803
return self.nested_struct_type
804
elif self.attr['sub-type'] in scalars:
805
scalar_pfx = '__' if ri.ku_space == 'user' else ''
806
return scalar_pfx + self.attr['sub-type']
807
elif self.attr['sub-type'] == 'binary' and 'exact-len' in self.checks:
808
return None # use arg_member()
809
else:
810
raise Exception(f"Sub-type {self.attr['sub-type']} not supported yet")
811
812
def arg_member(self, ri):
813
if self.sub_type == 'binary' and 'exact-len' in self.checks:
814
return [f'unsigned char (*{self.c_name})[{self.checks["exact-len"]}]',
815
f'unsigned int n_{self.c_name}']
816
return super().arg_member(ri)
817
818
def _attr_policy(self, policy):
819
if self.attr['sub-type'] == 'nest':
820
return f'NLA_POLICY_NESTED_ARRAY({self.nested_render_name}_nl_policy)'
821
return super()._attr_policy(policy)
822
823
def _attr_typol(self):
824
if self.attr['sub-type'] in scalars:
825
return f'.type = YNL_PT_U{c_upper(self.sub_type[1:])}, '
826
elif self.attr['sub-type'] == 'binary' and 'exact-len' in self.checks:
827
return f'.type = YNL_PT_BINARY, .len = {self.checks["exact-len"]}, '
828
elif self.attr['sub-type'] == 'nest':
829
return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
830
else:
831
raise Exception(f"Typol for IndexedArray sub-type {self.attr['sub-type']} not supported, yet")
832
833
def _attr_get(self, ri, var):
834
local_vars = ['const struct nlattr *attr2;']
835
get_lines = [f'attr_{self.c_name} = attr;',
836
'ynl_attr_for_each_nested(attr2, attr) {',
837
'\tif (__ynl_attr_validate(yarg, attr2, type))',
838
'\t\treturn YNL_PARSE_CB_ERROR;',
839
f'\tn_{self.c_name}++;',
840
'}']
841
return get_lines, None, local_vars
842
843
def attr_put(self, ri, var):
844
ri.cw.p(f'array = ynl_attr_nest_start(nlh, {self.enum_name});')
845
if self.sub_type in scalars:
846
put_type = self.sub_type
847
ri.cw.block_start(line=f'for (i = 0; i < {var}->_count.{self.c_name}; i++)')
848
ri.cw.p(f"ynl_attr_put_{put_type}(nlh, i, {var}->{self.c_name}[i]);")
849
ri.cw.block_end()
850
elif self.sub_type == 'binary' and 'exact-len' in self.checks:
851
ri.cw.p(f'for (i = 0; i < {var}->_count.{self.c_name}; i++)')
852
ri.cw.p(f"ynl_attr_put(nlh, i, {var}->{self.c_name}[i], {self.checks['exact-len']});")
853
elif self.sub_type == 'nest':
854
ri.cw.p(f'for (i = 0; i < {var}->_count.{self.c_name}; i++)')
855
ri.cw.p(f"{self.nested_render_name}_put(nlh, i, &{var}->{self.c_name}[i]);")
856
else:
857
raise Exception(f"Put for IndexedArray sub-type {self.attr['sub-type']} not supported, yet")
858
ri.cw.p('ynl_attr_nest_end(nlh, array);')
859
860
def _setter_lines(self, ri, member, presence):
861
return [f"{member} = {self.c_name};",
862
f"{presence} = n_{self.c_name};"]
863
864
865
class TypeNestTypeValue(Type):
866
def _complex_member_type(self, ri):
867
return self.nested_struct_type
868
869
def _attr_typol(self):
870
return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
871
872
def _attr_get(self, ri, var):
873
prev = 'attr'
874
tv_args = ''
875
get_lines = []
876
local_vars = []
877
init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
878
f"parg.data = &{var}->{self.c_name};"]
879
if 'type-value' in self.attr:
880
tv_names = [c_lower(x) for x in self.attr["type-value"]]
881
local_vars += [f'const struct nlattr *attr_{", *attr_".join(tv_names)};']
882
local_vars += [f'__u32 {", ".join(tv_names)};']
883
for level in self.attr["type-value"]:
884
level = c_lower(level)
885
get_lines += [f'attr_{level} = ynl_attr_data({prev});']
886
get_lines += [f'{level} = ynl_attr_type(attr_{level});']
887
prev = 'attr_' + level
888
889
tv_args = f", {', '.join(tv_names)}"
890
891
get_lines += [f"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"]
892
return get_lines, init_lines, local_vars
893
894
895
class TypeSubMessage(TypeNest):
896
def __init__(self, family, attr_set, attr, value):
897
super().__init__(family, attr_set, attr, value)
898
899
self.selector = Selector(attr, attr_set)
900
901
def _attr_typol(self):
902
typol = f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
903
typol += '.is_submsg = 1, '
904
# Reverse-parsing of the policy (ynl_err_walk() in ynl.c) does not
905
# support external selectors. No family uses sub-messages with external
906
# selector for requests so this is fine for now.
907
if not self.selector.is_external():
908
typol += f'.selector_type = {self.attr_set[self["selector"]].value} '
909
return typol
910
911
def _attr_get(self, ri, var):
912
sel = c_lower(self['selector'])
913
if self.selector.is_external():
914
sel_var = f"_sel_{sel}"
915
else:
916
sel_var = f"{var}->{sel}"
917
get_lines = [f'if (!{sel_var})',
918
'return ynl_submsg_failed(yarg, "%s", "%s");' %
919
(self.name, self['selector']),
920
f"if ({self.nested_render_name}_parse(&parg, {sel_var}, attr))",
921
"return YNL_PARSE_CB_ERROR;"]
922
init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
923
f"parg.data = &{var}->{self.c_name};"]
924
return get_lines, init_lines, None
925
926
927
class Selector:
928
def __init__(self, msg_attr, attr_set):
929
self.name = msg_attr["selector"]
930
931
if self.name in attr_set:
932
self.attr = attr_set[self.name]
933
self.attr.is_selector = True
934
self._external = False
935
else:
936
# The selector will need to get passed down thru the structs
937
self.attr = None
938
self._external = True
939
940
def set_attr(self, attr):
941
self.attr = attr
942
943
def is_external(self):
944
return self._external
945
946
947
class Struct:
948
def __init__(self, family, space_name, type_list=None, fixed_header=None,
949
inherited=None, submsg=None):
950
self.family = family
951
self.space_name = space_name
952
self.attr_set = family.attr_sets[space_name]
953
# Use list to catch comparisons with empty sets
954
self._inherited = inherited if inherited is not None else []
955
self.inherited = []
956
self.fixed_header = None
957
if fixed_header:
958
self.fixed_header = 'struct ' + c_lower(fixed_header)
959
self.submsg = submsg
960
961
self.nested = type_list is None
962
if family.name == c_lower(space_name):
963
self.render_name = c_lower(family.ident_name)
964
else:
965
self.render_name = c_lower(family.ident_name + '-' + space_name)
966
self.struct_name = 'struct ' + self.render_name
967
if self.nested and space_name in family.consts:
968
self.struct_name += '_'
969
self.ptr_name = self.struct_name + ' *'
970
# All attr sets this one contains, directly or multiple levels down
971
self.child_nests = set()
972
973
self.request = False
974
self.reply = False
975
self.recursive = False
976
self.in_multi_val = False # used by a MultiAttr or and legacy arrays
977
978
self.attr_list = []
979
self.attrs = dict()
980
if type_list is not None:
981
for t in type_list:
982
self.attr_list.append((t, self.attr_set[t]),)
983
else:
984
for t in self.attr_set:
985
self.attr_list.append((t, self.attr_set[t]),)
986
987
max_val = 0
988
self.attr_max_val = None
989
for name, attr in self.attr_list:
990
if attr.value >= max_val:
991
max_val = attr.value
992
self.attr_max_val = attr
993
self.attrs[name] = attr
994
995
def __iter__(self):
996
yield from self.attrs
997
998
def __getitem__(self, key):
999
return self.attrs[key]
1000
1001
def member_list(self):
1002
return self.attr_list
1003
1004
def set_inherited(self, new_inherited):
1005
if self._inherited != new_inherited:
1006
raise Exception("Inheriting different members not supported")
1007
self.inherited = [c_lower(x) for x in sorted(self._inherited)]
1008
1009
def external_selectors(self):
1010
sels = []
1011
for name, attr in self.attr_list:
1012
if isinstance(attr, TypeSubMessage) and attr.selector.is_external():
1013
sels.append(attr.selector)
1014
return sels
1015
1016
def free_needs_iter(self):
1017
for _, attr in self.attr_list:
1018
if attr.free_needs_iter():
1019
return True
1020
return False
1021
1022
1023
class EnumEntry(SpecEnumEntry):
1024
def __init__(self, enum_set, yaml, prev, value_start):
1025
super().__init__(enum_set, yaml, prev, value_start)
1026
1027
if prev:
1028
self.value_change = (self.value != prev.value + 1)
1029
else:
1030
self.value_change = (self.value != 0)
1031
self.value_change = self.value_change or self.enum_set['type'] == 'flags'
1032
1033
# Added by resolve:
1034
self.c_name = None
1035
delattr(self, "c_name")
1036
1037
def resolve(self):
1038
self.resolve_up(super())
1039
1040
self.c_name = c_upper(self.enum_set.value_pfx + self.name)
1041
1042
1043
class EnumSet(SpecEnumSet):
1044
def __init__(self, family, yaml):
1045
self.render_name = c_lower(family.ident_name + '-' + yaml['name'])
1046
1047
if 'enum-name' in yaml:
1048
if yaml['enum-name']:
1049
self.enum_name = 'enum ' + c_lower(yaml['enum-name'])
1050
self.user_type = self.enum_name
1051
else:
1052
self.enum_name = None
1053
else:
1054
self.enum_name = 'enum ' + self.render_name
1055
1056
if self.enum_name:
1057
self.user_type = self.enum_name
1058
else:
1059
self.user_type = 'int'
1060
1061
self.value_pfx = yaml.get('name-prefix', f"{family.ident_name}-{yaml['name']}-")
1062
self.header = yaml.get('header', None)
1063
self.enum_cnt_name = yaml.get('enum-cnt-name', None)
1064
1065
super().__init__(family, yaml)
1066
1067
def new_entry(self, entry, prev_entry, value_start):
1068
return EnumEntry(self, entry, prev_entry, value_start)
1069
1070
def value_range(self):
1071
low = min([x.value for x in self.entries.values()])
1072
high = max([x.value for x in self.entries.values()])
1073
1074
if high - low + 1 != len(self.entries):
1075
return None, None
1076
1077
return low, high
1078
1079
1080
class AttrSet(SpecAttrSet):
1081
def __init__(self, family, yaml):
1082
super().__init__(family, yaml)
1083
1084
if self.subset_of is None:
1085
if 'name-prefix' in yaml:
1086
pfx = yaml['name-prefix']
1087
elif self.name == family.name:
1088
pfx = family.ident_name + '-a-'
1089
else:
1090
pfx = f"{family.ident_name}-a-{self.name}-"
1091
self.name_prefix = c_upper(pfx)
1092
self.max_name = c_upper(self.yaml.get('attr-max-name', f"{self.name_prefix}max"))
1093
self.cnt_name = c_upper(self.yaml.get('attr-cnt-name', f"__{self.name_prefix}max"))
1094
else:
1095
self.name_prefix = family.attr_sets[self.subset_of].name_prefix
1096
self.max_name = family.attr_sets[self.subset_of].max_name
1097
self.cnt_name = family.attr_sets[self.subset_of].cnt_name
1098
1099
# Added by resolve:
1100
self.c_name = None
1101
delattr(self, "c_name")
1102
1103
def resolve(self):
1104
self.c_name = c_lower(self.name)
1105
if self.c_name in _C_KW:
1106
self.c_name += '_'
1107
if self.c_name == self.family.c_name:
1108
self.c_name = ''
1109
1110
def new_attr(self, elem, value):
1111
if elem['type'] in scalars:
1112
t = TypeScalar(self.family, self, elem, value)
1113
elif elem['type'] == 'unused':
1114
t = TypeUnused(self.family, self, elem, value)
1115
elif elem['type'] == 'pad':
1116
t = TypePad(self.family, self, elem, value)
1117
elif elem['type'] == 'flag':
1118
t = TypeFlag(self.family, self, elem, value)
1119
elif elem['type'] == 'string':
1120
t = TypeString(self.family, self, elem, value)
1121
elif elem['type'] == 'binary':
1122
if 'struct' in elem:
1123
t = TypeBinaryStruct(self.family, self, elem, value)
1124
elif elem.get('sub-type') in scalars:
1125
t = TypeBinaryScalarArray(self.family, self, elem, value)
1126
else:
1127
t = TypeBinary(self.family, self, elem, value)
1128
elif elem['type'] == 'bitfield32':
1129
t = TypeBitfield32(self.family, self, elem, value)
1130
elif elem['type'] == 'nest':
1131
t = TypeNest(self.family, self, elem, value)
1132
elif elem['type'] == 'indexed-array' and 'sub-type' in elem:
1133
if elem["sub-type"] in ['binary', 'nest', 'u32']:
1134
t = TypeIndexedArray(self.family, self, elem, value)
1135
else:
1136
raise Exception(f'new_attr: unsupported sub-type {elem["sub-type"]}')
1137
elif elem['type'] == 'nest-type-value':
1138
t = TypeNestTypeValue(self.family, self, elem, value)
1139
elif elem['type'] == 'sub-message':
1140
t = TypeSubMessage(self.family, self, elem, value)
1141
else:
1142
raise Exception(f"No typed class for type {elem['type']}")
1143
1144
if 'multi-attr' in elem and elem['multi-attr']:
1145
t = TypeMultiAttr(self.family, self, elem, value, t)
1146
1147
return t
1148
1149
1150
class Operation(SpecOperation):
1151
def __init__(self, family, yaml, req_value, rsp_value):
1152
# Fill in missing operation properties (for fixed hdr-only msgs)
1153
for mode in ['do', 'dump', 'event']:
1154
for direction in ['request', 'reply']:
1155
try:
1156
yaml[mode][direction].setdefault('attributes', [])
1157
except KeyError:
1158
pass
1159
1160
super().__init__(family, yaml, req_value, rsp_value)
1161
1162
self.render_name = c_lower(family.ident_name + '_' + self.name)
1163
1164
self.dual_policy = ('do' in yaml and 'request' in yaml['do']) and \
1165
('dump' in yaml and 'request' in yaml['dump'])
1166
1167
self.has_ntf = False
1168
1169
# Added by resolve:
1170
self.enum_name = None
1171
delattr(self, "enum_name")
1172
1173
def resolve(self):
1174
self.resolve_up(super())
1175
1176
if not self.is_async:
1177
self.enum_name = self.family.op_prefix + c_upper(self.name)
1178
else:
1179
self.enum_name = self.family.async_op_prefix + c_upper(self.name)
1180
1181
def mark_has_ntf(self):
1182
self.has_ntf = True
1183
1184
1185
class SubMessage(SpecSubMessage):
1186
def __init__(self, family, yaml):
1187
super().__init__(family, yaml)
1188
1189
self.render_name = c_lower(family.ident_name + '-' + yaml['name'])
1190
1191
def resolve(self):
1192
self.resolve_up(super())
1193
1194
1195
class Family(SpecFamily):
1196
def __init__(self, file_name, exclude_ops):
1197
# Added by resolve:
1198
self.c_name = None
1199
delattr(self, "c_name")
1200
self.op_prefix = None
1201
delattr(self, "op_prefix")
1202
self.async_op_prefix = None
1203
delattr(self, "async_op_prefix")
1204
self.mcgrps = None
1205
delattr(self, "mcgrps")
1206
self.consts = None
1207
delattr(self, "consts")
1208
self.hooks = None
1209
delattr(self, "hooks")
1210
1211
super().__init__(file_name, exclude_ops=exclude_ops)
1212
1213
self.fam_key = c_upper(self.yaml.get('c-family-name', self.yaml["name"] + '_FAMILY_NAME'))
1214
self.ver_key = c_upper(self.yaml.get('c-version-name', self.yaml["name"] + '_FAMILY_VERSION'))
1215
1216
if 'definitions' not in self.yaml:
1217
self.yaml['definitions'] = []
1218
1219
if 'uapi-header' in self.yaml:
1220
self.uapi_header = self.yaml['uapi-header']
1221
else:
1222
self.uapi_header = f"linux/{self.ident_name}.h"
1223
if self.uapi_header.startswith("linux/") and self.uapi_header.endswith('.h'):
1224
self.uapi_header_name = self.uapi_header[6:-2]
1225
else:
1226
self.uapi_header_name = self.ident_name
1227
1228
def resolve(self):
1229
self.resolve_up(super())
1230
1231
self.c_name = c_lower(self.ident_name)
1232
if 'name-prefix' in self.yaml['operations']:
1233
self.op_prefix = c_upper(self.yaml['operations']['name-prefix'])
1234
else:
1235
self.op_prefix = c_upper(self.yaml['name'] + '-cmd-')
1236
if 'async-prefix' in self.yaml['operations']:
1237
self.async_op_prefix = c_upper(self.yaml['operations']['async-prefix'])
1238
else:
1239
self.async_op_prefix = self.op_prefix
1240
1241
self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
1242
1243
self.hooks = dict()
1244
for when in ['pre', 'post']:
1245
self.hooks[when] = dict()
1246
for op_mode in ['do', 'dump']:
1247
self.hooks[when][op_mode] = dict()
1248
self.hooks[when][op_mode]['set'] = set()
1249
self.hooks[when][op_mode]['list'] = []
1250
1251
# dict space-name -> 'request': set(attrs), 'reply': set(attrs)
1252
self.root_sets = dict()
1253
# dict space-name -> Struct
1254
self.pure_nested_structs = dict()
1255
1256
self._mark_notify()
1257
self._mock_up_events()
1258
1259
self._load_root_sets()
1260
self._load_nested_sets()
1261
self._load_attr_use()
1262
self._load_selector_passing()
1263
self._load_hooks()
1264
1265
self.kernel_policy = self.yaml.get('kernel-policy', 'split')
1266
if self.kernel_policy == 'global':
1267
self._load_global_policy()
1268
1269
def new_enum(self, elem):
1270
return EnumSet(self, elem)
1271
1272
def new_attr_set(self, elem):
1273
return AttrSet(self, elem)
1274
1275
def new_operation(self, elem, req_value, rsp_value):
1276
return Operation(self, elem, req_value, rsp_value)
1277
1278
def new_sub_message(self, elem):
1279
return SubMessage(self, elem)
1280
1281
def is_classic(self):
1282
return self.proto == 'netlink-raw'
1283
1284
def _mark_notify(self):
1285
for op in self.msgs.values():
1286
if 'notify' in op:
1287
self.ops[op['notify']].mark_has_ntf()
1288
1289
# Fake a 'do' equivalent of all events, so that we can render their response parsing
1290
def _mock_up_events(self):
1291
for op in self.yaml['operations']['list']:
1292
if 'event' in op:
1293
op['do'] = {
1294
'reply': {
1295
'attributes': op['event']['attributes']
1296
}
1297
}
1298
1299
def _load_root_sets(self):
1300
for op_name, op in self.msgs.items():
1301
if 'attribute-set' not in op:
1302
continue
1303
1304
req_attrs = set()
1305
rsp_attrs = set()
1306
for op_mode in ['do', 'dump']:
1307
if op_mode in op and 'request' in op[op_mode]:
1308
req_attrs.update(set(op[op_mode]['request']['attributes']))
1309
if op_mode in op and 'reply' in op[op_mode]:
1310
rsp_attrs.update(set(op[op_mode]['reply']['attributes']))
1311
if 'event' in op:
1312
rsp_attrs.update(set(op['event']['attributes']))
1313
1314
if op['attribute-set'] not in self.root_sets:
1315
self.root_sets[op['attribute-set']] = {'request': req_attrs, 'reply': rsp_attrs}
1316
else:
1317
self.root_sets[op['attribute-set']]['request'].update(req_attrs)
1318
self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs)
1319
1320
def _sort_pure_types(self):
1321
# Try to reorder according to dependencies
1322
pns_key_list = list(self.pure_nested_structs.keys())
1323
pns_key_seen = set()
1324
rounds = len(pns_key_list) ** 2 # it's basically bubble sort
1325
for _ in range(rounds):
1326
if len(pns_key_list) == 0:
1327
break
1328
name = pns_key_list.pop(0)
1329
finished = True
1330
for _, spec in self.attr_sets[name].items():
1331
if 'nested-attributes' in spec:
1332
nested = spec['nested-attributes']
1333
elif 'sub-message' in spec:
1334
nested = spec.sub_message
1335
else:
1336
continue
1337
1338
# If the unknown nest we hit is recursive it's fine, it'll be a pointer
1339
if self.pure_nested_structs[nested].recursive:
1340
continue
1341
if nested not in pns_key_seen:
1342
# Dicts are sorted, this will make struct last
1343
struct = self.pure_nested_structs.pop(name)
1344
self.pure_nested_structs[name] = struct
1345
finished = False
1346
break
1347
if finished:
1348
pns_key_seen.add(name)
1349
else:
1350
pns_key_list.append(name)
1351
1352
def _load_nested_set_nest(self, spec):
1353
inherit = set()
1354
nested = spec['nested-attributes']
1355
if nested not in self.root_sets:
1356
if nested not in self.pure_nested_structs:
1357
self.pure_nested_structs[nested] = \
1358
Struct(self, nested, inherited=inherit,
1359
fixed_header=spec.get('fixed-header'))
1360
else:
1361
raise Exception(f'Using attr set as root and nested not supported - {nested}')
1362
1363
if 'type-value' in spec:
1364
if nested in self.root_sets:
1365
raise Exception("Inheriting members to a space used as root not supported")
1366
inherit.update(set(spec['type-value']))
1367
elif spec['type'] == 'indexed-array':
1368
inherit.add('idx')
1369
self.pure_nested_structs[nested].set_inherited(inherit)
1370
1371
return nested
1372
1373
def _load_nested_set_submsg(self, spec):
1374
# Fake the struct type for the sub-message itself
1375
# its not a attr_set but codegen wants attr_sets.
1376
submsg = self.sub_msgs[spec["sub-message"]]
1377
nested = submsg.name
1378
1379
attrs = []
1380
for name, fmt in submsg.formats.items():
1381
attr = {
1382
"name": name,
1383
"parent-sub-message": spec,
1384
}
1385
if 'attribute-set' in fmt:
1386
attr |= {
1387
"type": "nest",
1388
"nested-attributes": fmt['attribute-set'],
1389
}
1390
if 'fixed-header' in fmt:
1391
attr |= { "fixed-header": fmt["fixed-header"] }
1392
elif 'fixed-header' in fmt:
1393
attr |= {
1394
"type": "binary",
1395
"struct": fmt["fixed-header"],
1396
}
1397
else:
1398
attr["type"] = "flag"
1399
attrs.append(attr)
1400
1401
self.attr_sets[nested] = AttrSet(self, {
1402
"name": nested,
1403
"name-pfx": self.name + '-' + spec.name + '-',
1404
"attributes": attrs
1405
})
1406
1407
if nested not in self.pure_nested_structs:
1408
self.pure_nested_structs[nested] = Struct(self, nested, submsg=submsg)
1409
1410
return nested
1411
1412
def _load_nested_sets(self):
1413
attr_set_queue = list(self.root_sets.keys())
1414
attr_set_seen = set(self.root_sets.keys())
1415
1416
while len(attr_set_queue):
1417
a_set = attr_set_queue.pop(0)
1418
for attr, spec in self.attr_sets[a_set].items():
1419
if 'nested-attributes' in spec:
1420
nested = self._load_nested_set_nest(spec)
1421
elif 'sub-message' in spec:
1422
nested = self._load_nested_set_submsg(spec)
1423
else:
1424
continue
1425
1426
if nested not in attr_set_seen:
1427
attr_set_queue.append(nested)
1428
attr_set_seen.add(nested)
1429
1430
for root_set, rs_members in self.root_sets.items():
1431
for attr, spec in self.attr_sets[root_set].items():
1432
if 'nested-attributes' in spec:
1433
nested = spec['nested-attributes']
1434
elif 'sub-message' in spec:
1435
nested = spec.sub_message
1436
else:
1437
nested = None
1438
1439
if nested:
1440
if attr in rs_members['request']:
1441
self.pure_nested_structs[nested].request = True
1442
if attr in rs_members['reply']:
1443
self.pure_nested_structs[nested].reply = True
1444
1445
if spec.is_multi_val():
1446
child = self.pure_nested_structs.get(nested)
1447
child.in_multi_val = True
1448
1449
self._sort_pure_types()
1450
1451
# Propagate the request / reply / recursive
1452
for attr_set, struct in reversed(self.pure_nested_structs.items()):
1453
for _, spec in self.attr_sets[attr_set].items():
1454
if attr_set in struct.child_nests:
1455
struct.recursive = True
1456
1457
if 'nested-attributes' in spec:
1458
child_name = spec['nested-attributes']
1459
elif 'sub-message' in spec:
1460
child_name = spec.sub_message
1461
else:
1462
continue
1463
1464
struct.child_nests.add(child_name)
1465
child = self.pure_nested_structs.get(child_name)
1466
if child:
1467
if not child.recursive:
1468
struct.child_nests.update(child.child_nests)
1469
child.request |= struct.request
1470
child.reply |= struct.reply
1471
if spec.is_multi_val():
1472
child.in_multi_val = True
1473
1474
self._sort_pure_types()
1475
1476
def _load_attr_use(self):
1477
for _, struct in self.pure_nested_structs.items():
1478
if struct.request:
1479
for _, arg in struct.member_list():
1480
arg.set_request()
1481
if struct.reply:
1482
for _, arg in struct.member_list():
1483
arg.set_reply()
1484
1485
for root_set, rs_members in self.root_sets.items():
1486
for attr, spec in self.attr_sets[root_set].items():
1487
if attr in rs_members['request']:
1488
spec.set_request()
1489
if attr in rs_members['reply']:
1490
spec.set_reply()
1491
1492
def _load_selector_passing(self):
1493
def all_structs():
1494
for k, v in reversed(self.pure_nested_structs.items()):
1495
yield k, v
1496
for k, _ in self.root_sets.items():
1497
yield k, None # we don't have a struct, but it must be terminal
1498
1499
for attr_set, struct in all_structs():
1500
for _, spec in self.attr_sets[attr_set].items():
1501
if 'nested-attributes' in spec:
1502
child_name = spec['nested-attributes']
1503
elif 'sub-message' in spec:
1504
child_name = spec.sub_message
1505
else:
1506
continue
1507
1508
child = self.pure_nested_structs.get(child_name)
1509
for selector in child.external_selectors():
1510
if selector.name in self.attr_sets[attr_set]:
1511
sel_attr = self.attr_sets[attr_set][selector.name]
1512
selector.set_attr(sel_attr)
1513
else:
1514
raise Exception("Passing selector thru more than one layer not supported")
1515
1516
def _load_global_policy(self):
1517
global_set = set()
1518
attr_set_name = None
1519
for op_name, op in self.ops.items():
1520
if not op:
1521
continue
1522
if 'attribute-set' not in op:
1523
continue
1524
1525
if attr_set_name is None:
1526
attr_set_name = op['attribute-set']
1527
if attr_set_name != op['attribute-set']:
1528
raise Exception('For a global policy all ops must use the same set')
1529
1530
for op_mode in ['do', 'dump']:
1531
if op_mode in op:
1532
req = op[op_mode].get('request')
1533
if req:
1534
global_set.update(req.get('attributes', []))
1535
1536
self.global_policy = []
1537
self.global_policy_set = attr_set_name
1538
for attr in self.attr_sets[attr_set_name]:
1539
if attr in global_set:
1540
self.global_policy.append(attr)
1541
1542
def _load_hooks(self):
1543
for op in self.ops.values():
1544
for op_mode in ['do', 'dump']:
1545
if op_mode not in op:
1546
continue
1547
for when in ['pre', 'post']:
1548
if when not in op[op_mode]:
1549
continue
1550
name = op[op_mode][when]
1551
if name in self.hooks[when][op_mode]['set']:
1552
continue
1553
self.hooks[when][op_mode]['set'].add(name)
1554
self.hooks[when][op_mode]['list'].append(name)
1555
1556
1557
class RenderInfo:
1558
def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None):
1559
self.family = family
1560
self.nl = cw.nlib
1561
self.ku_space = ku_space
1562
self.op_mode = op_mode
1563
self.op = op
1564
1565
fixed_hdr = op.fixed_header if op else None
1566
self.fixed_hdr_len = 'ys->family->hdr_len'
1567
if op and op.fixed_header:
1568
if op.fixed_header != family.fixed_header:
1569
if family.is_classic():
1570
self.fixed_hdr_len = f"sizeof(struct {c_lower(fixed_hdr)})"
1571
else:
1572
raise Exception("Per-op fixed header not supported, yet")
1573
1574
1575
# 'do' and 'dump' response parsing is identical
1576
self.type_consistent = True
1577
self.type_oneside = False
1578
if op_mode != 'do' and 'dump' in op:
1579
if 'do' in op:
1580
if ('reply' in op['do']) != ('reply' in op["dump"]):
1581
self.type_consistent = False
1582
elif 'reply' in op['do'] and op["do"]["reply"] != op["dump"]["reply"]:
1583
self.type_consistent = False
1584
else:
1585
self.type_consistent = True
1586
self.type_oneside = True
1587
1588
self.attr_set = attr_set
1589
if not self.attr_set:
1590
self.attr_set = op['attribute-set']
1591
1592
self.type_name_conflict = False
1593
if op:
1594
self.type_name = c_lower(op.name)
1595
else:
1596
self.type_name = c_lower(attr_set)
1597
if attr_set in family.consts:
1598
self.type_name_conflict = True
1599
1600
self.cw = cw
1601
1602
self.struct = dict()
1603
if op_mode == 'notify':
1604
op_mode = 'do' if 'do' in op else 'dump'
1605
for op_dir in ['request', 'reply']:
1606
if op:
1607
type_list = []
1608
if op_dir in op[op_mode]:
1609
type_list = op[op_mode][op_dir]['attributes']
1610
self.struct[op_dir] = Struct(family, self.attr_set,
1611
fixed_header=fixed_hdr,
1612
type_list=type_list)
1613
if op_mode == 'event':
1614
self.struct['reply'] = Struct(family, self.attr_set,
1615
fixed_header=fixed_hdr,
1616
type_list=op['event']['attributes'])
1617
1618
def type_empty(self, key):
1619
return len(self.struct[key].attr_list) == 0 and \
1620
self.struct['request'].fixed_header is None
1621
1622
def needs_nlflags(self, direction):
1623
return self.op_mode == 'do' and direction == 'request' and self.family.is_classic()
1624
1625
1626
class CodeWriter:
1627
def __init__(self, nlib, out_file=None, overwrite=True):
1628
self.nlib = nlib
1629
self._overwrite = overwrite
1630
1631
self._nl = False
1632
self._block_end = False
1633
self._silent_block = False
1634
self._ind = 0
1635
self._ifdef_block = None
1636
if out_file is None:
1637
self._out = os.sys.stdout
1638
else:
1639
self._out = tempfile.NamedTemporaryFile('w+')
1640
self._out_file = out_file
1641
1642
def __del__(self):
1643
self.close_out_file()
1644
1645
def close_out_file(self):
1646
if self._out == os.sys.stdout:
1647
return
1648
# Avoid modifying the file if contents didn't change
1649
self._out.flush()
1650
if not self._overwrite and os.path.isfile(self._out_file):
1651
if filecmp.cmp(self._out.name, self._out_file, shallow=False):
1652
return
1653
with open(self._out_file, 'w+') as out_file:
1654
self._out.seek(0)
1655
shutil.copyfileobj(self._out, out_file)
1656
self._out.close()
1657
self._out = os.sys.stdout
1658
1659
@classmethod
1660
def _is_cond(cls, line):
1661
return line.startswith('if') or line.startswith('while') or line.startswith('for')
1662
1663
def p(self, line, add_ind=0):
1664
if self._block_end:
1665
self._block_end = False
1666
if line.startswith('else'):
1667
line = '} ' + line
1668
else:
1669
self._out.write('\t' * self._ind + '}\n')
1670
1671
if self._nl:
1672
self._out.write('\n')
1673
self._nl = False
1674
1675
ind = self._ind
1676
if line[-1] == ':':
1677
ind -= 1
1678
if self._silent_block:
1679
ind += 1
1680
self._silent_block = line.endswith(')') and CodeWriter._is_cond(line)
1681
self._silent_block |= line.strip() == 'else'
1682
if line[0] == '#':
1683
ind = 0
1684
if add_ind:
1685
ind += add_ind
1686
self._out.write('\t' * ind + line + '\n')
1687
1688
def nl(self):
1689
self._nl = True
1690
1691
def block_start(self, line=''):
1692
if line:
1693
line = line + ' '
1694
self.p(line + '{')
1695
self._ind += 1
1696
1697
def block_end(self, line=''):
1698
if line and line[0] not in {';', ','}:
1699
line = ' ' + line
1700
self._ind -= 1
1701
self._nl = False
1702
if not line:
1703
# Delay printing closing bracket in case "else" comes next
1704
if self._block_end:
1705
self._out.write('\t' * (self._ind + 1) + '}\n')
1706
self._block_end = True
1707
else:
1708
self.p('}' + line)
1709
1710
def write_doc_line(self, doc, indent=True):
1711
words = doc.split()
1712
line = ' *'
1713
for word in words:
1714
if len(line) + len(word) >= 79:
1715
self.p(line)
1716
line = ' *'
1717
if indent:
1718
line += ' '
1719
line += ' ' + word
1720
self.p(line)
1721
1722
def write_func_prot(self, qual_ret, name, args=None, doc=None, suffix=''):
1723
if not args:
1724
args = ['void']
1725
1726
if doc:
1727
self.p('/*')
1728
self.p(' * ' + doc)
1729
self.p(' */')
1730
1731
oneline = qual_ret
1732
if qual_ret[-1] != '*':
1733
oneline += ' '
1734
oneline += f"{name}({', '.join(args)}){suffix}"
1735
1736
if len(oneline) < 80:
1737
self.p(oneline)
1738
return
1739
1740
v = qual_ret
1741
if len(v) > 3:
1742
self.p(v)
1743
v = ''
1744
elif qual_ret[-1] != '*':
1745
v += ' '
1746
v += name + '('
1747
ind = '\t' * (len(v) // 8) + ' ' * (len(v) % 8)
1748
delta_ind = len(v) - len(ind)
1749
v += args[0]
1750
i = 1
1751
while i < len(args):
1752
next_len = len(v) + len(args[i])
1753
if v[0] == '\t':
1754
next_len += delta_ind
1755
if next_len > 76:
1756
self.p(v + ',')
1757
v = ind
1758
else:
1759
v += ', '
1760
v += args[i]
1761
i += 1
1762
self.p(v + ')' + suffix)
1763
1764
def write_func_lvar(self, local_vars):
1765
if not local_vars:
1766
return
1767
1768
if type(local_vars) is str:
1769
local_vars = [local_vars]
1770
1771
local_vars.sort(key=len, reverse=True)
1772
for var in local_vars:
1773
self.p(var)
1774
self.nl()
1775
1776
def write_func(self, qual_ret, name, body, args=None, local_vars=None):
1777
self.write_func_prot(qual_ret=qual_ret, name=name, args=args)
1778
self.block_start()
1779
self.write_func_lvar(local_vars=local_vars)
1780
1781
for line in body:
1782
self.p(line)
1783
self.block_end()
1784
1785
def writes_defines(self, defines):
1786
longest = 0
1787
for define in defines:
1788
if len(define[0]) > longest:
1789
longest = len(define[0])
1790
longest = ((longest + 8) // 8) * 8
1791
for define in defines:
1792
line = '#define ' + define[0]
1793
line += '\t' * ((longest - len(define[0]) + 7) // 8)
1794
if type(define[1]) is int:
1795
line += str(define[1])
1796
elif type(define[1]) is str:
1797
line += '"' + define[1] + '"'
1798
self.p(line)
1799
1800
def write_struct_init(self, members):
1801
longest = max([len(x[0]) for x in members])
1802
longest += 1 # because we prepend a .
1803
longest = ((longest + 8) // 8) * 8
1804
for one in members:
1805
line = '.' + one[0]
1806
line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8)
1807
line += '= ' + str(one[1]) + ','
1808
self.p(line)
1809
1810
def ifdef_block(self, config):
1811
config_option = None
1812
if config:
1813
config_option = 'CONFIG_' + c_upper(config)
1814
if self._ifdef_block == config_option:
1815
return
1816
1817
if self._ifdef_block:
1818
self.p('#endif /* ' + self._ifdef_block + ' */')
1819
if config_option:
1820
self.p('#ifdef ' + config_option)
1821
self._ifdef_block = config_option
1822
1823
1824
scalars = {'u8', 'u16', 'u32', 'u64', 's8', 's16', 's32', 's64', 'uint', 'sint'}
1825
1826
direction_to_suffix = {
1827
'reply': '_rsp',
1828
'request': '_req',
1829
'': ''
1830
}
1831
1832
op_mode_to_wrapper = {
1833
'do': '',
1834
'dump': '_list',
1835
'notify': '_ntf',
1836
'event': '',
1837
}
1838
1839
_C_KW = {
1840
'auto',
1841
'bool',
1842
'break',
1843
'case',
1844
'char',
1845
'const',
1846
'continue',
1847
'default',
1848
'do',
1849
'double',
1850
'else',
1851
'enum',
1852
'extern',
1853
'float',
1854
'for',
1855
'goto',
1856
'if',
1857
'inline',
1858
'int',
1859
'long',
1860
'register',
1861
'return',
1862
'short',
1863
'signed',
1864
'sizeof',
1865
'static',
1866
'struct',
1867
'switch',
1868
'typedef',
1869
'union',
1870
'unsigned',
1871
'void',
1872
'volatile',
1873
'while'
1874
}
1875
1876
1877
def rdir(direction):
1878
if direction == 'reply':
1879
return 'request'
1880
if direction == 'request':
1881
return 'reply'
1882
return direction
1883
1884
1885
def op_prefix(ri, direction, deref=False):
1886
suffix = f"_{ri.type_name}"
1887
1888
if not ri.op_mode:
1889
pass
1890
elif ri.op_mode == 'do':
1891
suffix += f"{direction_to_suffix[direction]}"
1892
else:
1893
if direction == 'request':
1894
suffix += '_req'
1895
if not ri.type_oneside:
1896
suffix += '_dump'
1897
else:
1898
if ri.type_consistent:
1899
if deref:
1900
suffix += f"{direction_to_suffix[direction]}"
1901
else:
1902
suffix += op_mode_to_wrapper[ri.op_mode]
1903
else:
1904
suffix += '_rsp'
1905
suffix += '_dump' if deref else '_list'
1906
1907
return f"{ri.family.c_name}{suffix}"
1908
1909
1910
def type_name(ri, direction, deref=False):
1911
return f"struct {op_prefix(ri, direction, deref=deref)}"
1912
1913
1914
def print_prototype(ri, direction, terminate=True, doc=None):
1915
suffix = ';' if terminate else ''
1916
1917
fname = ri.op.render_name
1918
if ri.op_mode == 'dump':
1919
fname += '_dump'
1920
1921
args = ['struct ynl_sock *ys']
1922
if 'request' in ri.op[ri.op_mode]:
1923
args.append(f"{type_name(ri, direction)} *" + f"{direction_to_suffix[direction][1:]}")
1924
1925
ret = 'int'
1926
if 'reply' in ri.op[ri.op_mode]:
1927
ret = f"{type_name(ri, rdir(direction))} *"
1928
1929
ri.cw.write_func_prot(ret, fname, args, doc=doc, suffix=suffix)
1930
1931
1932
def print_req_prototype(ri):
1933
print_prototype(ri, "request", doc=ri.op['doc'])
1934
1935
1936
def print_dump_prototype(ri):
1937
print_prototype(ri, "request")
1938
1939
1940
def put_typol_submsg(cw, struct):
1941
cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[] =')
1942
1943
i = 0
1944
for name, arg in struct.member_list():
1945
nest = ""
1946
if arg.type == 'nest':
1947
nest = f" .nest = &{arg.nested_render_name}_nest,"
1948
cw.p('[%d] = { .type = YNL_PT_SUBMSG, .name = "%s",%s },' %
1949
(i, name, nest))
1950
i += 1
1951
1952
cw.block_end(line=';')
1953
cw.nl()
1954
1955
cw.block_start(line=f'const struct ynl_policy_nest {struct.render_name}_nest =')
1956
cw.p(f'.max_attr = {i - 1},')
1957
cw.p(f'.table = {struct.render_name}_policy,')
1958
cw.block_end(line=';')
1959
cw.nl()
1960
1961
1962
def put_typol_fwd(cw, struct):
1963
cw.p(f'extern const struct ynl_policy_nest {struct.render_name}_nest;')
1964
1965
1966
def put_typol(cw, struct):
1967
if struct.submsg:
1968
put_typol_submsg(cw, struct)
1969
return
1970
1971
type_max = struct.attr_set.max_name
1972
cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
1973
1974
for _, arg in struct.member_list():
1975
arg.attr_typol(cw)
1976
1977
cw.block_end(line=';')
1978
cw.nl()
1979
1980
cw.block_start(line=f'const struct ynl_policy_nest {struct.render_name}_nest =')
1981
cw.p(f'.max_attr = {type_max},')
1982
cw.p(f'.table = {struct.render_name}_policy,')
1983
cw.block_end(line=';')
1984
cw.nl()
1985
1986
1987
def _put_enum_to_str_helper(cw, render_name, map_name, arg_name, enum=None):
1988
args = [f'int {arg_name}']
1989
if enum:
1990
args = [enum.user_type + ' ' + arg_name]
1991
cw.write_func_prot('const char *', f'{render_name}_str', args)
1992
cw.block_start()
1993
if enum and enum.type == 'flags':
1994
cw.p(f'{arg_name} = ffs({arg_name}) - 1;')
1995
cw.p(f'if ({arg_name} < 0 || {arg_name} >= (int)YNL_ARRAY_SIZE({map_name}))')
1996
cw.p('return NULL;')
1997
cw.p(f'return {map_name}[{arg_name}];')
1998
cw.block_end()
1999
cw.nl()
2000
2001
2002
def put_op_name_fwd(family, cw):
2003
cw.write_func_prot('const char *', f'{family.c_name}_op_str', ['int op'], suffix=';')
2004
2005
2006
def put_op_name(family, cw):
2007
map_name = f'{family.c_name}_op_strmap'
2008
cw.block_start(line=f"static const char * const {map_name}[] =")
2009
for op_name, op in family.msgs.items():
2010
if op.rsp_value:
2011
# Make sure we don't add duplicated entries, if multiple commands
2012
# produce the same response in legacy families.
2013
if family.rsp_by_value[op.rsp_value] != op:
2014
cw.p(f'// skip "{op_name}", duplicate reply value')
2015
continue
2016
2017
if op.req_value == op.rsp_value:
2018
cw.p(f'[{op.enum_name}] = "{op_name}",')
2019
else:
2020
cw.p(f'[{op.rsp_value}] = "{op_name}",')
2021
cw.block_end(line=';')
2022
cw.nl()
2023
2024
_put_enum_to_str_helper(cw, family.c_name + '_op', map_name, 'op')
2025
2026
2027
def put_enum_to_str_fwd(family, cw, enum):
2028
args = [enum.user_type + ' value']
2029
cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';')
2030
2031
2032
def put_enum_to_str(family, cw, enum):
2033
map_name = f'{enum.render_name}_strmap'
2034
cw.block_start(line=f"static const char * const {map_name}[] =")
2035
for entry in enum.entries.values():
2036
cw.p(f'[{entry.value}] = "{entry.name}",')
2037
cw.block_end(line=';')
2038
cw.nl()
2039
2040
_put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
2041
2042
2043
def put_local_vars(struct):
2044
local_vars = []
2045
has_array = False
2046
has_count = False
2047
for _, arg in struct.member_list():
2048
has_array |= arg.type == 'indexed-array'
2049
has_count |= arg.presence_type() == 'count'
2050
if has_array:
2051
local_vars.append('struct nlattr *array;')
2052
if has_count:
2053
local_vars.append('unsigned int i;')
2054
return local_vars
2055
2056
2057
def put_req_nested_prototype(ri, struct, suffix=';'):
2058
func_args = ['struct nlmsghdr *nlh',
2059
'unsigned int attr_type',
2060
f'{struct.ptr_name}obj']
2061
2062
ri.cw.write_func_prot('int', f'{struct.render_name}_put', func_args,
2063
suffix=suffix)
2064
2065
2066
def put_req_nested(ri, struct):
2067
local_vars = []
2068
init_lines = []
2069
2070
if struct.submsg is None:
2071
local_vars.append('struct nlattr *nest;')
2072
init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);")
2073
if struct.fixed_header:
2074
local_vars.append('void *hdr;')
2075
struct_sz = f'sizeof({struct.fixed_header})'
2076
init_lines.append(f"hdr = ynl_nlmsg_put_extra_header(nlh, {struct_sz});")
2077
init_lines.append(f"memcpy(hdr, &obj->_hdr, {struct_sz});")
2078
2079
local_vars += put_local_vars(struct)
2080
2081
put_req_nested_prototype(ri, struct, suffix='')
2082
ri.cw.block_start()
2083
ri.cw.write_func_lvar(local_vars)
2084
2085
for line in init_lines:
2086
ri.cw.p(line)
2087
2088
for _, arg in struct.member_list():
2089
arg.attr_put(ri, "obj")
2090
2091
if struct.submsg is None:
2092
ri.cw.p("ynl_attr_nest_end(nlh, nest);")
2093
2094
ri.cw.nl()
2095
ri.cw.p('return 0;')
2096
ri.cw.block_end()
2097
ri.cw.nl()
2098
2099
2100
def _multi_parse(ri, struct, init_lines, local_vars):
2101
if struct.fixed_header:
2102
local_vars += ['void *hdr;']
2103
if struct.nested:
2104
if struct.fixed_header:
2105
iter_line = f"ynl_attr_for_each_nested_off(attr, nested, sizeof({struct.fixed_header}))"
2106
else:
2107
iter_line = "ynl_attr_for_each_nested(attr, nested)"
2108
else:
2109
iter_line = "ynl_attr_for_each(attr, nlh, yarg->ys->family->hdr_len)"
2110
if ri.op.fixed_header != ri.family.fixed_header:
2111
if ri.family.is_classic():
2112
iter_line = f"ynl_attr_for_each(attr, nlh, sizeof({struct.fixed_header}))"
2113
else:
2114
raise Exception("Per-op fixed header not supported, yet")
2115
2116
indexed_arrays = set()
2117
multi_attrs = set()
2118
needs_parg = False
2119
var_set = set()
2120
for arg, aspec in struct.member_list():
2121
if aspec['type'] == 'indexed-array' and 'sub-type' in aspec:
2122
if aspec["sub-type"] in {'binary', 'nest'}:
2123
local_vars.append(f'const struct nlattr *attr_{aspec.c_name} = NULL;')
2124
indexed_arrays.add(arg)
2125
elif aspec['sub-type'] in scalars:
2126
local_vars.append(f'const struct nlattr *attr_{aspec.c_name} = NULL;')
2127
indexed_arrays.add(arg)
2128
else:
2129
raise Exception(f'Not supported sub-type {aspec["sub-type"]}')
2130
if 'multi-attr' in aspec:
2131
multi_attrs.add(arg)
2132
needs_parg |= 'nested-attributes' in aspec
2133
needs_parg |= 'sub-message' in aspec
2134
2135
try:
2136
_, _, l_vars = aspec._attr_get(ri, '')
2137
var_set |= set(l_vars) if l_vars else set()
2138
except Exception:
2139
pass # _attr_get() not implemented by simple types, ignore
2140
local_vars += list(var_set)
2141
if indexed_arrays or multi_attrs:
2142
local_vars.append('int i;')
2143
if needs_parg:
2144
local_vars.append('struct ynl_parse_arg parg;')
2145
init_lines.append('parg.ys = yarg->ys;')
2146
2147
all_multi = indexed_arrays | multi_attrs
2148
2149
for arg in sorted(all_multi):
2150
local_vars.append(f"unsigned int n_{struct[arg].c_name} = 0;")
2151
2152
ri.cw.block_start()
2153
ri.cw.write_func_lvar(local_vars)
2154
2155
for line in init_lines:
2156
ri.cw.p(line)
2157
ri.cw.nl()
2158
2159
for arg in struct.inherited:
2160
ri.cw.p(f'dst->{arg} = {arg};')
2161
2162
if struct.fixed_header:
2163
if struct.nested:
2164
ri.cw.p('hdr = ynl_attr_data(nested);')
2165
elif ri.family.is_classic():
2166
ri.cw.p('hdr = ynl_nlmsg_data(nlh);')
2167
else:
2168
ri.cw.p('hdr = ynl_nlmsg_data_offset(nlh, sizeof(struct genlmsghdr));')
2169
ri.cw.p(f"memcpy(&dst->_hdr, hdr, sizeof({struct.fixed_header}));")
2170
for arg in sorted(all_multi):
2171
aspec = struct[arg]
2172
ri.cw.p(f"if (dst->{aspec.c_name})")
2173
ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
2174
2175
ri.cw.nl()
2176
ri.cw.block_start(line=iter_line)
2177
ri.cw.p('unsigned int type = ynl_attr_type(attr);')
2178
ri.cw.nl()
2179
2180
first = True
2181
for _, arg in struct.member_list():
2182
good = arg.attr_get(ri, 'dst', first=first)
2183
# First may be 'unused' or 'pad', ignore those
2184
first &= not good
2185
2186
ri.cw.block_end()
2187
ri.cw.nl()
2188
2189
for arg in sorted(indexed_arrays):
2190
aspec = struct[arg]
2191
2192
ri.cw.block_start(line=f"if (n_{aspec.c_name})")
2193
ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
2194
ri.cw.p(f"dst->_count.{aspec.c_name} = n_{aspec.c_name};")
2195
ri.cw.p('i = 0;')
2196
if 'nested-attributes' in aspec:
2197
ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
2198
ri.cw.block_start(line=f"ynl_attr_for_each_nested(attr, attr_{aspec.c_name})")
2199
if 'nested-attributes' in aspec:
2200
ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
2201
ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr, ynl_attr_type(attr)))")
2202
ri.cw.p('return YNL_PARSE_CB_ERROR;')
2203
elif aspec.sub_type in scalars:
2204
ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.sub_type}(attr);")
2205
elif aspec.sub_type == 'binary' and 'exact-len' in aspec.checks:
2206
# Length is validated by typol
2207
ri.cw.p(f'memcpy(dst->{aspec.c_name}[i], ynl_attr_data(attr), {aspec.checks["exact-len"]});')
2208
else:
2209
raise Exception(f"Nest parsing type not supported in {aspec['name']}")
2210
ri.cw.p('i++;')
2211
ri.cw.block_end()
2212
ri.cw.block_end()
2213
ri.cw.nl()
2214
2215
for arg in sorted(multi_attrs):
2216
aspec = struct[arg]
2217
ri.cw.block_start(line=f"if (n_{aspec.c_name})")
2218
ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
2219
ri.cw.p(f"dst->_count.{aspec.c_name} = n_{aspec.c_name};")
2220
ri.cw.p('i = 0;')
2221
if 'nested-attributes' in aspec:
2222
ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
2223
ri.cw.block_start(line=iter_line)
2224
ri.cw.block_start(line=f"if (ynl_attr_type(attr) == {aspec.enum_name})")
2225
if 'nested-attributes' in aspec:
2226
ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
2227
ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))")
2228
ri.cw.p('return YNL_PARSE_CB_ERROR;')
2229
elif aspec.type in scalars:
2230
ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.type}(attr);")
2231
elif aspec.type == 'binary' and 'struct' in aspec:
2232
ri.cw.p('size_t len = ynl_attr_data_len(attr);')
2233
ri.cw.nl()
2234
ri.cw.p(f'if (len > sizeof(dst->{aspec.c_name}[0]))')
2235
ri.cw.p(f'len = sizeof(dst->{aspec.c_name}[0]);')
2236
ri.cw.p(f"memcpy(&dst->{aspec.c_name}[i], ynl_attr_data(attr), len);")
2237
elif aspec.type == 'string':
2238
ri.cw.p('unsigned int len;')
2239
ri.cw.nl()
2240
ri.cw.p('len = strnlen(ynl_attr_get_str(attr), ynl_attr_data_len(attr));')
2241
ri.cw.p(f'dst->{aspec.c_name}[i] = malloc(sizeof(struct ynl_string) + len + 1);')
2242
ri.cw.p(f"dst->{aspec.c_name}[i]->len = len;")
2243
ri.cw.p(f"memcpy(dst->{aspec.c_name}[i]->str, ynl_attr_get_str(attr), len);")
2244
ri.cw.p(f"dst->{aspec.c_name}[i]->str[len] = 0;")
2245
else:
2246
raise Exception(f'Nest parsing of type {aspec.type} not supported yet')
2247
ri.cw.p('i++;')
2248
ri.cw.block_end()
2249
ri.cw.block_end()
2250
ri.cw.block_end()
2251
ri.cw.nl()
2252
2253
if struct.nested:
2254
ri.cw.p('return 0;')
2255
else:
2256
ri.cw.p('return YNL_PARSE_CB_OK;')
2257
ri.cw.block_end()
2258
ri.cw.nl()
2259
2260
2261
def parse_rsp_submsg(ri, struct):
2262
parse_rsp_nested_prototype(ri, struct, suffix='')
2263
2264
var = 'dst'
2265
local_vars = {'const struct nlattr *attr = nested;',
2266
f'{struct.ptr_name}{var} = yarg->data;',
2267
'struct ynl_parse_arg parg;'}
2268
2269
for _, arg in struct.member_list():
2270
_, _, l_vars = arg._attr_get(ri, var)
2271
local_vars |= set(l_vars) if l_vars else set()
2272
2273
ri.cw.block_start()
2274
ri.cw.write_func_lvar(list(local_vars))
2275
ri.cw.p('parg.ys = yarg->ys;')
2276
ri.cw.nl()
2277
2278
first = True
2279
for name, arg in struct.member_list():
2280
kw = 'if' if first else 'else if'
2281
first = False
2282
2283
ri.cw.block_start(line=f'{kw} (!strcmp(sel, "{name}"))')
2284
get_lines, init_lines, _ = arg._attr_get(ri, var)
2285
for line in init_lines or []:
2286
ri.cw.p(line)
2287
for line in get_lines:
2288
ri.cw.p(line)
2289
if arg.presence_type() == 'present':
2290
ri.cw.p(f"{var}->_present.{arg.c_name} = 1;")
2291
ri.cw.block_end()
2292
ri.cw.p('return 0;')
2293
ri.cw.block_end()
2294
ri.cw.nl()
2295
2296
2297
def parse_rsp_nested_prototype(ri, struct, suffix=';'):
2298
func_args = ['struct ynl_parse_arg *yarg',
2299
'const struct nlattr *nested']
2300
for sel in struct.external_selectors():
2301
func_args.append('const char *_sel_' + sel.name)
2302
if struct.submsg:
2303
func_args.insert(1, 'const char *sel')
2304
for arg in struct.inherited:
2305
func_args.append('__u32 ' + arg)
2306
2307
ri.cw.write_func_prot('int', f'{struct.render_name}_parse', func_args,
2308
suffix=suffix)
2309
2310
2311
def parse_rsp_nested(ri, struct):
2312
if struct.submsg:
2313
return parse_rsp_submsg(ri, struct)
2314
2315
parse_rsp_nested_prototype(ri, struct, suffix='')
2316
2317
local_vars = ['const struct nlattr *attr;',
2318
f'{struct.ptr_name}dst = yarg->data;']
2319
init_lines = []
2320
2321
if struct.member_list():
2322
_multi_parse(ri, struct, init_lines, local_vars)
2323
else:
2324
# Empty nest
2325
ri.cw.block_start()
2326
ri.cw.p('return 0;')
2327
ri.cw.block_end()
2328
ri.cw.nl()
2329
2330
2331
def parse_rsp_msg(ri, deref=False):
2332
if 'reply' not in ri.op[ri.op_mode] and ri.op_mode != 'event':
2333
return
2334
2335
func_args = ['const struct nlmsghdr *nlh',
2336
'struct ynl_parse_arg *yarg']
2337
2338
local_vars = [f'{type_name(ri, "reply", deref=deref)} *dst;',
2339
'const struct nlattr *attr;']
2340
init_lines = ['dst = yarg->data;']
2341
2342
ri.cw.write_func_prot('int', f'{op_prefix(ri, "reply", deref=deref)}_parse', func_args)
2343
2344
if ri.struct["reply"].member_list():
2345
_multi_parse(ri, ri.struct["reply"], init_lines, local_vars)
2346
else:
2347
# Empty reply
2348
ri.cw.block_start()
2349
ri.cw.p('return YNL_PARSE_CB_OK;')
2350
ri.cw.block_end()
2351
ri.cw.nl()
2352
2353
2354
def print_req(ri):
2355
ret_ok = '0'
2356
ret_err = '-1'
2357
direction = "request"
2358
local_vars = ['struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };',
2359
'struct nlmsghdr *nlh;',
2360
'int err;']
2361
2362
if 'reply' in ri.op[ri.op_mode]:
2363
ret_ok = 'rsp'
2364
ret_err = 'NULL'
2365
local_vars += [f'{type_name(ri, rdir(direction))} *rsp;']
2366
2367
if ri.struct["request"].fixed_header:
2368
local_vars += ['size_t hdr_len;',
2369
'void *hdr;']
2370
2371
local_vars += put_local_vars(ri.struct['request'])
2372
2373
print_prototype(ri, direction, terminate=False)
2374
ri.cw.block_start()
2375
ri.cw.write_func_lvar(local_vars)
2376
2377
if ri.family.is_classic():
2378
ri.cw.p(f"nlh = ynl_msg_start_req(ys, {ri.op.enum_name}, req->_nlmsg_flags);")
2379
else:
2380
ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
2381
2382
ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
2383
ri.cw.p(f"ys->req_hdr_len = {ri.fixed_hdr_len};")
2384
if 'reply' in ri.op[ri.op_mode]:
2385
ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
2386
ri.cw.nl()
2387
2388
if ri.struct['request'].fixed_header:
2389
ri.cw.p("hdr_len = sizeof(req->_hdr);")
2390
ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);")
2391
ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);")
2392
ri.cw.nl()
2393
2394
for _, attr in ri.struct["request"].member_list():
2395
attr.attr_put(ri, "req")
2396
ri.cw.nl()
2397
2398
if 'reply' in ri.op[ri.op_mode]:
2399
ri.cw.p('rsp = calloc(1, sizeof(*rsp));')
2400
ri.cw.p('yrs.yarg.data = rsp;')
2401
ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;")
2402
if ri.op.value is not None:
2403
ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};')
2404
else:
2405
ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};')
2406
ri.cw.nl()
2407
ri.cw.p("err = ynl_exec(ys, nlh, &yrs);")
2408
ri.cw.p('if (err < 0)')
2409
if 'reply' in ri.op[ri.op_mode]:
2410
ri.cw.p('goto err_free;')
2411
else:
2412
ri.cw.p('return -1;')
2413
ri.cw.nl()
2414
2415
ri.cw.p(f"return {ret_ok};")
2416
ri.cw.nl()
2417
2418
if 'reply' in ri.op[ri.op_mode]:
2419
ri.cw.p('err_free:')
2420
ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}")
2421
ri.cw.p(f"return {ret_err};")
2422
2423
ri.cw.block_end()
2424
2425
2426
def print_dump(ri):
2427
direction = "request"
2428
print_prototype(ri, direction, terminate=False)
2429
ri.cw.block_start()
2430
local_vars = ['struct ynl_dump_state yds = {};',
2431
'struct nlmsghdr *nlh;',
2432
'int err;']
2433
2434
if ri.struct['request'].fixed_header:
2435
local_vars += ['size_t hdr_len;',
2436
'void *hdr;']
2437
2438
if 'request' in ri.op[ri.op_mode]:
2439
local_vars += put_local_vars(ri.struct['request'])
2440
2441
ri.cw.write_func_lvar(local_vars)
2442
2443
ri.cw.p('yds.yarg.ys = ys;')
2444
ri.cw.p(f"yds.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
2445
ri.cw.p("yds.yarg.data = NULL;")
2446
ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});")
2447
ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;")
2448
if ri.op.value is not None:
2449
ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};')
2450
else:
2451
ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};')
2452
ri.cw.nl()
2453
if ri.family.is_classic():
2454
ri.cw.p(f"nlh = ynl_msg_start_dump(ys, {ri.op.enum_name});")
2455
else:
2456
ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
2457
2458
if ri.struct['request'].fixed_header:
2459
ri.cw.p("hdr_len = sizeof(req->_hdr);")
2460
ri.cw.p("hdr = ynl_nlmsg_put_extra_header(nlh, hdr_len);")
2461
ri.cw.p("memcpy(hdr, &req->_hdr, hdr_len);")
2462
ri.cw.nl()
2463
2464
if "request" in ri.op[ri.op_mode]:
2465
ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
2466
ri.cw.p(f"ys->req_hdr_len = {ri.fixed_hdr_len};")
2467
ri.cw.nl()
2468
for _, attr in ri.struct["request"].member_list():
2469
attr.attr_put(ri, "req")
2470
ri.cw.nl()
2471
2472
ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);')
2473
ri.cw.p('if (err < 0)')
2474
ri.cw.p('goto free_list;')
2475
ri.cw.nl()
2476
2477
ri.cw.p('return yds.first;')
2478
ri.cw.nl()
2479
ri.cw.p('free_list:')
2480
ri.cw.p(call_free(ri, rdir(direction), 'yds.first'))
2481
ri.cw.p('return NULL;')
2482
ri.cw.block_end()
2483
2484
2485
def call_free(ri, direction, var):
2486
return f"{op_prefix(ri, direction)}_free({var});"
2487
2488
2489
def free_arg_name(direction):
2490
if direction:
2491
return direction_to_suffix[direction][1:]
2492
return 'obj'
2493
2494
2495
def print_alloc_wrapper(ri, direction, struct=None):
2496
name = op_prefix(ri, direction)
2497
struct_name = name
2498
if ri.type_name_conflict:
2499
struct_name += '_'
2500
2501
args = ["void"]
2502
cnt = "1"
2503
if struct and struct.in_multi_val:
2504
args = ["unsigned int n"]
2505
cnt = "n"
2506
2507
ri.cw.write_func_prot(f'static inline struct {struct_name} *',
2508
f"{name}_alloc", args)
2509
ri.cw.block_start()
2510
ri.cw.p(f'return calloc({cnt}, sizeof(struct {struct_name}));')
2511
ri.cw.block_end()
2512
2513
2514
def print_free_prototype(ri, direction, suffix=';'):
2515
name = op_prefix(ri, direction)
2516
struct_name = name
2517
if ri.type_name_conflict:
2518
struct_name += '_'
2519
arg = free_arg_name(direction)
2520
ri.cw.write_func_prot('void', f"{name}_free", [f"struct {struct_name} *{arg}"], suffix=suffix)
2521
2522
2523
def print_nlflags_set(ri, direction):
2524
name = op_prefix(ri, direction)
2525
ri.cw.write_func_prot('static inline void', f"{name}_set_nlflags",
2526
[f"struct {name} *req", "__u16 nl_flags"])
2527
ri.cw.block_start()
2528
ri.cw.p('req->_nlmsg_flags = nl_flags;')
2529
ri.cw.block_end()
2530
ri.cw.nl()
2531
2532
2533
def _print_type(ri, direction, struct):
2534
suffix = f'_{ri.type_name}{direction_to_suffix[direction]}'
2535
if not direction and ri.type_name_conflict:
2536
suffix += '_'
2537
2538
if ri.op_mode == 'dump' and not ri.type_oneside:
2539
suffix += '_dump'
2540
2541
ri.cw.block_start(line=f"struct {ri.family.c_name}{suffix}")
2542
2543
if ri.needs_nlflags(direction):
2544
ri.cw.p('__u16 _nlmsg_flags;')
2545
ri.cw.nl()
2546
if struct.fixed_header:
2547
ri.cw.p(struct.fixed_header + ' _hdr;')
2548
ri.cw.nl()
2549
2550
for type_filter in ['present', 'len', 'count']:
2551
meta_started = False
2552
for _, attr in struct.member_list():
2553
line = attr.presence_member(ri.ku_space, type_filter)
2554
if line:
2555
if not meta_started:
2556
ri.cw.block_start(line="struct")
2557
meta_started = True
2558
ri.cw.p(line)
2559
if meta_started:
2560
ri.cw.block_end(line=f'_{type_filter};')
2561
ri.cw.nl()
2562
2563
for arg in struct.inherited:
2564
ri.cw.p(f"__u32 {arg};")
2565
2566
for _, attr in struct.member_list():
2567
attr.struct_member(ri)
2568
2569
ri.cw.block_end(line=';')
2570
ri.cw.nl()
2571
2572
2573
def print_type(ri, direction):
2574
_print_type(ri, direction, ri.struct[direction])
2575
2576
2577
def print_type_full(ri, struct):
2578
_print_type(ri, "", struct)
2579
2580
if struct.request and struct.in_multi_val:
2581
print_alloc_wrapper(ri, "", struct)
2582
ri.cw.nl()
2583
free_rsp_nested_prototype(ri)
2584
ri.cw.nl()
2585
2586
# Name conflicts are too hard to deal with with the current code base,
2587
# they are very rare so don't bother printing setters in that case.
2588
if ri.ku_space == 'user' and not ri.type_name_conflict:
2589
for _, attr in struct.member_list():
2590
attr.setter(ri, ri.attr_set, "", var="obj")
2591
ri.cw.nl()
2592
2593
2594
def print_type_helpers(ri, direction, deref=False):
2595
print_free_prototype(ri, direction)
2596
ri.cw.nl()
2597
2598
if ri.needs_nlflags(direction):
2599
print_nlflags_set(ri, direction)
2600
2601
if ri.ku_space == 'user' and direction == 'request':
2602
for _, attr in ri.struct[direction].member_list():
2603
attr.setter(ri, ri.attr_set, direction, deref=deref)
2604
ri.cw.nl()
2605
2606
2607
def print_req_type_helpers(ri):
2608
if ri.type_empty("request"):
2609
return
2610
print_alloc_wrapper(ri, "request")
2611
print_type_helpers(ri, "request")
2612
2613
2614
def print_rsp_type_helpers(ri):
2615
if 'reply' not in ri.op[ri.op_mode]:
2616
return
2617
print_type_helpers(ri, "reply")
2618
2619
2620
def print_parse_prototype(ri, direction, terminate=True):
2621
suffix = "_rsp" if direction == "reply" else "_req"
2622
term = ';' if terminate else ''
2623
2624
ri.cw.write_func_prot('void', f"{ri.op.render_name}{suffix}_parse",
2625
['const struct nlattr **tb',
2626
f"struct {ri.op.render_name}{suffix} *req"],
2627
suffix=term)
2628
2629
2630
def print_req_type(ri):
2631
if ri.type_empty("request"):
2632
return
2633
print_type(ri, "request")
2634
2635
2636
def print_req_free(ri):
2637
if 'request' not in ri.op[ri.op_mode]:
2638
return
2639
_free_type(ri, 'request', ri.struct['request'])
2640
2641
2642
def print_rsp_type(ri):
2643
if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]:
2644
direction = 'reply'
2645
elif ri.op_mode == 'event':
2646
direction = 'reply'
2647
else:
2648
return
2649
print_type(ri, direction)
2650
2651
2652
def print_wrapped_type(ri):
2653
ri.cw.block_start(line=f"{type_name(ri, 'reply')}")
2654
if ri.op_mode == 'dump':
2655
ri.cw.p(f"{type_name(ri, 'reply')} *next;")
2656
elif ri.op_mode == 'notify' or ri.op_mode == 'event':
2657
ri.cw.p('__u16 family;')
2658
ri.cw.p('__u8 cmd;')
2659
ri.cw.p('struct ynl_ntf_base_type *next;')
2660
ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);")
2661
ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__((aligned(8)));")
2662
ri.cw.block_end(line=';')
2663
ri.cw.nl()
2664
print_free_prototype(ri, 'reply')
2665
ri.cw.nl()
2666
2667
2668
def _free_type_members_iter(ri, struct):
2669
if struct.free_needs_iter():
2670
ri.cw.p('unsigned int i;')
2671
ri.cw.nl()
2672
2673
2674
def _free_type_members(ri, var, struct, ref=''):
2675
for _, attr in struct.member_list():
2676
attr.free(ri, var, ref)
2677
2678
2679
def _free_type(ri, direction, struct):
2680
var = free_arg_name(direction)
2681
2682
print_free_prototype(ri, direction, suffix='')
2683
ri.cw.block_start()
2684
_free_type_members_iter(ri, struct)
2685
_free_type_members(ri, var, struct)
2686
if direction:
2687
ri.cw.p(f'free({var});')
2688
ri.cw.block_end()
2689
ri.cw.nl()
2690
2691
2692
def free_rsp_nested_prototype(ri):
2693
print_free_prototype(ri, "")
2694
2695
2696
def free_rsp_nested(ri, struct):
2697
_free_type(ri, "", struct)
2698
2699
2700
def print_rsp_free(ri):
2701
if 'reply' not in ri.op[ri.op_mode]:
2702
return
2703
_free_type(ri, 'reply', ri.struct['reply'])
2704
2705
2706
def print_dump_type_free(ri):
2707
sub_type = type_name(ri, 'reply')
2708
2709
print_free_prototype(ri, 'reply', suffix='')
2710
ri.cw.block_start()
2711
ri.cw.p(f"{sub_type} *next = rsp;")
2712
ri.cw.nl()
2713
ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)')
2714
_free_type_members_iter(ri, ri.struct['reply'])
2715
ri.cw.p('rsp = next;')
2716
ri.cw.p('next = rsp->next;')
2717
ri.cw.nl()
2718
2719
_free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2720
ri.cw.p('free(rsp);')
2721
ri.cw.block_end()
2722
ri.cw.block_end()
2723
ri.cw.nl()
2724
2725
2726
def print_ntf_type_free(ri):
2727
print_free_prototype(ri, 'reply', suffix='')
2728
ri.cw.block_start()
2729
_free_type_members_iter(ri, ri.struct['reply'])
2730
_free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2731
ri.cw.p('free(rsp);')
2732
ri.cw.block_end()
2733
ri.cw.nl()
2734
2735
2736
def print_req_policy_fwd(cw, struct, ri=None, terminate=True):
2737
if terminate and ri and policy_should_be_static(struct.family):
2738
return
2739
2740
if terminate:
2741
prefix = 'extern '
2742
else:
2743
if ri and policy_should_be_static(struct.family):
2744
prefix = 'static '
2745
else:
2746
prefix = ''
2747
2748
suffix = ';' if terminate else ' = {'
2749
2750
max_attr = struct.attr_max_val
2751
if ri:
2752
name = ri.op.render_name
2753
if ri.op.dual_policy:
2754
name += '_' + ri.op_mode
2755
else:
2756
name = struct.render_name
2757
cw.p(f"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}")
2758
2759
2760
def print_req_policy(cw, struct, ri=None):
2761
if ri and ri.op:
2762
cw.ifdef_block(ri.op.get('config-cond', None))
2763
print_req_policy_fwd(cw, struct, ri=ri, terminate=False)
2764
for _, arg in struct.member_list():
2765
arg.attr_policy(cw)
2766
cw.p("};")
2767
cw.ifdef_block(None)
2768
cw.nl()
2769
2770
2771
def kernel_can_gen_family_struct(family):
2772
return family.proto == 'genetlink'
2773
2774
2775
def policy_should_be_static(family):
2776
return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family)
2777
2778
2779
def print_kernel_policy_ranges(family, cw):
2780
first = True
2781
for _, attr_set in family.attr_sets.items():
2782
if attr_set.subset_of:
2783
continue
2784
2785
for _, attr in attr_set.items():
2786
if not attr.request:
2787
continue
2788
if 'full-range' not in attr.checks:
2789
continue
2790
2791
if first:
2792
cw.p('/* Integer value ranges */')
2793
first = False
2794
2795
sign = '' if attr.type[0] == 'u' else '_signed'
2796
suffix = 'ULL' if attr.type[0] == 'u' else 'LL'
2797
cw.block_start(line=f'static const struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =')
2798
members = []
2799
if 'min' in attr.checks:
2800
members.append(('min', attr.get_limit_str('min', suffix=suffix)))
2801
if 'max' in attr.checks:
2802
members.append(('max', attr.get_limit_str('max', suffix=suffix)))
2803
cw.write_struct_init(members)
2804
cw.block_end(line=';')
2805
cw.nl()
2806
2807
2808
def print_kernel_policy_sparse_enum_validates(family, cw):
2809
first = True
2810
for _, attr_set in family.attr_sets.items():
2811
if attr_set.subset_of:
2812
continue
2813
2814
for _, attr in attr_set.items():
2815
if not attr.request:
2816
continue
2817
if not attr.enum_name:
2818
continue
2819
if 'sparse' not in attr.checks:
2820
continue
2821
2822
if first:
2823
cw.p('/* Sparse enums validation callbacks */')
2824
first = False
2825
2826
cw.write_func_prot('static int', f'{c_lower(attr.enum_name)}_validate',
2827
['const struct nlattr *attr', 'struct netlink_ext_ack *extack'])
2828
cw.block_start()
2829
cw.block_start(line=f'switch (nla_get_{attr["type"]}(attr))')
2830
enum = family.consts[attr['enum']]
2831
first_entry = True
2832
for entry in enum.entries.values():
2833
if first_entry:
2834
first_entry = False
2835
else:
2836
cw.p('fallthrough;')
2837
cw.p(f'case {entry.c_name}:')
2838
cw.p('return 0;')
2839
cw.block_end()
2840
cw.p('NL_SET_ERR_MSG_ATTR(extack, attr, "invalid enum value");')
2841
cw.p('return -EINVAL;')
2842
cw.block_end()
2843
cw.nl()
2844
2845
2846
def print_kernel_op_table_fwd(family, cw, terminate):
2847
exported = not kernel_can_gen_family_struct(family)
2848
2849
if not terminate or exported:
2850
cw.p(f"/* Ops table for {family.ident_name} */")
2851
2852
pol_to_struct = {'global': 'genl_small_ops',
2853
'per-op': 'genl_ops',
2854
'split': 'genl_split_ops'}
2855
struct_type = pol_to_struct[family.kernel_policy]
2856
2857
if not exported:
2858
cnt = ""
2859
elif family.kernel_policy == 'split':
2860
cnt = 0
2861
for op in family.ops.values():
2862
if 'do' in op:
2863
cnt += 1
2864
if 'dump' in op:
2865
cnt += 1
2866
else:
2867
cnt = len(family.ops)
2868
2869
qual = 'static const' if not exported else 'const'
2870
line = f"{qual} struct {struct_type} {family.c_name}_nl_ops[{cnt}]"
2871
if terminate:
2872
cw.p(f"extern {line};")
2873
else:
2874
cw.block_start(line=line + ' =')
2875
2876
if not terminate:
2877
return
2878
2879
cw.nl()
2880
for name in family.hooks['pre']['do']['list']:
2881
cw.write_func_prot('int', c_lower(name),
2882
['const struct genl_split_ops *ops',
2883
'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2884
for name in family.hooks['post']['do']['list']:
2885
cw.write_func_prot('void', c_lower(name),
2886
['const struct genl_split_ops *ops',
2887
'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2888
for name in family.hooks['pre']['dump']['list']:
2889
cw.write_func_prot('int', c_lower(name),
2890
['struct netlink_callback *cb'], suffix=';')
2891
for name in family.hooks['post']['dump']['list']:
2892
cw.write_func_prot('int', c_lower(name),
2893
['struct netlink_callback *cb'], suffix=';')
2894
2895
cw.nl()
2896
2897
for op_name, op in family.ops.items():
2898
if op.is_async:
2899
continue
2900
2901
if 'do' in op:
2902
name = c_lower(f"{family.ident_name}-nl-{op_name}-doit")
2903
cw.write_func_prot('int', name,
2904
['struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2905
2906
if 'dump' in op:
2907
name = c_lower(f"{family.ident_name}-nl-{op_name}-dumpit")
2908
cw.write_func_prot('int', name,
2909
['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix=';')
2910
cw.nl()
2911
2912
2913
def print_kernel_op_table_hdr(family, cw):
2914
print_kernel_op_table_fwd(family, cw, terminate=True)
2915
2916
2917
def print_kernel_op_table(family, cw):
2918
print_kernel_op_table_fwd(family, cw, terminate=False)
2919
if family.kernel_policy == 'global' or family.kernel_policy == 'per-op':
2920
for op_name, op in family.ops.items():
2921
if op.is_async:
2922
continue
2923
2924
cw.ifdef_block(op.get('config-cond', None))
2925
cw.block_start()
2926
members = [('cmd', op.enum_name)]
2927
if 'dont-validate' in op:
2928
members.append(('validate',
2929
' | '.join([c_upper('genl-dont-validate-' + x)
2930
for x in op['dont-validate']])), )
2931
for op_mode in ['do', 'dump']:
2932
if op_mode in op:
2933
name = c_lower(f"{family.ident_name}-nl-{op_name}-{op_mode}it")
2934
members.append((op_mode + 'it', name))
2935
if family.kernel_policy == 'per-op':
2936
struct = Struct(family, op['attribute-set'],
2937
type_list=op['do']['request']['attributes'])
2938
2939
name = c_lower(f"{family.ident_name}-{op_name}-nl-policy")
2940
members.append(('policy', name))
2941
members.append(('maxattr', struct.attr_max_val.enum_name))
2942
if 'flags' in op:
2943
members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in op['flags']])))
2944
cw.write_struct_init(members)
2945
cw.block_end(line=',')
2946
elif family.kernel_policy == 'split':
2947
cb_names = {'do': {'pre': 'pre_doit', 'post': 'post_doit'},
2948
'dump': {'pre': 'start', 'post': 'done'}}
2949
2950
for op_name, op in family.ops.items():
2951
for op_mode in ['do', 'dump']:
2952
if op.is_async or op_mode not in op:
2953
continue
2954
2955
cw.ifdef_block(op.get('config-cond', None))
2956
cw.block_start()
2957
members = [('cmd', op.enum_name)]
2958
if 'dont-validate' in op:
2959
dont_validate = []
2960
for x in op['dont-validate']:
2961
if op_mode == 'do' and x in ['dump', 'dump-strict']:
2962
continue
2963
if op_mode == "dump" and x == 'strict':
2964
continue
2965
dont_validate.append(x)
2966
2967
if dont_validate:
2968
members.append(('validate',
2969
' | '.join([c_upper('genl-dont-validate-' + x)
2970
for x in dont_validate])), )
2971
name = c_lower(f"{family.ident_name}-nl-{op_name}-{op_mode}it")
2972
if 'pre' in op[op_mode]:
2973
members.append((cb_names[op_mode]['pre'], c_lower(op[op_mode]['pre'])))
2974
members.append((op_mode + 'it', name))
2975
if 'post' in op[op_mode]:
2976
members.append((cb_names[op_mode]['post'], c_lower(op[op_mode]['post'])))
2977
if 'request' in op[op_mode]:
2978
struct = Struct(family, op['attribute-set'],
2979
type_list=op[op_mode]['request']['attributes'])
2980
2981
if op.dual_policy:
2982
name = c_lower(f"{family.ident_name}-{op_name}-{op_mode}-nl-policy")
2983
else:
2984
name = c_lower(f"{family.ident_name}-{op_name}-nl-policy")
2985
members.append(('policy', name))
2986
members.append(('maxattr', struct.attr_max_val.enum_name))
2987
flags = (op['flags'] if 'flags' in op else []) + ['cmd-cap-' + op_mode]
2988
members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in flags])))
2989
cw.write_struct_init(members)
2990
cw.block_end(line=',')
2991
cw.ifdef_block(None)
2992
2993
cw.block_end(line=';')
2994
cw.nl()
2995
2996
2997
def print_kernel_mcgrp_hdr(family, cw):
2998
if not family.mcgrps['list']:
2999
return
3000
3001
cw.block_start('enum')
3002
for grp in family.mcgrps['list']:
3003
grp_id = c_upper(f"{family.ident_name}-nlgrp-{grp['name']},")
3004
cw.p(grp_id)
3005
cw.block_end(';')
3006
cw.nl()
3007
3008
3009
def print_kernel_mcgrp_src(family, cw):
3010
if not family.mcgrps['list']:
3011
return
3012
3013
cw.block_start('static const struct genl_multicast_group ' + family.c_name + '_nl_mcgrps[] =')
3014
for grp in family.mcgrps['list']:
3015
name = grp['name']
3016
grp_id = c_upper(f"{family.ident_name}-nlgrp-{name}")
3017
cw.p('[' + grp_id + '] = { "' + name + '", },')
3018
cw.block_end(';')
3019
cw.nl()
3020
3021
3022
def print_kernel_family_struct_hdr(family, cw):
3023
if not kernel_can_gen_family_struct(family):
3024
return
3025
3026
cw.p(f"extern struct genl_family {family.c_name}_nl_family;")
3027
cw.nl()
3028
if 'sock-priv' in family.kernel_family:
3029
cw.p(f'void {family.c_name}_nl_sock_priv_init({family.kernel_family["sock-priv"]} *priv);')
3030
cw.p(f'void {family.c_name}_nl_sock_priv_destroy({family.kernel_family["sock-priv"]} *priv);')
3031
cw.nl()
3032
3033
3034
def print_kernel_family_struct_src(family, cw):
3035
if not kernel_can_gen_family_struct(family):
3036
return
3037
3038
if 'sock-priv' in family.kernel_family:
3039
# Generate "trampolines" to make CFI happy
3040
cw.write_func("static void", f"__{family.c_name}_nl_sock_priv_init",
3041
[f"{family.c_name}_nl_sock_priv_init(priv);"],
3042
["void *priv"])
3043
cw.nl()
3044
cw.write_func("static void", f"__{family.c_name}_nl_sock_priv_destroy",
3045
[f"{family.c_name}_nl_sock_priv_destroy(priv);"],
3046
["void *priv"])
3047
cw.nl()
3048
3049
cw.block_start(f"struct genl_family {family.ident_name}_nl_family __ro_after_init =")
3050
cw.p('.name\t\t= ' + family.fam_key + ',')
3051
cw.p('.version\t= ' + family.ver_key + ',')
3052
cw.p('.netnsok\t= true,')
3053
cw.p('.parallel_ops\t= true,')
3054
cw.p('.module\t\t= THIS_MODULE,')
3055
if family.kernel_policy == 'per-op':
3056
cw.p(f'.ops\t\t= {family.c_name}_nl_ops,')
3057
cw.p(f'.n_ops\t\t= ARRAY_SIZE({family.c_name}_nl_ops),')
3058
elif family.kernel_policy == 'split':
3059
cw.p(f'.split_ops\t= {family.c_name}_nl_ops,')
3060
cw.p(f'.n_split_ops\t= ARRAY_SIZE({family.c_name}_nl_ops),')
3061
if family.mcgrps['list']:
3062
cw.p(f'.mcgrps\t\t= {family.c_name}_nl_mcgrps,')
3063
cw.p(f'.n_mcgrps\t= ARRAY_SIZE({family.c_name}_nl_mcgrps),')
3064
if 'sock-priv' in family.kernel_family:
3065
cw.p(f'.sock_priv_size\t= sizeof({family.kernel_family["sock-priv"]}),')
3066
cw.p(f'.sock_priv_init\t= __{family.c_name}_nl_sock_priv_init,')
3067
cw.p(f'.sock_priv_destroy = __{family.c_name}_nl_sock_priv_destroy,')
3068
cw.block_end(';')
3069
3070
3071
def uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'):
3072
start_line = 'enum'
3073
if enum_name in obj:
3074
if obj[enum_name]:
3075
start_line = 'enum ' + c_lower(obj[enum_name])
3076
elif ckey and ckey in obj:
3077
start_line = 'enum ' + family.c_name + '_' + c_lower(obj[ckey])
3078
cw.block_start(line=start_line)
3079
3080
3081
def render_uapi_unified(family, cw, max_by_define, separate_ntf):
3082
max_name = c_upper(family.get('cmd-max-name', f"{family.op_prefix}MAX"))
3083
cnt_name = c_upper(family.get('cmd-cnt-name', f"__{family.op_prefix}MAX"))
3084
max_value = f"({cnt_name} - 1)"
3085
3086
uapi_enum_start(family, cw, family['operations'], 'enum-name')
3087
val = 0
3088
for op in family.msgs.values():
3089
if separate_ntf and ('notify' in op or 'event' in op):
3090
continue
3091
3092
suffix = ','
3093
if op.value != val:
3094
suffix = f" = {op.value},"
3095
val = op.value
3096
cw.p(op.enum_name + suffix)
3097
val += 1
3098
cw.nl()
3099
cw.p(cnt_name + ('' if max_by_define else ','))
3100
if not max_by_define:
3101
cw.p(f"{max_name} = {max_value}")
3102
cw.block_end(line=';')
3103
if max_by_define:
3104
cw.p(f"#define {max_name} {max_value}")
3105
cw.nl()
3106
3107
3108
def render_uapi_directional(family, cw, max_by_define):
3109
max_name = f"{family.op_prefix}USER_MAX"
3110
cnt_name = f"__{family.op_prefix}USER_CNT"
3111
max_value = f"({cnt_name} - 1)"
3112
3113
cw.block_start(line='enum')
3114
cw.p(c_upper(f'{family.name}_MSG_USER_NONE = 0,'))
3115
val = 0
3116
for op in family.msgs.values():
3117
if 'do' in op and 'event' not in op:
3118
suffix = ','
3119
if op.value and op.value != val:
3120
suffix = f" = {op.value},"
3121
val = op.value
3122
cw.p(op.enum_name + suffix)
3123
val += 1
3124
cw.nl()
3125
cw.p(cnt_name + ('' if max_by_define else ','))
3126
if not max_by_define:
3127
cw.p(f"{max_name} = {max_value}")
3128
cw.block_end(line=';')
3129
if max_by_define:
3130
cw.p(f"#define {max_name} {max_value}")
3131
cw.nl()
3132
3133
max_name = f"{family.op_prefix}KERNEL_MAX"
3134
cnt_name = f"__{family.op_prefix}KERNEL_CNT"
3135
max_value = f"({cnt_name} - 1)"
3136
3137
cw.block_start(line='enum')
3138
cw.p(c_upper(f'{family.name}_MSG_KERNEL_NONE = 0,'))
3139
val = 0
3140
for op in family.msgs.values():
3141
if ('do' in op and 'reply' in op['do']) or 'notify' in op or 'event' in op:
3142
enum_name = op.enum_name
3143
if 'event' not in op and 'notify' not in op:
3144
enum_name = f'{enum_name}_REPLY'
3145
3146
suffix = ','
3147
if op.value and op.value != val:
3148
suffix = f" = {op.value},"
3149
val = op.value
3150
cw.p(enum_name + suffix)
3151
val += 1
3152
cw.nl()
3153
cw.p(cnt_name + ('' if max_by_define else ','))
3154
if not max_by_define:
3155
cw.p(f"{max_name} = {max_value}")
3156
cw.block_end(line=';')
3157
if max_by_define:
3158
cw.p(f"#define {max_name} {max_value}")
3159
cw.nl()
3160
3161
3162
def render_uapi(family, cw):
3163
hdr_prot = f"_UAPI_LINUX_{c_upper(family.uapi_header_name)}_H"
3164
hdr_prot = hdr_prot.replace('/', '_')
3165
cw.p('#ifndef ' + hdr_prot)
3166
cw.p('#define ' + hdr_prot)
3167
cw.nl()
3168
3169
defines = [(family.fam_key, family["name"]),
3170
(family.ver_key, family.get('version', 1))]
3171
cw.writes_defines(defines)
3172
cw.nl()
3173
3174
defines = []
3175
for const in family['definitions']:
3176
if const.get('header'):
3177
continue
3178
3179
if const['type'] != 'const':
3180
cw.writes_defines(defines)
3181
defines = []
3182
cw.nl()
3183
3184
# Write kdoc for enum and flags (one day maybe also structs)
3185
if const['type'] == 'enum' or const['type'] == 'flags':
3186
enum = family.consts[const['name']]
3187
3188
if enum.header:
3189
continue
3190
3191
if enum.has_doc():
3192
if enum.has_entry_doc():
3193
cw.p('/**')
3194
doc = ''
3195
if 'doc' in enum:
3196
doc = ' - ' + enum['doc']
3197
cw.write_doc_line(enum.enum_name + doc)
3198
else:
3199
cw.p('/*')
3200
cw.write_doc_line(enum['doc'], indent=False)
3201
for entry in enum.entries.values():
3202
if entry.has_doc():
3203
doc = '@' + entry.c_name + ': ' + entry['doc']
3204
cw.write_doc_line(doc)
3205
cw.p(' */')
3206
3207
uapi_enum_start(family, cw, const, 'name')
3208
name_pfx = const.get('name-prefix', f"{family.ident_name}-{const['name']}-")
3209
for entry in enum.entries.values():
3210
suffix = ','
3211
if entry.value_change:
3212
suffix = f" = {entry.user_value()}" + suffix
3213
cw.p(entry.c_name + suffix)
3214
3215
if const.get('render-max', False):
3216
cw.nl()
3217
cw.p('/* private: */')
3218
if const['type'] == 'flags':
3219
max_name = c_upper(name_pfx + 'mask')
3220
max_val = f' = {enum.get_mask()},'
3221
cw.p(max_name + max_val)
3222
else:
3223
cnt_name = enum.enum_cnt_name
3224
max_name = c_upper(name_pfx + 'max')
3225
if not cnt_name:
3226
cnt_name = '__' + name_pfx + 'max'
3227
cw.p(c_upper(cnt_name) + ',')
3228
cw.p(max_name + ' = (' + c_upper(cnt_name) + ' - 1)')
3229
cw.block_end(line=';')
3230
cw.nl()
3231
elif const['type'] == 'const':
3232
name_pfx = const.get('name-prefix', f"{family.ident_name}-")
3233
defines.append([c_upper(family.get('c-define-name',
3234
f"{name_pfx}{const['name']}")),
3235
const['value']])
3236
3237
if defines:
3238
cw.writes_defines(defines)
3239
cw.nl()
3240
3241
max_by_define = family.get('max-by-define', False)
3242
3243
for _, attr_set in family.attr_sets.items():
3244
if attr_set.subset_of:
3245
continue
3246
3247
max_value = f"({attr_set.cnt_name} - 1)"
3248
3249
val = 0
3250
uapi_enum_start(family, cw, attr_set.yaml, 'enum-name')
3251
for _, attr in attr_set.items():
3252
suffix = ','
3253
if attr.value != val:
3254
suffix = f" = {attr.value},"
3255
val = attr.value
3256
val += 1
3257
cw.p(attr.enum_name + suffix)
3258
if attr_set.items():
3259
cw.nl()
3260
cw.p(attr_set.cnt_name + ('' if max_by_define else ','))
3261
if not max_by_define:
3262
cw.p(f"{attr_set.max_name} = {max_value}")
3263
cw.block_end(line=';')
3264
if max_by_define:
3265
cw.p(f"#define {attr_set.max_name} {max_value}")
3266
cw.nl()
3267
3268
# Commands
3269
separate_ntf = 'async-prefix' in family['operations']
3270
3271
if family.msg_id_model == 'unified':
3272
render_uapi_unified(family, cw, max_by_define, separate_ntf)
3273
elif family.msg_id_model == 'directional':
3274
render_uapi_directional(family, cw, max_by_define)
3275
else:
3276
raise Exception(f'Unsupported message enum-model {family.msg_id_model}')
3277
3278
if separate_ntf:
3279
uapi_enum_start(family, cw, family['operations'], enum_name='async-enum')
3280
for op in family.msgs.values():
3281
if separate_ntf and not ('notify' in op or 'event' in op):
3282
continue
3283
3284
suffix = ','
3285
if 'value' in op:
3286
suffix = f" = {op['value']},"
3287
cw.p(op.enum_name + suffix)
3288
cw.block_end(line=';')
3289
cw.nl()
3290
3291
# Multicast
3292
defines = []
3293
for grp in family.mcgrps['list']:
3294
name = grp['name']
3295
defines.append([c_upper(grp.get('c-define-name', f"{family.ident_name}-mcgrp-{name}")),
3296
f'{name}'])
3297
cw.nl()
3298
if defines:
3299
cw.writes_defines(defines)
3300
cw.nl()
3301
3302
cw.p(f'#endif /* {hdr_prot} */')
3303
3304
3305
def _render_user_ntf_entry(ri, op):
3306
if not ri.family.is_classic():
3307
ri.cw.block_start(line=f"[{op.enum_name}] = ")
3308
else:
3309
crud_op = ri.family.req_by_value[op.rsp_value]
3310
ri.cw.block_start(line=f"[{crud_op.enum_name}] = ")
3311
ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),")
3312
ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,")
3313
ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,")
3314
ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,")
3315
ri.cw.block_end(line=',')
3316
3317
3318
def render_user_family(family, cw, prototype):
3319
symbol = f'const struct ynl_family ynl_{family.c_name}_family'
3320
if prototype:
3321
cw.p(f'extern {symbol};')
3322
return
3323
3324
if family.ntfs:
3325
cw.block_start(line=f"static const struct ynl_ntf_info {family.c_name}_ntf_info[] = ")
3326
for ntf_op_name, ntf_op in family.ntfs.items():
3327
if 'notify' in ntf_op:
3328
op = family.ops[ntf_op['notify']]
3329
ri = RenderInfo(cw, family, "user", op, "notify")
3330
elif 'event' in ntf_op:
3331
ri = RenderInfo(cw, family, "user", ntf_op, "event")
3332
else:
3333
raise Exception('Invalid notification ' + ntf_op_name)
3334
_render_user_ntf_entry(ri, ntf_op)
3335
for op_name, op in family.ops.items():
3336
if 'event' not in op:
3337
continue
3338
ri = RenderInfo(cw, family, "user", op, "event")
3339
_render_user_ntf_entry(ri, op)
3340
cw.block_end(line=";")
3341
cw.nl()
3342
3343
cw.block_start(f'{symbol} = ')
3344
cw.p(f'.name\t\t= "{family.c_name}",')
3345
if family.is_classic():
3346
cw.p('.is_classic\t= true,')
3347
cw.p(f'.classic_id\t= {family.get("protonum")},')
3348
if family.is_classic():
3349
if family.fixed_header:
3350
cw.p(f'.hdr_len\t= sizeof(struct {c_lower(family.fixed_header)}),')
3351
elif family.fixed_header:
3352
cw.p(f'.hdr_len\t= sizeof(struct genlmsghdr) + sizeof(struct {c_lower(family.fixed_header)}),')
3353
else:
3354
cw.p('.hdr_len\t= sizeof(struct genlmsghdr),')
3355
if family.ntfs:
3356
cw.p(f".ntf_info\t= {family.c_name}_ntf_info,")
3357
cw.p(f".ntf_info_size\t= YNL_ARRAY_SIZE({family.c_name}_ntf_info),")
3358
cw.block_end(line=';')
3359
3360
3361
def family_contains_bitfield32(family):
3362
for _, attr_set in family.attr_sets.items():
3363
if attr_set.subset_of:
3364
continue
3365
for _, attr in attr_set.items():
3366
if attr.type == "bitfield32":
3367
return True
3368
return False
3369
3370
3371
def find_kernel_root(full_path):
3372
sub_path = ''
3373
while True:
3374
sub_path = os.path.join(os.path.basename(full_path), sub_path)
3375
full_path = os.path.dirname(full_path)
3376
maintainers = os.path.join(full_path, "MAINTAINERS")
3377
if os.path.exists(maintainers):
3378
return full_path, sub_path[:-1]
3379
3380
3381
def main():
3382
parser = argparse.ArgumentParser(description='Netlink simple parsing generator')
3383
parser.add_argument('--mode', dest='mode', type=str, required=True,
3384
choices=('user', 'kernel', 'uapi'))
3385
parser.add_argument('--spec', dest='spec', type=str, required=True)
3386
parser.add_argument('--header', dest='header', action='store_true', default=None)
3387
parser.add_argument('--source', dest='header', action='store_false')
3388
parser.add_argument('--user-header', nargs='+', default=[])
3389
parser.add_argument('--cmp-out', action='store_true', default=None,
3390
help='Do not overwrite the output file if the new output is identical to the old')
3391
parser.add_argument('--exclude-op', action='append', default=[])
3392
parser.add_argument('-o', dest='out_file', type=str, default=None)
3393
args = parser.parse_args()
3394
3395
if args.header is None:
3396
parser.error("--header or --source is required")
3397
3398
exclude_ops = [re.compile(expr) for expr in args.exclude_op]
3399
3400
try:
3401
parsed = Family(args.spec, exclude_ops)
3402
if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
3403
print('Spec license:', parsed.license)
3404
print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
3405
os.sys.exit(1)
3406
except yaml.YAMLError as exc:
3407
print(exc)
3408
os.sys.exit(1)
3409
return
3410
3411
cw = CodeWriter(BaseNlLib(), args.out_file, overwrite=(not args.cmp_out))
3412
3413
_, spec_kernel = find_kernel_root(args.spec)
3414
if args.mode == 'uapi' or args.header:
3415
cw.p(f'/* SPDX-License-Identifier: {parsed.license} */')
3416
else:
3417
cw.p(f'// SPDX-License-Identifier: {parsed.license}')
3418
cw.p("/* Do not edit directly, auto-generated from: */")
3419
cw.p(f"/*\t{spec_kernel} */")
3420
cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
3421
if args.exclude_op or args.user_header:
3422
line = ''
3423
line += ' --user-header '.join([''] + args.user_header)
3424
line += ' --exclude-op '.join([''] + args.exclude_op)
3425
cw.p(f'/* YNL-ARG{line} */')
3426
cw.nl()
3427
3428
if args.mode == 'uapi':
3429
render_uapi(parsed, cw)
3430
return
3431
3432
hdr_prot = f"_LINUX_{parsed.c_name.upper()}_GEN_H"
3433
if args.header:
3434
cw.p('#ifndef ' + hdr_prot)
3435
cw.p('#define ' + hdr_prot)
3436
cw.nl()
3437
3438
if args.out_file:
3439
hdr_file = os.path.basename(args.out_file[:-2]) + ".h"
3440
else:
3441
hdr_file = "generated_header_file.h"
3442
3443
if args.mode == 'kernel':
3444
cw.p('#include <net/netlink.h>')
3445
cw.p('#include <net/genetlink.h>')
3446
cw.nl()
3447
if not args.header:
3448
if args.out_file:
3449
cw.p(f'#include "{hdr_file}"')
3450
cw.nl()
3451
headers = ['uapi/' + parsed.uapi_header]
3452
headers += parsed.kernel_family.get('headers', [])
3453
else:
3454
cw.p('#include <stdlib.h>')
3455
cw.p('#include <string.h>')
3456
if args.header:
3457
cw.p('#include <linux/types.h>')
3458
if family_contains_bitfield32(parsed):
3459
cw.p('#include <linux/netlink.h>')
3460
else:
3461
cw.p(f'#include "{hdr_file}"')
3462
cw.p('#include "ynl.h"')
3463
headers = []
3464
for definition in parsed['definitions'] + parsed['attribute-sets']:
3465
if 'header' in definition:
3466
headers.append(definition['header'])
3467
if args.mode == 'user':
3468
headers.append(parsed.uapi_header)
3469
seen_header = []
3470
for one in headers:
3471
if one not in seen_header:
3472
cw.p(f"#include <{one}>")
3473
seen_header.append(one)
3474
cw.nl()
3475
3476
if args.mode == "user":
3477
if not args.header:
3478
cw.p("#include <linux/genetlink.h>")
3479
cw.nl()
3480
for one in args.user_header:
3481
cw.p(f'#include "{one}"')
3482
else:
3483
cw.p('struct ynl_sock;')
3484
cw.nl()
3485
render_user_family(parsed, cw, True)
3486
cw.nl()
3487
3488
if args.mode == "kernel":
3489
if args.header:
3490
for _, struct in sorted(parsed.pure_nested_structs.items()):
3491
if struct.request:
3492
cw.p('/* Common nested types */')
3493
break
3494
for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
3495
if struct.request:
3496
print_req_policy_fwd(cw, struct)
3497
cw.nl()
3498
3499
if parsed.kernel_policy == 'global':
3500
cw.p(f"/* Global operation policy for {parsed.name} */")
3501
3502
struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
3503
print_req_policy_fwd(cw, struct)
3504
cw.nl()
3505
3506
if parsed.kernel_policy in {'per-op', 'split'}:
3507
for op_name, op in parsed.ops.items():
3508
if 'do' in op and 'event' not in op:
3509
ri = RenderInfo(cw, parsed, args.mode, op, "do")
3510
print_req_policy_fwd(cw, ri.struct['request'], ri=ri)
3511
cw.nl()
3512
3513
print_kernel_op_table_hdr(parsed, cw)
3514
print_kernel_mcgrp_hdr(parsed, cw)
3515
print_kernel_family_struct_hdr(parsed, cw)
3516
else:
3517
print_kernel_policy_ranges(parsed, cw)
3518
print_kernel_policy_sparse_enum_validates(parsed, cw)
3519
3520
for _, struct in sorted(parsed.pure_nested_structs.items()):
3521
if struct.request:
3522
cw.p('/* Common nested types */')
3523
break
3524
for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
3525
if struct.request:
3526
print_req_policy(cw, struct)
3527
cw.nl()
3528
3529
if parsed.kernel_policy == 'global':
3530
cw.p(f"/* Global operation policy for {parsed.name} */")
3531
3532
struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
3533
print_req_policy(cw, struct)
3534
cw.nl()
3535
3536
for op_name, op in parsed.ops.items():
3537
if parsed.kernel_policy in {'per-op', 'split'}:
3538
for op_mode in ['do', 'dump']:
3539
if op_mode in op and 'request' in op[op_mode]:
3540
cw.p(f"/* {op.enum_name} - {op_mode} */")
3541
ri = RenderInfo(cw, parsed, args.mode, op, op_mode)
3542
print_req_policy(cw, ri.struct['request'], ri=ri)
3543
cw.nl()
3544
3545
print_kernel_op_table(parsed, cw)
3546
print_kernel_mcgrp_src(parsed, cw)
3547
print_kernel_family_struct_src(parsed, cw)
3548
3549
if args.mode == "user":
3550
if args.header:
3551
cw.p('/* Enums */')
3552
put_op_name_fwd(parsed, cw)
3553
3554
for name, const in parsed.consts.items():
3555
if isinstance(const, EnumSet):
3556
put_enum_to_str_fwd(parsed, cw, const)
3557
cw.nl()
3558
3559
cw.p('/* Common nested types */')
3560
for attr_set, struct in parsed.pure_nested_structs.items():
3561
ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
3562
print_type_full(ri, struct)
3563
3564
for op_name, op in parsed.ops.items():
3565
cw.p(f"/* ============== {op.enum_name} ============== */")
3566
3567
if 'do' in op and 'event' not in op:
3568
cw.p(f"/* {op.enum_name} - do */")
3569
ri = RenderInfo(cw, parsed, args.mode, op, "do")
3570
print_req_type(ri)
3571
print_req_type_helpers(ri)
3572
cw.nl()
3573
print_rsp_type(ri)
3574
print_rsp_type_helpers(ri)
3575
cw.nl()
3576
print_req_prototype(ri)
3577
cw.nl()
3578
3579
if 'dump' in op:
3580
cw.p(f"/* {op.enum_name} - dump */")
3581
ri = RenderInfo(cw, parsed, args.mode, op, 'dump')
3582
print_req_type(ri)
3583
print_req_type_helpers(ri)
3584
if not ri.type_consistent or ri.type_oneside:
3585
print_rsp_type(ri)
3586
print_wrapped_type(ri)
3587
print_dump_prototype(ri)
3588
cw.nl()
3589
3590
if op.has_ntf:
3591
cw.p(f"/* {op.enum_name} - notify */")
3592
ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
3593
if not ri.type_consistent:
3594
raise Exception(f'Only notifications with consistent types supported ({op.name})')
3595
print_wrapped_type(ri)
3596
3597
for op_name, op in parsed.ntfs.items():
3598
if 'event' in op:
3599
ri = RenderInfo(cw, parsed, args.mode, op, 'event')
3600
cw.p(f"/* {op.enum_name} - event */")
3601
print_rsp_type(ri)
3602
cw.nl()
3603
print_wrapped_type(ri)
3604
cw.nl()
3605
else:
3606
cw.p('/* Enums */')
3607
put_op_name(parsed, cw)
3608
3609
for name, const in parsed.consts.items():
3610
if isinstance(const, EnumSet):
3611
put_enum_to_str(parsed, cw, const)
3612
cw.nl()
3613
3614
has_recursive_nests = False
3615
cw.p('/* Policies */')
3616
for struct in parsed.pure_nested_structs.values():
3617
if struct.recursive:
3618
put_typol_fwd(cw, struct)
3619
has_recursive_nests = True
3620
if has_recursive_nests:
3621
cw.nl()
3622
for struct in parsed.pure_nested_structs.values():
3623
put_typol(cw, struct)
3624
for name in parsed.root_sets:
3625
struct = Struct(parsed, name)
3626
put_typol(cw, struct)
3627
3628
cw.p('/* Common nested types */')
3629
if has_recursive_nests:
3630
for attr_set, struct in parsed.pure_nested_structs.items():
3631
ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
3632
free_rsp_nested_prototype(ri)
3633
if struct.request:
3634
put_req_nested_prototype(ri, struct)
3635
if struct.reply:
3636
parse_rsp_nested_prototype(ri, struct)
3637
cw.nl()
3638
for attr_set, struct in parsed.pure_nested_structs.items():
3639
ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
3640
3641
free_rsp_nested(ri, struct)
3642
if struct.request:
3643
put_req_nested(ri, struct)
3644
if struct.reply:
3645
parse_rsp_nested(ri, struct)
3646
3647
for op_name, op in parsed.ops.items():
3648
cw.p(f"/* ============== {op.enum_name} ============== */")
3649
if 'do' in op and 'event' not in op:
3650
cw.p(f"/* {op.enum_name} - do */")
3651
ri = RenderInfo(cw, parsed, args.mode, op, "do")
3652
print_req_free(ri)
3653
print_rsp_free(ri)
3654
parse_rsp_msg(ri)
3655
print_req(ri)
3656
cw.nl()
3657
3658
if 'dump' in op:
3659
cw.p(f"/* {op.enum_name} - dump */")
3660
ri = RenderInfo(cw, parsed, args.mode, op, "dump")
3661
if not ri.type_consistent or ri.type_oneside:
3662
parse_rsp_msg(ri, deref=True)
3663
print_req_free(ri)
3664
print_dump_type_free(ri)
3665
print_dump(ri)
3666
cw.nl()
3667
3668
if op.has_ntf:
3669
cw.p(f"/* {op.enum_name} - notify */")
3670
ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
3671
if not ri.type_consistent:
3672
raise Exception(f'Only notifications with consistent types supported ({op.name})')
3673
print_ntf_type_free(ri)
3674
3675
for op_name, op in parsed.ntfs.items():
3676
if 'event' in op:
3677
cw.p(f"/* {op.enum_name} - event */")
3678
3679
ri = RenderInfo(cw, parsed, args.mode, op, "do")
3680
parse_rsp_msg(ri)
3681
3682
ri = RenderInfo(cw, parsed, args.mode, op, "event")
3683
print_ntf_type_free(ri)
3684
cw.nl()
3685
render_user_family(parsed, cw, False)
3686
3687
if args.header:
3688
cw.p(f'#endif /* {hdr_prot} */')
3689
3690
3691
if __name__ == "__main__":
3692
main()
3693
3694