// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file contains type and function definitions relevant to Mojo invitation
// APIs.
//
// Note: This header should be compilable as C.

#ifndef MOJO_PUBLIC_C_SYSTEM_INVITATION_H_
#define MOJO_PUBLIC_C_SYSTEM_INVITATION_H_

#include <stdint.h>

#include "mojo/public/c/system/macros.h"
#include "mojo/public/c/system/platform_handle.h"
#include "mojo/public/c/system/system_export.h"
#include "mojo/public/c/system/types.h"

// Flags included in |MojoProcessErrorDetails| indicating additional status
// information.
typedef uint32_t MojoProcessErrorFlags;

// No flags.
#define MOJO_PROCESS_ERROR_FLAG_NONE ((MojoProcessErrorFlags)0)

// If set, the process has been disconnected. No further
// |MojoProcessErrorHandler| invocations occur for it, and any IPC primitives
// (message pipes, data pipes) which were connected to it have been or will
// imminently be disconnected.
#define MOJO_PROCESS_ERROR_FLAG_DISCONNECTED ((MojoProcessErrorFlags)1)

// Details regarding why an invited process has had its connection to this
// process terminated by the system. See |MojoProcessErrorHandler| and
// |MojoSendInvitation()|.
struct MOJO_ALIGNAS(8) MojoProcessErrorDetails {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // The length of the string pointed to by |error_message| below.
  uint32_t error_message_length;

  // An error message corresponding to the reason why the connection was
  // terminated. This is an information message which may be useful to
  // developers.
  MOJO_POINTER_FIELD(const char*, error_message);

  // See |MojoProcessErrorFlags|.
  MojoProcessErrorFlags flags;
};
MOJO_STATIC_ASSERT(sizeof(MojoProcessErrorDetails) == 24,
                   "MojoProcessErrorDetails has wrong size.");

// An opaque process handle value which must be provided when sending an
// invitation to another process via a platform transport. See
// |MojoSendInvitation()|.
struct MOJO_ALIGNAS(8) MojoPlatformProcessHandle {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // The process handle value. For Windows this is a valid process HANDLE value.
  // For Fuchsia it must be |zx_handle_t| process handle, and for all other
  // POSIX systems, it's a PID.
  uint64_t value;
};
MOJO_STATIC_ASSERT(sizeof(MojoPlatformProcessHandle) == 16,
                   "MojoPlatformProcesHandle has wrong size.");

// Enumeration indicating the type of transport over which an invitation will be
// sent or received.
typedef uint32_t MojoInvitationTransportType;

// The channel transport type embodies common platform-specific OS primitives
// for FIFO message passing:
//   - For Windows, this is a named pipe.
//   - For Fuchsia, it's a channel.
//   - For all other POSIX systems, it's a Unix domain socket pair.
//
// See |MojoInvitationTransportHandle| for details.
#define MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL ((MojoInvitationTransportType)0)

// Similar to CHANNEL transport, but used for an endpoint which requires an
// additional step to accept an inbound connection. This corresponds to a
// bound listening socket on POSIX, or named pipe server handle on Windows.
//
// The remote endpoint should establish a working connection to the server side
// and wrap the handle to that connection using a CHANNEL transport.
#define MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER \
  ((MojoInvitationTransportType)1)

// A transport endpoint over which an invitation may be sent or received via
// |MojoSendInvitation()| or |MojoAcceptInvitation()| respectively.
struct MOJO_ALIGNAS(8) MojoInvitationTransportEndpoint {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // The type of this transport endpoint. See |MojoInvitationTransportType|.
  MojoInvitationTransportType type;

  // The number of platform handles in |platform_handles| below.
  uint32_t num_platform_handles;

  // Platform handle(s) corresponding to the system object(s) backing this
  // endpoint. The concrete type of the handle(s) depends on |type|.
  //
  // For |MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL| endpoints:
  //   - On Windows, this is a single named pipe HANDLE
  //   - On Fuchsua, this is a single channel Fuchsia handle
  //   - On other POSIX systems, this is a single Unix domain socket file
  //     descriptor.
  MOJO_POINTER_FIELD(const struct MojoPlatformHandle*, platform_handles);
};
MOJO_STATIC_ASSERT(sizeof(MojoInvitationTransportEndpoint) == 24,
                   "MojoInvitationTransportEndpoint has wrong size.");

// Flags passed to |MojoCreateInvitation()| via |MojoCreateInvitationOptions|.
typedef uint32_t MojoCreateInvitationFlags;

// No flags. Default behavior.
#define MOJO_CREATE_INVITATION_FLAG_NONE ((MojoCreateInvitationFlags)0)

// Options passed to |MojoCreateInvitation()|.
struct MOJO_ALIGNAS(8) MojoCreateInvitationOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoCreateInvitationFlags|.
  MojoCreateInvitationFlags flags;
};
MOJO_STATIC_ASSERT(sizeof(MojoCreateInvitationOptions) == 8,
                   "MojoCreateInvitationOptions has wrong size");

// Flags passed to |MojoAttachMessagePipeToInvitation()| via
// |MojoAttachMessagePipeToInvitationOptions|.
typedef uint32_t MojoAttachMessagePipeToInvitationFlags;

// No flags. Default behavior.
#define MOJO_ATTACH_MESSAGE_PIPE_TO_INVITATION_FLAG_NONE \
  ((MojoAttachMessagePipeToInvitationFlags)0)

// Options passed to |MojoAttachMessagePipeToInvitation()|.
struct MOJO_ALIGNAS(8) MojoAttachMessagePipeToInvitationOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoAttachMessagePipeToInvitationFlags|.
  MojoAttachMessagePipeToInvitationFlags flags;
};
MOJO_STATIC_ASSERT(sizeof(MojoAttachMessagePipeToInvitationOptions) == 8,
                   "MojoAttachMessagePipeToInvitationOptions has wrong size");

// Flags passed to |MojoExtractMessagePipeFromInvitation()| via
// |MojoExtractMessagePipeFromInvitationOptions|.
typedef uint32_t MojoExtractMessagePipeFromInvitationFlags;

// No flags. Default behavior.
#define MOJO_EXTRACT_MESSAGE_PIPE_FROM_INVITATION_FLAG_NONE \
  ((MojoExtractMessagePipeFromInvitationFlags)0)

// Options passed to |MojoExtractMessagePipeFromInvitation()|.
struct MOJO_ALIGNAS(8) MojoExtractMessagePipeFromInvitationOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoExtractMessagePipeFromInvitationFlags|.
  MojoExtractMessagePipeFromInvitationFlags flags;
};
MOJO_STATIC_ASSERT(
    sizeof(MojoExtractMessagePipeFromInvitationOptions) == 8,
    "MojoExtractMessagePipeFromInvitationOptions has wrong size");

// Flags passed to |MojoSendInvitation()| via |MojoSendInvitationOptions|.
typedef uint32_t MojoSendInvitationFlags;

// No flags. Default behavior.
#define MOJO_SEND_INVITATION_FLAG_NONE ((MojoSendInvitationFlags)0)

// Send an isolated invitation to the receiver. Isolated invitations only
// establish communication between the sender and the receiver. Accepting an
// isolated invitation does not make IPC possible between the sender and any
// other members of the receiver's process graph, nor does it make IPC possible
// between the receiver and any other members of the sender's process graph.
//
// Invitations sent with this flag set must be accepted with the corresponding
// |MOJO_ACCEPT_INVITATION_FLAG_ISOLATED| flag set, and may only have a single
// message pipe attached with a name of exactly four zero-bytes ("\0\0\0\0").
#define MOJO_SEND_INVITATION_FLAG_ISOLATED ((MojoSendInvitationFlags)1)

// Options passed to |MojoSendInvitation()|.
struct MOJO_ALIGNAS(8) MojoSendInvitationOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoSendInvitationFlags|.
  MojoSendInvitationFlags flags;

  // If |flags| includes |MOJO_SEND_INVITATION_FLAG_ISOLATED| then these fields
  // specify a name identifying the established isolated connection. There are
  // no restrictions on the value given. If |isolated_connection_name_length| is
  // non-zero, the system ensures that only one isolated process connection can
  // exist for the given name at any time.
  MOJO_POINTER_FIELD(const char*, isolated_connection_name);
  uint32_t isolated_connection_name_length;
};
MOJO_STATIC_ASSERT(sizeof(MojoSendInvitationOptions) == 24,
                   "MojoSendInvitationOptions has wrong size");

// Flags passed to |MojoAcceptInvitation()| via |MojoAcceptInvitationOptions|.
typedef uint32_t MojoAcceptInvitationFlags;

// No flags. Default behavior.
#define MOJO_ACCEPT_INVITATION_FLAG_NONE ((MojoAcceptInvitationFlags)0)

// Accept an isoalted invitation from the sender. See
// |MOJO_SEND_INVITATION_FLAG_ISOLATED| for details.
#define MOJO_ACCEPT_INVITATION_FLAG_ISOLATED ((MojoAcceptInvitationFlags)1)

// Options passed to |MojoAcceptInvitation()|.
struct MOJO_ALIGNAS(8) MojoAcceptInvitationOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoAcceptInvitationFlags|.
  MojoAcceptInvitationFlags flags;
};
MOJO_STATIC_ASSERT(sizeof(MojoAcceptInvitationOptions) == 8,
                   "MojoAcceptInvitationOptions has wrong size");

#ifdef __cplusplus
extern "C" {
#endif

// A callback which may be invoked by the system when a connection to an invited
// process is terminated due to a communication error (i.e. the invited process
// has sent a message which fails some validation check in the system). See
// |MojoSendInvitation()|.
//
// |context| is the value of |context| given to |MojoSendInvitation()| when
// inviting the process for whom this callback is being invoked.
typedef void (*MojoProcessErrorHandler)(
    uintptr_t context,
    const struct MojoProcessErrorDetails* details);

// Creates a new invitation to be sent to another process.
//
// An invitation is used to invite another process to join this process's
// IPC network. The caller must already be a member of a Mojo network, either
// either by itself having been previously invited, or by being the Mojo broker
// process initialized via the Mojo Core Embedder API.
//
// Invitations can have message pipes attached to them, and these message pipes
// are used to bootstrap Mojo IPC between the inviter and the invitee. See
// |MojoAttachMessagePipeToInvitation()| for attaching message pipes, and
// |MojoSendInvitation()| for sending an invitation.
//
// |options| controls behavior. May be null for default behavior.
// |invitation_handle| must be the address of storage for a MojoHandle value
//     to be output upon success.
//
// NOTE: If discarding an invitation instead of sending it with
// |MojoSendInvitation()|, you must close its handle (i.e. |MojoClose()|) to
// avoid leaking associated system resources.
//
// Returns:
//   |MOJO_RESULT_OK| if the invitation was created successfully. The new
//       invitation's handle is stored in |*invitation_handle| before returning.
//   |MOJO_RESULT_INVALID_ARGUMENT| if |options| was non-null but malformed.
//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a handle could not be allocated for the
//       new invitation.
MOJO_SYSTEM_EXPORT MojoResult
MojoCreateInvitation(const struct MojoCreateInvitationOptions* options,
                     MojoHandle* invitation_handle);

// Attaches a message pipe endpoint to an invitation.
//
// This creates a new message pipe which will span the boundary between the
// calling process and the invitation's eventual target process. One end of the
// new pipe is attached to the invitation while the other end is returned to the
// caller. Every attached message pipe has an arbitrary |name| value which
// identifies it within the invitation.
//
// Message pipes can be extracted by the recipient by calling
// |MojoExtractMessagePipeFromInvitation()|. It is up to applications to
// communicate out-of-band or establish a convention for how attached pipes
// are named.
//
// |invitation_handle| is the invitation to which a pipe should be attached.
// |name| is an arbitrary name to give this pipe, required to extract the pipe
//     on the receiving end of the invitation. Note that the name is scoped to
//     this invitation only, so e.g. multiple invitations may attach pipes with
//     the name "foo", but any given invitation may only have a single pipe
//     attached with that name.
// |name_num_bytes| is the number of bytes from |name| to use as the name.
// |options| controls behavior. May be null for default behavior.
// |message_pipe_handle| is the address of storage for a MojoHandle value.
//     Upon success, the handle of the local endpoint of the new message pipe
//     will be stored here.
//
// Returns:
//   |MOJO_RESULT_OK| if the pipe was created and attached successfully. The
//       local endpoint of the pipe has its handle stored in
//       |*message_pipe_handle| before returning. The other endpoint is attached
//       to the invitation.
//   |MOJO_RESULT_INVALID_ARGUMENT| if |invitation_handle| was not an invitation
//       handle, |options| was non-null but malformed, or |message_pipe_handle|
//       was null.
//   |MOJO_RESULT_ALREADY_EXISTS| if |name| was already in use for this
//       invitation.
//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a handle could not be allocated for the
//       new local message pipe endpoint.
MOJO_SYSTEM_EXPORT MojoResult MojoAttachMessagePipeToInvitation(
    MojoHandle invitation_handle,
    const void* name,
    uint32_t name_num_bytes,
    const struct MojoAttachMessagePipeToInvitationOptions* options,
    MojoHandle* message_pipe_handle);

// Extracts a message pipe endpoint from an invitation.
//
// |invitation_handle| is the invitation from which to extract the endpoint.
// |name| is the name of the endpoint within the invitation. This corresponds
//     to the name that was given to |MojoAttachMessagePipeToInvitation()| when
//     the endpoint was attached.
// |name_num_bytes| is the number of bytes from |name| to use as the name.
// |options| controls behavior. May be null for default behavior.
// |message_pipe_handle| is the address of storage for a MojoHandle value.
//     Upon success, the handle of the extracted message pipe endpoint will be
//     stored here.
//
// Note that it is possible to extract an endpoint from an invitation even
// before the invitation has been sent to a remote process. If this is done and
// then the invitation is sent, the receiver will not see this endpoint as it
// will no longer be attached.
//
// Returns:
//   |MOJO_RESULT_OK| if a new local message pipe endpoint was successfully
//       created and returned in |*message_pipe_handle|. Note that the
//       association of this endpoint with an invitation attachment is
//       necessarily an asynchronous operation, and it is not known at return
//       whether an attachment named |name| actually exists on the invitation.
//       As such, the operation may still fail eventually, resuling in a broken
//       pipe, i.e. the extracted pipe will signal peer closure.
//   |MOJO_RESULT_INVALID_ARGUMENT| if |invitation_handle| was not an invitation
//       handle, |options| was non-null but malformed, or |message_pipe_handle|
//       was null.
//   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a handle could not be allocated for the
//       new local message pipe endpoint.
//   |MOJO_RESULT_NOT_FOUND| if it is known at call time that there is no pipe
//       named |name| attached to the invitation. This is possible if the
//       invtation was created within the calling process by
//       |MojoCreateInvitation()|.
MOJO_SYSTEM_EXPORT MojoResult MojoExtractMessagePipeFromInvitation(
    MojoHandle invitation_handle,
    const void* name,
    uint32_t name_num_bytes,
    const struct MojoExtractMessagePipeFromInvitationOptions* options,
    MojoHandle* message_pipe_handle);

// Sends an invitation on a transport endpoint to bootstrap IPC between the
// calling process and another process.
//
// |invitation_handle| is the handle of the invitation to send.
// |process_handle| is an opaque, platform-specific handle to the remote
//     process. See |MojoPlatformProcessHandle|. This is not necessarily
//     required to be a valid process handle, but on some platforms (namely
//     Windows and Mac) it's important if the invitation target will need to
//     send or receive any kind of platform handles (including shared memory)
//     over Mojo message pipes.
// |transport_endpoint| is one endpoint of a platform transport primitive, the
//     other endpoint of which should be established within the process
//     corresponding to |*process_handle|. See |MojoInvitationTransportEndpoint|
//     for details.
// |error_handler| is a function to invoke if the connection to the invitee
//     encounters any kind of error condition, e.g. a message validation failure
//     reported by |MojoNotifyBadMessage()|, or permanent disconnection. See
//     |MojoProcessErrorDetails| for more information.
// |error_handler_context| is an arbitrary value to be associated with this
//     process invitation. This value is passed as the |context| argument to
//     |error_handler| when invoked regarding this invitee.
// |options| controls behavior. May be null for default behavior.
//
// This assumes ownership of any platform handles in |transport_endpoint| if
// and only if returning |MOJO_RESULT_OK|. In that case, |invitation_handle| is
// also invalidated.
//
// NOTE: It's pointless to send an invitation without at least one message pipe
// attached, so it is considered an error to attempt to do so.
//
// Returns:
//   |MOJO_RESULT_OK| if the invitation was successfully sent over the
//       transport. |invitation_handle| is implicitly closed. Note that this
//       does not guarantee that the invitation has been received by the target
//       yet, or that it ever will be (e.g. the target process may terminate
//       first or simply fail to accept the invitation).
//   |MOJO_RESULT_INVALID_ARGUMENT| if |invitation_handle| was not an invitation
//       handle, |transport_endpoint| was null or malformed, or |options| was
//       non-null but malformed.
//   |MOJO_RESULT_ABORTED| if the system failed to issue any necessary
//       communication via |transport_endpoint|, possibly due to a configuration
//       issue with the endpoint. The caller may attempt to correct this
//       situation and call again.
//   |MOJO_RESULT_FAILED_PRECONDITION| if there were no message pipes attached
//       to the invitation. The caller may correct this situation and call
//       again.
//   |MOJO_RESULT_UNIMPLEMENTED| if the transport endpoint type is not supported
//       by the system's version of Mojo.
MOJO_SYSTEM_EXPORT MojoResult MojoSendInvitation(
    MojoHandle invitation_handle,
    const struct MojoPlatformProcessHandle* process_handle,
    const struct MojoInvitationTransportEndpoint* transport_endpoint,
    MojoProcessErrorHandler error_handler,
    uintptr_t error_handler_context,
    const struct MojoSendInvitationOptions* options);

// Accepts an invitation from a transport endpoint to complete IPC bootstrapping
// between the calling process and whoever sent the invitation from the other
// end of the transport.
//
// |transport_endpoint| is one endpoint of a platform transport primitive, the
//     other endpoint of which should be established within a process
//     who has sent or will send an invitation via that endpoint. See
//     |MojoInvitationTransportEndpoint| for details.
// |options| controls behavior. May be null for default behavior.
// |invitation_handle| is the address of storage for a MojoHandle value. Upon
//     success, the handle of the accepted invitation will be stored here.
//
// Once an invitation is accepted, message pipes endpoints may be extracted from
// it by calling |MojoExtractMessagePipeFromInvitation()|.
//
// Note that it is necessary to eventually close (i.e. |MojoClose()|) any
// accepted invitation handle in order to clean up any associated system
// resources. If an accepted invitation is closed while it still has message
// pipes attached (i.e. not exracted as above), those pipe endpoints are also
// closed.
//
// Returns:
//   |MOJO_RESULT_OK| if the invitation was successfully accepted. The handle
//       to the invitation is stored in |*invitation_handle| before returning.
//   |MOJO_RESULT_INVALID_ARGUMENT| if |transport_endpoint| was null or
//       malfored, |options| was non-null but malformed, or |invitation_handle|
//       was null.
//   |MOJO_RESULT_ABORTED| if the system failed to receive any communication via
//       |transport_endpoint|, possibly due to some configuration error. The
//       caller may attempt to correct this situation and call again.
//   |MOJO_RESULT_UNIMPLEMENTED| if the transport endpoint type is not supported
//       by the system's version of Mojo.
MOJO_SYSTEM_EXPORT MojoResult MojoAcceptInvitation(
    const struct MojoInvitationTransportEndpoint* transport_endpoint,
    const struct MojoAcceptInvitationOptions* options,
    MojoHandle* invitation_handle);

#ifdef __cplusplus
}  // extern "C"
#endif

#endif  // MOJO_PUBLIC_C_SYSTEM_INVITATION_H_