Skip to content

Commit

Permalink
Fix #960, add socket shutdown implementation
Browse files Browse the repository at this point in the history
Adds an OS_SocketShutdown() wrapper around the BSD socket shutdown() API.
This allows a data transfer of a stream socket to be gracefully shut
down prior to socket closure.
  • Loading branch information
jphickey committed Apr 28, 2021
1 parent bcb8050 commit f7d9966
Show file tree
Hide file tree
Showing 15 changed files with 445 additions and 70 deletions.
1 change: 1 addition & 0 deletions src/os/inc/osapi-error.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ typedef char os_err_name_t[OS_ERROR_NAME_LENGTH];
#define OS_ERR_OPERATION_NOT_SUPPORTED (-38) /**< @brief Requested operation not support on supplied object(s) */
#define OS_ERR_INVALID_SIZE (-40) /**< @brief Invalid Size */
#define OS_ERR_OUTPUT_TOO_LARGE (-41) /**< @brief Size of output exceeds limit */
#define OS_ERR_INVALID_ARGUMENT (-42) /**< @brief Invalid argument value (other than ID or size) */

/*
** Defines for File System Calls
Expand Down
24 changes: 24 additions & 0 deletions src/os/inc/osapi-sockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ typedef enum
OS_SocketType_MAX /**< @brief Maximum */
} OS_SocketType_t;

/* NOTE: The shutdown mode enums are also a bitmask, so the specific values are important here */
/** @brief Shutdown Mode */
typedef enum
{
OS_SocketShutdownMode_NONE = 0, /**< @brief Reserved value, no effect */
OS_SocketShutdownMode_SHUT_READ = 1, /**< @brief Disable future reading */
OS_SocketShutdownMode_SHUT_WRITE = 2, /**< @brief Disable future writing */
OS_SocketShutdownMode_SHUT_READWRITE = 3 /**< @brief Disable future reading or writing */
} OS_SocketShutdownMode_t;

/**
* @brief Storage buffer for generic network address
*
Expand Down Expand Up @@ -279,6 +289,20 @@ int32 OS_SocketBind(osal_id_t sock_id, const OS_SockAddr_t *Addr);
*/
int32 OS_SocketConnect(osal_id_t sock_id, const OS_SockAddr_t *Addr, int32 timeout);

/*-------------------------------------------------------------------------------------*/
/**
* @brief Implement graceful shutdown of a stream socket
*
* This can be utilized to indicate the end of data stream without immediately closing
* the socket, giving the remote side an indication that the data transfer is complete.
*
* @param[in] sock_id The socket ID
* @param[in] Mode Whether to shutdown reading, writing, or both.
*
* @return Execution status, see @ref OSReturnCodes
*/
int32 OS_SocketShutdown(osal_id_t sock_id, OS_SocketShutdownMode_t Mode);

/*-------------------------------------------------------------------------------------*/
/**
* @brief Waits for and accept the next incoming connection on the given socket
Expand Down
43 changes: 43 additions & 0 deletions src/os/portable/os-impl-bsd-sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,49 @@ int32 OS_SocketConnect_Impl(const OS_object_token_t *token, const OS_SockAddr_t
return return_code;
} /* end OS_SocketConnect_Impl */

/*----------------------------------------------------------------
Function: OS_SocketShutdown_Impl
Purpose: Connects the socket to a remote address.
Socket must be of the STREAM variety.
Returns: OS_SUCCESS on success, or relevant error code
------------------------------------------------------------------*/
int32 OS_SocketShutdown_Impl(const OS_object_token_t *token, OS_SocketShutdownMode_t Mode)
{
OS_impl_file_internal_record_t *conn_impl;
int32 return_code;
int how;

conn_impl = OS_OBJECT_TABLE_GET(OS_impl_filehandle_table, *token);

/* Note that when called via the shared layer,
* the "Mode" arg has already been checked/validated. */
if (Mode == OS_SocketShutdownMode_SHUT_READ)
{
how = SHUT_RD;
}
else if (Mode == OS_SocketShutdownMode_SHUT_WRITE)
{
how = SHUT_WR;
}
else
{
how = SHUT_RDWR;
}

if (shutdown(conn_impl->fd, how) == 0)
{
return_code = OS_SUCCESS;
}
else
{
return_code = OS_ERROR;
}

return return_code;
} /* end OS_SocketShutdown_Impl */

/*----------------------------------------------------------------
*
* Function: OS_SocketAccept_Impl
Expand Down
10 changes: 10 additions & 0 deletions src/os/portable/os-impl-no-sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ int32 OS_SocketConnect_Impl(const OS_object_token_t *token, const OS_SockAddr_t
return OS_ERR_NOT_IMPLEMENTED;
}

/*----------------------------------------------------------------
* Implementation for no network configuration
*
* See prototype for argument/return detail
*-----------------------------------------------------------------*/
int32 OS_SocketShutdown_Impl(const OS_object_token_t *token, OS_SocketShutdownMode_t Mode)
{
return OS_ERR_NOT_IMPLEMENTED;
}

/*----------------------------------------------------------------
* Implementation for no network configuration
*
Expand Down
1 change: 1 addition & 0 deletions src/os/posix/inc/os-impl-sockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define OS_NETWORK_SUPPORTS_IPV6

Expand Down
9 changes: 9 additions & 0 deletions src/os/shared/inc/os-shared-sockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ int32 OS_SocketAccept_Impl(const OS_object_token_t *sock_token, const OS_object_
------------------------------------------------------------------*/
int32 OS_SocketConnect_Impl(const OS_object_token_t *token, const OS_SockAddr_t *Addr, int32 timeout);

/*----------------------------------------------------------------
Function: OS_SocketShutdown_Impl
Purpose: Graceful shutdown of a stream socket
Returns: OS_SUCCESS on success, or relevant error code
------------------------------------------------------------------*/
int32 OS_SocketShutdown_Impl(const OS_object_token_t *token, OS_SocketShutdownMode_t Mode);

/*----------------------------------------------------------------
Function: OS_SocketRecvFrom_Impl
Expand Down
56 changes: 56 additions & 0 deletions src/os/shared/src/osapi-sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,62 @@ int32 OS_SocketConnect(osal_id_t sock_id, const OS_SockAddr_t *Addr, int32 Timeo
return return_code;
} /* end OS_SocketConnect */

/*----------------------------------------------------------------
*
* Function: OS_SocketShutdown
*
* Purpose: Implemented per public OSAL API
* See description in API and header file for detail
*
*-----------------------------------------------------------------*/
int32 OS_SocketShutdown(osal_id_t sock_id, OS_SocketShutdownMode_t Mode)
{
OS_stream_internal_record_t *stream;
OS_object_token_t token;
int32 return_code;

/* Confirm that "Mode" is one of the 3 acceptable values */
BUGCHECK(Mode == OS_SocketShutdownMode_SHUT_READ || Mode == OS_SocketShutdownMode_SHUT_WRITE ||
Mode == OS_SocketShutdownMode_SHUT_READWRITE,
OS_ERR_INVALID_ARGUMENT);

return_code = OS_ObjectIdGetById(OS_LOCK_MODE_GLOBAL, LOCAL_OBJID_TYPE, sock_id, &token);
if (return_code == OS_SUCCESS)
{
stream = OS_OBJECT_TABLE_GET(OS_stream_table, token);

if (stream->socket_domain == OS_SocketDomain_INVALID)
{
return_code = OS_ERR_INCORRECT_OBJ_TYPE;
}
else if (stream->socket_type == OS_SocketType_STREAM && (stream->stream_state & OS_STREAM_STATE_CONNECTED) == 0)
{
/* Stream socket must not be connected */
return_code = OS_ERR_INCORRECT_OBJ_STATE;
}
else
{
return_code = OS_SocketShutdown_Impl(&token, Mode);

if (return_code == OS_SUCCESS)
{
if (Mode & OS_SocketShutdownMode_SHUT_READ)
{
stream->stream_state &= ~OS_STREAM_STATE_READABLE;
}
if (Mode & OS_SocketShutdownMode_SHUT_WRITE)
{
stream->stream_state &= ~OS_STREAM_STATE_WRITABLE;
}
}
}

OS_ObjectIdRelease(&token);
}

return return_code;
} /* end OS_SocketShutdown */

/*----------------------------------------------------------------
*
* Function: OS_SocketRecvFrom
Expand Down
Loading

0 comments on commit f7d9966

Please sign in to comment.