fix(ipc): preserve legacy preedit and bound client waits
This commit is contained in:
179
ipc.c
179
ipc.c
@@ -1,12 +1,102 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include "ipc.h"
|
||||
|
||||
static int64_t
|
||||
nowms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
if(clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
|
||||
return -1;
|
||||
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
deadline(void)
|
||||
{
|
||||
int64_t now;
|
||||
|
||||
now = nowms();
|
||||
if(now < 0)
|
||||
return -1;
|
||||
return now + Ipcwaitms;
|
||||
}
|
||||
|
||||
static int
|
||||
waitfd(int fd, short events, int64_t until)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
int64_t now, left;
|
||||
int n;
|
||||
|
||||
pfd.fd = fd;
|
||||
pfd.events = events;
|
||||
for(;;){
|
||||
now = nowms();
|
||||
if(now < 0)
|
||||
return -1;
|
||||
left = until - now;
|
||||
if(left <= 0){
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
pfd.revents = 0;
|
||||
n = poll(&pfd, 1, left > INT32_MAX ? INT32_MAX : (int)left);
|
||||
if(n < 0 && errno == EINTR)
|
||||
continue;
|
||||
if(n < 0)
|
||||
return -1;
|
||||
if(n == 0){
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
if(pfd.revents & POLLNVAL){
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if(pfd.revents & (events|POLLERR|POLLHUP))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
readwait(int fd, void *buf, size_t n, int64_t until)
|
||||
{
|
||||
unsigned char *p;
|
||||
ssize_t r;
|
||||
|
||||
p = buf;
|
||||
while(n > 0){
|
||||
if(waitfd(fd, POLLIN, until) < 0)
|
||||
return -1;
|
||||
r = recv(fd, p, n, MSG_DONTWAIT);
|
||||
if(r < 0 && errno == EINTR)
|
||||
continue;
|
||||
if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
continue;
|
||||
if(r < 0)
|
||||
return -1;
|
||||
if(r == 0){
|
||||
errno = ECONNRESET;
|
||||
return -1;
|
||||
}
|
||||
p += r;
|
||||
n -= r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
putlen(unsigned char p[Ipclensz], size_t n)
|
||||
{
|
||||
@@ -70,7 +160,9 @@ int
|
||||
ipcconnect(void)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int e, fd;
|
||||
socklen_t nerr;
|
||||
int e, err, fd, fdflags, flags;
|
||||
int64_t until;
|
||||
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sun_family = AF_UNIX;
|
||||
@@ -81,8 +173,33 @@ ipcconnect(void)
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if(fd < 0)
|
||||
return -1;
|
||||
if(connect(fd, (struct sockaddr*)&addr, sizeof addr) == 0)
|
||||
return fd;
|
||||
fdflags = fcntl(fd, F_GETFD);
|
||||
if(fdflags < 0 || fcntl(fd, F_SETFD, fdflags|FD_CLOEXEC) < 0)
|
||||
goto Bad;
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if(flags < 0 || fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0)
|
||||
goto Bad;
|
||||
until = deadline();
|
||||
if(until < 0)
|
||||
goto Bad;
|
||||
if(connect(fd, (struct sockaddr*)&addr, sizeof addr) < 0){
|
||||
if(errno != EINPROGRESS && errno != EALREADY && errno != EINTR &&
|
||||
errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
goto Bad;
|
||||
if(waitfd(fd, POLLOUT, until) < 0)
|
||||
goto Bad;
|
||||
nerr = sizeof err;
|
||||
if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &nerr) < 0)
|
||||
goto Bad;
|
||||
if(err != 0){
|
||||
errno = err;
|
||||
goto Bad;
|
||||
}
|
||||
}
|
||||
if(fcntl(fd, F_SETFL, flags) < 0)
|
||||
goto Bad;
|
||||
return fd;
|
||||
Bad:
|
||||
e = errno;
|
||||
close(fd);
|
||||
errno = e;
|
||||
@@ -255,14 +372,28 @@ ipcsend(int fd, const void *buf, size_t n)
|
||||
{
|
||||
const unsigned char *p;
|
||||
ssize_t r;
|
||||
int64_t until;
|
||||
|
||||
p = buf;
|
||||
if(n == 0)
|
||||
return 0;
|
||||
until = deadline();
|
||||
if(until < 0)
|
||||
return -1;
|
||||
while(n > 0){
|
||||
r = send(fd, p, n, MSG_NOSIGNAL);
|
||||
if(waitfd(fd, POLLOUT, until) < 0)
|
||||
return -1;
|
||||
r = send(fd, p, n, MSG_NOSIGNAL|MSG_DONTWAIT);
|
||||
if(r < 0 && errno == EINTR)
|
||||
continue;
|
||||
if(r <= 0)
|
||||
if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
continue;
|
||||
if(r < 0)
|
||||
return -1;
|
||||
if(r == 0){
|
||||
errno = EPIPE;
|
||||
return -1;
|
||||
}
|
||||
p += r;
|
||||
n -= r;
|
||||
}
|
||||
@@ -270,7 +401,8 @@ ipcsend(int fd, const void *buf, size_t n)
|
||||
}
|
||||
|
||||
static int
|
||||
readfield(int fd, size_t n, char *dst, size_t cap)
|
||||
readfield(int fd, size_t n, char *dst, size_t cap, size_t *copied,
|
||||
int64_t until)
|
||||
{
|
||||
unsigned char discard[128];
|
||||
size_t keep, part;
|
||||
@@ -280,17 +412,18 @@ readfield(int fd, size_t n, char *dst, size_t cap)
|
||||
keep = 0;
|
||||
if(cap > 0)
|
||||
keep = n >= cap ? cap - 1 : n;
|
||||
if(keep > 0 && ipcreadn(fd, dst, keep) < 0)
|
||||
if(keep > 0 && readwait(fd, dst, keep, until) < 0)
|
||||
return -1;
|
||||
if(cap > 0)
|
||||
dst[keep] = '\0';
|
||||
n -= keep;
|
||||
while(n > 0){
|
||||
part = n < sizeof discard ? n : sizeof discard;
|
||||
if(ipcreadn(fd, discard, part) < 0)
|
||||
if(readwait(fd, discard, part, until) < 0)
|
||||
return -1;
|
||||
n -= part;
|
||||
}
|
||||
*copied = keep;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -299,6 +432,8 @@ ipcreadresp(int fd, int want, char *commit, size_t ccap,
|
||||
char *preedit, size_t pcap, Ipcresp *resp)
|
||||
{
|
||||
unsigned char hdr[Ipcresphdrsz], npreedit[Ipclensz];
|
||||
size_t wirelen;
|
||||
int64_t until;
|
||||
|
||||
if(resp == NULL || (ccap > 0 && commit == NULL) ||
|
||||
(pcap > 0 && preedit == NULL))
|
||||
@@ -308,20 +443,30 @@ ipcreadresp(int fd, int want, char *commit, size_t ccap,
|
||||
if(pcap > 0)
|
||||
preedit[0] = '\0';
|
||||
memset(resp, 0, sizeof *resp);
|
||||
if(ipcreadn(fd, hdr, sizeof hdr) < 0)
|
||||
until = deadline();
|
||||
if(until < 0 || readwait(fd, hdr, sizeof hdr, until) < 0)
|
||||
return -1;
|
||||
resp->eaten = hdr[0] != 0;
|
||||
resp->ncommit = getlen(hdr + 1);
|
||||
if(resp->ncommit > Ipcfieldmax)
|
||||
if(hdr[0] > 1){
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
if(readfield(fd, resp->ncommit, commit, ccap) < 0)
|
||||
}
|
||||
resp->eaten = hdr[0];
|
||||
wirelen = getlen(hdr + 1);
|
||||
if(wirelen > Ipcfieldmax){
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
}
|
||||
if(readfield(fd, wirelen, commit, ccap, &resp->commitlen, until) < 0)
|
||||
return -1;
|
||||
if(!want)
|
||||
return 0;
|
||||
if(ipcreadn(fd, npreedit, sizeof npreedit) < 0)
|
||||
if(readwait(fd, npreedit, sizeof npreedit, until) < 0)
|
||||
return -1;
|
||||
resp->npreedit = getlen(npreedit);
|
||||
if(resp->npreedit > Ipcfieldmax)
|
||||
wirelen = getlen(npreedit);
|
||||
if(wirelen > Ipcfieldmax){
|
||||
errno = EPROTO;
|
||||
return -1;
|
||||
return readfield(fd, resp->npreedit, preedit, pcap);
|
||||
}
|
||||
return readfield(fd, wirelen, preedit, pcap, &resp->preeditlen,
|
||||
until);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user