Revert "Revert "oran-shell-release: release image for F""
[pti/rtp.git] / meta-starlingx / meta-stx-distro / recipes-security / gssproxy / files / client-Switch-to-non-blocking-sockets.patch
1 From 1962e6128a4d86a7c54977577e1e4224cadbb5f7 Mon Sep 17 00:00:00 2001
2 From: Alexander Scheel <ascheel@redhat.com>
3 Date: Wed, 2 Aug 2017 15:11:49 -0400
4 Subject: [PATCH] [client] Switch to non-blocking sockets
5
6 Switch the gssproxy client library to non-blocking sockets, allowing
7 for timeout and retry operations.  The client will automatically retry
8 both send() and recv() operations three times on ETIMEDOUT.  If the
9 combined send() and recv() hit the three time limit, ETIMEDOUT will be
10 exposed to the caller in the minor status.
11
12 Signed-off-by: Alexander Scheel <ascheel@redhat.com>
13 Reviewed-by: Simo Sorce <simo@redhat.com>
14 [rharwood@redhat.com: commit message cleanups, rebased]
15 Reviewed-by: Robbie Harwood <rharwood@redhat.com>
16 (cherry picked from commit d035646c8feb0b78f0c157580ca02c46cd00dd7e)
17 ---
18  proxy/src/client/gpm_common.c | 317 +++++++++++++++++++++++++++++++---
19  1 file changed, 295 insertions(+), 22 deletions(-)
20
21 diff --git a/proxy/src/client/gpm_common.c b/proxy/src/client/gpm_common.c
22 index 2133618..dba23a6 100644
23 --- a/proxy/src/client/gpm_common.c
24 +++ b/proxy/src/client/gpm_common.c
25 @@ -7,9 +7,15 @@
26  #include <stdlib.h>
27  #include <time.h>
28  #include <pthread.h>
29 +#include <sys/epoll.h>
30 +#include <fcntl.h>
31 +#include <sys/timerfd.h>
32  
33  #define FRAGMENT_BIT (1 << 31)
34  
35 +#define RESPONSE_TIMEOUT 15
36 +#define MAX_TIMEOUT_RETRY 3
37 +
38  struct gpm_ctx {
39      pthread_mutex_t lock;
40      int fd;
41 @@ -20,6 +26,9 @@ struct gpm_ctx {
42      gid_t gid;
43  
44      int next_xid;
45 +
46 +    int epollfd;
47 +    int timerfd;
48  };
49  
50  /* a single global struct is not particularly efficient,
51 @@ -39,6 +48,8 @@ static void gpm_init_once(void)
52      pthread_mutex_init(&gpm_global_ctx.lock, &attr);
53  
54      gpm_global_ctx.fd = -1;
55 +    gpm_global_ctx.epollfd = -1;
56 +    gpm_global_ctx.timerfd = -1;
57  
58      seedp = time(NULL) + getpid() + pthread_self();
59      gpm_global_ctx.next_xid = rand_r(&seedp);
60 @@ -69,6 +80,7 @@ static int gpm_open_socket(struct gpm_ctx *gpmctx)
61      struct sockaddr_un addr = {0};
62      char name[PATH_MAX];
63      int ret;
64 +    unsigned flags;
65      int fd = -1;
66  
67      ret = get_pipe_name(name);
68 @@ -86,6 +98,18 @@ static int gpm_open_socket(struct gpm_ctx *gpmctx)
69          goto done;
70      }
71  
72 +    ret = fcntl(fd, F_GETFD, &flags);
73 +    if (ret != 0) {
74 +        ret = errno;
75 +        goto done;
76 +    }
77 +
78 +    ret = fcntl(fd, F_SETFD, flags | O_NONBLOCK);
79 +    if (ret != 0) {
80 +        ret = errno;
81 +        goto done;
82 +    }
83 +
84      ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
85      if (ret == -1) {
86          ret = errno;
87 @@ -163,6 +187,158 @@ static int gpm_release_sock(struct gpm_ctx *gpmctx)
88      return pthread_mutex_unlock(&gpmctx->lock);
89  }
90  
91 +static void gpm_timer_close(struct gpm_ctx *gpmctx) {
92 +    if (gpmctx->timerfd < 0) {
93 +        return;
94 +    }
95 +
96 +    close(gpmctx->timerfd);
97 +    gpmctx->timerfd = -1;
98 +}
99 +
100 +static int gpm_timer_setup(struct gpm_ctx *gpmctx, int timeout_seconds) {
101 +    int ret;
102 +    struct itimerspec its;
103 +
104 +    if (gpmctx->timerfd >= 0) {
105 +        gpm_timer_close(gpmctx);
106 +    }
107 +
108 +    gpmctx->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
109 +    if (gpmctx->timerfd < 0) {
110 +        return errno;
111 +    }
112 +
113 +    its.it_interval.tv_sec = timeout_seconds;
114 +    its.it_interval.tv_nsec = 0;
115 +    its.it_value.tv_sec = timeout_seconds;
116 +    its.it_value.tv_nsec = 0;
117 +
118 +    ret = timerfd_settime(gpmctx->timerfd, 0, &its, NULL);
119 +    if (ret) {
120 +        ret = errno;
121 +        gpm_timer_close(gpmctx);
122 +        return ret;
123 +    }
124 +
125 +    return 0;
126 +}
127 +
128 +static void gpm_epoll_close(struct gpm_ctx *gpmctx) {
129 +    if (gpmctx->epollfd < 0) {
130 +        return;
131 +    }
132 +
133 +    close(gpmctx->epollfd);
134 +    gpmctx->epollfd = -1;
135 +}
136 +
137 +static int gpm_epoll_setup(struct gpm_ctx *gpmctx) {
138 +    struct epoll_event ev;
139 +    int ret;
140 +
141 +    if (gpmctx->epollfd >= 0) {
142 +        gpm_epoll_close(gpmctx);
143 +    }
144 +
145 +    gpmctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
146 +    if (gpmctx->epollfd == -1) {
147 +        return errno;
148 +    }
149 +
150 +    /* Add timer */
151 +    ev.events = EPOLLIN;
152 +    ev.data.fd = gpmctx->timerfd;
153 +    ret = epoll_ctl(gpmctx->epollfd, EPOLL_CTL_ADD, gpmctx->timerfd, &ev);
154 +    if (ret == -1) {
155 +        ret = errno;
156 +        gpm_epoll_close(gpmctx);
157 +        return ret;
158 +    }
159 +
160 +    return ret;
161 +}
162 +
163 +static int gpm_epoll_wait(struct gpm_ctx *gpmctx, uint32_t event_flags) {
164 +    int ret;
165 +    int epoll_ret;
166 +    struct epoll_event ev;
167 +    struct epoll_event events[2];
168 +    uint64_t timer_read;
169 +
170 +    if (gpmctx->epollfd < 0) {
171 +        ret = gpm_epoll_setup(gpmctx);
172 +        if (ret)
173 +            return ret;
174 +    }
175 +
176 +    ev.events = event_flags;
177 +    ev.data.fd = gpmctx->fd;
178 +    epoll_ret = epoll_ctl(gpmctx->epollfd, EPOLL_CTL_ADD, gpmctx->fd, &ev);
179 +    if (epoll_ret == -1) {
180 +        ret = errno;
181 +        gpm_epoll_close(gpmctx);
182 +        return ret;
183 +    }
184 +
185 +    do {
186 +        epoll_ret = epoll_wait(gpmctx->epollfd, events, 2, -1);
187 +    } while (epoll_ret < 0 && errno == EINTR);
188 +
189 +    if (epoll_ret < 0) {
190 +        /* Error while waiting that isn't EINTR */
191 +        ret = errno;
192 +        gpm_epoll_close(gpmctx);
193 +    } else if (epoll_ret == 0) {
194 +        /* Shouldn't happen as timeout == -1; treat it like a timeout
195 +         * occurred. */
196 +        ret = ETIMEDOUT;
197 +        gpm_epoll_close(gpmctx);
198 +    } else if (epoll_ret == 1 && events[0].data.fd == gpmctx->timerfd) {
199 +        /* Got an event which is only our timer */
200 +        ret = read(gpmctx->timerfd, &timer_read, sizeof(uint64_t));
201 +        if (ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
202 +            /* In the case when reading from the timer failed, don't hide the
203 +             * timer error behind ETIMEDOUT such that it isn't retried */
204 +            ret = errno;
205 +        } else {
206 +            /* If ret == 0, then we definitely timed out. Else, if ret == -1
207 +             * and errno == EAGAIN or errno == EWOULDBLOCK, we're in a weird
208 +             * edge case where epoll thinks the timer can be read, but it
209 +             * is blocking more; treat it like a TIMEOUT and retry, as
210 +             * nothing around us would handle EAGAIN from timer and retry
211 +             * it. */
212 +            ret = ETIMEDOUT;
213 +        }
214 +        gpm_epoll_close(gpmctx);
215 +    } else {
216 +        /* If ret == 2, then we ignore the timerfd; that way if the next
217 +         * operation cannot be performed immediately, we timeout and retry.
218 +         * If ret == 1 and data.fd == gpmctx->fd, return 0. */
219 +        ret = 0;
220 +    }
221 +
222 +    epoll_ret = epoll_ctl(gpmctx->epollfd, EPOLL_CTL_DEL, gpmctx->fd, NULL);
223 +    if (epoll_ret == -1) {
224 +        /* If we previously had an error, expose that error instead of
225 +         * clobbering it with errno; else if no error, then assume it is
226 +         * better to notify of the error deleting the event than it is
227 +         * to continue. */
228 +        if (ret == 0)
229 +            ret = errno;
230 +        gpm_epoll_close(gpmctx);
231 +    }
232 +
233 +    return ret;
234 +}
235 +
236 +static int gpm_retry_socket(struct gpm_ctx *gpmctx)
237 +{
238 +    gpm_epoll_close(gpmctx);
239 +    gpm_close_socket(gpmctx);
240 +    return gpm_open_socket(gpmctx);
241 +}
242 +
243  /* must be called after the lock has been grabbed */
244  static int gpm_send_buffer(struct gpm_ctx *gpmctx,
245                             char *buffer, uint32_t length)
246 @@ -183,8 +359,13 @@ static int gpm_send_buffer(struct gpm_ctx *gpmctx,
247      retry = false;
248      do {
249          do {
250 +            ret = gpm_epoll_wait(gpmctx, EPOLLOUT);
251 +            if (ret != 0) {
252 +                goto done;
253 +            }
254 +
255              ret = 0;
256 -            wn = send(gpmctx->fd, &size, sizeof(uint32_t), MSG_NOSIGNAL);
257 +            wn = write(gpmctx->fd, &size, sizeof(uint32_t));
258              if (wn == -1) {
259                  ret = errno;
260              }
261 @@ -192,8 +373,7 @@ static int gpm_send_buffer(struct gpm_ctx *gpmctx,
262          if (wn != 4) {
263              /* reopen and retry once */
264              if (retry == false) {
265 -                gpm_close_socket(gpmctx);
266 -                ret = gpm_open_socket(gpmctx);
267 +                ret = gpm_retry_socket(gpmctx);
268                  if (ret == 0) {
269                      retry = true;
270                      continue;
271 @@ -208,9 +388,14 @@ static int gpm_send_buffer(struct gpm_ctx *gpmctx,
272  
273      pos = 0;
274      while (length > pos) {
275 -        wn = send(gpmctx->fd, buffer + pos, length - pos, MSG_NOSIGNAL);
276 +        ret = gpm_epoll_wait(gpmctx, EPOLLOUT);
277 +        if (ret) {
278 +            goto done;
279 +        }
280 +
281 +        wn = write(gpmctx->fd, buffer + pos, length - pos);
282          if (wn == -1) {
283 -            if (errno == EINTR) {
284 +            if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
285                  continue;
286              }
287              ret = errno;
288 @@ -231,7 +416,7 @@ done:
289  
290  /* must be called after the lock has been grabbed */
291  static int gpm_recv_buffer(struct gpm_ctx *gpmctx,
292 -                           char *buffer, uint32_t *length)
293 +                           char **buffer, uint32_t *length)
294  {
295      uint32_t size;
296      ssize_t rn;
297 @@ -239,6 +424,11 @@ static int gpm_recv_buffer(struct gpm_ctx *gpmctx,
298      int ret;
299  
300      do {
301 +        ret = gpm_epoll_wait(gpmctx, EPOLLIN);
302 +        if (ret) {
303 +            goto done;
304 +        }
305 +
306          ret = 0;
307          rn = read(gpmctx->fd, &size, sizeof(uint32_t));
308          if (rn == -1) {
309 @@ -258,11 +448,22 @@ static int gpm_recv_buffer(struct gpm_ctx *gpmctx,
310          goto done;
311      }
312  
313 +    *buffer = malloc(*length);
314 +    if (*buffer == NULL) {
315 +        ret = ENOMEM;
316 +        goto done;
317 +    }
318 +
319      pos = 0;
320      while (*length > pos) {
321 -        rn = read(gpmctx->fd, buffer + pos, *length - pos);
322 +        ret = gpm_epoll_wait(gpmctx, EPOLLIN);
323 +        if (ret) {
324 +            goto done;
325 +        }
326 +
327 +        rn = read(gpmctx->fd, *buffer + pos, *length - pos);
328          if (rn == -1) {
329 -            if (errno == EINTR) {
330 +            if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
331                  continue;
332              }
333              ret = errno;
334 @@ -281,6 +482,7 @@ done:
335      if (ret) {
336          /* on errors we can only close the fd and return */
337          gpm_close_socket(gpmctx);
338 +        gpm_epoll_close(gpmctx);
339      }
340      return ret;
341  }
342 @@ -309,6 +511,63 @@ static struct gpm_ctx *gpm_get_ctx(void)
343      return &gpm_global_ctx;
344  }
345  
346 +static int gpm_send_recv_loop(struct gpm_ctx *gpmctx, char *send_buffer,
347 +                              uint32_t send_length, char** recv_buffer,
348 +                              uint32_t *recv_length)
349 +{
350 +    int ret;
351 +    int retry_count;
352 +
353 +    /* setup timer */
354 +    ret = gpm_timer_setup(gpmctx, RESPONSE_TIMEOUT);
355 +    if (ret)
356 +        return ret;
357 +
358 +    for (retry_count = 0; retry_count < MAX_TIMEOUT_RETRY; retry_count++) {
359 +        /* send to proxy */
360 +        ret = gpm_send_buffer(gpmctx, send_buffer, send_length);
361 +
362 +        if (ret == 0) {
363 +            /* No error, continue to recv */
364 +        } else if (ret == ETIMEDOUT) {
365 +            /* Close and reopen socket before trying again */
366 +            ret = gpm_retry_socket(gpmctx);
367 +            if (ret != 0)
368 +                return ret;
369 +            ret = ETIMEDOUT;
370 +
371 +            /* RETRY entire send */
372 +            continue;
373 +        } else {
374 +            /* Other error */
375 +            return ret;
376 +        }
377 +
378 +        /* receive answer */
379 +        ret = gpm_recv_buffer(gpmctx, recv_buffer, recv_length);
380 +        if (ret == 0) {
381 +            /* No error */
382 +            break;
383 +        } else if (ret == ETIMEDOUT) {
384 +            /* Close and reopen socket before trying again */
385 +            ret = gpm_retry_socket(gpmctx);
386 +
387 +            /* Free buffer and set it to NULL to prevent free(xdr_reply_ctx) */
388 +            free(recv_buffer);
389 +            recv_buffer = NULL;
390 +
391 +            if (ret != 0)
392 +                return ret;
393 +            ret = ETIMEDOUT;
394 +        } else {
395 +            /* Other error */
396 +            return ret;
397 +        }
398 +    }
399 +
400 +    return ret;
401 +}
402 +
403  OM_uint32 gpm_release_buffer(OM_uint32 *minor_status,
404                               gss_buffer_t buffer)
405  {
406 @@ -399,15 +658,20 @@ int gpm_make_call(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res)
407      gp_rpc_msg msg;
408      XDR xdr_call_ctx;
409      XDR xdr_reply_ctx;
410 -    char buffer[MAX_RPC_SIZE];
411 -    uint32_t length;
412 +    char *send_buffer = NULL;
413 +    char *recv_buffer = NULL;
414 +    uint32_t send_length;
415 +    uint32_t recv_length;
416      uint32_t xid;
417      bool xdrok;
418      bool sockgrab = false;
419      int ret;
420  
421 -    xdrmem_create(&xdr_call_ctx, buffer, MAX_RPC_SIZE, XDR_ENCODE);
422 -    xdrmem_create(&xdr_reply_ctx, buffer, MAX_RPC_SIZE, XDR_DECODE);
423 +    send_buffer = malloc(MAX_RPC_SIZE);
424 +    if (send_buffer == NULL)
425 +        return ENOMEM;
426 +
427 +    xdrmem_create(&xdr_call_ctx, send_buffer, MAX_RPC_SIZE, XDR_ENCODE);
428  
429      memset(&msg, 0, sizeof(gp_rpc_msg));
430      msg.header.type = GP_RPC_CALL;
431 @@ -450,22 +714,22 @@ int gpm_make_call(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res)
432          goto done;
433      }
434  
435 -    /* send to proxy */
436 -    ret = gpm_send_buffer(gpmctx, buffer, xdr_getpos(&xdr_call_ctx));
437 -    if (ret) {
438 -        goto done;
439 -    }
440 +    /* set send_length */
441 +    send_length = xdr_getpos(&xdr_call_ctx);
442  
443 -    /* receive answer */
444 -    ret = gpm_recv_buffer(gpmctx, buffer, &length);
445 -    if (ret) {
446 +    /* Send request, receive response with timeout */
447 +    ret = gpm_send_recv_loop(gpmctx, send_buffer, send_length, &recv_buffer,
448 +                             &recv_length);
449 +    if (ret)
450          goto done;
451 -    }
452  
453      /* release the lock */
454      gpm_release_sock(gpmctx);
455      sockgrab = false;
456  
457 +    /* Create the reply context */
458 +    xdrmem_create(&xdr_reply_ctx, recv_buffer, recv_length, XDR_DECODE);
459 +
460      /* decode header */
461      memset(&msg, 0, sizeof(gp_rpc_msg));
462      xdrok = xdr_gp_rpc_msg(&xdr_reply_ctx, &msg);
463 @@ -489,12 +753,21 @@ int gpm_make_call(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res)
464      }
465  
466  done:
467 +    gpm_timer_close(gpmctx);
468 +    gpm_epoll_close(gpmctx);
469 +
470      if (sockgrab) {
471          gpm_release_sock(gpmctx);
472      }
473      xdr_free((xdrproc_t)xdr_gp_rpc_msg, (char *)&msg);
474      xdr_destroy(&xdr_call_ctx);
475 -    xdr_destroy(&xdr_reply_ctx);
476 +
477 +    if (recv_buffer != NULL)
478 +        xdr_destroy(&xdr_reply_ctx);
479 +
480 +    free(send_buffer);
481 +    free(recv_buffer);
482 +
483      return ret;
484  }
485