1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_event.h>
11
12
13static ngx_chain_t *ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec,
14    ngx_chain_t *in, ngx_log_t *log);
15static ssize_t ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec);
16
17
18ngx_chain_t *
19ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
20{
21    ssize_t        n;
22    off_t          send;
23    ngx_chain_t   *cl;
24    ngx_event_t   *wev;
25    ngx_iovec_t    vec;
26    struct iovec   iovs[NGX_IOVS_PREALLOCATE];
27
28    wev = c->write;
29
30    if (!wev->ready) {
31        return in;
32    }
33
34#if (NGX_HAVE_KQUEUE)
35
36    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
37        (void) ngx_connection_error(c, wev->kq_errno,
38                               "kevent() reported about an closed connection");
39        wev->error = 1;
40        return NGX_CHAIN_ERROR;
41    }
42
43#endif
44
45    /* the maximum limit size is the maximum size_t value - the page size */
46
47    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
48        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
49    }
50
51    send = 0;
52
53    vec.iovs = iovs;
54    vec.nalloc = NGX_IOVS_PREALLOCATE;
55
56    for ( ;; ) {
57
58        /* create the iovec and coalesce the neighbouring bufs */
59
60        cl = ngx_udp_output_chain_to_iovec(&vec, in, c->log);
61
62        if (cl == NGX_CHAIN_ERROR) {
63            return NGX_CHAIN_ERROR;
64        }
65
66        if (cl && cl->buf->in_file) {
67            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
68                          "file buf in sendmsg "
69                          "t:%d r:%d f:%d %p %p-%p %p %O-%O",
70                          cl->buf->temporary,
71                          cl->buf->recycled,
72                          cl->buf->in_file,
73                          cl->buf->start,
74                          cl->buf->pos,
75                          cl->buf->last,
76                          cl->buf->file,
77                          cl->buf->file_pos,
78                          cl->buf->file_last);
79
80            ngx_debug_point();
81
82            return NGX_CHAIN_ERROR;
83        }
84
85        if (cl == in) {
86            return in;
87        }
88
89        send += vec.size;
90
91        n = ngx_sendmsg(c, &vec);
92
93        if (n == NGX_ERROR) {
94            return NGX_CHAIN_ERROR;
95        }
96
97        if (n == NGX_AGAIN) {
98            wev->ready = 0;
99            return in;
100        }
101
102        c->sent += n;
103
104        in = ngx_chain_update_sent(in, n);
105
106        if (send >= limit || in == NULL) {
107            return in;
108        }
109    }
110}
111
112
113static ngx_chain_t *
114ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, ngx_log_t *log)
115{
116    size_t         total, size;
117    u_char        *prev;
118    ngx_uint_t     n, flush;
119    ngx_chain_t   *cl;
120    struct iovec  *iov;
121
122    cl = in;
123    iov = NULL;
124    prev = NULL;
125    total = 0;
126    n = 0;
127    flush = 0;
128
129    for ( /* void */ ; in && !flush; in = in->next) {
130
131        if (in->buf->flush || in->buf->last_buf) {
132            flush = 1;
133        }
134
135        if (ngx_buf_special(in->buf)) {
136            continue;
137        }
138
139        if (in->buf->in_file) {
140            break;
141        }
142
143        if (!ngx_buf_in_memory(in->buf)) {
144            ngx_log_error(NGX_LOG_ALERT, log, 0,
145                          "bad buf in output chain "
146                          "t:%d r:%d f:%d %p %p-%p %p %O-%O",
147                          in->buf->temporary,
148                          in->buf->recycled,
149                          in->buf->in_file,
150                          in->buf->start,
151                          in->buf->pos,
152                          in->buf->last,
153                          in->buf->file,
154                          in->buf->file_pos,
155                          in->buf->file_last);
156
157            ngx_debug_point();
158
159            return NGX_CHAIN_ERROR;
160        }
161
162        size = in->buf->last - in->buf->pos;
163
164        if (prev == in->buf->pos) {
165            iov->iov_len += size;
166
167        } else {
168            if (n == vec->nalloc) {
169                ngx_log_error(NGX_LOG_ALERT, log, 0,
170                              "too many parts in a datagram");
171                return NGX_CHAIN_ERROR;
172            }
173
174            iov = &vec->iovs[n++];
175
176            iov->iov_base = (void *) in->buf->pos;
177            iov->iov_len = size;
178        }
179
180        prev = in->buf->pos + size;
181        total += size;
182    }
183
184    if (!flush) {
185#if (NGX_SUPPRESS_WARN)
186        vec->size = 0;
187        vec->count = 0;
188#endif
189        return cl;
190    }
191
192    vec->count = n;
193    vec->size = total;
194
195    return in;
196}
197
198
199static ssize_t
200ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec)
201{
202    ssize_t        n;
203    ngx_err_t      err;
204    struct msghdr  msg;
205
206    ngx_memzero(&msg, sizeof(struct msghdr));
207
208    if (c->socklen) {
209        msg.msg_name = c->sockaddr;
210        msg.msg_namelen = c->socklen;
211    }
212
213    msg.msg_iov = vec->iovs;
214    msg.msg_iovlen = vec->count;
215
216eintr:
217
218    n = sendmsg(c->fd, &msg, 0);
219
220    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
221                   "sendmsg: %z of %uz", n, vec->size);
222
223    if (n == -1) {
224        err = ngx_errno;
225
226        switch (err) {
227        case NGX_EAGAIN:
228            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
229                           "sendmsg() not ready");
230            return NGX_AGAIN;
231
232        case NGX_EINTR:
233            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
234                           "sendmsg() was interrupted");
235            goto eintr;
236
237        default:
238            c->write->error = 1;
239            ngx_connection_error(c, err, "sendmsg() failed");
240            return NGX_ERROR;
241        }
242    }
243
244    return n;
245}
246