fix(ipc): preserve legacy preedit and bound client waits

This commit is contained in:
2026-08-14 22:17:59 +09:00
parent 5dc2530951
commit 534ddcd9bb
6 changed files with 309 additions and 29 deletions

View File

@@ -1,10 +1,31 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include "test.h"
#undef accept
#undef listen
#undef send
static int64_t
nowms(void)
{
struct timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
return 0;
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
static void ipcclientdeadlines(struct ct*);
static void ipcconnectcloexec(struct ct*);
void
ipc_masks_modifiers(struct ct *t)
{
@@ -196,15 +217,15 @@ ipc_response_empty_and_preedit(struct ct *t)
CT_EQ_INT(t, 0, ipcreadresp(fd[1], 0, commit, sizeof commit,
nil, 0, &resp))){
CT_EQ_INT(t, 0, resp.eaten);
CT_EQ_SIZE(t, 0, resp.ncommit);
CT_EQ_SIZE(t, 0, resp.npreedit);
CT_EQ_SIZE(t, 0, resp.commitlen);
CT_EQ_SIZE(t, 0, resp.preeditlen);
CT_EQ_STR(t, "", commit);
}
if(CT_EQ_INT(t, 0, ipcreadresp(fd[1], 1, commit, sizeof commit,
preedit, sizeof preedit, &resp))){
CT_EQ_INT(t, 1, resp.eaten);
CT_EQ_SIZE(t, 2, resp.ncommit);
CT_EQ_SIZE(t, 4, resp.npreedit);
CT_EQ_SIZE(t, 2, resp.commitlen);
CT_EQ_SIZE(t, 4, resp.preeditlen);
CT_EQ_STR(t, "go", commit);
CT_EQ_STR(t, "kana", preedit);
}
@@ -234,8 +255,8 @@ ipc_response_max_and_drain(struct ct *t)
CT_EQ_INT(t, 0, ipcsend(fd[0], second, nsecond)) &&
CT_EQ_INT(t, 0, ipcreadresp(fd[1], 1, commit, sizeof commit,
preedit, sizeof preedit, &resp))){
CT_EQ_SIZE(t, Ipcfieldmax, resp.ncommit);
CT_EQ_SIZE(t, Ipcfieldmax, resp.npreedit);
CT_EQ_SIZE(t, sizeof commit - 1, resp.commitlen);
CT_EQ_SIZE(t, sizeof preedit - 1, resp.preeditlen);
CT_EQ_STR(t, "xxxxxxx", commit);
CT_EQ_STR(t, "xxxxxxx", preedit);
}
@@ -284,6 +305,19 @@ ipc_response_fragmented_and_truncated(struct ct *t)
preedit, sizeof preedit, &resp));
close(fd[0]);
close(fd[1]);
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
CT_ERRORF(t, "socketpair failed");
return;
}
frame[0] = 2;
CT_EQ_INT(t, 0, ipcsend(fd[0], frame, n));
errno = 0;
CT_EQ_INT(t, -1, ipcreadresp(fd[1], 1, commit, sizeof commit,
preedit, sizeof preedit, &resp));
CT_EQ_INT(t, EPROTO, errno);
close(fd[0]);
close(fd[1]);
}
void
@@ -298,4 +332,99 @@ ipc_broken_peer_send(struct ct *t)
close(fd[1]);
CT_EQ_INT(t, -1, ipcsend(fd[0], "x", 1));
close(fd[0]);
ipcclientdeadlines(t);
ipcconnectcloexec(t);
}
static void
ipcclientdeadlines(struct ct *t)
{
char fill[4096], commit[8];
Ipcresp resp;
int fd[2];
int64_t start, elapsed;
ssize_t n;
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
CT_ERRORF(t, "socketpair failed");
return;
}
start = nowms();
errno = 0;
CT_EQ_INT(t, -1, ipcreadresp(fd[0], 0, commit, sizeof commit,
nil, 0, &resp));
elapsed = nowms() - start;
CT_EQ_INT(t, ETIMEDOUT, errno);
CT_CHECK(t, elapsed >= 0 && elapsed < 4*Ipcwaitms);
memset(fill, 'x', sizeof fill);
for(;;){
n = send(fd[0], fill, sizeof fill, MSG_DONTWAIT|MSG_NOSIGNAL);
if(n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
break;
if(n <= 0){
CT_ERRORF(t, "fill send failed: %s", strerror(errno));
close(fd[0]);
close(fd[1]);
return;
}
}
start = nowms();
errno = 0;
CT_EQ_INT(t, -1, ipcsend(fd[0], "x", 1));
elapsed = nowms() - start;
CT_EQ_INT(t, ETIMEDOUT, errno);
CT_CHECK(t, elapsed >= 0 && elapsed < 4*Ipcwaitms);
close(fd[0]);
close(fd[1]);
}
static void
ipcconnectcloexec(struct ct *t)
{
struct sockaddr_un addr;
char root[] = "/tmp/strans-ipc.XXXXXX";
char *old, *saved;
int accepted, client, flags, listener;
accepted = client = listener = -1;
old = getenv("XDG_RUNTIME_DIR");
saved = old == nil ? nil : strdup(old);
if(mkdtemp(root) == nil){
CT_ERRORF(t, "mkdtemp failed: %s", strerror(errno));
free(saved);
return;
}
setenv("XDG_RUNTIME_DIR", root, 1);
memset(&addr, 0, sizeof addr);
addr.sun_family = AF_UNIX;
if(ipcpath(addr.sun_path, sizeof addr.sun_path) < 0 ||
(listener = socket(AF_UNIX, SOCK_STREAM, 0)) < 0 ||
bind(listener, (struct sockaddr*)&addr, sizeof addr) < 0 ||
listen(listener, 1) < 0){
CT_ERRORF(t, "listen failed: %s", strerror(errno));
goto Out;
}
client = ipcconnect();
if(!CT_CHECK(t, client >= 0))
goto Out;
accepted = accept(listener, nil, nil);
if(!CT_CHECK(t, accepted >= 0))
goto Out;
flags = fcntl(client, F_GETFD);
CT_CHECK(t, flags >= 0 && (flags & FD_CLOEXEC) != 0);
Out:
if(accepted >= 0)
close(accepted);
if(client >= 0)
close(client);
if(listener >= 0)
close(listener);
unlink(addr.sun_path);
rmdir(root);
if(saved != nil){
setenv("XDG_RUNTIME_DIR", saved, 1);
free(saved);
}else
unsetenv("XDG_RUNTIME_DIR");
}