2 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
3 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
5 #include "cmogstored.h"
10 unsigned char buf
[FLEXIBLE_ARRAY_MEMBER
];
13 static void * wbuf_newv(size_t total
, struct iovec
*iov
, int iovcnt
)
15 struct mog_wbuf
*wbuf
= malloc(sizeof(struct mog_wbuf
) + total
);
19 if (!wbuf
) return MOG_WR_ERROR
;
26 for (i
= 0; i
< iovcnt
; i
++)
27 dst
= mempcpy(dst
, iov
[i
].iov_base
, iov
[i
].iov_len
);
32 static void * wbuf_new(void *buf
, size_t len
)
39 return wbuf_newv(len
, &iov
, 1);
42 MOG_NOINLINE
static void sysbug(const char *fn
, ssize_t bytes
)
44 syslog(LOG_ERR
, "%s returned %zd bytes written but: %m", fn
, bytes
);
47 enum mog_write_state
mog_tryflush(int fd
, struct mog_wbuf
**x
)
49 struct mog_wbuf
*wbuf
= *x
;
50 unsigned char *ptr
= wbuf
->buf
+ wbuf
->off
;
51 size_t len
= wbuf
->len
- wbuf
->off
;
54 ssize_t w
= write(fd
, ptr
, len
);
58 return MOG_WRSTATE_DONE
;
68 assert(w
< 0 && "no error from write(2)");
71 case_EAGAIN
: return MOG_WRSTATE_BUSY
;
76 return MOG_WRSTATE_ERR
;
82 * - NULL on full write
83 * - MOG_WR_ERROR on error (and sets errno)
84 * - address to a new mog_wbuf with unbuffered contents on partial write
86 void * mog_trywritev(int fd
, struct iovec
*iov
, int iovcnt
)
92 for (i
= 0; i
< iovcnt
; i
++)
93 len
+= iov
[i
].iov_len
;
98 w
= writev(fd
, iov
, iovcnt
);
105 TRACE(CMOGSTORED_WRITE_BUFFERED(fd
, len
));
106 return wbuf_newv(len
, iov
, iovcnt
);
107 case EINTR
: goto retry
;
108 case 0: sysbug("writev", w
);
112 struct iovec
*new_iov
= iov
;
116 /* skip over iovecs we've already written completely */
117 for (i
= 0; i
< iovcnt
; i
++, new_iov
++) {
121 * partially written iovec,
122 * modify and retry with current iovec in front
124 if (new_iov
->iov_len
> (size_t)w
) {
125 unsigned char *base
= new_iov
->iov_base
;
127 new_iov
->iov_len
-= w
;
129 new_iov
->iov_base
= (void *)base
;
133 w
-= new_iov
->iov_len
;
136 /* retry without the already-written iovecs */
145 * - NULL on full write
146 * - MOG_WR_ERROR on error (and sets errno)
147 * - address to a new mog_wbuf with unbuffered contents on partial write
149 void * mog_trysend(int fd
, void *buf
, size_t len
, off_t more
)
152 int flags
= more
> 0 ? MOG_MSG_MORE
: 0;
155 ssize_t w
= send(fd
, buf
, len
, flags
);
157 if (w
== (ssize_t
)len
)
158 return NULL
; /* all done */
160 buf
= (char *)buf
+ w
;
166 * we bail on w == 0, too. send should normally
167 * return zero, but in case there's a kernel bug
168 * we should not infinite loop
172 TRACE(CMOGSTORED_WRITE_BUFFERED(fd
, len
));
173 return wbuf_new(buf
, len
);
174 case EINTR
: continue;
175 case 0: sysbug("send", w
);
187 return mog_trywritev(fd
, &iov
, 1);