SafeAPI Backend POSIX
POSIX/Linux OAL backend for safeAPIFreamwork
Loading...
Searching...
No Matches
sapi_posix_backend_ipc.c
Go to the documentation of this file.
1/* _GNU_SOURCE (implies _POSIX_C_SOURCE) rather than _POSIX_C_SOURCE alone:
2 * F_SETPIPE_SZ below is a Linux/glibc extension, not standard POSIX, and
3 * is itself guarded with #ifdef so this file still builds - just without
4 * that best-effort pipe-sizing call - on a non-glibc POSIX target. */
5#define _GNU_SOURCE
6
20
21#include <errno.h>
22#include <fcntl.h>
23#include <poll.h>
24#include <time.h>
25#include <unistd.h>
26
27typedef struct
28{
29 int read_fd;
30 int write_fd;
31 size_t message_size;
33
34typedef char posix_ipc_storage_fits_[(sizeof(posix_ipc_state_t) <= sizeof(sapi_ipc_storage_t)) ? 1 : -1];
35
38static int remaining_ms(const struct timespec *deadline)
39{
40 struct timespec now;
41 int64_t delta_ms;
42
43 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0)
44 {
45 return -1;
46 }
47 delta_ms = ((int64_t)deadline->tv_sec - (int64_t)now.tv_sec) * 1000;
48 delta_ms += ((int64_t)deadline->tv_nsec - (int64_t)now.tv_nsec) / 1000000;
49 if (delta_ms < 0)
50 {
51 delta_ms = 0;
52 }
53 return (int)delta_ms;
54}
55
56static void compute_deadline(sapi_duration_ms_t timeout_ms, struct timespec *out_deadline)
57{
58 (void)clock_gettime(CLOCK_MONOTONIC, out_deadline);
59 out_deadline->tv_sec += (time_t)(timeout_ms / 1000U);
60 out_deadline->tv_nsec += (long)((timeout_ms % 1000U) * 1000000L);
61 if (out_deadline->tv_nsec >= 1000000000L)
62 {
63 out_deadline->tv_sec += 1;
64 out_deadline->tv_nsec -= 1000000000L;
65 }
66}
67
73static sapi_status_t transfer_all(int fd, const void *write_src, void *read_dst, size_t size,
74 sapi_duration_ms_t timeout_ms, bool read_not_write)
75{
76 struct timespec deadline;
77 size_t done = 0;
78 const uint8_t *src_bytes = (const uint8_t *)write_src;
79 uint8_t *dst_bytes = (uint8_t *)read_dst;
80
81 compute_deadline(timeout_ms, &deadline);
82
83 while (done < size)
84 {
85 struct pollfd pfd;
86 int rc;
87 ssize_t n;
88 int wait_ms = remaining_ms(&deadline);
89
90 if (wait_ms <= 0)
91 {
92 return SAPI_STATUS_TIMEOUT;
93 }
94
95 pfd.fd = fd;
96 pfd.events = read_not_write ? POLLIN : POLLOUT;
97 pfd.revents = 0;
98 rc = poll(&pfd, 1, wait_ms);
99 if (rc == 0)
100 {
101 return SAPI_STATUS_TIMEOUT;
102 }
103 if (rc < 0)
104 {
105 if (errno == EINTR)
106 {
107 continue;
108 }
109 return SAPI_STATUS_INTERNAL_ERROR;
110 }
111
112 n = read_not_write ? read(fd, dst_bytes + done, size - done) : write(fd, src_bytes + done, size - done);
113 if (n < 0)
114 {
115 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
116 {
117 continue;
118 }
119 return SAPI_STATUS_INTERNAL_ERROR;
120 }
121 if (n == 0)
122 {
123 /* read() returning 0 means the write end was closed. */
124 return SAPI_STATUS_INTERNAL_ERROR;
125 }
126 done += (size_t)n;
127 }
128
129 return SAPI_STATUS_OK;
130}
131
132static sapi_status_t backend_create(sapi_ipc_storage_t *storage, const sapi_ipc_config_t *config,
133 sapi_ipc_handle_t *out_handle)
134{
135 posix_ipc_state_t *state;
136 int fds[2];
137 long requested_capacity;
138
139 if ((storage == NULL) || (config == NULL) || (out_handle == NULL) || (config->message_size == 0U)
140 || (config->queue_depth == 0U))
141 {
142 return SAPI_STATUS_INVALID_PARAM;
143 }
144
145 if (pipe(fds) != 0)
146 {
147 return SAPI_STATUS_INTERNAL_ERROR;
148 }
149 (void)fcntl(fds[0], F_SETFL, O_NONBLOCK);
150 (void)fcntl(fds[1], F_SETFL, O_NONBLOCK);
151
152 /* Best-effort: try to size the kernel pipe buffer to roughly
153 * message_size * queue_depth so REQ-OAL-IPC-001's "bounded" queue
154 * depth is approximated. Failure (permission, exceeds system limit)
155 * is not fatal - the pipe still works with its default capacity. */
156 requested_capacity = (long)(config->message_size * config->queue_depth);
157#ifdef F_SETPIPE_SZ
158 (void)fcntl(fds[1], F_SETPIPE_SZ, requested_capacity);
159#else
160 (void)requested_capacity; /* best-effort sizing unavailable on this target */
161#endif
162
163 state = (posix_ipc_state_t *)(void *)storage;
164 state->read_fd = fds[0];
165 state->write_fd = fds[1];
166 state->message_size = config->message_size;
167 *out_handle = (sapi_ipc_handle_t)(void *)storage;
168 return SAPI_STATUS_OK;
169}
170
171static sapi_status_t backend_send(sapi_ipc_handle_t handle, const void *message, size_t message_size,
172 sapi_duration_ms_t timeout_ms)
173{
174 posix_ipc_state_t *state = (posix_ipc_state_t *)(void *)handle;
175
176 if ((state == NULL) || (message == NULL) || (message_size == 0U))
177 {
178 return SAPI_STATUS_INVALID_PARAM;
179 }
180 return transfer_all(state->write_fd, message, NULL, message_size, timeout_ms, false);
181}
182
183static sapi_status_t backend_receive(sapi_ipc_handle_t handle, void *out_message, size_t buffer_size,
184 sapi_duration_ms_t timeout_ms)
185{
186 posix_ipc_state_t *state = (posix_ipc_state_t *)(void *)handle;
187
188 if ((state == NULL) || (out_message == NULL) || (buffer_size == 0U))
189 {
190 return SAPI_STATUS_INVALID_PARAM;
191 }
192 return transfer_all(state->read_fd, NULL, out_message, buffer_size, timeout_ms, true);
193}
194
195static sapi_status_t backend_destroy(sapi_ipc_handle_t handle)
196{
197 posix_ipc_state_t *state = (posix_ipc_state_t *)(void *)handle;
198
199 if (state == NULL)
200 {
201 return SAPI_STATUS_INVALID_PARAM;
202 }
203 (void)close(state->read_fd);
204 (void)close(state->write_fd);
205 return SAPI_STATUS_OK;
206}
207
208static const sapi_ipc_backend_t s_posix_ipc_backend = { backend_create, backend_send, backend_receive,
209 backend_destroy };
210
211const sapi_ipc_backend_t *sapi_posix_backend_ipc(void)
212{
213 return &s_posix_ipc_backend;
214}
Real POSIX/Linux backend for every OAL service (ADR-018).
const sapi_ipc_backend_t * sapi_posix_backend_ipc(void)
The POSIX sapi_ipc backend (pipe() per channel).
static sapi_status_t transfer_all(int fd, const void *write_src, void *read_dst, size_t size, sapi_duration_ms_t timeout_ms, bool read_not_write)
static int remaining_ms(const struct timespec *deadline)