246 lines
4.7 KiB
C
246 lines
4.7 KiB
C
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include "ipc.h"
|
|
|
|
#if !defined(MSG_NOSIGNAL) && !defined(SO_NOSIGPIPE)
|
|
#error "ipcsend requires MSG_NOSIGNAL or SO_NOSIGPIPE"
|
|
#endif
|
|
|
|
static void
|
|
putlen(unsigned char p[Ipclensz], size_t n)
|
|
{
|
|
p[0] = n;
|
|
p[1] = n >> 8;
|
|
}
|
|
|
|
static size_t
|
|
getlen(const unsigned char p[Ipclensz])
|
|
{
|
|
return p[0] | (p[1] << 8);
|
|
}
|
|
|
|
int
|
|
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] == '/' ? "" : "/";
|
|
n = snprintf(dst, cap, "%s%sstrans.sock", dir, sep);
|
|
}else
|
|
n = snprintf(dst, cap, "/tmp/strans.%lu",
|
|
(unsigned long)getuid());
|
|
if(n < 0 || (size_t)n >= cap)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ipcconnect(void)
|
|
{
|
|
struct sockaddr_un addr;
|
|
int e, fd;
|
|
|
|
memset(&addr, 0, sizeof addr);
|
|
addr.sun_family = AF_UNIX;
|
|
if(ipcpath(addr.sun_path, sizeof addr.sun_path) < 0){
|
|
errno = ENAMETOOLONG;
|
|
return -1;
|
|
}
|
|
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if(fd < 0)
|
|
return -1;
|
|
if(connect(fd, (struct sockaddr*)&addr, sizeof addr) == 0)
|
|
return fd;
|
|
e = errno;
|
|
close(fd);
|
|
errno = e;
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
ipcpackreq(unsigned char req[Ipcreqsz], int want, unsigned int mod,
|
|
unsigned int key)
|
|
{
|
|
req[0] = want ? Ipcreqwant : 0;
|
|
req[1] = mod;
|
|
req[2] = key;
|
|
req[3] = key >> 8;
|
|
req[4] = key >> 16;
|
|
req[5] = key >> 24;
|
|
}
|
|
|
|
void
|
|
ipcpackreset(unsigned char req[Ipcreqsz], int want)
|
|
{
|
|
memset(req, 0, Ipcreqsz);
|
|
req[0] = Ipcreqreset | (want ? Ipcreqwant : 0);
|
|
}
|
|
|
|
void
|
|
ipcunpackreq(const unsigned char req[Ipcreqsz], int *want, unsigned int *mod,
|
|
unsigned int *key)
|
|
{
|
|
*want = (req[0] & Ipcreqwant) != 0;
|
|
*mod = req[1] & Mmask;
|
|
*key = (unsigned int)req[2] |
|
|
((unsigned int)req[3] << 8) |
|
|
((unsigned int)req[4] << 16) |
|
|
((unsigned int)req[5] << 24);
|
|
}
|
|
|
|
int
|
|
ipcreqreset(const unsigned char req[Ipcreqsz])
|
|
{
|
|
return (req[0] & Ipcreqreset) != 0;
|
|
}
|
|
|
|
int
|
|
ipcpackresp(unsigned char *dst, size_t cap, int eaten,
|
|
const char *commit, size_t ncommit, const char *preedit, size_t npreedit,
|
|
int want)
|
|
{
|
|
size_t n;
|
|
|
|
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)
|
|
return -1;
|
|
dst[0] = eaten != 0;
|
|
putlen(dst + 1, ncommit);
|
|
if(ncommit > 0)
|
|
memcpy(dst + Ipcresphdrsz, commit, ncommit);
|
|
if(want){
|
|
putlen(dst + Ipcresphdrsz + ncommit, npreedit);
|
|
if(npreedit > 0)
|
|
memcpy(dst + Ipcresphdrsz + ncommit + Ipclensz,
|
|
preedit, npreedit);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int
|
|
ipcreadn(int fd, void *buf, size_t n)
|
|
{
|
|
unsigned char *p;
|
|
ssize_t r;
|
|
|
|
p = buf;
|
|
while(n > 0){
|
|
r = read(fd, p, n);
|
|
if(r < 0 && errno == EINTR)
|
|
continue;
|
|
if(r <= 0)
|
|
return -1;
|
|
p += r;
|
|
n -= r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ipcsend(int fd, const void *buf, size_t n)
|
|
{
|
|
const unsigned char *p;
|
|
ssize_t r;
|
|
int flags;
|
|
#if !defined(MSG_NOSIGNAL) && defined(SO_NOSIGPIPE)
|
|
int one, sr;
|
|
#endif
|
|
|
|
p = buf;
|
|
#ifdef MSG_NOSIGNAL
|
|
flags = MSG_NOSIGNAL;
|
|
#else
|
|
flags = 0;
|
|
#ifdef SO_NOSIGPIPE
|
|
one = 1;
|
|
do
|
|
sr = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof one);
|
|
while(sr < 0 && errno == EINTR);
|
|
if(sr < 0)
|
|
return -1;
|
|
#endif
|
|
#endif
|
|
while(n > 0){
|
|
r = send(fd, p, n, flags);
|
|
if(r < 0 && errno == EINTR)
|
|
continue;
|
|
if(r <= 0)
|
|
return -1;
|
|
p += r;
|
|
n -= r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
readfield(int fd, size_t n, char *dst, size_t cap)
|
|
{
|
|
unsigned char discard[128];
|
|
size_t keep, part;
|
|
|
|
if(cap > 0 && dst == NULL)
|
|
return -1;
|
|
keep = 0;
|
|
if(cap > 0)
|
|
keep = n >= cap ? cap - 1 : n;
|
|
if(keep > 0 && ipcreadn(fd, dst, keep) < 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)
|
|
return -1;
|
|
n -= part;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ipcreadresp(int fd, int want, char *commit, size_t ccap,
|
|
char *preedit, size_t pcap, Ipcresp *resp)
|
|
{
|
|
unsigned char hdr[Ipcresphdrsz], npreedit[Ipclensz];
|
|
|
|
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';
|
|
memset(resp, 0, sizeof *resp);
|
|
if(ipcreadn(fd, hdr, sizeof hdr) < 0)
|
|
return -1;
|
|
resp->eaten = hdr[0] != 0;
|
|
resp->ncommit = getlen(hdr + 1);
|
|
if(resp->ncommit > Ipcfieldmax)
|
|
return -1;
|
|
if(readfield(fd, resp->ncommit, commit, ccap) < 0)
|
|
return -1;
|
|
if(!want)
|
|
return 0;
|
|
if(ipcreadn(fd, npreedit, sizeof npreedit) < 0)
|
|
return -1;
|
|
resp->npreedit = getlen(npreedit);
|
|
if(resp->npreedit > Ipcfieldmax)
|
|
return -1;
|
|
return readfield(fd, resp->npreedit, preedit, pcap);
|
|
}
|