tools: Add xdrgen

Add a Python-based tool for translating XDR specifications into XDR
encoder and decoder functions written in the Linux kernel's C coding
style. The generator attempts to match the usual C coding style of
the Linux kernel's SunRPC consumers.

This approach is similar to the netlink code generator in
tools/net/ynl .

The maintainability benefits of machine-generated XDR code include:

- Stronger type checking
- Reduces the number of bugs introduced by human error
- Makes the XDR code easier to audit and analyze
- Enables rapid prototyping of new RPC-based protocols
- Hardens the layering between protocol logic and marshaling
- Makes it easier to add observability on demand
- Unit tests might be built for both the tool and (automatically)
  for the generated code

In addition, converting the XDR layer to use memory-safe languages
such as Rust will be easier if much of the code can be converted
automatically.

Tested-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
Chuck Lever 2024-09-13 14:08:13 -04:00
parent 45bb63ed20
commit 4b132aacb0
153 changed files with 4196 additions and 0 deletions

View File

@ -0,0 +1,243 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* This header defines XDR data type primitives specified in
* Section 4 of RFC 4506, used by RPC programs implemented
* in the Linux kernel.
*/
#ifndef _SUNRPC_XDRGEN__BUILTINS_H_
#define _SUNRPC_XDRGEN__BUILTINS_H_
#include <linux/sunrpc/xdr.h>
static inline bool
xdrgen_decode_void(struct xdr_stream *xdr)
{
return true;
}
static inline bool
xdrgen_encode_void(struct xdr_stream *xdr)
{
return true;
}
static inline bool
xdrgen_decode_bool(struct xdr_stream *xdr, bool *ptr)
{
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*ptr = (*p != xdr_zero);
return true;
}
static inline bool
xdrgen_encode_bool(struct xdr_stream *xdr, bool val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*p = val ? xdr_one : xdr_zero;
return true;
}
static inline bool
xdrgen_decode_int(struct xdr_stream *xdr, s32 *ptr)
{
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*ptr = be32_to_cpup(p);
return true;
}
static inline bool
xdrgen_encode_int(struct xdr_stream *xdr, s32 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*p = cpu_to_be32(val);
return true;
}
static inline bool
xdrgen_decode_unsigned_int(struct xdr_stream *xdr, u32 *ptr)
{
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*ptr = be32_to_cpup(p);
return true;
}
static inline bool
xdrgen_encode_unsigned_int(struct xdr_stream *xdr, u32 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*p = cpu_to_be32(val);
return true;
}
static inline bool
xdrgen_decode_long(struct xdr_stream *xdr, s32 *ptr)
{
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*ptr = be32_to_cpup(p);
return true;
}
static inline bool
xdrgen_encode_long(struct xdr_stream *xdr, s32 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*p = cpu_to_be32(val);
return true;
}
static inline bool
xdrgen_decode_unsigned_long(struct xdr_stream *xdr, u32 *ptr)
{
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*ptr = be32_to_cpup(p);
return true;
}
static inline bool
xdrgen_encode_unsigned_long(struct xdr_stream *xdr, u32 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
if (unlikely(!p))
return false;
*p = cpu_to_be32(val);
return true;
}
static inline bool
xdrgen_decode_hyper(struct xdr_stream *xdr, s64 *ptr)
{
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2);
if (unlikely(!p))
return false;
*ptr = get_unaligned_be64(p);
return true;
}
static inline bool
xdrgen_encode_hyper(struct xdr_stream *xdr, s64 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
if (unlikely(!p))
return false;
put_unaligned_be64(val, p);
return true;
}
static inline bool
xdrgen_decode_unsigned_hyper(struct xdr_stream *xdr, u64 *ptr)
{
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2);
if (unlikely(!p))
return false;
*ptr = get_unaligned_be64(p);
return true;
}
static inline bool
xdrgen_encode_unsigned_hyper(struct xdr_stream *xdr, u64 val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
if (unlikely(!p))
return false;
put_unaligned_be64(val, p);
return true;
}
static inline bool
xdrgen_decode_string(struct xdr_stream *xdr, string *ptr, u32 maxlen)
{
__be32 *p;
u32 len;
if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT))
return false;
if (unlikely(maxlen && len > maxlen))
return false;
if (len != 0) {
p = xdr_inline_decode(xdr, len);
if (unlikely(!p))
return false;
ptr->data = (unsigned char *)p;
}
ptr->len = len;
return true;
}
static inline bool
xdrgen_encode_string(struct xdr_stream *xdr, string val, u32 maxlen)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len));
if (unlikely(!p))
return false;
xdr_encode_opaque(p, val.data, val.len);
return true;
}
static inline bool
xdrgen_decode_opaque(struct xdr_stream *xdr, opaque *ptr, u32 maxlen)
{
__be32 *p;
u32 len;
if (unlikely(xdr_stream_decode_u32(xdr, &len) != XDR_UNIT))
return false;
if (unlikely(maxlen && len > maxlen))
return false;
if (len != 0) {
p = xdr_inline_decode(xdr, len);
if (unlikely(!p))
return false;
ptr->data = (u8 *)p;
}
ptr->len = len;
return true;
}
static inline bool
xdrgen_encode_opaque(struct xdr_stream *xdr, opaque val)
{
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len));
if (unlikely(!p))
return false;
xdr_encode_opaque(p, val.data, val.len);
return true;
}
#endif /* _SUNRPC_XDRGEN__BUILTINS_H_ */

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* This header defines XDR data type primitives specified in
* Section 4 of RFC 4506, used by RPC programs implemented
* in the Linux kernel.
*/
#ifndef _SUNRPC_XDRGEN__DEFS_H_
#define _SUNRPC_XDRGEN__DEFS_H_
#define TRUE (true)
#define FALSE (false)
typedef struct {
u32 len;
unsigned char *data;
} string;
typedef struct {
u32 len;
u8 *data;
} opaque;
#endif /* _SUNRPC_XDRGEN__DEFS_H_ */

2
tools/net/sunrpc/xdrgen/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__
generators/__pycache__

View File

@ -0,0 +1,244 @@
xdrgen - Linux Kernel XDR code generator
Introduction
------------
SunRPC programs are typically specified using a language defined by
RFC 4506. In fact, all IETF-published NFS specifications provide a
description of the specified protocol using this language.
Since the 1990's, user space consumers of SunRPC have had access to
a tool that could read such XDR specifications and then generate C
code that implements the RPC portions of that protocol. This tool is
called rpcgen.
This RPC-level code is code that handles input directly from the
network, and thus a high degree of memory safety and sanity checking
is needed to help ensure proper levels of security. Bugs in this
code can have significant impact on security and performance.
However, it is code that is repetitive and tedious to write by hand.
The C code generated by rpcgen makes extensive use of the facilities
of the user space TI-RPC library and libc. Furthermore, the dialect
of the generated code is very traditional K&R C.
The Linux kernel's implementation of SunRPC-based protocols hand-roll
their XDR implementation. There are two main reasons for this:
1. libtirpc (and its predecessors) operate only in user space. The
kernel's RPC implementation and its API are significantly
different than libtirpc.
2. rpcgen-generated code is believed to be less efficient than code
that is hand-written.
These days, gcc and its kin are capable of optimizing code better
than human authors. There are only a few instances where writing
XDR code by hand will make a measurable performance different.
In addition, the current hand-written code in the Linux kernel is
difficult to audit and prove that it implements exactly what is in
the protocol specification.
In order to accrue the benefits of machine-generated XDR code in the
kernel, a tool is needed that will output C code that works against
the kernel's SunRPC implementation rather than libtirpc.
Enter xdrgen.
Dependencies
------------
These dependencies are typically packaged by Linux distributions:
- python3
- python3-lark
- python3-jinja2
These dependencies are available via PyPi:
- pip install 'lark[interegular]'
XDR Specifications
------------------
When adding a new protocol implementation to the kernel, the XDR
specification can be derived by feeding a .txt copy of the RFC to
the script located in tools/net/sunrpc/extract.sh.
$ extract.sh < rfc0001.txt > new2.x
Operation
---------
Once a .x file is available, use xdrgen to generate source and
header files containing an implementation of XDR encoding and
decoding functions for the specified protocol.
$ ./xdrgen definitions new2.x > include/linux/sunrpc/xdrgen/new2.h
$ ./xdrgen declarations new2.x > new2xdr_gen.h
and
$ ./xdrgen source new2.x > new2xdr_gen.c
The files are ready to use for a server-side protocol implementation,
or may be used as a guide for implementing these routines by hand.
By default, the only comments added to this code are kdoc comments
that appear directly in front of the public per-procedure APIs. For
deeper introspection, specifying the "--annotate" flag will insert
additional comments in the generated code to help readers match the
generated code to specific parts of the XDR specification.
Because the generated code is targeted for the Linux kernel, it
is tagged with a GPLv2-only license.
The xdrgen tool can also provide lexical and syntax checking of
an XDR specification:
$ ./xdrgen lint xdr/new.x
How It Works
------------
xdrgen does not use machine learning to generate source code. The
translation is entirely deterministic.
RFC 4506 Section 6 contains a BNF grammar of the XDR specification
language. The grammar has been adapted for use by the Python Lark
module.
The xdr.ebnf file in this directory contains the grammar used to
parse XDR specifications. xdrgen configures Lark using the grammar
in xdr.ebnf. Lark parses the target XDR specification using this
grammar, creating a parse tree.
xdrgen then transforms the parse tree into an abstract syntax tree.
This tree is passed to a series of code generators.
The generators are implemented as Python classes residing in the
generators/ directory. Each generator emits code created from Jinja2
templates stored in the templates/ directory.
The source code is generated in the same order in which they appear
in the specification to ensure the generated code compiles. This
conforms with the behavior of rpcgen.
xdrgen assumes that the generated source code is further compiled by
a compiler that can optimize in a number of ways, including:
- Unused functions are discarded (ie, not added to the executable)
- Aggressive function inlining removes unnecessary stack frames
- Single-arm switch statements are replaced by a single conditional
branch
And so on.
Pragmas
-------
Pragma directives specify exceptions to the normal generation of
encoding and decoding functions. Currently one directive is
implemented: "public".
Pragma exclude
------ -------
pragma exclude <RPC procedure> ;
In some cases, a procedure encoder or decoder function might need
special processing that cannot be automatically generated. The
automatically-generated functions might conflict or interfere with
the hand-rolled function. To avoid editing the generated source code
by hand, a pragma can specify that the procedure's encoder and
decoder functions are not included in the generated header and
source.
For example:
pragma exclude NFSPROC3_READDIRPLUS;
Excludes the decoder function for the READDIRPLUS argument and the
encoder function for the READDIRPLUS result.
Note that because data item encoder and decoder functions are
defined "static __maybe_unused", subsequent compilation
automatically excludes data item encoder and decoder functions that
are used only by excluded procedure.
Pragma header
------ ------
pragma header <string> ;
Provide a name to use for the header file. For example:
pragma header nlm4;
Adds
#include "nlm4xdr_gen.h"
to the generated source file.
Pragma public
------ ------
pragma public <XDR data item> ;
Normally XDR encoder and decoder functions are "static". In case an
implementer wants to call these functions from other source code,
s/he can add a public pragma in the input .x file to indicate a set
of functions that should get a prototype in the generated header,
and the function definitions will not be declared static.
For example:
pragma public nfsstat3;
Adds these prototypes in the generated header:
bool xdrgen_decode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 *ptr);
bool xdrgen_encode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 value);
And, in the generated source code, both of these functions appear
without the "static __maybe_unused" modifiers.
Future Work
-----------
Finish implementing XDR pointer and list types.
Generate client-side procedure functions
Expand the README into a user guide similar to rpcgen(1)
Add more pragma directives:
* @pages -- use xdr_read/write_pages() for the specified opaque
field
* @skip -- do not decode, but rather skip, the specified argument
field
Enable something like a #include to dynamically insert the content
of other specification files
Properly support line-by-line pass-through via the "%" decorator
Build a unit test suite for verifying translation of XDR language
into compilable code
Add a command-line option to insert trace_printk call sites in the
generated source code, for improved (temporary) observability
Generate kernel Rust code as well as C code

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
# Just to make sphinx-apidoc document this directory

View File

@ -0,0 +1,113 @@
# SPDX-License-Identifier: GPL-2.0
"""Define a base code generator class"""
import sys
from jinja2 import Environment, FileSystemLoader, Template
from xdr_ast import _XdrAst, Specification, _RpcProgram, _XdrTypeSpecifier
from xdr_ast import public_apis, pass_by_reference, get_header_name
from xdr_parse import get_xdr_annotate
def create_jinja2_environment(language: str, xdr_type: str) -> Environment:
"""Open a set of templates based on output language"""
match language:
case "C":
environment = Environment(
loader=FileSystemLoader(sys.path[0] + "/templates/C/" + xdr_type + "/"),
trim_blocks=True,
lstrip_blocks=True,
)
environment.globals["annotate"] = get_xdr_annotate()
environment.globals["public_apis"] = public_apis
environment.globals["pass_by_reference"] = pass_by_reference
return environment
case _:
raise NotImplementedError("Language not supported")
def get_jinja2_template(
environment: Environment, template_type: str, template_name: str
) -> Template:
"""Retrieve a Jinja2 template for emitting source code"""
return environment.get_template(template_type + "/" + template_name + ".j2")
def find_xdr_program_name(root: Specification) -> str:
"""Retrieve the RPC program name from an abstract syntax tree"""
raw_name = get_header_name()
if raw_name != "none":
return raw_name.lower()
for definition in root.definitions:
if isinstance(definition.value, _RpcProgram):
raw_name = definition.value.name
return raw_name.lower().removesuffix("_program").removesuffix("_prog")
return "noprog"
def header_guard_infix(filename: str) -> str:
"""Extract the header guard infix from the specification filename"""
basename = filename.split("/")[-1]
program = basename.replace(".x", "")
return program.upper()
def kernel_c_type(spec: _XdrTypeSpecifier) -> str:
"""Return name of C type"""
builtin_native_c_type = {
"bool": "bool",
"int": "s32",
"unsigned_int": "u32",
"long": "s32",
"unsigned_long": "u32",
"hyper": "s64",
"unsigned_hyper": "u64",
}
if spec.type_name in builtin_native_c_type:
return builtin_native_c_type[spec.type_name]
return spec.type_name
class Boilerplate:
"""Base class to generate boilerplate for source files"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
raise NotImplementedError("No language support defined")
def emit_declaration(self, filename: str, root: Specification) -> None:
"""Emit declaration header boilerplate"""
raise NotImplementedError("Header boilerplate generation not supported")
def emit_definition(self, filename: str, root: Specification) -> None:
"""Emit definition header boilerplate"""
raise NotImplementedError("Header boilerplate generation not supported")
def emit_source(self, filename: str, root: Specification) -> None:
"""Emit generic source code for this XDR type"""
raise NotImplementedError("Source boilerplate generation not supported")
class SourceGenerator:
"""Base class to generate header and source code for XDR types"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
raise NotImplementedError("No language support defined")
def emit_declaration(self, node: _XdrAst) -> None:
"""Emit one function declaration for this XDR type"""
raise NotImplementedError("Declaration generation not supported")
def emit_decoder(self, node: _XdrAst) -> None:
"""Emit one decoder function for this XDR type"""
raise NotImplementedError("Decoder generation not supported")
def emit_definition(self, node: _XdrAst) -> None:
"""Emit one definition for this XDR type"""
raise NotImplementedError("Definition generation not supported")
def emit_encoder(self, node: _XdrAst) -> None:
"""Emit one encoder function for this XDR type"""
raise NotImplementedError("Encoder generation not supported")

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate code to handle XDR constants"""
from generators import SourceGenerator, create_jinja2_environment
from xdr_ast import _XdrConstant
class XdrConstantGenerator(SourceGenerator):
"""Generate source code for XDR constants"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "constants")
self.peer = peer
def emit_definition(self, node: _XdrConstant) -> None:
"""Emit one definition for a constant"""
template = self.environment.get_template("definition.j2")
print(template.render(name=node.name, value=node.value))

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate code to handle XDR enum types"""
from generators import SourceGenerator, create_jinja2_environment
from xdr_ast import _XdrEnum, public_apis
class XdrEnumGenerator(SourceGenerator):
"""Generate source code for XDR enum types"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "enum")
self.peer = peer
def emit_declaration(self, node: _XdrEnum) -> None:
"""Emit one declaration pair for an XDR enum type"""
if node.name in public_apis:
template = self.environment.get_template("declaration/close.j2")
print(template.render(name=node.name))
def emit_definition(self, node: _XdrEnum) -> None:
"""Emit one definition for an XDR enum type"""
template = self.environment.get_template("definition/open.j2")
print(template.render(name=node.name))
template = self.environment.get_template("definition/enumerator.j2")
for enumerator in node.enumerators:
print(template.render(name=enumerator.name, value=enumerator.value))
template = self.environment.get_template("definition/close.j2")
print(template.render(name=node.name))
def emit_decoder(self, node: _XdrEnum) -> None:
"""Emit one decoder function for an XDR enum type"""
template = self.environment.get_template("decoder/enum.j2")
print(template.render(name=node.name))
def emit_encoder(self, node: _XdrEnum) -> None:
"""Emit one encoder function for an XDR enum type"""
template = self.environment.get_template("encoder/enum.j2")
print(template.render(name=node.name))

View File

@ -0,0 +1,33 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate header bottom boilerplate"""
import os.path
import time
from generators import Boilerplate, header_guard_infix
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import Specification
class XdrHeaderBottomGenerator(Boilerplate):
"""Generate header boilerplate"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "header_bottom")
self.peer = peer
def emit_declaration(self, filename: str, root: Specification) -> None:
"""Emit the bottom header guard"""
template = get_jinja2_template(self.environment, "declaration", "header")
print(template.render(infix=header_guard_infix(filename)))
def emit_definition(self, filename: str, root: Specification) -> None:
"""Emit the bottom header guard"""
template = get_jinja2_template(self.environment, "definition", "header")
print(template.render(infix=header_guard_infix(filename)))
def emit_source(self, filename: str, root: Specification) -> None:
pass

View File

@ -0,0 +1,45 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate header top boilerplate"""
import os.path
import time
from generators import Boilerplate, header_guard_infix
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import Specification
class XdrHeaderTopGenerator(Boilerplate):
"""Generate header boilerplate"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "header_top")
self.peer = peer
def emit_declaration(self, filename: str, root: Specification) -> None:
"""Emit the top header guard"""
template = get_jinja2_template(self.environment, "declaration", "header")
print(
template.render(
infix=header_guard_infix(filename),
filename=filename,
mtime=time.ctime(os.path.getmtime(filename)),
)
)
def emit_definition(self, filename: str, root: Specification) -> None:
"""Emit the top header guard"""
template = get_jinja2_template(self.environment, "definition", "header")
print(
template.render(
infix=header_guard_infix(filename),
filename=filename,
mtime=time.ctime(os.path.getmtime(filename)),
)
)
def emit_source(self, filename: str, root: Specification) -> None:
pass

View File

@ -0,0 +1,272 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate code to handle XDR pointer types"""
from jinja2 import Environment
from generators import SourceGenerator, kernel_c_type
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrVariableLengthString
from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
from xdr_ast import _XdrOptionalData, _XdrPointer, _XdrDeclaration
from xdr_ast import public_apis
def emit_pointer_declaration(environment: Environment, node: _XdrPointer) -> None:
"""Emit a declaration pair for an XDR pointer type"""
if node.name in public_apis:
template = get_jinja2_template(environment, "declaration", "close")
print(template.render(name=node.name))
def emit_pointer_member_definition(
environment: Environment, field: _XdrDeclaration
) -> None:
"""Emit a definition for one field in an XDR struct"""
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "definition", field.template)
print(template.render(name=field.name))
elif isinstance(field, _XdrVariableLengthString):
template = get_jinja2_template(environment, "definition", field.template)
print(template.render(name=field.name))
elif isinstance(field, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
type=kernel_c_type(field.spec),
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrOptionalData):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
)
)
def emit_pointer_definition(environment: Environment, node: _XdrPointer) -> None:
"""Emit a definition for an XDR pointer type"""
template = get_jinja2_template(environment, "definition", "open")
print(template.render(name=node.name))
for field in node.fields[0:-1]:
emit_pointer_member_definition(environment, field)
template = get_jinja2_template(environment, "definition", "close")
print(template.render(name=node.name))
def emit_pointer_member_decoder(
environment: Environment, field: _XdrDeclaration
) -> None:
"""Emit a decoder for one field in an XDR pointer"""
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrVariableLengthString):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
size=field.size,
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
maxsize=field.maxsize,
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrOptionalData):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
classifier=field.spec.c_classifier,
)
)
def emit_pointer_decoder(environment: Environment, node: _XdrPointer) -> None:
"""Emit one decoder function for an XDR pointer type"""
template = get_jinja2_template(environment, "decoder", "open")
print(template.render(name=node.name))
for field in node.fields[0:-1]:
emit_pointer_member_decoder(environment, field)
template = get_jinja2_template(environment, "decoder", "close")
print(template.render())
def emit_pointer_member_encoder(
environment: Environment, field: _XdrDeclaration
) -> None:
"""Emit an encoder for one field in a XDR pointer"""
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
)
)
elif isinstance(field, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrVariableLengthString):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrOptionalData):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
classifier=field.spec.c_classifier,
)
)
def emit_pointer_encoder(environment: Environment, node: _XdrPointer) -> None:
"""Emit one encoder function for an XDR pointer type"""
template = get_jinja2_template(environment, "encoder", "open")
print(template.render(name=node.name))
for field in node.fields[0:-1]:
emit_pointer_member_encoder(environment, field)
template = get_jinja2_template(environment, "encoder", "close")
print(template.render())
class XdrPointerGenerator(SourceGenerator):
"""Generate source code for XDR pointer"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "pointer")
self.peer = peer
def emit_declaration(self, node: _XdrPointer) -> None:
"""Emit one declaration pair for an XDR pointer type"""
emit_pointer_declaration(self.environment, node)
def emit_definition(self, node: _XdrPointer) -> None:
"""Emit one declaration for an XDR pointer type"""
emit_pointer_definition(self.environment, node)
def emit_decoder(self, node: _XdrPointer) -> None:
"""Emit one decoder function for an XDR pointer type"""
emit_pointer_decoder(self.environment, node)
def emit_encoder(self, node: _XdrPointer) -> None:
"""Emit one encoder function for an XDR pointer type"""
emit_pointer_encoder(self.environment, node)

View File

@ -0,0 +1,168 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate code for an RPC program's procedures"""
from jinja2 import Environment
from generators import SourceGenerator, create_jinja2_environment
from xdr_ast import _RpcProgram, _RpcVersion, excluded_apis
def emit_version_definitions(
environment: Environment, program: str, version: _RpcVersion
) -> None:
"""Emit procedure numbers for each RPC version's procedures"""
template = environment.get_template("definition/open.j2")
print(template.render(program=program.upper()))
template = environment.get_template("definition/procedure.j2")
for procedure in version.procedures:
if procedure.name not in excluded_apis:
print(
template.render(
name=procedure.name,
value=procedure.number,
)
)
template = environment.get_template("definition/close.j2")
print(template.render())
def emit_version_declarations(
environment: Environment, program: str, version: _RpcVersion
) -> None:
"""Emit declarations for each RPC version's procedures"""
arguments = set()
for procedure in version.procedures:
if procedure.name not in excluded_apis:
arguments.add(procedure.argument.type_name)
if len(arguments) > 0:
print("")
template = environment.get_template("declaration/argument.j2")
for argument in arguments:
print(template.render(program=program, argument=argument))
results = set()
for procedure in version.procedures:
if procedure.name not in excluded_apis:
results.add(procedure.result.type_name)
if len(results) > 0:
print("")
template = environment.get_template("declaration/result.j2")
for result in results:
print(template.render(program=program, result=result))
def emit_version_argument_decoders(
environment: Environment, program: str, version: _RpcVersion
) -> None:
"""Emit server argument decoders for each RPC version's procedures"""
arguments = set()
for procedure in version.procedures:
if procedure.name not in excluded_apis:
arguments.add(procedure.argument.type_name)
template = environment.get_template("decoder/argument.j2")
for argument in arguments:
print(template.render(program=program, argument=argument))
def emit_version_result_decoders(
environment: Environment, program: str, version: _RpcVersion
) -> None:
"""Emit client result decoders for each RPC version's procedures"""
results = set()
for procedure in version.procedures:
if procedure.name not in excluded_apis:
results.add(procedure.result.type_name)
template = environment.get_template("decoder/result.j2")
for result in results:
print(template.render(program=program, result=result))
def emit_version_argument_encoders(
environment: Environment, program: str, version: _RpcVersion
) -> None:
"""Emit client argument encoders for each RPC version's procedures"""
arguments = set()
for procedure in version.procedures:
if procedure.name not in excluded_apis:
arguments.add(procedure.argument.type_name)
template = environment.get_template("encoder/argument.j2")
for argument in arguments:
print(template.render(program=program, argument=argument))
def emit_version_result_encoders(
environment: Environment, program: str, version: _RpcVersion
) -> None:
"""Emit server result encoders for each RPC version's procedures"""
results = set()
for procedure in version.procedures:
if procedure.name not in excluded_apis:
results.add(procedure.result.type_name)
template = environment.get_template("encoder/result.j2")
for result in results:
print(template.render(program=program, result=result))
class XdrProgramGenerator(SourceGenerator):
"""Generate source code for an RPC program's procedures"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "program")
self.peer = peer
def emit_definition(self, node: _RpcProgram) -> None:
"""Emit procedure numbers for each of an RPC programs's procedures"""
raw_name = node.name
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
for version in node.versions:
emit_version_definitions(self.environment, program, version)
def emit_declaration(self, node: _RpcProgram) -> None:
"""Emit a declaration pair for each of an RPC programs's procedures"""
raw_name = node.name
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
for version in node.versions:
emit_version_declarations(self.environment, program, version)
def emit_decoder(self, node: _RpcProgram) -> None:
"""Emit all decoder functions for an RPC program's procedures"""
raw_name = node.name
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
match self.peer:
case "server":
for version in node.versions:
emit_version_argument_decoders(
self.environment, program, version,
)
case "client":
for version in node.versions:
emit_version_result_decoders(
self.environment, program, version,
)
def emit_encoder(self, node: _RpcProgram) -> None:
"""Emit all encoder functions for an RPC program's procedures"""
raw_name = node.name
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
match self.peer:
case "server":
for version in node.versions:
emit_version_result_encoders(
self.environment, program, version,
)
case "client":
for version in node.versions:
emit_version_argument_encoders(
self.environment, program, version,
)

View File

@ -0,0 +1,32 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate source code boilerplate"""
import os.path
import time
from generators import Boilerplate
from generators import find_xdr_program_name, create_jinja2_environment
from xdr_ast import _RpcProgram, Specification, get_header_name
class XdrSourceTopGenerator(Boilerplate):
"""Generate source code boilerplate"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "source_top")
self.peer = peer
def emit_source(self, filename: str, root: Specification) -> None:
"""Emit the top source boilerplate"""
name = find_xdr_program_name(root)
template = self.environment.get_template(self.peer + ".j2")
print(
template.render(
program=name,
filename=filename,
mtime=time.ctime(os.path.getmtime(filename)),
)
)

View File

@ -0,0 +1,272 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate code to handle XDR struct types"""
from jinja2 import Environment
from generators import SourceGenerator, kernel_c_type
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrVariableLengthString
from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration
from xdr_ast import public_apis
def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None:
"""Emit one declaration pair for an XDR struct type"""
if node.name in public_apis:
template = get_jinja2_template(environment, "declaration", "close")
print(template.render(name=node.name))
def emit_struct_member_definition(
environment: Environment, field: _XdrDeclaration
) -> None:
"""Emit a definition for one field in an XDR struct"""
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "definition", field.template)
print(template.render(name=field.name))
elif isinstance(field, _XdrVariableLengthString):
template = get_jinja2_template(environment, "definition", field.template)
print(template.render(name=field.name))
elif isinstance(field, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
type=kernel_c_type(field.spec),
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrOptionalData):
template = get_jinja2_template(environment, "definition", field.template)
print(
template.render(
name=field.name,
type=kernel_c_type(field.spec),
classifier=field.spec.c_classifier,
)
)
def emit_struct_definition(environment: Environment, node: _XdrStruct) -> None:
"""Emit one definition for an XDR struct type"""
template = get_jinja2_template(environment, "definition", "open")
print(template.render(name=node.name))
for field in node.fields:
emit_struct_member_definition(environment, field)
template = get_jinja2_template(environment, "definition", "close")
print(template.render(name=node.name))
def emit_struct_member_decoder(
environment: Environment, field: _XdrDeclaration
) -> None:
"""Emit a decoder for one field in an XDR struct"""
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrVariableLengthString):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
size=field.size,
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
maxsize=field.maxsize,
classifier=field.spec.c_classifier,
)
)
elif isinstance(field, _XdrOptionalData):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
classifier=field.spec.c_classifier,
)
)
def emit_struct_decoder(environment: Environment, node: _XdrStruct) -> None:
"""Emit one decoder function for an XDR struct type"""
template = get_jinja2_template(environment, "decoder", "open")
print(template.render(name=node.name))
for field in node.fields:
emit_struct_member_decoder(environment, field)
template = get_jinja2_template(environment, "decoder", "close")
print(template.render())
def emit_struct_member_encoder(
environment: Environment, field: _XdrDeclaration
) -> None:
"""Emit an encoder for one field in an XDR struct"""
if isinstance(field, _XdrBasic):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
)
)
elif isinstance(field, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrVariableLengthString):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
size=field.size,
)
)
elif isinstance(field, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrOptionalData):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
name=field.name,
type=field.spec.type_name,
classifier=field.spec.c_classifier,
)
)
def emit_struct_encoder(environment: Environment, node: _XdrStruct) -> None:
"""Emit one encoder function for an XDR struct type"""
template = get_jinja2_template(environment, "encoder", "open")
print(template.render(name=node.name))
for field in node.fields:
emit_struct_member_encoder(environment, field)
template = get_jinja2_template(environment, "encoder", "close")
print(template.render())
class XdrStructGenerator(SourceGenerator):
"""Generate source code for XDR structs"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "struct")
self.peer = peer
def emit_declaration(self, node: _XdrStruct) -> None:
"""Emit one declaration pair for an XDR struct type"""
emit_struct_declaration(self.environment, node)
def emit_definition(self, node: _XdrStruct) -> None:
"""Emit one definition for an XDR struct type"""
emit_struct_definition(self.environment, node)
def emit_decoder(self, node: _XdrStruct) -> None:
"""Emit one decoder function for an XDR struct type"""
emit_struct_decoder(self.environment, node)
def emit_encoder(self, node: _XdrStruct) -> None:
"""Emit one encoder function for an XDR struct type"""
emit_struct_encoder(self.environment, node)

View File

@ -0,0 +1,255 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate code to handle XDR typedefs"""
from jinja2 import Environment
from generators import SourceGenerator, kernel_c_type
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrTypedef, _XdrVariableLengthString
from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
from xdr_ast import _XdrOptionalData, _XdrVoid, _XdrDeclaration
from xdr_ast import public_apis
def emit_typedef_declaration(environment: Environment, node: _XdrDeclaration) -> None:
"""Emit a declaration pair for one XDR typedef"""
if node.name not in public_apis:
return
if isinstance(node, _XdrBasic):
template = get_jinja2_template(environment, "declaration", node.template)
print(
template.render(
name=node.name,
type=kernel_c_type(node.spec),
classifier=node.spec.c_classifier,
)
)
elif isinstance(node, _XdrVariableLengthString):
template = get_jinja2_template(environment, "declaration", node.template)
print(template.render(name=node.name))
elif isinstance(node, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "declaration", node.template)
print(template.render(name=node.name, size=node.size))
elif isinstance(node, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "declaration", node.template)
print(template.render(name=node.name))
elif isinstance(node, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "declaration", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
size=node.size,
)
)
elif isinstance(node, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "declaration", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
classifier=node.spec.c_classifier,
)
)
elif isinstance(node, _XdrOptionalData):
raise NotImplementedError("<optional_data> typedef not yet implemented")
elif isinstance(node, _XdrVoid):
raise NotImplementedError("<void> typedef not yet implemented")
else:
raise NotImplementedError("typedef: type not recognized")
def emit_type_definition(environment: Environment, node: _XdrDeclaration) -> None:
"""Emit a definition for one XDR typedef"""
if isinstance(node, _XdrBasic):
template = get_jinja2_template(environment, "definition", node.template)
print(
template.render(
name=node.name,
type=kernel_c_type(node.spec),
classifier=node.spec.c_classifier,
)
)
elif isinstance(node, _XdrVariableLengthString):
template = get_jinja2_template(environment, "definition", node.template)
print(template.render(name=node.name))
elif isinstance(node, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "definition", node.template)
print(template.render(name=node.name, size=node.size))
elif isinstance(node, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "definition", node.template)
print(template.render(name=node.name))
elif isinstance(node, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "definition", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
size=node.size,
)
)
elif isinstance(node, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "definition", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
classifier=node.spec.c_classifier,
)
)
elif isinstance(node, _XdrOptionalData):
raise NotImplementedError("<optional_data> typedef not yet implemented")
elif isinstance(node, _XdrVoid):
raise NotImplementedError("<void> typedef not yet implemented")
else:
raise NotImplementedError("typedef: type not recognized")
def emit_typedef_decoder(environment: Environment, node: _XdrDeclaration) -> None:
"""Emit a decoder function for one XDR typedef"""
if isinstance(node, _XdrBasic):
template = get_jinja2_template(environment, "decoder", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
)
)
elif isinstance(node, _XdrVariableLengthString):
template = get_jinja2_template(environment, "decoder", node.template)
print(
template.render(
name=node.name,
maxsize=node.maxsize,
)
)
elif isinstance(node, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "decoder", node.template)
print(
template.render(
name=node.name,
size=node.size,
)
)
elif isinstance(node, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "decoder", node.template)
print(
template.render(
name=node.name,
maxsize=node.maxsize,
)
)
elif isinstance(node, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "decoder", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
size=node.size,
classifier=node.spec.c_classifier,
)
)
elif isinstance(node, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "decoder", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
maxsize=node.maxsize,
)
)
elif isinstance(node, _XdrOptionalData):
raise NotImplementedError("<optional_data> typedef not yet implemented")
elif isinstance(node, _XdrVoid):
raise NotImplementedError("<void> typedef not yet implemented")
else:
raise NotImplementedError("typedef: type not recognized")
def emit_typedef_encoder(environment: Environment, node: _XdrDeclaration) -> None:
"""Emit an encoder function for one XDR typedef"""
if isinstance(node, _XdrBasic):
template = get_jinja2_template(environment, "encoder", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
)
)
elif isinstance(node, _XdrVariableLengthString):
template = get_jinja2_template(environment, "encoder", node.template)
print(
template.render(
name=node.name,
maxsize=node.maxsize,
)
)
elif isinstance(node, _XdrFixedLengthOpaque):
template = get_jinja2_template(environment, "encoder", node.template)
print(
template.render(
name=node.name,
size=node.size,
)
)
elif isinstance(node, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "encoder", node.template)
print(
template.render(
name=node.name,
maxsize=node.maxsize,
)
)
elif isinstance(node, _XdrFixedLengthArray):
template = get_jinja2_template(environment, "encoder", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
size=node.size,
)
)
elif isinstance(node, _XdrVariableLengthArray):
template = get_jinja2_template(environment, "encoder", node.template)
print(
template.render(
name=node.name,
type=node.spec.type_name,
maxsize=node.maxsize,
)
)
elif isinstance(node, _XdrOptionalData):
raise NotImplementedError("<optional_data> typedef not yet implemented")
elif isinstance(node, _XdrVoid):
raise NotImplementedError("<void> typedef not yet implemented")
else:
raise NotImplementedError("typedef: type not recognized")
class XdrTypedefGenerator(SourceGenerator):
"""Generate source code for XDR typedefs"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "typedef")
self.peer = peer
def emit_declaration(self, node: _XdrTypedef) -> None:
"""Emit one declaration pair for an XDR enum type"""
emit_typedef_declaration(self.environment, node.declaration)
def emit_definition(self, node: _XdrTypedef) -> None:
"""Emit one definition for an XDR typedef"""
emit_type_definition(self.environment, node.declaration)
def emit_decoder(self, node: _XdrTypedef) -> None:
"""Emit one decoder function for an XDR typedef"""
emit_typedef_decoder(self.environment, node.declaration)
def emit_encoder(self, node: _XdrTypedef) -> None:
"""Emit one encoder function for an XDR typedef"""
emit_typedef_encoder(self.environment, node.declaration)

View File

@ -0,0 +1,243 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Generate code to handle XDR unions"""
from jinja2 import Environment
from generators import SourceGenerator
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid
from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis
def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None:
"""Emit one declaration pair for an XDR union type"""
if node.name in public_apis:
template = get_jinja2_template(environment, "declaration", "close")
print(template.render(name=node.name))
def emit_union_switch_spec_definition(
environment: Environment, node: _XdrDeclaration
) -> None:
"""Emit a definition for an XDR union's discriminant"""
assert isinstance(node, _XdrBasic)
template = get_jinja2_template(environment, "definition", "switch_spec")
print(
template.render(
name=node.name,
type=node.spec.type_name,
classifier=node.spec.c_classifier,
)
)
def emit_union_case_spec_definition(
environment: Environment, node: _XdrDeclaration
) -> None:
"""Emit a definition for an XDR union's case arm"""
if isinstance(node.arm, _XdrVoid):
return
assert isinstance(node.arm, _XdrBasic)
template = get_jinja2_template(environment, "definition", "case_spec")
print(
template.render(
name=node.arm.name,
type=node.arm.spec.type_name,
classifier=node.arm.spec.c_classifier,
)
)
def emit_union_definition(environment: Environment, node: _XdrUnion) -> None:
"""Emit one XDR union definition"""
template = get_jinja2_template(environment, "definition", "open")
print(template.render(name=node.name))
emit_union_switch_spec_definition(environment, node.discriminant)
for case in node.cases:
emit_union_case_spec_definition(environment, case)
if node.default is not None:
emit_union_case_spec_definition(environment, node.default)
template = get_jinja2_template(environment, "definition", "close")
print(template.render(name=node.name))
def emit_union_switch_spec_decoder(
environment: Environment, node: _XdrDeclaration
) -> None:
"""Emit a decoder for an XDR union's discriminant"""
assert isinstance(node, _XdrBasic)
template = get_jinja2_template(environment, "decoder", "switch_spec")
print(template.render(name=node.name, type=node.spec.type_name))
def emit_union_case_spec_decoder(environment: Environment, node: _XdrCaseSpec) -> None:
"""Emit decoder functions for an XDR union's case arm"""
if isinstance(node.arm, _XdrVoid):
return
template = get_jinja2_template(environment, "decoder", "case_spec")
for case in node.values:
print(template.render(case=case))
assert isinstance(node.arm, _XdrBasic)
template = get_jinja2_template(environment, "decoder", node.arm.template)
print(
template.render(
name=node.arm.name,
type=node.arm.spec.type_name,
classifier=node.arm.spec.c_classifier,
)
)
template = get_jinja2_template(environment, "decoder", "break")
print(template.render())
def emit_union_default_spec_decoder(environment: Environment, node: _XdrUnion) -> None:
"""Emit a decoder function for an XDR union's default arm"""
default_case = node.default
# Avoid a gcc warning about a default case with boolean discriminant
if default_case is None and node.discriminant.spec.type_name == "bool":
return
template = get_jinja2_template(environment, "decoder", "default_spec")
print(template.render())
if default_case is None or isinstance(default_case.arm, _XdrVoid):
template = get_jinja2_template(environment, "decoder", "break")
print(template.render())
return
assert isinstance(default_case.arm, _XdrBasic)
template = get_jinja2_template(environment, "decoder", default_case.arm.template)
print(
template.render(
name=default_case.arm.name,
type=default_case.arm.spec.type_name,
classifier=default_case.arm.spec.c_classifier,
)
)
def emit_union_decoder(environment: Environment, node: _XdrUnion) -> None:
"""Emit one XDR union decoder"""
template = get_jinja2_template(environment, "decoder", "open")
print(template.render(name=node.name))
emit_union_switch_spec_decoder(environment, node.discriminant)
for case in node.cases:
emit_union_case_spec_decoder(environment, case)
emit_union_default_spec_decoder(environment, node)
template = get_jinja2_template(environment, "decoder", "close")
print(template.render())
def emit_union_switch_spec_encoder(
environment: Environment, node: _XdrDeclaration
) -> None:
"""Emit an encoder for an XDR union's discriminant"""
assert isinstance(node, _XdrBasic)
template = get_jinja2_template(environment, "encoder", "switch_spec")
print(template.render(name=node.name, type=node.spec.type_name))
def emit_union_case_spec_encoder(environment: Environment, node: _XdrCaseSpec) -> None:
"""Emit encoder functions for an XDR union's case arm"""
if isinstance(node.arm, _XdrVoid):
return
template = get_jinja2_template(environment, "encoder", "case_spec")
for case in node.values:
print(template.render(case=case))
assert isinstance(node.arm, _XdrBasic)
template = get_jinja2_template(environment, "encoder", node.arm.template)
print(
template.render(
name=node.arm.name,
type=node.arm.spec.type_name,
)
)
template = get_jinja2_template(environment, "encoder", "break")
print(template.render())
def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -> None:
"""Emit an encoder function for an XDR union's default arm"""
default_case = node.default
# Avoid a gcc warning about a default case with boolean discriminant
if default_case is None and node.discriminant.spec.type_name == "bool":
return
template = get_jinja2_template(environment, "encoder", "default_spec")
print(template.render())
if default_case is None or isinstance(default_case.arm, _XdrVoid):
template = get_jinja2_template(environment, "encoder", "break")
print(template.render())
return
assert isinstance(default_case.arm, _XdrBasic)
template = get_jinja2_template(environment, "encoder", default_case.arm.template)
print(
template.render(
name=default_case.arm.name,
type=default_case.arm.spec.type_name,
)
)
def emit_union_encoder(environment, node: _XdrUnion) -> None:
"""Emit one XDR union encoder"""
template = get_jinja2_template(environment, "encoder", "open")
print(template.render(name=node.name))
emit_union_switch_spec_encoder(environment, node.discriminant)
for case in node.cases:
emit_union_case_spec_encoder(environment, case)
emit_union_default_spec_encoder(environment, node)
template = get_jinja2_template(environment, "encoder", "close")
print(template.render())
class XdrUnionGenerator(SourceGenerator):
"""Generate source code for XDR unions"""
def __init__(self, language: str, peer: str):
"""Initialize an instance of this class"""
self.environment = create_jinja2_environment(language, "union")
self.peer = peer
def emit_declaration(self, node: _XdrUnion) -> None:
"""Emit one declaration pair for an XDR union"""
emit_union_declaration(self.environment, node)
def emit_definition(self, node: _XdrUnion) -> None:
"""Emit one definition for an XDR union"""
emit_union_definition(self.environment, node)
def emit_decoder(self, node: _XdrUnion) -> None:
"""Emit one decoder function for an XDR union"""
emit_union_decoder(self.environment, node)
def emit_encoder(self, node: _XdrUnion) -> None:
"""Emit one encoder function for an XDR union"""
emit_union_encoder(self.environment, node)

View File

@ -0,0 +1,119 @@
// A Lark grammar for the XDR specification language based on
// https://tools.ietf.org/html/rfc4506 Section 6.3
declaration : "opaque" identifier "[" value "]" -> fixed_length_opaque
| "opaque" identifier "<" [ value ] ">" -> variable_length_opaque
| "string" identifier "<" [ value ] ">" -> variable_length_string
| type_specifier identifier "[" value "]" -> fixed_length_array
| type_specifier identifier "<" [ value ] ">" -> variable_length_array
| type_specifier "*" identifier -> optional_data
| type_specifier identifier -> basic
| "void" -> void
value : decimal_constant
| hexadecimal_constant
| octal_constant
| identifier
constant : decimal_constant | hexadecimal_constant | octal_constant
type_specifier : unsigned_hyper
| unsigned_long
| unsigned_int
| hyper
| long
| int
| float
| double
| quadruple
| bool
| enum_type_spec
| struct_type_spec
| union_type_spec
| identifier
unsigned_hyper : "unsigned" "hyper"
unsigned_long : "unsigned" "long"
unsigned_int : "unsigned" "int"
hyper : "hyper"
long : "long"
int : "int"
float : "float"
double : "double"
quadruple : "quadruple"
bool : "bool"
enum_type_spec : "enum" enum_body
enum_body : "{" ( identifier "=" value ) ( "," identifier "=" value )* "}"
struct_type_spec : "struct" struct_body
struct_body : "{" ( declaration ";" )+ "}"
union_type_spec : "union" union_body
union_body : switch_spec "{" case_spec+ [ default_spec ] "}"
switch_spec : "switch" "(" declaration ")"
case_spec : ( "case" value ":" )+ declaration ";"
default_spec : "default" ":" declaration ";"
constant_def : "const" identifier "=" value ";"
type_def : "typedef" declaration ";" -> typedef
| "enum" identifier enum_body ";" -> enum
| "struct" identifier struct_body ";" -> struct
| "union" identifier union_body ";" -> union
specification : definition*
definition : constant_def
| type_def
| program_def
| pragma_def
//
// RPC program definitions not specified in RFC 4506
//
program_def : "program" identifier "{" version_def+ "}" "=" constant ";"
version_def : "version" identifier "{" procedure_def+ "}" "=" constant ";"
procedure_def : type_specifier identifier "(" type_specifier ")" "=" constant ";"
pragma_def : "pragma" directive identifier [ identifier ] ";"
directive : exclude_directive
| header_directive
| pages_directive
| public_directive
| skip_directive
exclude_directive : "exclude"
header_directive : "header"
pages_directive : "pages"
public_directive : "public"
skip_directive : "skip"
//
// XDR language primitives
//
identifier : /([a-z]|[A-Z])(_|[a-z]|[A-Z]|[0-9])*/
decimal_constant : /[\+-]?(0|[1-9][0-9]*)/
hexadecimal_constant : /0x([a-f]|[A-F]|[0-9])+/
octal_constant : /0[0-7]+/
PASSTHRU : "%" | "%" /.+/
%ignore PASSTHRU
%import common.C_COMMENT
%ignore C_COMMENT
%import common.WS
%ignore WS

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
# Just to make sphinx-apidoc document this directory

View File

@ -0,0 +1,76 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Translate an XDR specification into executable code that
can be compiled for the Linux kernel."""
import logging
from argparse import Namespace
from lark import logger
from lark.exceptions import UnexpectedInput
from generators.constant import XdrConstantGenerator
from generators.enum import XdrEnumGenerator
from generators.header_bottom import XdrHeaderBottomGenerator
from generators.header_top import XdrHeaderTopGenerator
from generators.pointer import XdrPointerGenerator
from generators.program import XdrProgramGenerator
from generators.typedef import XdrTypedefGenerator
from generators.struct import XdrStructGenerator
from generators.union import XdrUnionGenerator
from xdr_ast import transform_parse_tree, _RpcProgram, Specification
from xdr_ast import _XdrConstant, _XdrEnum, _XdrPointer
from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
logger.setLevel(logging.INFO)
def emit_header_declarations(
root: Specification, language: str, peer: str
) -> None:
"""Emit header declarations"""
for definition in root.definitions:
if isinstance(definition.value, _XdrEnum):
gen = XdrEnumGenerator(language, peer)
elif isinstance(definition.value, _XdrPointer):
gen = XdrPointerGenerator(language, peer)
elif isinstance(definition.value, _XdrTypedef):
gen = XdrTypedefGenerator(language, peer)
elif isinstance(definition.value, _XdrStruct):
gen = XdrStructGenerator(language, peer)
elif isinstance(definition.value, _XdrUnion):
gen = XdrUnionGenerator(language, peer)
elif isinstance(definition.value, _RpcProgram):
gen = XdrProgramGenerator(language, peer)
else:
continue
gen.emit_declaration(definition.value)
def handle_parse_error(e: UnexpectedInput) -> bool:
"""Simple parse error reporting, no recovery attempted"""
print(e)
return True
def subcmd(args: Namespace) -> int:
"""Generate definitions and declarations"""
set_xdr_annotate(args.annotate)
parser = xdr_parser()
with open(args.filename, encoding="utf-8") as f:
parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
ast = transform_parse_tree(parse_tree)
gen = XdrHeaderTopGenerator(args.language, args.peer)
gen.emit_declaration(args.filename, ast)
emit_header_declarations(ast, args.language, args.peer)
gen = XdrHeaderBottomGenerator(args.language, args.peer)
gen.emit_declaration(args.filename, ast)
return 0

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Translate an XDR specification into executable code that
can be compiled for the Linux kernel."""
import logging
from argparse import Namespace
from lark import logger
from lark.exceptions import UnexpectedInput
from generators.constant import XdrConstantGenerator
from generators.enum import XdrEnumGenerator
from generators.header_bottom import XdrHeaderBottomGenerator
from generators.header_top import XdrHeaderTopGenerator
from generators.pointer import XdrPointerGenerator
from generators.program import XdrProgramGenerator
from generators.typedef import XdrTypedefGenerator
from generators.struct import XdrStructGenerator
from generators.union import XdrUnionGenerator
from xdr_ast import transform_parse_tree, Specification
from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPointer
from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
logger.setLevel(logging.INFO)
def emit_header_definitions(
root: Specification, language: str, peer: str
) -> None:
"""Emit header definitions"""
for definition in root.definitions:
if isinstance(definition.value, _XdrConstant):
gen = XdrConstantGenerator(language, peer)
elif isinstance(definition.value, _XdrEnum):
gen = XdrEnumGenerator(language, peer)
elif isinstance(definition.value, _XdrPointer):
gen = XdrPointerGenerator(language, peer)
elif isinstance(definition.value, _RpcProgram):
gen = XdrProgramGenerator(language, peer)
elif isinstance(definition.value, _XdrTypedef):
gen = XdrTypedefGenerator(language, peer)
elif isinstance(definition.value, _XdrStruct):
gen = XdrStructGenerator(language, peer)
elif isinstance(definition.value, _XdrUnion):
gen = XdrUnionGenerator(language, peer)
else:
continue
gen.emit_definition(definition.value)
def handle_parse_error(e: UnexpectedInput) -> bool:
"""Simple parse error reporting, no recovery attempted"""
print(e)
return True
def subcmd(args: Namespace) -> int:
"""Generate definitions"""
set_xdr_annotate(args.annotate)
parser = xdr_parser()
with open(args.filename, encoding="utf-8") as f:
parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
ast = transform_parse_tree(parse_tree)
gen = XdrHeaderTopGenerator(args.language, args.peer)
gen.emit_definition(args.filename, ast)
emit_header_definitions(ast, args.language, args.peer)
gen = XdrHeaderBottomGenerator(args.language, args.peer)
gen.emit_definition(args.filename, ast)
return 0

View File

@ -0,0 +1,33 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Translate an XDR specification into executable code that
can be compiled for the Linux kernel."""
import logging
from argparse import Namespace
from lark import logger
from lark.exceptions import UnexpectedInput
from xdr_parse import xdr_parser
from xdr_ast import transform_parse_tree
logger.setLevel(logging.DEBUG)
def handle_parse_error(e: UnexpectedInput) -> bool:
"""Simple parse error reporting, no recovery attempted"""
print(e)
return True
def subcmd(args: Namespace) -> int:
"""Lexical and syntax check of an XDR specification"""
parser = xdr_parser()
with open(args.filename, encoding="utf-8") as f:
parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
transform_parse_tree(parse_tree)
return 0

View File

@ -0,0 +1,118 @@
#!/usr/bin/env python3
# ex: set filetype=python:
"""Translate an XDR specification into executable code that
can be compiled for the Linux kernel."""
import logging
from argparse import Namespace
from lark import logger
from lark.exceptions import UnexpectedInput
from generators.source_top import XdrSourceTopGenerator
from generators.enum import XdrEnumGenerator
from generators.pointer import XdrPointerGenerator
from generators.program import XdrProgramGenerator
from generators.typedef import XdrTypedefGenerator
from generators.struct import XdrStructGenerator
from generators.union import XdrUnionGenerator
from xdr_ast import transform_parse_tree, _RpcProgram, Specification
from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer
from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
logger.setLevel(logging.INFO)
def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None:
"""Emit one XDR decoder function for a source file"""
if isinstance(node, _XdrEnum):
gen = XdrEnumGenerator(language, peer)
elif isinstance(node, _XdrPointer):
gen = XdrPointerGenerator(language, peer)
elif isinstance(node, _XdrTypedef):
gen = XdrTypedefGenerator(language, peer)
elif isinstance(node, _XdrStruct):
gen = XdrStructGenerator(language, peer)
elif isinstance(node, _XdrUnion):
gen = XdrUnionGenerator(language, peer)
elif isinstance(node, _RpcProgram):
gen = XdrProgramGenerator(language, peer)
else:
return
gen.emit_decoder(node)
def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None:
"""Emit one XDR encoder function for a source file"""
if isinstance(node, _XdrEnum):
gen = XdrEnumGenerator(language, peer)
elif isinstance(node, _XdrPointer):
gen = XdrPointerGenerator(language, peer)
elif isinstance(node, _XdrTypedef):
gen = XdrTypedefGenerator(language, peer)
elif isinstance(node, _XdrStruct):
gen = XdrStructGenerator(language, peer)
elif isinstance(node, _XdrUnion):
gen = XdrUnionGenerator(language, peer)
elif isinstance(node, _RpcProgram):
gen = XdrProgramGenerator(language, peer)
else:
return
gen.emit_encoder(node)
def generate_server_source(filename: str, root: Specification, language: str) -> None:
"""Generate server-side source code"""
gen = XdrSourceTopGenerator(language, "server")
gen.emit_source(filename, root)
for definition in root.definitions:
emit_source_decoder(definition.value, language, "server")
for definition in root.definitions:
emit_source_encoder(definition.value, language, "server")
def generate_client_source(filename: str, root: Specification, language: str) -> None:
"""Generate server-side source code"""
gen = XdrSourceTopGenerator(language, "client")
gen.emit_source(filename, root)
# cel: todo: client needs XDR size macros
for definition in root.definitions:
emit_source_encoder(definition.value, language, "client")
for definition in root.definitions:
emit_source_decoder(definition.value, language, "client")
# cel: todo: client needs PROC macros
def handle_parse_error(e: UnexpectedInput) -> bool:
"""Simple parse error reporting, no recovery attempted"""
print(e)
return True
def subcmd(args: Namespace) -> int:
"""Generate encoder and decoder functions"""
set_xdr_annotate(args.annotate)
parser = xdr_parser()
with open(args.filename, encoding="utf-8") as f:
parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
ast = transform_parse_tree(parse_tree)
match args.peer:
case "server":
generate_server_source(args.filename, ast, args.language)
case "client":
generate_client_source(args.filename, ast, args.language)
case _:
print("Code generation for", args.peer, "is not yet supported")
return 0

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
enum { {{ name }} = {{ value }} };

View File

@ -0,0 +1,4 @@
{# SPDX-License-Identifier: GPL-2.0 #}
bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr);
bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value);

View File

@ -0,0 +1,19 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* enum {{ name }} */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr)
{
u32 val;
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
*ptr = val;
return true;
}

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{{ name }} = {{ value }},

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
enum {{ name }} {

View File

@ -0,0 +1,14 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* enum {{ name }} */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#endif /* _LINUX_XDRGEN_{{ infix }}_DECL_H */

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#endif /* _LINUX_XDRGEN_{{ infix }}_DEF_H */

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: {{ filename }} */
/* XDR specification modification time: {{ mtime }} */
#ifndef _LINUX_XDRGEN_{{ infix }}_DECL_H
#define _LINUX_XDRGEN_{{ infix }}_DECL_H
#include <linux/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/xdrgen/_defs.h>
#include <linux/sunrpc/xdrgen/_builtins.h>
#include <linux/sunrpc/xdrgen/{{ infix.lower() }}.h>

View File

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: {{ filename }} */
/* XDR specification modification time: {{ mtime }} */
#ifndef _LINUX_XDRGEN_{{ infix }}_DEF_H
#define _LINUX_XDRGEN_{{ infix }}_DEF_H
#include <linux/types.h>
#include <linux/sunrpc/xdrgen/_defs.h>

View File

@ -0,0 +1,4 @@
{# SPDX-License-Identifier: GPL-2.0 #}
bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr);
bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value);

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (basic) */
{% endif %}
if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}))
return false;

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
return true;
};

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (fixed-length array) */
{% endif %}
for (u32 i = 0; i < {{ size }}; i++) {
if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0)
return false;
}

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (fixed-length opaque) */
{% endif %}
if (xdr_stream_decode_opaque_fixed(xdr, ptr->{{ name }}, {{ size }}) < 0)
return false;

View File

@ -0,0 +1,22 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* pointer {{ name }} */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
{
bool opted;
{% if annotate %}
/* opted */
{% endif %}
if (!xdrgen_decode_bool(xdr, &opted))
return false;
if (!opted)
return true;

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (optional data) */
{% endif %}
if (!xdrgen_decode_{{ type }}(xdr, ptr->{{ name }}))
return false;

View File

@ -0,0 +1,13 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length array) */
{% endif %}
if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) != XDR_UNIT)
return false;
{% if maxsize != "0" %}
if (ptr->{{ name }}.count > {{ maxsize }})
return false;
{% endif %}
for (u32 i = 0; i < ptr->{{ name }}.count; i++)
if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.element[i]))
return false;

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length opaque) */
{% endif %}
if (!xdrgen_decode_opaque(xdr, (opaque *)ptr, {{ maxsize }}))
return false;

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length string) */
{% endif %}
if (!xdrgen_decode_string(xdr, (string *)ptr, {{ maxsize }}))
return false;

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (basic) */
{% endif %}
{{ classifier }}{{ type }} {{ name }};

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (fixed-length array) */
{% endif %}
{{ type }} {{ name }}[{{ size }}];

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (fixed-length opaque) */
{% endif %}
u8 {{ name }}[{{ size }}];

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* pointer {{ name }} */
{% endif %}
struct {{ name }} {

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (optional data) */
{% endif %}
{{ classifier }}{{ type }} *{{ name }};

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length array) */
{% endif %}
struct {
u32 count;
{{ classifier }}{{ type }} *element;
} {{ name }};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length opaque) */
{% endif %}
opaque {{ name }};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length string) */
{% endif %}
string {{ name }};

View File

@ -0,0 +1,10 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (basic) */
{% endif %}
{% if type in pass_by_reference %}
if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}))
{% else %}
if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
{% endif %}
return false;

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
return true;
};

View File

@ -0,0 +1,12 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (fixed-length array) */
{% endif %}
for (u32 i = 0; i < {{ size }}; i++) {
{% if type in pass_by_reference %}
if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0)
{% else %}
if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0)
{% endif %}
return false;
}

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (fixed-length opaque) */
{% endif %}
if (xdr_stream_encode_opaque_fixed(xdr, value->{{ name }}, {{ size }}) < 0)
return false;

View File

@ -0,0 +1,20 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* pointer {{ name }} */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value)
{
{% if annotate %}
/* opted */
{% endif %}
if (!xdrgen_encode_bool(xdr, value != NULL))
return false;
if (!value)
return true;

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (optional data) */
{% endif %}
if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
return false;

View File

@ -0,0 +1,15 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length array) */
{% endif %}
if (value->{{ name }}.count > {{ maxsize }})
return false;
if (xdr_stream_encode_u32(xdr, value->{{ name }}.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value->{{ name }}.count; i++)
{% if type in pass_by_reference %}
if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}.element[i]))
{% else %}
if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}.element[i]))
{% endif %}
return false;

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length opaque) */
{% endif %}
if (value->{{ name }}.len > {{ maxsize }})
return false;
if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0)
return false;

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length string) */
{% endif %}
if (value->{{ name }}.len > {{ maxsize }})
return false;
if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0)
return false;

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_stream *xdr);

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_stream *xdr);

View File

@ -0,0 +1,21 @@
{# SPDX-License-Identifier: GPL-2.0 #}
/**
* {{ program }}_svc_decode_{{ argument }} - Decode a {{ argument }} argument
* @rqstp: RPC transaction context
* @xdr: source XDR data stream
*
* Return values:
* %true: procedure arguments decoded successfully
* %false: decode failed
*/
bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
{% if argument == 'void' %}
return xdrgen_decode_void(xdr);
{% else %}
struct {{ argument }} *argp = rqstp->rq_argp;
return xdrgen_decode_{{ argument }}(xdr, argp);
{% endif %}
}

View File

@ -0,0 +1,22 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* Decode {{ result }} results */
{% endif %}
static int {{ program }}_xdr_dec_{{ result }}(struct rpc_rqst *req,
struct xdr_stream *xdr, void *data)
{
{% if result == 'void' %}
xdrgen_decode_void(xdr);
{% else %}
struct {{ result }} *result = data;
if (!xdrgen_decode_{{ result }}(xdr, result))
return -EIO;
if (result->stat != nfs_ok) {
trace_nfs_xdr_status(xdr, (int)result->stat);
return {{ program }}_stat_to_errno(result->stat);
}
{% endif %}
return 0;
}

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* procedure numbers for {{ program }} */
{% endif %}
enum {

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{{ name }} = {{ value }},

View File

@ -0,0 +1,16 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{%if annotate %}
/* Encode {{ argument }} arguments */
{% endif %}
static void {{ program }}_xdr_enc_{{ argument }}(struct rpc_rqst *req,
struct xdr_stream *xdr, const void *data)
{
{% if argument == 'void' %}
xdrgen_encode_void(xdr);
{% else %}
const struct {{ argument }} *args = data;
xdrgen_encode_{{ argument }}(xdr, args);
{% endif %}
}

View File

@ -0,0 +1,21 @@
{# SPDX-License-Identifier: GPL-2.0 #}
/**
* {{ program }}_svc_encode_{{ result }} - Encode a {{ result }} result
* @rqstp: RPC transaction context
* @xdr: target XDR data stream
*
* Return values:
* %true: procedure results encoded successfully
* %false: encode failed
*/
bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
{% if result == 'void' %}
return xdrgen_encode_void(xdr);
{% else %}
struct {{ result }} *resp = rqstp->rq_resp;
return xdrgen_encode_{{ result }}(xdr, resp);
{% endif %}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: {{ filename }}
// XDR specification modification time: {{ mtime }}
#include <linux/sunrpc/xprt.h>
#include "{{ program }}xdr_gen.h"

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: {{ filename }}
// XDR specification modification time: {{ mtime }}
#include <linux/sunrpc/svc.h>
#include "{{ program }}xdr_gen.h"

View File

@ -0,0 +1,4 @@
{# SPDX-License-Identifier: GPL-2.0 #}
bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr);
bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value);

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (basic) */
{% endif %}
if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}))
return false;

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
return true;
};

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (fixed-length array) */
{% endif %}
for (u32 i = 0; i < {{ size }}; i++) {
if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0)
return false;
}

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (fixed-length opaque) */
{% endif %}
if (xdr_stream_decode_opaque_fixed(xdr, ptr->{{ name }}, {{ size }}) < 0)
return false;

View File

@ -0,0 +1,12 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* struct {{ name }} */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
{

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (optional data) */
{% endif %}
if (!xdrgen_decode_{{ type }}(xdr, ptr->{{ name }}))
return false;

View File

@ -0,0 +1,13 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length array) */
{% endif %}
if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) != XDR_UNIT)
return false;
{% if maxsize != "0" %}
if (ptr->{{ name }}.count > {{ maxsize }})
return false;
{% endif %}
for (u32 i = 0; i < ptr->{{ name }}.count; i++)
if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.element[i]))
return false;

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length opaque) */
{% endif %}
if (!xdrgen_decode_opaque(xdr, (opaque *)ptr, {{ maxsize }}))
return false;

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length string) */
{% endif %}
if (!xdrgen_decode_string(xdr, (string *)ptr, {{ maxsize }}))
return false;

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (basic) */
{% endif %}
{{ classifier }}{{ type }} {{ name }};

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (fixed-length array) */
{% endif %}
{{ type }} {{ name }}[{{ size }}];

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (fixed-length opaque) */
{% endif %}
u8 {{ name }}[{{ size }}];

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* struct {{ name }} */
{% endif %}
struct {{ name }} {

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (optional data) */
{% endif %}
{{ classifier }}{{ type }} *{{ name }};

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length array) */
{% endif %}
struct {
u32 count;
{{ classifier }}{{ type }} *element;
} {{ name }};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length opaque) */
{% endif %}
opaque {{ name }};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length string) */
{% endif %}
string {{ name }};

View File

@ -0,0 +1,10 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (basic) */
{% endif %}
{% if type in pass_by_reference %}
if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}))
{% else %}
if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
{% endif %}
return false;

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
return true;
};

View File

@ -0,0 +1,12 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (fixed-length array) */
{% endif %}
for (u32 i = 0; i < {{ size }}; i++) {
{% if type in pass_by_reference %}
if (xdrgen_encode_{{ type }}(xdr, &value->items[i]) < 0)
{% else %}
if (xdrgen_encode_{{ type }}(xdr, value->items[i]) < 0)
{% endif %}
return false;
}

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (fixed-length opaque) */
{% endif %}
if (xdr_stream_encode_opaque_fixed(xdr, value->{{ name }}, {{ size }}) < 0)
return false;

View File

@ -0,0 +1,12 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* struct {{ name }} */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value)
{

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (optional data) */
{% endif %}
if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
return false;

View File

@ -0,0 +1,15 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length array) */
{% endif %}
if (value->{{ name }}.count > {{ maxsize }})
return false;
if (xdr_stream_encode_u32(xdr, value->{{ name }}.count) != XDR_UNIT)
return false;
for (u32 i = 0; i < value->{{ name }}.count; i++)
{% if type in pass_by_reference %}
if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}.element[i]))
{% else %}
if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}.element[i]))
{% endif %}
return false;

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length opaque) */
{% endif %}
if (value->{{ name }}.len > {{ maxsize }})
return false;
if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0)
return false;

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* member {{ name }} (variable-length string) */
{% endif %}
if (value->{{ name }}.len > {{ maxsize }})
return false;
if (xdr_stream_encode_opaque(xdr, value->{{ name }}.data, value->{{ name }}.len) < 0)
return false;

Some files were not shown because too many files have changed in this diff Show More