about summary refs log tree commit diff
path: root/src/libs6/s6_fdholder_setdump.c
blob: 58d70032ec771e9d256d718c1610ad0e8c32338e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/* ISC license. */

#include <sys/uio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>

#include <skalibs/uint32.h>
#include <skalibs/allreadwrite.h>
#include <skalibs/bytestr.h>
#include <skalibs/error.h>
#include <skalibs/tai.h>
#include <skalibs/unixmessage.h>

#include <s6/fdholder.h>

#include <skalibs/posixishard.h>

int s6_fdholder_setdump (s6_fdholder_t *a, s6_fdholder_fd_t const *list, unsigned int ntot, tain const *deadline, tain *stamp)
{
  uint32_t trips ;
  if (!ntot) return 1 ;
  for (unsigned int i = 0 ; i < ntot ; i++)
  {
    size_t zpos = byte_chr(list[i].id, S6_FDHOLDER_ID_SIZE + 1, 0) ;
    if (!zpos || zpos >= S6_FDHOLDER_ID_SIZE + 1) return (errno = EINVAL, 0) ;
  }
  {
    char pack[5] = "!" ;
    unixmessage m = { .s = pack, .len = 5, .fds = 0, .nfds = 0 } ;
    uint32_pack_big(pack+1, ntot) ;
    if (!unixmessage_put(&a->connection.out, &m)) return 0 ;
    if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return 0 ;
    if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return 0 ;
    if (!m.len || m.nfds) { unixmessage_drop(&m) ; return (errno = EPROTO, 0) ; }
    if (m.s[0]) return (errno = (unsigned char)m.s[0], 0) ;
    if (m.len != 5) return (errno = EPROTO, 0) ;
    uint32_unpack_big(m.s + 1, &trips) ;
    if (trips != 1 + (ntot-1) / UNIXMESSAGE_MAXFDS) return (errno = EPROTO, 0) ;
  }
  {
    struct iovec v[1 + (UNIXMESSAGE_MAXFDS << 1)] = { [0] = { .iov_base = ".", .iov_len = 1 } } ;
    int fds[UNIXMESSAGE_MAXFDS] ;
    char pack[UNIXMESSAGE_MAXFDS][TAIN_PACK + 1] ;
    for (unsigned int j = 0 ; j < UNIXMESSAGE_MAXFDS ; j++)
    {
      v[1 + (j<<1)].iov_base = pack[j] ;
      v[1 + (j<<1)].iov_len = TAIN_PACK + 1 ;
    }
    for (unsigned int i = 0 ; i < trips ; i++)
    {
      {
        unsigned int n = ntot > UNIXMESSAGE_MAXFDS ? UNIXMESSAGE_MAXFDS : ntot ;
        unixmessagev m = { .v = v, .vlen = 1 + (n<<1), .fds = fds, .nfds = n } ;
        for (unsigned int j = 0 ; j < n ; j++, list++, ntot--)
        {
          size_t len = strlen(list->id) ;
          tain_pack(pack[j], &list->limit) ;
          pack[j][TAIN_PACK] = (unsigned char)len ;
          v[2 + (j<<1)].iov_base = (char *)list->id ;
          v[2 + (j<<1)].iov_len = len + 1 ;
          fds[j] = list->fd ;
        }
        if (!unixmessage_putv(&a->connection.out, &m)) return 0 ;
      }
      if (!unixmessage_sender_timed_flush(&a->connection.out, deadline, stamp)) return 0 ;
      {
        unixmessage m ;
        if (sanitize_read(unixmessage_timed_receive(&a->connection.in, &m, deadline, stamp)) < 0) return 0 ;
        if (m.len != 1 || m.nfds)
        {
          unixmessage_drop(&m) ;
          return (errno = EPROTO, 0) ;
        }
        if (!error_isagain((unsigned char)m.s[0]) && i < trips-1)
          return errno = m.s[0] ? (unsigned char)m.s[0] : EPROTO, 0 ;
        if (i == trips - 1 && m.s[0])
          return errno = error_isagain((unsigned char)m.s[0]) ? EPROTO : (unsigned char)m.s[0], 0 ;
      }
    }
  }
  return 1 ;
}