Files
kuailexs 7f79f5fae5 整理文件夹
Change-Id: I283c06f2b804fa636b8356e258d38bd6da2e15ab
2020-02-29 23:15:45 +08:00

447 lines
12 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <errno.h>
#include <pthread.h>
#ifdef __ANDROID__
#include <android/log.h>
#include "sys/shm.h"
#define DBG(...) __android_log_print(ANDROID_LOG_INFO, "shmem", __VA_ARGS__)
#else
#include <sys/shm.h>
#define DBG(format, ...) fprintf(stderr, format "\n", __VA_ARGS__)
#endif
#include "libancillary/ancillary.h"
#include "cutils/ashmem.h"
static pthread_t listening_thread_id = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
typedef struct
{
int id;
void *addr;
int descriptor;
size_t size;
int markedForDeletion;
} shmem_t;
static shmem_t *shmem = NULL;
static size_t shmem_amount = 0;
static size_t shmem_counter = 0;
static int sock = 0;
static int sockid = 0;
#define SOCKNAME "/dev/shm/%08x"
#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
static int get_shmid(unsigned int index)
{
return sockid * 0x10000 + index;
}
static int get_sockid(int shmid)
{
return shmid / 0x10000;
}
static unsigned int get_index(int shmid)
{
return shmid % 0x10000;
}
static int shm_find_id(int shmid)
{
int i;
for (i = 0; i < shmem_amount; i++)
{
if (shmem[i].id == shmid)
return i;
}
DBG ("%s: cannot find shmid %x", __PRETTY_FUNCTION__, shmid);
return -1;
}
static void *listening_thread(void * arg)
{
struct sockaddr_un addr;
socklen_t len = sizeof(addr);
int sendsock;
DBG ("%s: thread started", __PRETTY_FUNCTION__);
while ((sendsock = accept (sock, (struct sockaddr *)&addr, &len)) != -1)
{
unsigned int shmid;
int idx;
if (recv (sendsock, &idx, sizeof(idx), 0) != sizeof(idx))
{
DBG ("%s: ERROR: recv() returned not %d bytes", __PRETTY_FUNCTION__, sizeof(idx));
close (sendsock);
continue;
}
pthread_mutex_lock (&mutex);
shmid = get_shmid (idx);
idx = shm_find_id (shmid);
if (idx != -1)
{
if (ancil_send_fd (sendsock, shmem[idx].descriptor) != 0)
DBG ("%s: ERROR: ancil_send_fd() failed: %s", __PRETTY_FUNCTION__, strerror(errno));
}
else
DBG ("%s: ERROR: cannot find shmid 0x%x", __PRETTY_FUNCTION__, shmid);
pthread_mutex_unlock (&mutex);
close (sendsock);
len = sizeof(addr);
}
DBG ("%s: ERROR: listen() failed, thread stopped", __PRETTY_FUNCTION__);
return NULL;
}
/* Get shared memory segment. */
int shmget (key_t key, size_t size, int flags)
{
char buf[256];
int idx;
DBG ("%s: key %d size %zu flags 0%o (flags are ignored)", __PRETTY_FUNCTION__, key, size, flags);
if (key != IPC_PRIVATE)
{
DBG ("%s: key %d != IPC_PRIVATE, this is not supported", __PRETTY_FUNCTION__, key);
errno = EINVAL;
return -1;
}
if (!listening_thread_id)
{
int i;
sock = socket (AF_UNIX, SOCK_STREAM, 0);
if (!sock)
{
DBG ("%s: cannot create UNIX socket: %s", __PRETTY_FUNCTION__, strerror(errno));
errno = EINVAL;
return -1;
}
for (i = 0; i < 4096; i++)
{
struct sockaddr_un addr;
int len;
memset (&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
sockid = (getpid() + i) & 0xffff;
sprintf (&addr.sun_path[1], SOCKNAME, sockid);
len = sizeof(addr.sun_family) + strlen(&addr.sun_path[1]) + 1;
if (bind (sock, (struct sockaddr *)&addr, len) != 0)
{
//DBG ("%s: cannot bind UNIX socket %s: %s, trying next one, len %d", __PRETTY_FUNCTION__, &addr.sun_path[1], strerror(errno), len);
continue;
}
DBG ("%s: bound UNIX socket %s", __PRETTY_FUNCTION__, addr.sun_path + 1);
break;
}
if (i == 4096)
{
DBG ("%s: cannot bind UNIX socket, bailing out", __PRETTY_FUNCTION__);
sockid = 0;
errno = ENOMEM;
return -1;
}
if (listen (sock, 4) != 0)
{
DBG ("%s: listen failed", __PRETTY_FUNCTION__);
errno = ENOMEM;
return -1;
}
pthread_create (&listening_thread_id, NULL, &listening_thread, NULL);
}
pthread_mutex_lock (&mutex);
idx = shmem_amount;
sprintf (buf, SOCKNAME "-%d", sockid, idx);
shmem_amount ++;
shmem_counter = (shmem_counter + 1) & 0x7fff;
size_t shmid = shmem_counter;
shmem = realloc (shmem, shmem_amount * sizeof(shmem_t));
size = ROUND_UP(size, getpagesize ());
shmem[idx].size = size;
shmem[idx].descriptor = ashmem_create_region (buf, size);
shmem[idx].addr = NULL;
shmem[idx].id = get_shmid(shmid);
shmem[idx].markedForDeletion = 0;
if (shmem[idx].descriptor < 0)
{
DBG ("%s: ashmem_create_region() failed for size %zu: %s", __PRETTY_FUNCTION__, size, strerror(errno));
shmem_amount --;
shmem = realloc (shmem, shmem_amount * sizeof(shmem_t));
pthread_mutex_unlock (&mutex);
return -1;
}
DBG ("%s: ID %d shmid %x FD %d size %zu", __PRETTY_FUNCTION__, idx, get_shmid(shmid), shmem[idx].descriptor, shmem[idx].size);
/*
status = ashmem_set_prot_region (shmem[idx].descriptor, 0666);
if (status < 0)
{
DBG ("%s: ashmem_set_prot_region() failed for size %zu: %s %d", __PRETTY_FUNCTION__, size, strerror(status), status);
shmem_amount --;
shmem = realloc (shmem, shmem_amount * sizeof(shmem_t));
pthread_mutex_unlock (&mutex);
return -1;
}
*/
/*
status = ashmem_pin_region (shmem[idx].descriptor, 0, shmem[idx].size);
if (status < 0)
{
DBG ("%s: ashmem_pin_region() failed for size %zu: %s %d", __PRETTY_FUNCTION__, size, strerror(status), status);
shmem_amount --;
shmem = realloc (shmem, shmem_amount * sizeof(shmem_t));
pthread_mutex_unlock (&mutex);
return -1;
}
*/
pthread_mutex_unlock (&mutex);
return get_shmid(shmid);
}
/* Attach shared memory segment. */
void *shmat (int shmid, const void *shmaddr, int shmflg)
{
int idx;
int sid = get_sockid (shmid);
void *addr;
DBG ("%s: shmid %x shmaddr %p shmflg %d", __PRETTY_FUNCTION__, shmid, shmaddr, shmflg);
if (shmaddr != NULL)
{
DBG ("%s: shmaddr != NULL not supported", __PRETTY_FUNCTION__);
errno = EINVAL;
return (void *)-1;
}
pthread_mutex_lock (&mutex);
idx = shm_find_id (shmid);
if (idx == -1 && sid != sockid)
{
struct sockaddr_un addr;
int addrlen;
int recvsock;
int descriptor;
int size;
pthread_mutex_unlock (&mutex);
DBG ("%s: sockid %x", __PRETTY_FUNCTION__, sid);
idx = get_index (shmid);
memset (&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
sprintf (&addr.sun_path[1], SOCKNAME, sid);
addrlen = sizeof(addr.sun_family) + strlen(&addr.sun_path[1]) + 1;
DBG ("%s: addr %s", __PRETTY_FUNCTION__, &addr.sun_path[1]);
recvsock = socket (AF_UNIX, SOCK_STREAM, 0);
if (!recvsock)
{
DBG ("%s: cannot create UNIX socket: %s", __PRETTY_FUNCTION__, strerror(errno));
errno = EINVAL;
return (void *)-1;
}
if (connect (recvsock, (struct sockaddr *)&addr, addrlen) != 0)
{
DBG ("%s: cannot connect to UNIX socket %s: %s, len %d", __PRETTY_FUNCTION__, addr.sun_path + 1, strerror(errno), addrlen);
close (recvsock);
errno = EINVAL;
return (void *)-1;
}
DBG ("%s: connected to socket %s", __PRETTY_FUNCTION__, &addr.sun_path[1]);
if (send (recvsock, &idx, sizeof(idx), 0) != sizeof(idx))
{
DBG ("%s: send() failed on socket %s: %s", __PRETTY_FUNCTION__, addr.sun_path + 1, strerror(errno));
close (recvsock);
errno = EINVAL;
return (void *)-1;
}
if (ancil_recv_fd (recvsock, &descriptor) != 0)
{
DBG ("%s: ERROR: ancil_recv_fd() failed on socket %s: %s", __PRETTY_FUNCTION__, addr.sun_path + 1, strerror(errno));
close (recvsock);
errno = EINVAL;
return (void *)-1;
}
close (recvsock);
DBG ("%s: got FD %d", __PRETTY_FUNCTION__, descriptor);
size = ashmem_get_size_region(descriptor);
if (size == 0 || size == -1)
{
DBG ("%s: ERROR: ashmem_get_size_region() returned %d on socket %s: %s", __PRETTY_FUNCTION__, size, addr.sun_path + 1, strerror(errno));
errno = EINVAL;
return (void *)-1;
}
DBG ("%s: got size %d", __PRETTY_FUNCTION__, size);
pthread_mutex_lock (&mutex);
idx = shmem_amount;
shmem_amount ++;
shmem = realloc (shmem, shmem_amount * sizeof(shmem_t));
shmem[idx].id = shmid;
shmem[idx].descriptor = descriptor;
shmem[idx].size = size;
shmem[idx].addr = NULL;
shmem[idx].markedForDeletion = 0;
DBG ("%s: created new remote shmem ID %d shmid %x FD %d size %zu", __PRETTY_FUNCTION__, idx, shmid, shmem[idx].descriptor, shmem[idx].size);
}
if (idx == -1)
{
DBG ("%s: shmid %x does not exist", __PRETTY_FUNCTION__, shmid);
pthread_mutex_unlock (&mutex);
errno = EINVAL;
return (void *)-1;
}
if (shmem[idx].addr == NULL)
{
shmem[idx].addr = mmap(NULL, shmem[idx].size, PROT_READ | (shmflg == 0 ? PROT_WRITE : 0), MAP_SHARED, shmem[idx].descriptor, 0);
if (shmem[idx].addr == MAP_FAILED)
{
DBG ("%s: mmap() failed for ID %x FD %d: %s", __PRETTY_FUNCTION__, idx, shmem[idx].descriptor, strerror(errno));
shmem[idx].addr = NULL;
}
}
addr = shmem[idx].addr;
DBG ("%s: mapped addr %p for FD %d ID %d", __PRETTY_FUNCTION__, addr, shmem[idx].descriptor, idx);
pthread_mutex_unlock (&mutex);
return addr ? addr : (void *)-1;
}
static void delete_shmem(int idx)
{
if (shmem[idx].descriptor)
close (shmem[idx].descriptor);
shmem_amount --;
memmove (&shmem[idx], &shmem[idx+1], (shmem_amount - idx) * sizeof(shmem_t));
}
/* Detach shared memory segment. */
int shmdt (const void *shmaddr)
{
pthread_mutex_lock (&mutex);
int i;
for (i = 0; i < shmem_amount; i++)
{
if (shmem[i].addr == shmaddr)
{
if (munmap (shmem[i].addr, shmem[i].size) != 0)
DBG ("%s: munmap %p failed", __PRETTY_FUNCTION__, shmaddr);
shmem[i].addr = NULL;
DBG ("%s: unmapped addr %p for FD %d ID %d shmid %x", __PRETTY_FUNCTION__, shmaddr, shmem[i].descriptor, i, shmem[i].id);
if (shmem[i].markedForDeletion || get_sockid (shmem[i].id) != sockid)
{
DBG ("%s: deleting shmid %x", __PRETTY_FUNCTION__, shmem[i].id);
delete_shmem(i);
}
pthread_mutex_unlock (&mutex);
return 0;
}
}
pthread_mutex_unlock (&mutex);
DBG ("%s: invalid address %p", __PRETTY_FUNCTION__, shmaddr);
errno = EINVAL;
return -1;
}
static int shm_remove (int shmid)
{
int idx;
DBG ("%s: deleting shmid %x", __PRETTY_FUNCTION__, shmid);
pthread_mutex_lock (&mutex);
idx = shm_find_id (shmid);
if (idx == -1)
{
DBG ("%s: ERROR: shmid %x does not exist", __PRETTY_FUNCTION__, shmid);
pthread_mutex_unlock (&mutex);
errno = EINVAL;
return -1;
}
if (shmem[idx].addr)
{
DBG ("%s: shmid %x is still mapped to addr %p, it will be deleted on shmdt() call", __PRETTY_FUNCTION__, shmid, shmem[idx].addr);
// KDE lib creates shared memory segment, marks it for deletion, and then uses it as if it's not deleted
shmem[idx].markedForDeletion = 1;
pthread_mutex_unlock (&mutex);
return 0;
}
delete_shmem(idx);
pthread_mutex_unlock (&mutex);
return 0;
}
static int shm_stat (int shmid, struct shmid_ds *buf)
{
int idx;
pthread_mutex_lock (&mutex);
idx = shm_find_id (shmid);
if (idx == -1)
{
DBG ("%s: ERROR: shmid %x does not exist", __PRETTY_FUNCTION__, shmid);
pthread_mutex_unlock (&mutex);
errno = EINVAL;
return -1;
}
if (!buf)
{
DBG ("%s: ERROR: buf == NULL for shmid %x", __PRETTY_FUNCTION__, shmid);
pthread_mutex_unlock (&mutex);
errno = EINVAL;
return -1;
}
/* Report max permissive mode */
memset (buf, 0, sizeof(struct shmid_ds));
buf->shm_segsz = shmem[idx].size;
buf->shm_nattch = 1;
buf->shm_perm.__key = IPC_PRIVATE;
buf->shm_perm.uid = geteuid();
buf->shm_perm.gid = getegid();
buf->shm_perm.cuid = geteuid();
buf->shm_perm.cgid = getegid();
buf->shm_perm.mode = 0666;
buf->shm_perm.__seq = 1;
DBG ("%s: shmid %x size %d", __PRETTY_FUNCTION__, shmid, buf->shm_segsz);
pthread_mutex_unlock (&mutex);
return 0;
}
/* Shared memory control operation. */
int shmctl (int shmid, int cmd, struct shmid_ds *buf)
{
//DBG ("%s: shmid %x cmd %d buf %p", __PRETTY_FUNCTION__, shmid, cmd, buf);
if (cmd == IPC_RMID)
{
return shm_remove (shmid);
}
if (cmd == IPC_STAT)
{
return shm_stat (shmid, buf);
}
DBG ("%s: cmd %d not implemented yet!", __PRETTY_FUNCTION__, cmd);
errno = EINVAL;
return -1;
}