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:
233
ipc.c
233
ipc.c
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user