ipc: strip machinery no peer uses; keep client state in one Keyreq

ipc.c: an AF_UNIX nonblocking connect completes at once or fails with
EAGAIN, so the EINPROGRESS/poll/SO_ERROR path and the fcntl juggling
were dead; the deadline plumbing checked a clock that cannot fail and
re-tested the deadline before every transfer although only waitfd
blocks; readfield's truncation and discard loop served the unit test,
since every caller owns char[Ipcfieldmax+1]; NULL-argument checks on
in-tree encoders are gone (peer validation stays). The primary key frame
is Ipckey, not 'legacy'; ipckeysym names the keysym mapping.

srv.c: the per-connection capability and caret already lived in the
persistent Keyreq; the mirror locals and 'negotiated' flag guarded a
protocol rule no client relied on. proccreate never fails in libthread.

gtk: focus-out no longer round-trips a reset before closing the socket
that releases the engine; the caret dedup compares the packed frame;
srvconnect's preedit no-ops and the insimple flag are gone.
This commit is contained in:
2026-08-16 15:53:08 +09:00
parent 43b469f70b
commit eb0f88f764
11 changed files with 205 additions and 394 deletions

233
ipc.c
View File

@@ -1,7 +1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
@@ -17,57 +16,32 @@ nowms(void)
{
struct timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
return -1;
clock_gettime(CLOCK_MONOTONIC, &ts);
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
checkdeadline(int64_t until)
{
int64_t now;
now = nowms();
if(now < 0)
return -1;
if(now >= until){
errno = ETIMEDOUT;
return -1;
}
return 0;
return nowms() + Ipcwaitms;
}
static int
waitfd(int fd, short events, int64_t until)
{
struct pollfd pfd;
int64_t now, left;
int64_t left;
int n;
pfd.fd = fd;
pfd.events = events;
for(;;){
now = nowms();
if(now < 0)
return -1;
left = until - now;
left = until - nowms();
if(left <= 0){
errno = ETIMEDOUT;
return -1;
}
pfd.revents = 0;
n = poll(&pfd, 1, left > INT32_MAX ? INT32_MAX : (int)left);
n = poll(&pfd, 1, left);
if(n < 0 && errno == EINTR)
continue;
if(n < 0)
@@ -76,12 +50,7 @@ waitfd(int fd, short events, int64_t until)
errno = ETIMEDOUT;
return -1;
}
if(pfd.revents & POLLNVAL){
errno = EBADF;
return -1;
}
if(pfd.revents & (events|POLLERR|POLLHUP))
return 0;
return 0;
}
}
@@ -93,8 +62,6 @@ readwait(int fd, void *buf, size_t n, int64_t until)
p = buf;
while(n > 0){
if(checkdeadline(until) < 0)
return -1;
r = recv(fd, p, n, MSG_DONTWAIT);
if(r < 0 && errno == EINTR)
continue;
@@ -143,15 +110,10 @@ put32(unsigned char *p, int32_t v)
static int32_t
get32(const unsigned char *p)
{
uint32_t u;
u = (uint32_t)p[0] |
return (int32_t)((uint32_t)p[0] |
((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24);
if(u <= INT32_MAX)
return u;
return -(int32_t)(~u) - 1;
((uint32_t)p[3] << 24));
}
int
@@ -160,8 +122,6 @@ ipcpath(char *dst, size_t cap)
const char *dir, *sep;
int n;
if(dst == NULL || cap == 0)
return -1;
dir = getenv("XDG_RUNTIME_DIR");
if(dir != NULL && dir[0] == '/'){
sep = dir[strlen(dir)-1] == '/' ? "" : "/";
@@ -174,13 +134,16 @@ ipcpath(char *dst, size_t cap)
return 0;
}
/*
* A nonblocking AF_UNIX connect completes at once or fails with EAGAIN
* when the daemon's backlog is full; the socket stays nonblocking because
* every transfer below polls with a deadline.
*/
int
ipcconnect(void)
{
struct sockaddr_un addr;
socklen_t nerr;
int e, err, fd, fdflags, flags;
int64_t until;
int e, fd;
memset(&addr, 0, sizeof addr);
addr.sun_family = AF_UNIX;
@@ -188,39 +151,16 @@ ipcconnect(void)
errno = ENAMETOOLONG;
return -1;
}
fd = socket(AF_UNIX, SOCK_STREAM, 0);
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if(fd < 0)
return -1;
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)
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;
}
e = errno;
close(fd);
errno = e;
return -1;
}
if(fcntl(fd, F_SETFL, flags) < 0)
goto Bad;
return fd;
Bad:
e = errno;
close(fd);
errno = e;
return -1;
}
/*
@@ -228,7 +168,7 @@ Bad:
* as themselves, the function keysyms 0xff00-0xffff as Kspec+offset.
*/
uint32_t
ipckey(uint32_t sym, uint32_t unicode)
ipckeysym(uint32_t sym, uint32_t unicode)
{
if(unicode >= ' ' && unicode != 0x7f)
return unicode;
@@ -277,25 +217,20 @@ ipcpackcap(unsigned char req[Ipcreqsz], int want)
req[2] = Ipcopcap;
}
int
void
ipcpackcaret(unsigned char req[Ipccaretsz], int valid, int32_t x,
int32_t y, int32_t h)
{
if(valid != 0 && valid != 1)
return -1;
if(valid && h < 0)
return -1;
memset(req, 0, Ipccaretsz);
req[0] = Ipcext;
req[1] = Ipcversion;
req[2] = Ipcopcaret;
req[3] = valid;
if(!valid)
return 0;
put32(req + 4, x);
put32(req + 8, y);
put32(req + 12, h);
return 0;
req[3] = valid != 0;
if(valid){
put32(req + 4, x);
put32(req + 8, y);
put32(req + 12, h);
}
}
void
@@ -314,7 +249,7 @@ int
ipcreqtype(const unsigned char req[Ipcreqsz])
{
if((req[0] & Ipcext) == 0)
return Ipclegacy;
return Ipckey;
if(req[1] != Ipcversion)
return Ipcunknown;
switch(req[2]){
@@ -335,24 +270,12 @@ int
ipcunpackcaret(const unsigned char req[Ipccaretsz], int *valid,
int32_t *x, int32_t *y, int32_t *h)
{
int32_t wireh;
if(valid == NULL || x == NULL || y == NULL || h == NULL ||
ipcreqtype(req) != Ipccaret)
return -1;
wireh = get32(req + 12);
if(wireh < 0)
if(ipcreqtype(req) != Ipccaret || get32(req + 12) < 0)
return -1;
*valid = req[3];
if(!*valid){
*x = 0;
*y = 0;
*h = 0;
return 0;
}
*x = get32(req + 4);
*y = get32(req + 8);
*h = wireh;
*x = *valid ? get32(req + 4) : 0;
*y = *valid ? get32(req + 8) : 0;
*h = *valid ? get32(req + 12) : 0;
return 0;
}
@@ -371,21 +294,15 @@ ipcpackresp(unsigned char *dst, size_t cap, int eaten,
if(ncommit > Ipcfieldmax || npreedit > Ipcfieldmax)
return -1;
if((ncommit > 0 && commit == NULL) ||
(want && npreedit > 0 && preedit == NULL))
return -1;
n = Ipcresphdrsz + ncommit + (want ? Ipclensz + npreedit : 0);
if(dst == NULL || cap < n)
if(cap < n)
return -1;
dst[0] = eaten != 0;
putlen(dst + 1, ncommit);
if(ncommit > 0)
memcpy(dst + Ipcresphdrsz, commit, ncommit);
memcpy(dst + Ipcresphdrsz, commit, ncommit);
if(want){
putlen(dst + Ipcresphdrsz + ncommit, npreedit);
if(npreedit > 0)
memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz,
preedit, npreedit);
memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz, preedit, npreedit);
}
return n;
}
@@ -417,14 +334,8 @@ ipcsend(int fd, const void *buf, size_t n)
int64_t until;
p = buf;
if(n == 0)
return 0;
until = deadline();
if(until < 0)
return -1;
while(n > 0){
if(checkdeadline(until) < 0)
return -1;
r = send(fd, p, n, MSG_NOSIGNAL|MSG_DONTWAIT);
if(r < 0 && errno == EINTR)
continue;
@@ -445,73 +356,53 @@ ipcsend(int fd, const void *buf, size_t n)
return 0;
}
/* Reads a length-prefixed field into dst[Ipcfieldmax+1]. */
static int
readfield(int fd, size_t n, char *dst, size_t cap, size_t *copied,
int64_t until)
readfield(int fd, char *dst, int64_t until)
{
unsigned char discard[128];
size_t keep, part;
unsigned char len[Ipclensz];
size_t n;
if(cap > 0 && dst == NULL)
if(readwait(fd, len, sizeof len, until) < 0)
return -1;
keep = 0;
if(cap > 0)
keep = n >= cap ? cap - 1 : n;
if(keep > 0 && readwait(fd, dst, keep, until) < 0)
n = getlen(len);
if(n > Ipcfieldmax){
errno = EPROTO;
return -1;
if(cap > 0)
dst[keep] = '\0';
n -= keep;
while(n > 0){
part = n < sizeof discard ? n : sizeof discard;
if(readwait(fd, discard, part, until) < 0)
return -1;
n -= part;
}
*copied = keep;
return 0;
if(readwait(fd, dst, n, until) < 0)
return -1;
dst[n] = '\0';
return n;
}
int
ipcreadresp(int fd, int want, char *commit, size_t ccap,
char *preedit, size_t pcap, Ipcresp *resp)
ipcreadresp(int fd, int want, char *commit, char *preedit, Ipcresp *resp)
{
unsigned char hdr[Ipcresphdrsz], npreedit[Ipclensz];
size_t wirelen;
unsigned char eaten;
int64_t until;
int n;
if(resp == NULL || (ccap > 0 && commit == NULL) ||
(pcap > 0 && preedit == NULL))
return -1;
if(ccap > 0)
commit[0] = '\0';
if(pcap > 0)
preedit[0] = '\0';
commit[0] = '\0';
preedit[0] = '\0';
memset(resp, 0, sizeof *resp);
until = deadline();
if(until < 0 || readwait(fd, hdr, sizeof hdr, until) < 0)
if(readwait(fd, &eaten, 1, until) < 0)
return -1;
if(hdr[0] > 1){
if(eaten > 1){
errno = EPROTO;
return -1;
}
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)
resp->eaten = eaten;
n = readfield(fd, commit, until);
if(n < 0)
return -1;
resp->commitlen = n;
if(!want)
return 0;
if(readwait(fd, npreedit, sizeof npreedit, until) < 0)
n = readfield(fd, preedit, until);
if(n < 0)
return -1;
wirelen = getlen(npreedit);
if(wirelen > Ipcfieldmax){
errno = EPROTO;
return -1;
}
return readfield(fd, wirelen, preedit, pcap, &resp->preeditlen,
until);
resp->preeditlen = n;
return 0;
}