#include "dat.h" #include "fn.h" #include #include static char adir[256]; static char sockpath[sizeof(((struct sockaddr_un*)0)->sun_path)]; static dev_t sockdev; static ino_t sockino; static Channel *clientc; /* Removes the socket only while it is still the one we announced. */ static void srvunlink(void) { struct stat st; if(sockpath[0] != '\0' && lstat(sockpath, &st) == 0 && st.st_dev == sockdev && st.st_ino == sockino) unlink(sockpath); sockpath[0] = '\0'; } static int srvnote(void *v, char *note) { USED(v); USED(note); srvunlink(); return 0; } static int srvreadreq(int fd, Keyreq *kr, int *want) { uchar req[Ipccaretsz]; int valid; int32_t x, y, h; if(ipcreadn(fd, req, Ipcreqsz) < 0) return -1; *want = 0; kr->ks = 0; kr->mod = 0; switch(ipcreqtype(req)){ case Ipckey: ipcunpackreq(req, want, &kr->mod, &kr->ks); kr->op = ipcreqreset(req) ? Keyreset : Keypress; return 0; case Ipccap: *want = (req[0] & Ipcreqwant) != 0; kr->op = Keycap; return 0; case Ipccaret: if(ipcreadn(fd, req + Ipcreqsz, Ipccaretsz - Ipcreqsz) < 0 || ipcunpackcaret(req, &valid, &x, &y, &h) < 0) return -1; kr->op = Keycaret; kr->caret.valid = valid; kr->caret.x = x; kr->caret.y = y; kr->caret.h = h; return 0; } return -1; } /* * One Keyreq persists for the connection: the negotiated capability and * the last caret ride along with every request, and the socket closing * releases the engine. */ static void clientthread(void *arg) { Keyreq kr; Keyres res; uchar out[Ipcmaxresp], token; char commit[Maxutf], preedit[Maxutf]; int fd, n, ncommit, npreedit, want; fd = (int)(uintptr)arg; threadsetname("client %d", fd); memset(&kr, 0, sizeof kr); kr.reply = chancreate(sizeof(Keyres), 0); kr.owner = &fd; kr.cap = Cclientpreedit; while(srvreadreq(fd, &kr, &want) >= 0){ if(kr.op != Keycaret) kr.cap = want ? Cclientpreedit : 0; chansend(keyc, &kr); chanrecv(kr.reply, &res); if(kr.op == Keycaret) continue; ncommit = stoutf(&res.commit, commit, sizeof commit); npreedit = stoutf(&res.preedit, preedit, sizeof preedit); /* A capability reply is always eaten: it marks the extension. */ n = ipcpackresp(out, sizeof out, kr.op == Keycap || res.eaten, commit, ncommit, preedit, npreedit, want); if(n < 0 || ipcsend(fd, out, n) < 0) break; } kr.op = Keyrelease; kr.ks = 0; kr.mod = 0; chansend(keyc, &kr); chanrecv(kr.reply, &res); chanfree(kr.reply); close(fd); chanrecv(clientc, &token); } void srvinit(void) { struct stat st; char *addr; if(ipcpath(sockpath, sizeof sockpath) < 0) die("IPC path is too long"); addr = smprint("unix!%s", sockpath); if(addr == nil) die("out of memory"); if(announce(addr, adir) < 0) die("IPC endpoint is already in use: %r"); free(addr); if(chmod(sockpath, 0600) < 0 || lstat(sockpath, &st) < 0) die("can't protect IPC endpoint: %s", sockpath); sockdev = st.st_dev; sockino = st.st_ino; atexit(srvunlink); threadnotify(srvnote, 1); } void srvthread(void*) { char ldir[40]; int fd; uchar token; threadsetname("srv"); token = 0; clientc = chancreate(sizeof token, Maxclients); for(;;){ fd = listen(adir, ldir); if(fd < 0) continue; if(channbsend(clientc, &token) <= 0){ close(fd); continue; } proccreate(clientthread, (void*)(uintptr)fd, 8192); } }