# Copyright (c) 2022 Firebuild Inc.
# All rights reserved.
# Free for personal use and commercial trial.
# Non-trial commercial use requires licenses available from https://firebuild.com.
# Modification and redistribution are permitted, but commercial use of
# derivative works is subject to the same requirements of this license
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# This is the definition of the FBB format used for communication
# between the interceptor and the supervisor.

# This is a Python dictionary, to be read and processed by "generate_fbb".

{
  "types_with_custom_debugger": ["mode_t"],
  "varnames_with_custom_debugger": ["error_no"],

  "extra_c": """
    /* Debugger method for mode_t fields. */
    static void fbbcomm_debug_mode_t(FILE *f, mode_t mode) {
      fprintf(f, "\\"");
      debug_mode_t(f, mode);
      fprintf(f, "\\"");
    }

    /* Debugger method for error_no values. */
    static void fbbcomm_debug_error_no(FILE *f, int error_no) {
      fprintf(f, "\\"");
      debug_error_no(f, error_no);
      fprintf(f, "\\"");
    }

    /* Debugger method for fields that represent wstatus values. */
    static void fbbcomm_debug_wstatus(FILE *f, int wstatus, bool is_serialized, const void *fbb) {
      (void)is_serialized;  /* unused */
      (void)fbb;  /* unused */
      fprintf(f, "\\"");
      debug_wstatus(f, wstatus);
      fprintf(f, "\\"");
    }

    /* Debugger method for int fields that represent open()'s O_* flags. */
    static void fbbcomm_debug_open_flags(FILE *f, int flags, bool is_serialized, const void *fbb) {
      (void)is_serialized;  /* unused */
      (void)fbb;  /* unused */
      fprintf(f, "\\"");
      debug_open_flags(f, flags);
      fprintf(f, "\\"");
    }

    /* Debugger method for int fields that represent AT_* flags. */
    static void fbbcomm_debug_at_flags(FILE *f, int flags, bool is_serialized, const void *fbb) {
      (void)is_serialized;  /* unused */
      (void)fbb;  /* unused */
      fprintf(f, "\\"");
      debug_at_flags(f, flags);
      fprintf(f, "\\"");
    }

    /* Debugger method for int fields that represent fcntl()'s F_* command. */
    static void fbbcomm_debug_fcntl_cmd(FILE *f, int cmd, bool is_serialized, const void *fbb) {
      (void)is_serialized;  /* unused */
      (void)fbb;  /* unused */
      fprintf(f, "\\"");
      debug_fcntl_cmd(f, cmd);
      fprintf(f, "\\"");
    }

    /* Debugger method for int fields that represent fcntl()'s argument or non-error return value,
     * which could be FD_* or O_* or other flags depending on the command. */
    static void fbbcomm_debug_fcntl_arg_or_ret(FILE *f, int arg_or_ret, bool is_serialized, const void *fbb) {
      int cmd = is_serialized ?
          fbbcomm_serialized_fcntl_get_cmd((FBBCOMM_Serialized_fcntl *)fbb) :
          fbbcomm_builder_fcntl_get_cmd((FBBCOMM_Builder_fcntl *)fbb);
      fprintf(f, "\\"");
      debug_fcntl_arg_or_ret(f, cmd, arg_or_ret);
      fprintf(f, "\\"");
    }

    /* Debugger method for int fields that represent CLONE_* flags. */
    static void fbbcomm_debug_clone_flags(FILE *f, int flags, bool is_serialized, const void *fbb) {
      (void)is_serialized;  /* unused */
      (void)fbb;  /* unused */
      fprintf(f, "\\"");
      debug_clone_flags(f, flags);
      fprintf(f, "\\"");
    }

  """,

  "extra_h": """
    #include "common/debug_sysflags.h"
  """,

  "tags": [
    # interceptor library queries Firebuild supervisor if it can shortcut execution of the process
    ("scproc_query", [
      # process id
      (OPTIONAL, "pid_t", "pid"),
      # parent pid
      (OPTIONAL, "pid_t", "ppid"),
      # working dir process started in
      (OPTIONAL, STRING, "cwd"),
      # only argv, sending argc would be redundant
      (ARRAY, STRING, "arg"),
      # environment variables in unprocessed NAME=value form
      (ARRAY, STRING, "env_var"),
      # umask
      (REQUIRED, "mode_t", "umask"),
      # full path of the binary, it is expected to be in absolute and canonical form
      # fds (2: [<read fd>, <write fd>]) likely being used by GNU Make's jobserver
      (ARRAY, "int", "jobserver_fds"),
      # full path of the binary
      (OPTIONAL, STRING, "executable"),
      # original executed_path converted to canonical and absolute path
      # set only when the original executed path is different from executable
      (OPTIONAL, STRING, "executed_path"),
      # pathname used to execute the program
      # set only if it is relative or not canonical path, i.e. it differs from executed_path
      (OPTIONAL, STRING, "original_executed_path"),
      # loaded shared libraries in the beginning
      (ARRAY, STRING, "libs"),
      # interceptor's version
      (OPTIONAL, STRING, "version"),
    ]),

    # Submessage within "scproc_resp". Contains the list of client-side fds (dups of each other)
    # where to reopen one particular attached fd.
    # Does not contain "flags": the supervisor sets up fcntl(..., F_SETFL, ...) correctly and the
    # interceptor knows the desired CLOEXEC state.
    ("scproc_resp_reopen_fd", [
      (ARRAY, "int", "fds"),
    ]),

    # Firebuild supervisor's response with details of shortcutting.
    # Unlike most others, this message type is used in the supervisor->interceptor direction of the
    # communication. Nevertheless, it resides in the same namespace.
    # Ancillary data (SCM_RIGHTS) contains the fds to reopen, excatly as many as the number of items
    # in "reopen_fds".
    ("scproc_resp", [
      (REQUIRED, "bool", "shortcut"),
      (OPTIONAL, "int", "exit_status"),
      # disable interception and remove libfirebuild from LD_PRELOAD
      (OPTIONAL, "bool", "dont_intercept"),
      # makes sense only for shortcut = false
      (OPTIONAL, "int32_t", "debug_flags"),
      # Client-side fds to reopen to, messages of type "scproc_resp_reopen_fd".
      # Each item in this array corresponds to one ancillary fd.
      (ARRAY, FBB, "reopen_fds"),
      # The inherited seekable fds that were appended to while shortcutting;
      # the interceptor needs to seek forward in them.
      (ARRAY, "int", "fds_appended_to"),
      # File backed seekable fds that could be seeked to the end or not.
      # If they are not at the end the interceptor must send back an inherited_fd_offset message.
      (ARRAY, "int", "seekable_fds"),
      # Size of the backed seekable fds as the supervisor knows it.
      (ARRAY, "int64_t", "seekable_fds_size"),
    ]),

    # The inherited fd's offset at the start of the process
    ("inherited_fd_offset", [
      (REQUIRED, "int", "fd"),
      (REQUIRED, "int64_t", "offset"),
    ]),

    # Those function calls are not handled specially in interceptor lib and
    # are reported once per process to supervisor
    ("gen_call", [
      # function name
      (REQUIRED, STRING, "call"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    # Firebuild error
    ("fb_error", [
      # error message
      (REQUIRED, STRING, "msg"),
    ]),
    # Firebuild debugging message
    ("fb_debug", [
      # error message
      (REQUIRED, STRING, "msg"),
    ]),

    ("fcntl", [
      # file descriptor
      (OPTIONAL, "int", "fd"),
      # command
      (OPTIONAL, "int", "cmd", "fbbcomm_debug_fcntl_cmd"),
      # arg, if present
      (OPTIONAL, "int", "arg", "fbbcomm_debug_fcntl_arg_or_ret"),
      # return value, depends on cmd
      (OPTIONAL, "int", "ret", "fbbcomm_debug_fcntl_arg_or_ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("ioctl", [
      # file descriptor
      (OPTIONAL, "int", "fd"),
      # command
      (OPTIONAL, "unsigned long", "cmd"),
      # return value, depends on cmd
      (OPTIONAL, "int", "ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("pre_open", [
      # dir file descriptor for openat()
      (OPTIONAL, "int", "dirfd"),
      # file path
      (OPTIONAL, STRING, "pathname"),
    ]),

    ("open", [
      # dir file descriptor for openat()
      (OPTIONAL, "int", "dirfd"),
      # file path
      (OPTIONAL, STRING, "pathname"),
      # flags, decoding is left for Firebuild supervisor
      (REQUIRED, "int", "flags", "fbbcomm_debug_open_flags"),
      # mode if (flags & O_CREAT), decoding is left for Firebuild supervisor
      (OPTIONAL, "mode_t", "mode"),
      # return value, the file descriptor if != -1
      (OPTIONAL, "int", "ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
      # pre_open message has been sent before this message
      (REQUIRED, "bool", "pre_open_sent"),
      (OPTIONAL, "bool", "tmp_file"),
    ]),

    ("freopen", [
      # file path, can be NULL
      (OPTIONAL, STRING, "pathname"),
      # flags, decoding is left for Firebuild supervisor
      (OPTIONAL, "int", "flags", "fbbcomm_debug_open_flags"),
      # file descriptor associated to the stream to be reopened
      (OPTIONAL, "int", "oldfd"),
      # return value, the file descriptor if != -1
      (OPTIONAL, "int", "ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
      # pre_open message has been sent before this message
      (REQUIRED, "bool", "pre_open_sent"),
    ]),

    ("chdir", [
      # directory path
      (OPTIONAL, STRING, "pathname"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("fchdir", [
      # directory fd
      (OPTIONAL, "int", "fd"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("readlink", [
      # dir file descriptor for readlinkat()
      (OPTIONAL, "int", "dirfd"),
      # path name
      (OPTIONAL, STRING, "pathname"),
      # buffer size
      (OPTIONAL, "size_t", "bufsiz"),
      # returned path
      (OPTIONAL, STRING, "ret_target"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("rename", [
      # old dir file descriptor for renameat[2]()
      (OPTIONAL, "int", "olddirfd"),
      # path to old file
      (OPTIONAL, STRING, "oldpath"),
      # new dir file descriptor for renameat[2]()
      (OPTIONAL, "int", "newdirfd"),
      # path to new file
      (OPTIONAL, STRING, "newpath"),
      # flags for renmaeat2()
      (OPTIONAL, "unsigned int", "flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("faccessat", [
      # dir file descriptor
      (OPTIONAL, "int", "dirfd"),
      # path to file
      (OPTIONAL, STRING, "pathname"),
      # access mode (NOT related to "mode_t")
      (REQUIRED, "int", "mode"),
      # flags
      (OPTIONAL, "int", "flags", "fbbcomm_debug_at_flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("fstatat", [
      # fstat()'s fd, or fstatat()'s dirfd
      (OPTIONAL, "int", "fd"),
      # path to file, except for fstat()
      (OPTIONAL, STRING, "pathname"),
      # it could be lstat() encoded as AT_SYMLINK_NOFOLLOW or fstatat(..., flags)
      (OPTIONAL, "int", "flags", "fbbcomm_debug_at_flags"),
      # Returned file type and mode
      (OPTIONAL, "mode_t", "st_mode"),
      # Returned file size
      (OPTIONAL, "off_t", "st_size"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("mkdir", [
      # dir file descriptor for mkdirat()
      (OPTIONAL, "int", "dirfd"),
      # dir path
      (OPTIONAL, STRING, "pathname"),
      # mode
      (REQUIRED, "mode_t", "mode"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
      (OPTIONAL, "bool", "tmp_dir"),
    ]),

    ("rmdir", [
      # dir path
      (OPTIONAL, STRING, "pathname"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
      # pre_open message has been sent before this message
      (REQUIRED, "bool", "pre_open_sent"),
    ]),

    ("close", [
      # file descriptor
      (OPTIONAL, "int", "fd"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("closefrom", [
      (REQUIRED, "int", "lowfd"),
    ]),

    ("close_range", [
      (REQUIRED, "unsigned int", "first"),
      (REQUIRED, "unsigned int", "last"),
      (REQUIRED, "int", "flags"),
      (OPTIONAL, "int", "error_no"),
    ]),

    ("umask", [
      # The new mask
      (REQUIRED, "mode_t", "mask"),
      # The old mask
      (REQUIRED, "mode_t", "ret"),
    ]),

    ("fchmodat", [
      # fchmod()'s fd, or fchmodat()'s dirfd
      (OPTIONAL, "int", "fd"),
      # file path, except for fchmod()
      (OPTIONAL, STRING, "pathname"),
      # mode
      (REQUIRED, "mode_t", "mode"),
      # flags for fchmodat(), AT_SYMLINK_NOFOLLOW for lchmod()
      (OPTIONAL, "int", "flags", "fbbcomm_debug_at_flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("fchownat", [
      # fchown()'s fd, or fchownat()'s dirfd
      (OPTIONAL, "int", "fd"),
      # file path
      (OPTIONAL, STRING, "pathname"),
      # uid
      (OPTIONAL, "uid_t", "owner"),
      # gid
      (OPTIONAL, "gid_t", "group"),
      # flags for fchownat(), AT_SYMLINK_NOFOLLOW for lchown()
      (OPTIONAL, "int", "flags", "fbbcomm_debug_at_flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("unlink", [
      # dir file descriptor for unlinkat()
      (OPTIONAL, "int", "dirfd"),
      # path name
      (OPTIONAL, STRING, "pathname"),
      # flags for unlinkat()
      (OPTIONAL, "int", "flags", "fbbcomm_debug_at_flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
      # pre_open message has been sent before this message
      (REQUIRED, "bool", "pre_open_sent"),
    ]),

    ("link", [
      # old dir file descriptor for linkat()
      (OPTIONAL, "int", "olddirfd"),
      # old file path
      (OPTIONAL, STRING, "oldpath"),
      # new dir file descriptor for linkat()
      (OPTIONAL, "int", "newdirfd"),
      # new file path
      (OPTIONAL, STRING, "newpath"),
      # flags for linkat()
      (OPTIONAL, "int", "flags", "fbbcomm_debug_at_flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("symlink", [
      # old file path
      (OPTIONAL, STRING, "target"),
      # new dir file descriptor for symlinkat()
      (OPTIONAL, "int", "newdirfd"),
      # new file path
      (OPTIONAL, STRING, "newpath"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("lockf", [
      # file fd
      (OPTIONAL, "int", "fd"),
      # lock command
      (OPTIONAL, "int", "cmd"),
      # file range
      (OPTIONAL, "off_t", "len"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("clock_gettime", [
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("utime", [
      # ..at(), like utimensat
      (OPTIONAL, "int", "dirfd"),
      # file name
      (OPTIONAL, STRING, "pathname"),
      # all timestamps should be set to current time
      (REQUIRED, "bool", "all_utime_now"),
      # flags for utimensat(), AT_SYMLINK_NOFOLLOW for lutimes()
      (OPTIONAL, "int", "flags", "fbbcomm_debug_at_flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("futime", [
      # file fd
      (REQUIRED, "int", "fd"),
      # all timestamps should be set to current time
      (REQUIRED, "bool", "all_utime_now"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    # Step 1/3 of intercepting a pipe() or pipe2() call. See #656 for design rationale.
    # Request the supervisor to create an intercepted unnamed pipe for the interceptor.
    ("pipe_request", [
      (OPTIONAL, "int", "flags", "fbbcomm_debug_open_flags"),
    ]),

    # Step 2/3 of intercepting a pipe() or pipe2() call. This message is sent from the supervisor
    # to the interceptor, with the two fds attached as ancillary data (SCM_RIGHTS).
    ("pipe_created", [
      # if an error occurred in the supervisor while creating the pipe
      (OPTIONAL, "int", "error_no"),
    ]),

    # Step 3/3 of intercepting a pipe() or pipe2() call.
    # Notify the supervisor about the fd numbers in the interceptor.
    ("pipe_fds", [
      # The fd used for reading
      (REQUIRED, "int", "fd0"),
      # The fd used for writing
      (REQUIRED, "int", "fd1"),
    ]),

    # for dup
    ("dup", [
      # old file fd
      (REQUIRED, "int", "oldfd"),
      # new fd
      (REQUIRED, "int", "ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    # for dup2 and dup3
    ("dup3", [
      # old file fd
      (REQUIRED, "int", "oldfd"),
      # new file fd
      (REQUIRED, "int", "newfd"),
      # flags
      (OPTIONAL, "int", "flags", "fbbcomm_debug_open_flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("dlopen", [
      # file path
      (OPTIONAL, STRING, "filename"),
      # flag, decoding is left for Firebuild supervisor
      (OPTIONAL, "int", "flag"),
      # absolute filename where (if) the library was found
      (OPTIONAL, STRING, "absolute_filename"),
      # dlopen() does not set errno. Also, as per #920, we cannot get
      # the error string in a simple way without altering the
      # intercepted process's behavior and without nasty hacks. So let's
      # just go with a simple boolean denoting success vs. failure.
      (REQUIRED, "bool", "error"),
    ]),

    ("memfd_create", [
      # name
      (REQUIRED, STRING, "name"),
      # flags, decoding is left for Firebuild supervisor
      (REQUIRED, "int", "flags"),
      # return value, the file descriptor if != -1
      (REQUIRED, "int", "ret"),
    ]),

    ("timerfd_create", [
      (REQUIRED, "int", "ret"),
      # flags, decoding is left for Firebuild supervisor
      (REQUIRED, "int", "flags"),
    ]),

    ("epoll_create", [
      # flags for epoll_create1(), decoding is left for Firebuild supervisor
      (OPTIONAL, "int", "flags"),
      (REQUIRED, "int", "ret"),
    ]),

    ("eventfd", [
      # flags, decoding is left for Firebuild supervisor
      (REQUIRED, "int", "flags"),
      (REQUIRED, "int", "ret"),
    ]),

    ("signalfd", [
      # old file fd
      (REQUIRED, "int", "fd"),
      # flags, decoding is left for Firebuild supervisor
      (REQUIRED, "int", "flags"),
      (REQUIRED, "int", "ret"),
    ]),

    ("seccomp", [
      # always EINVAL at the moment, since the original call is not called
      (REQUIRED, "int", "error_no"),
    ]),

    ("exec", [
      # file to execute
      (OPTIONAL, STRING, "file"),
      # file fd to execute, in case of fexecve()
      (OPTIONAL, "int", "fd"),
      # dir fd to execute, in case of execveat()
      (OPTIONAL, "int", "dirfd"),
      # argv[]
      (ARRAY, STRING, "arg"),
      # envp[]
      (ARRAY, STRING, "env"),
      # true, in case of execvp()/execvpe()
      (OPTIONAL, "bool", "with_p"),
      # PATH, or confstr(_CS_PATH) if PATH is not set
      (OPTIONAL, STRING, "path"),
      # user CPU time in microseconds since last exec()
      (REQUIRED, "int64_t", "utime_u"),
      # system CPU time in microseconds since laste exec()
      (REQUIRED, "int64_t", "stime_u"),
    ]),

    ("exec_failed", [
      # error no., when ret = -1
      (REQUIRED, "int", "error_no"),
    ]),

    # system(3)
    ("system", [
      # command, only SystemRet is sent when command was NULL
      (OPTIONAL, STRING, "cmd"),
      # return value is sent in SystemRet
    ]),

    ("system_ret", [
      # command, not present when it was NULL
      (OPTIONAL, STRING, "cmd"),
      # return value
      (REQUIRED, "int", "ret", "fbbcomm_debug_wstatus"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    # popen(3)
    ("popen", [
      # command
      (OPTIONAL, STRING, "cmd"),
      # type, represented as flags
      (REQUIRED, "int", "type_flags", "fbbcomm_debug_open_flags"),
    ]),

    ("popen_parent", [
      # return value
      (REQUIRED, "int", "fd"),
    ]),

    # This message goes from the supervisor to the interceptor, ACKing the "popen_parent" message.
    # The FBB message is empty, but there's the pipe's reopened fd attached as ancillary data.
    ("popen_fd", [
    ]),

    ("popen_failed", [
      # command, to let the supervisor remove it from expected_children
      (OPTIONAL, STRING, "cmd"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("pclose", [
      # file descriptor
      (REQUIRED, "int", "fd"),
      # return value
      (REQUIRED, "int", "ret", "fbbcomm_debug_wstatus"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    # This is not used as a toplevel message, but in the "file_actions" field of a
    # "posix_spawn_parent" message, corresponding to an earlier posix_spawn_file_actions_addopen() call.
    ("posix_spawn_file_action_open", [
      (REQUIRED, "int", "fd"),
      # Note: path is not absolute!
      (REQUIRED, STRING, "pathname"),
      (REQUIRED, "int", "flags", "fbbcomm_debug_open_flags"),
      (REQUIRED, "mode_t", "mode"),
    ]),

    # This is not used as a toplevel message, but in the "file_actions" field of a
    # "posix_spawn_parent" message, corresponding to an earlier posix_spawn_file_actions_addclose() call.
    ("posix_spawn_file_action_close", [
      (REQUIRED, "int", "fd"),
    ]),

    # This is not used as a toplevel message, but in the "file_actions" field of a
    # "posix_spawn_parent" message, corresponding to an earlier posix_spawn_file_actions_addclosefrom_np() call.
    ("posix_spawn_file_action_closefrom", [
      (REQUIRED, "int", "lowfd"),
    ]),

    # This is not used as a toplevel message, but in the "file_actions" field of a
    # "posix_spawn_parent" message, corresponding to an earlier posix_spawn_file_actions_adddup2() call.
    ("posix_spawn_file_action_dup2", [
      (REQUIRED, "int", "oldfd"),
      (REQUIRED, "int", "newfd"),
    ]),

    # This is not used as a toplevel message, but in the "file_actions" field of a
    # "posix_spawn_parent" message, corresponding to an earlier posix_spawn_file_actions_addchdir_np() call.
    ("posix_spawn_file_action_chdir", [
      (REQUIRED, STRING, "pathname"),
    ]),

    # This is not used as a toplevel message, but in the "file_actions" field of a
    # "posix_spawn_parent" message, corresponding to an earlier posix_spawn_file_actions_addfchdir_np() call.
    ("posix_spawn_file_action_fchdir", [
      (REQUIRED, "int", "fd"),
    ]),

    # posix_spawn[p](3)
    ("posix_spawn", [
      # command
      (OPTIONAL, STRING, "file"),
      # only argv, sending argc would be redundant
      (ARRAY, STRING, "arg"),
      # environment variables in unprocessed NAME=value form
      (ARRAY, STRING, "env"),
      # posix_spawn_file_actions_t, each action is an FBB message of one of
      # posix_spawn_file_action_*
      (ARRAY, FBB, "file_actions"),
      # spawn or spawnp
      (REQUIRED, "bool", "is_spawnp"),
    ]),

    ("posix_spawn_parent", [
      # command args repeated, to let the supervisor remove it from expected_child
      (ARRAY, STRING, "arg"),
      # posix_spawn_file_actions_t, each action is an FBB message of one of
      # posix_spawn_file_action_*
      (ARRAY, FBB, "file_actions"),
      # child's process id
      (REQUIRED, "pid_t", "pid"),
    ]),

    ("posix_spawn_failed", [
      # command args repeated, to let the supervisor remove it from expected_child
      (ARRAY, STRING, "arg"),
      # posix_spawn_file_actions_t, each action is an FBB message of one of
      # posix_spawn_file_action_*
      (ARRAY, FBB, "file_actions"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("wait", [
      # pid whose termination the parent has successfully waited for
      (REQUIRED, "pid_t", "pid"),
      # not set for waitid
      (OPTIONAL, "int", "wstatus", "fbbcomm_debug_wstatus"),
      # only set for waitid
      (OPTIONAL, "int", "si_status"),
      # only set for waitid
      (OPTIONAL, "int", "si_code"),
    ]),

    ("sysconf", [
      # name
      (OPTIONAL, "int", "name"),
      # value
      (OPTIONAL, "long", "ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("gethostname", [
      # name
      (OPTIONAL, STRING, "name"),
      # value
      (OPTIONAL, "size_t", "len"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("getdomainname", [
      # name
      (OPTIONAL, STRING, "name"),
      # value
      (OPTIONAL, "size_t", "len"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("truncate", [
      # name
      (REQUIRED, STRING, "pathname"),
      (REQUIRED, "long", "length"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("pathconf", [
      # path name
      (OPTIONAL, STRING, "path"),
      # option name
      (OPTIONAL, "int", "name"),
      # option value
      (OPTIONAL, "long", "ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("fpathconf", [
      # file fd
      (OPTIONAL, "int", "fd"),
      # option name
      (OPTIONAL, "int", "name"),
      # option value
      (OPTIONAL, "long", "ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    # The first time the process attempts to read (including the recv() family) from the given inherited fd.
    # Also re-sent once with is_pread=true if that value was false the first time.
    ("read_from_inherited", [
      # file fd
      (REQUIRED, "int", "fd"),
      # Whether the read happened at an explicit offset with pread() and friends,
      # rather than at the file's current offset in a way that advances this offset.
      # Also false if preadv2() with offset == -1 mimics plain old sequential read().
      (REQUIRED, "bool", "is_pread"),
    ]),

    # The first time the process attempts to write (including the send() family) to the given inherited fd.
    # Also re-sent once with is_pwrite=true if that value was false the first time.
    ("write_to_inherited", [
      # file fd
      (REQUIRED, "int", "fd"),
      # Whether the write happened at an explicit offset with pwrite() and friends,
      # rather than at the file's current offset in a way that advances this offset.
      # Also false if pwritev2() with offset == -1 mimics plain old sequential write().
      (REQUIRED, "bool", "is_pwrite"),
    ]),

    # The first time the process attempts to query or change the seek offset.
    # Also re-sent once with modify_offset=true if that value was false the first time.
    ("seek_in_inherited", [
      # file fd
      (REQUIRED, "int", "fd"),
      # Whether the operation requested to change the offset
      (REQUIRED, "bool", "modify_offset"),
    ]),

    # Received some file descriptors via a recv[m]msg() and SCM_RIGHTS.
    ("recvmsg_scm_rights", [
      (REQUIRED, "bool", "cloexec"),
      (ARRAY, "int", "fds"),
    ]),

    # Resource usage collected at exit. The exit status will be collected by the wait() in the parent
    ("rusage", [
      # user CPU time in microseconds
      (REQUIRED, "int64_t", "utime_u"),
      # system CPU time in microseconds
      (REQUIRED, "int64_t", "stime_u"),
    ]),

    # fork()'s child
    ("fork_child", [
      # process id
      (REQUIRED, "pid_t", "pid"),
      # process parent id
      (REQUIRED, "pid_t", "ppid"),
    ]),

    # process that called fork()
    ("fork_parent", [
    ]),

    # disables interception and shortcutting
    ("clone", [
      (REQUIRED, "int", "flags", "fbbcomm_debug_clone_flags"),
    ]),

    # reading from /dev/urandom is allowed (GRND_RANDOM flag not set)
    ("getrandom", [
      (OPTIONAL, "int", "flags"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("socket", [
      (REQUIRED, "int", "domain"),
      (REQUIRED, "int", "type"),
      (REQUIRED, "int", "protocol"),
      # return value, the file descriptor if != -1
      (OPTIONAL, "int", "ret"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("socketpair", [
      (REQUIRED, "int", "domain"),
      (REQUIRED, "int", "type"),
      (REQUIRED, "int", "protocol"),
      (OPTIONAL, "int", "fd0"),
      (OPTIONAL, "int", "fd1"),
      # error no., when ret = -1
      (OPTIONAL, "int", "error_no"),
    ]),

    ("statfs", [
      (OPTIONAL, STRING, "pathname"),
      (OPTIONAL, "int", "error_no"),
    ]),
  ]
}
