From 4147a47ed76edbb6bb07aaae161127217e605c4f Mon Sep 17 00:00:00 2001 From: Maxython Date: Mon, 3 Feb 2025 20:12:04 +0300 Subject: [PATCH] shmem: improvements + global socket implementation (test) The possibility of remote interaction with shmem is implemented, i.e. the shmem socket is multifunctional and can not only send data, but also start an action and update shmem data. This allows you to implement shmem actions close to the original system V shared memory from Linux. For example: - Delete shmem if the process is not native - Count the number of attached and process ids - Filter shmget actions according to flags (i.e. shmget now understands the IPC_CREAT and IPC_EXCL flags) Also added new tests to check all these functionalities. --- .gitignore | 1 + Makefile | 2 +- shmem.c | 733 ++++++++++++++++++--- test/Makefile | 8 +- test/after-fork.c | 1 + test/at-memory.c | 1 + test/deadlock.c | 1 + test/error-codes.c | 1 + test/interactions-shm/Makefile | 39 ++ test/interactions-shm/check-attachments.sh | 18 + test/interactions-shm/create-ipc.c | 8 + test/interactions-shm/create-shm.c | 9 + test/interactions-shm/endless-attachment.c | 13 + test/interactions-shm/read.c | 15 + test/interactions-shm/remove.c | 15 + test/interactions-shm/shm-available.sh | 6 + test/interactions-shm/status.c | 12 + test/interactions-shm/testcase-1.sh | 7 + test/interactions-shm/testcase-2.sh | 12 + test/interactions-shm/testcase-3.sh | 22 + test/interactions-shm/testcase-4.sh | 23 + test/interactions-shm/write.c | 14 + test/ipc-private.c | 2 + test/utils.h | 2 + test/with-key.c | 1 + 25 files changed, 866 insertions(+), 100 deletions(-) create mode 100644 test/interactions-shm/Makefile create mode 100755 test/interactions-shm/check-attachments.sh create mode 100644 test/interactions-shm/create-ipc.c create mode 100644 test/interactions-shm/create-shm.c create mode 100644 test/interactions-shm/endless-attachment.c create mode 100644 test/interactions-shm/read.c create mode 100644 test/interactions-shm/remove.c create mode 100755 test/interactions-shm/shm-available.sh create mode 100644 test/interactions-shm/status.c create mode 100644 test/interactions-shm/testcase-1.sh create mode 100644 test/interactions-shm/testcase-2.sh create mode 100644 test/interactions-shm/testcase-3.sh create mode 100644 test/interactions-shm/testcase-4.sh create mode 100644 test/interactions-shm/write.c diff --git a/.gitignore b/.gitignore index 1930e81..c0fed70 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.so tags test/test-* +test/interactions-shm/test-* diff --git a/Makefile b/Makefile index 4a876ae..ca1865f 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ libandroid-shmem.a: shmem.o $(AR) rcu $@ shmem.o libandroid-shmem.so: shmem.o - $(CC) $(LDFLAGS) -shared shmem.o -o $@ -llog -landroid + $(CC) $(LDFLAGS) -shared shmem.o -o $@ -llog -landroid -Wl,-e,__gsocket shmem.o: shmem.c shm.h $(CC) $(CFLAGS) -c shmem.c -o $@ diff --git a/shmem.c b/shmem.c index f95fc07..85d21a9 100644 --- a/shmem.c +++ b/shmem.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -25,8 +26,32 @@ #define DBG(...) __android_log_print(ANDROID_LOG_INFO, "shmem", __VA_ARGS__) #define ASHV_KEY_SYMLINK_PATH _PATH_TMP "ashv_key_%d" #define ANDROID_SHMEM_SOCKNAME "/dev/shm/%08x" +#define ANDROID_SHMEM_GLOBAL_SOCKNAME "/dev/shm/global" +#define ANDROID_SHMEM_GLOBAL_PROCNAME "global-shmem" +#define ANDROID_SHMEM_GLOBAL_EXECPATH "/data/data/com.termux/files/usr/lib/libandroid-shmem.so" +#define ANDROID_SHMEM_GLOBAL_ENV_EXECPATH getenv("ANDROID_SHMEM_GLOBAL_EXECPATH") +#define ANDROID_SHMEM_GLOBAL_ENABLE getenv("ANDROID_SHMEM_GLOBAL_DISABLE") == NULL #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) +// Action numbers +#define ASHV_PUT 0 +#define ASHV_GET 1 +#define ASHV_UPD 2 +#define ASHV_RM 3 +#define ASHV_AT 4 +#define ASHV_DT 5 + +#ifndef TEST +# ifndef LINKER +# if INTPTR_MAX == INT64_MAX +# define LINKER "/system/bin/linker64" +# else +# define LINKER "/system/bin/linker" +# endif +# endif // LINKER +const char interp[] __attribute__ ((section(".interp"))) = LINKER; +#endif // TEST + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct { @@ -38,6 +63,9 @@ typedef struct { size_t size; bool markedForDeletion; key_t key; + int countAttach; + pid_t *attachedPids; + bool global; } shmem_t; static shmem_t* shmem = NULL; @@ -50,6 +78,10 @@ static int ashv_local_socket_id = 0; // created for. static int ashv_pid_setup = 0; static pthread_t ashv_listening_thread_id = 0; +#ifndef TEST +static int global_sendsock; +#endif // TEST +static bool global_conf = false; static int ancil_send_fd(int sock, int fd) { @@ -187,12 +219,98 @@ static int ashv_socket_id_from_shmid(int shmid) static int ashv_find_local_index(int shmid) { - for (size_t i = 0; i < shmem_amount; i++) - if (shmem[i].id == shmid) + for (size_t i = 0; i < shmem_amount; i++) { + if (shmem[i].id == shmid) { return i; + } + } return -1; } +#ifndef TEST +static int ashv_write_pids(int sendsock, int idx) +{ + if (shmem[idx].countAttach > 0) { + pid_t pids[shmem[idx].countAttach]; + for (int i=0; i 0) { + pid_t pids[shmem[idx].countAttach]; + if ((unsigned int)read(recvsock, &pids, sizeof(pid_t)*shmem[idx].countAttach) != sizeof(pid_t)*shmem[idx].countAttach) { + DBG("%s: ERROR: read pids failed: %s", __PRETTY_FUNCTION__, strerror(errno)); + return -1; + } + shmem[idx].attachedPids = NULL; + shmem[idx].attachedPids = realloc(shmem[idx].attachedPids, sizeof(pid_t)*shmem[idx].countAttach); + for (int i=0; isun_family = AF_UNIX; + sprintf(&addr->sun_path[1], ANDROID_SHMEM_GLOBAL_SOCKNAME); + int addrlen = sizeof(addr->sun_family) + strlen(&addr->sun_path[1]) + 1; + + int gsock = socket(AF_UNIX, SOCK_STREAM, 0); + if (gsock == -1) { + DBG ("%s: cannot create UNIX socket: %s", __PRETTY_FUNCTION__, strerror(errno)); + return -1; + } + if (connect(gsock, (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); + goto close; + } + + global_conf = true; + + if (send(gsock, &action, sizeof(action), MSG_NOSIGNAL) != sizeof(action)) { + DBG("%s: send acction failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); + goto close; + } + if (send(gsock, &shmid, sizeof(shmid), MSG_NOSIGNAL) != sizeof(shmid)) { + DBG("%s: send shmid failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); + goto close; + } + + return gsock; +close: + close(gsock); + return -1; +} + +static int ashv_status_gsocket(int gsock) +{ + int status; + if (read(gsock, &status, sizeof(int)) != sizeof(int)) { + DBG("%s: ERROR: read status failed", __PRETTY_FUNCTION__); + status = -1; + } + close(gsock); + global_conf = false; + return status; +} + +static int ashv_put_shm_gsocket(int shmid) +{ + bool stop = false; + struct sockaddr_un addr; + int gsock; + while((gsock = ashv_connect_gsocket(&addr, ASHV_PUT, shmid)) == -1) { + if (stop || ashv_run_gsocket() == -1) { + return -1; + } + stop = true; + } + + return ashv_status_gsocket(gsock); +} + +static int ashv_rm_shm_gsocket(int shmid) +{ + struct sockaddr_un addr; + int gsock = ashv_connect_gsocket(&addr, ASHV_RM, shmid); + if (gsock == -1) { + return -1; + } + + return ashv_status_gsocket(gsock); +} + +static int ashv_send_pid_gsocket(int action, int shmid) +{ + struct sockaddr_un addr; + int gsock = ashv_connect_gsocket(&addr, action, shmid); + if (gsock == -1) { + return -1; + } + + if (write(gsock, &ashv_pid_setup, sizeof(pid_t)) != sizeof(pid_t)) { + DBG("%s: ERROR: write pid failed", __PRETTY_FUNCTION__); + close(gsock); + return -1; + } + + return ashv_status_gsocket(gsock); +} + +#define ASHV_READ_GSOCK(type, var) \ + type var; \ + if (read(gsock, &var, sizeof(type)) != sizeof(type)) { \ + DBG("%s: ERROR: read %s failed", __PRETTY_FUNCTION__, #var); \ + goto error_close; \ + } + +static int ashv_get_shm_gsocket(int shmid) +{ + struct sockaddr_un addr; + int gsock = ashv_connect_gsocket(&addr, ASHV_GET, shmid); + if (gsock == -1) { + return -1; + } + + ASHV_READ_GSOCK(key_t, key) + ASHV_READ_GSOCK(bool, markedForDeletion) + ASHV_READ_GSOCK(int, countAttach) + + if (markedForDeletion && countAttach == 0) { + DBG("%s: shmid %d is marked for deletion so it is not passed", __PRETTY_FUNCTION__, shmid); + goto error_close; + } + + int descriptor = ancil_recv_fd(gsock); + if (descriptor < 0) { + DBG("%s: ERROR: ancil_recv_fd() failed on socket %s: %s", __PRETTY_FUNCTION__, addr.sun_path + 1, strerror(errno)); + goto error_close; + } + global_conf = false; + close(gsock); + + int 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)); + return -1; + } + + int 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 = markedForDeletion; + shmem[idx].key = key; + shmem[idx].countAttach = countAttach; + shmem[idx].global = true; + return idx; +error_close: + global_conf = false; + close(gsock); + return -1; +} + +static int ashv_upd_shm_gsocket(int idx) +{ + int shmid = shmem[idx].id; + struct sockaddr_un addr; + int gsock = ashv_connect_gsocket(&addr, ASHV_UPD, shmid); + if (gsock == -1) { + goto removal; + } + + if (read(gsock, &shmem[idx].markedForDeletion, sizeof(bool)) != sizeof(bool)) { + close(gsock); + goto removal; + } + + if (read(gsock, &shmem[idx].countAttach, sizeof(int)) != sizeof(int)) { + close(gsock); + goto removal; + } + + if (ashv_read_pids(gsock, idx) != 0) { + close(gsock); + goto removal; + } + + return ashv_status_gsocket(gsock); +removal: + DBG("%s: gsocket returned an error, shm %d will have a delete mark", __PRETTY_FUNCTION__, shmid); + shmem[idx].countAttach = 0; + shmem[idx].markedForDeletion = true; + shmem[idx].global = false; + shmem[idx].attachedPids = NULL; + if (shmem[idx].addr != NULL) { + android_shmem_attach_pid(idx, ashv_pid_setup); + } + global_conf = false; + return -1; +} + +#define FIND_SHMEM \ + int idx = ashv_find_local_index(shmid); \ + if (idx == -1) { \ + if (ANDROID_SHMEM_GLOBAL_ENABLE) { \ + idx = ashv_get_shm_gsocket(shmid); \ + } \ + if (idx == -1 && ashv_socket_id_from_shmid(shmid) != ashv_local_socket_id) { \ + idx = ashv_put_remote_segment(shmid); \ + } \ + } + /* Get shared memory area identifier. */ int shmget(key_t key, size_t size, int flags) { - (void) flags; - ashv_check_pid(); // Counter wrapping around at 15 bits. @@ -358,13 +862,22 @@ int shmget(key_t key, size_t size, int flags) path_buffer[path_length] = '\0'; int shmid = atoi(path_buffer); if (shmid != 0) { - int idx = ashv_find_local_index(shmid); - - if (idx == -1) { - idx = ashv_read_remote_segment(shmid); + FIND_SHMEM + if (idx != -1 && shmem[idx].global && ANDROID_SHMEM_GLOBAL_ENABLE) { + ashv_upd_shm_gsocket(idx); + if (shmem[idx].markedForDeletion && shmem[idx].countAttach == 0) { + android_shmem_delete(idx); + idx = -1; + } } if (idx != -1) { + if (flags & IPC_CREAT && flags & IPC_EXCL) { + DBG("%s: shm with key %d should be created but it already exists (IPC_CREAT+IPC_EXCL)", __PRETTY_FUNCTION__, key); + errno = EEXIST; + pthread_mutex_unlock(&mutex); + return -1; + } pthread_mutex_unlock(&mutex); return shmem[idx].id; } @@ -383,12 +896,26 @@ int shmget(key_t key, size_t size, int flags) } if (symlink(num_buffer, symlink_path) == 0) break; } + + if (!(flags & IPC_CREAT)) { + DBG("%s: shm with key %d was not found and no command was given to create it (no IPC_CREAT)", __PRETTY_FUNCTION__, key); + errno = ENOENT; + pthread_mutex_unlock(&mutex); + return -1; + } } int idx = shmem_amount; char buf[256]; sprintf(buf, ANDROID_SHMEM_SOCKNAME "-%d", ashv_local_socket_id, idx); + size = ROUND_UP(size, getpagesize()); + int descriptor = ashmem_create_region(buf, size); + if (descriptor < 0) { + DBG("%s: ashmem_create_region() failed for size %zu: %s", __PRETTY_FUNCTION__, size, strerror(errno)); + pthread_mutex_unlock(&mutex); + return -1; + } shmem_amount++; if (shmid == -1) { @@ -397,21 +924,19 @@ int shmget(key_t key, size_t size, int flags) } 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].descriptor = descriptor; shmem[idx].addr = NULL; shmem[idx].id = shmid; shmem[idx].markedForDeletion = false; shmem[idx].key = key; - - 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; + shmem[idx].countAttach = 0; + shmem[idx].attachedPids = NULL; + shmem[idx].global = false; + if (ANDROID_SHMEM_GLOBAL_ENABLE && ashv_put_shm_gsocket(shmid) == 0) { + shmem[idx].global = true; } + //DBG("%s: ID %d shmid %x FD %d size %zu", __PRETTY_FUNCTION__, idx, shmid, shmem[idx].descriptor, shmem[idx].size); /* status = ashmem_set_prot_region (shmem[idx].descriptor, 0666); @@ -438,29 +963,41 @@ int shmget(key_t key, size_t size, int flags) return shmid; } +#define INIT_SHMEM(ret_err) \ + FIND_SHMEM \ + if (idx == -1) { \ + DBG ("%s: ERROR: shmid %x does not exist\n", __PRETTY_FUNCTION__, shmid); \ + pthread_mutex_unlock(&mutex); \ + errno = EINVAL; \ + return ret_err; \ + } \ + if (ANDROID_SHMEM_GLOBAL_ENABLE && shmem[idx].global) { \ + ashv_upd_shm_gsocket(idx); \ + } \ + if (shmem[idx].markedForDeletion && shmem[idx].countAttach == 0) { \ + DBG ("%s: shmid %d marked for deletion, it will be deleted\n", __PRETTY_FUNCTION__, shmid); \ + android_shmem_delete(idx); \ + pthread_mutex_unlock(&mutex); \ + errno = EINVAL; \ + return ret_err; \ + } + /* Attach shared memory segment. */ void* shmat(int shmid, void const* shmaddr, int shmflg) { ashv_check_pid(); - int socket_id = ashv_socket_id_from_shmid(shmid); void *addr; pthread_mutex_lock(&mutex); - int idx = ashv_find_local_index(shmid); - if (idx == -1 && socket_id != ashv_local_socket_id) { - idx = ashv_read_remote_segment(shmid); - } - - if (idx == -1) { - DBG ("%s: shmid %x does not exist", __PRETTY_FUNCTION__, shmid); - pthread_mutex_unlock(&mutex); - errno = EINVAL; - return (void*) -1; - } + INIT_SHMEM((void*)-1) if (shmem[idx].addr == NULL) { + if (ANDROID_SHMEM_GLOBAL_ENABLE && shmem[idx].global) { + ashv_send_pid_gsocket(ASHV_AT, shmid); + } + android_shmem_attach_pid(idx, ashv_pid_setup); int mmap_prot = PROT_READ | ((shmflg & SHM_RDONLY) == 0 ? PROT_WRITE : 0); int mmap_flags = MAP_SHARED | (shmaddr != 0 ? MAP_FIXED : 0); shmem[idx].addr = mmap((void*) shmaddr, shmem[idx].size, mmap_prot, mmap_flags, shmem[idx].descriptor, 0); @@ -482,20 +1019,29 @@ int shmdt(void const* shmaddr) ashv_check_pid(); pthread_mutex_lock(&mutex); - for (size_t i = 0; i < shmem_amount; i++) { + for (size_t 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 %zu shmid %x", __PRETTY_FUNCTION__, shmaddr, shmem[i].descriptor, i, shmem[i].id); - if (shmem[i].markedForDeletion || ashv_socket_id_from_shmid(shmem[i].id) != ashv_local_socket_id) { + + if (ANDROID_SHMEM_GLOBAL_ENABLE && shmem[i].global) { + ashv_upd_shm_gsocket(i); + ashv_send_pid_gsocket(ASHV_DT, shmem[i].id); + } + android_shmem_detach_pid(i, ashv_pid_setup); + + if (shmem[i].markedForDeletion && shmem[i].countAttach == 0) { DBG ("%s: deleting shmid %x", __PRETTY_FUNCTION__, shmem[i].id); + if (ANDROID_SHMEM_GLOBAL_ENABLE && shmem[i].global) { + ashv_rm_shm_gsocket(shmem[i].id); + } android_shmem_delete(i); } pthread_mutex_unlock(&mutex); return 0; - } } pthread_mutex_unlock(&mutex); @@ -509,21 +1055,14 @@ int libandroid_shmat_fd(int shmid, size_t* out_size) { ashv_check_pid(); - int socket_id = ashv_socket_id_from_shmid(shmid); int fd; pthread_mutex_lock(&mutex); - int idx = ashv_find_local_index(shmid); - if (idx == -1 && socket_id != ashv_local_socket_id) { - idx = ashv_read_remote_segment(shmid); - } + INIT_SHMEM(-1) - if (idx == -1) { - DBG ("%s: shmid %x does not exist", __PRETTY_FUNCTION__, shmid); - pthread_mutex_unlock(&mutex); - errno = EINVAL; - return -1; + if (ANDROID_SHMEM_GLOBAL_ENABLE && shmem[idx].global) { + ashv_send_pid_gsocket(ASHV_AT, shmid); } fd = shmem[idx].descriptor; @@ -543,8 +1082,17 @@ int libandroid_shmdt_fd(int fd) for (size_t i = 0; i < shmem_amount; i++) { if (shmem[i].descriptor == fd) { DBG("%s: unmapped for FD %d ID %zu shmid %x", __PRETTY_FUNCTION__, shmem[i].descriptor, i, shmem[i].id); - if (shmem[i].markedForDeletion || ashv_socket_id_from_shmid(shmem[i].id) != ashv_local_socket_id) { + if (ANDROID_SHMEM_GLOBAL_ENABLE && shmem[i].global) { + ashv_upd_shm_gsocket(i); + ashv_send_pid_gsocket(ASHV_DT, shmem[i].id); + } + android_shmem_detach_pid(i, ashv_pid_setup); + + if (shmem[i].markedForDeletion && shmem[i].countAttach == 0) { DBG ("%s: deleting shmid %x", __PRETTY_FUNCTION__, shmem[i].id); + if (ANDROID_SHMEM_GLOBAL_ENABLE && shmem[i].global) { + ashv_rm_shm_gsocket(shmem[i].id); + } android_shmem_delete(i); } pthread_mutex_unlock(&mutex); @@ -563,46 +1111,32 @@ int shmctl(int shmid, int cmd, struct shmid_ds *buf) { ashv_check_pid(); - if (cmd == IPC_RMID) { + pthread_mutex_lock(&mutex); + + INIT_SHMEM(-1) + + switch (cmd) { + case IPC_RMID: DBG("%s: IPC_RMID for shmid=%x", __PRETTY_FUNCTION__, shmid); - pthread_mutex_lock(&mutex); - int idx = ashv_find_local_index(shmid); - if (idx == -1) { - DBG("%s: shmid=%x does not exist locally", __PRETTY_FUNCTION__, shmid); - /* We do not rm non-local regions, but do not report an error for that. */ - pthread_mutex_unlock(&mutex); - return 0; - } - if (shmem[idx].addr) { - // shmctl(2): The segment will actually be destroyed only - // after the last process detaches it (i.e., when the shm_nattch - // member of the associated structure shmid_ds is zero. - shmem[idx].markedForDeletion = true; - } else { - android_shmem_delete(idx); + if (ANDROID_SHMEM_GLOBAL_ENABLE && shmem[idx].global) { + ashv_rm_shm_gsocket(shmid); } - pthread_mutex_unlock(&mutex); - return 0; - } else if (cmd == IPC_STAT) { + ashv_delete_segment(idx); + + goto ok; + case SHM_STAT: + case SHM_STAT_ANY: + case IPC_STAT: if (!buf) { DBG ("%s: ERROR: buf == NULL for shmid %x", __PRETTY_FUNCTION__, shmid); - errno = EINVAL; - return -1; + goto error; } - pthread_mutex_lock(&mutex); - int idx = ashv_find_local_index(shmid); - if (idx == -1) { - DBG ("%s: ERROR: shmid %x does not exist", __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_nattch = shmem[idx].countAttach; buf->shm_perm.key = shmem[idx].key; buf->shm_perm.uid = geteuid(); buf->shm_perm.gid = getegid(); @@ -611,11 +1145,16 @@ int shmctl(int shmid, int cmd, struct shmid_ds *buf) buf->shm_perm.mode = 0666; buf->shm_perm.seq = 1; - pthread_mutex_unlock (&mutex); - return 0; + goto ok; + default: + DBG("%s: cmd %d not implemented yet!", __PRETTY_FUNCTION__, cmd); + goto error; } - - DBG("%s: cmd %d not implemented yet!", __PRETTY_FUNCTION__, cmd); +ok: + pthread_mutex_unlock (&mutex); + return 0; +error: + pthread_mutex_unlock (&mutex); errno = EINVAL; return -1; } diff --git a/test/Makefile b/test/Makefile index fbb54a2..2d2f66b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,4 +1,4 @@ -CFLAGS += -Wall -Wextra -Werror -std=c99 +CFLAGS += -Wall -Wextra -Werror -std=c99 -DTEST OS := $(shell uname -o 2> /dev/null) @@ -13,7 +13,7 @@ cleanup-memory: test ./cleanup-shared-memory.sh endif -test: $(addprefix testcase-,$(patsubst %.c,%,$(wildcard *.c))) ; +test: $(addprefix testcase-,$(patsubst %.c,%,$(wildcard *.c))) test-interactions-shm ; testcase-% : test-% ./$^ @@ -21,8 +21,12 @@ testcase-% : test-% test-%: %.c $(EXTRA_CFILE) $(CC) $(CFLAGS) $+ -o $@ +test-interactions-shm: ./interactions-shm/ + $(MAKE) -C ./interactions-shm/ + clean: rm -f test-* + $(MAKE) clean -C ./interactions-shm/ .PHONY: clean \ test \ diff --git a/test/after-fork.c b/test/after-fork.c index 4b2efa1..838d55d 100644 --- a/test/after-fork.c +++ b/test/after-fork.c @@ -18,6 +18,7 @@ int main() { if ((shm_child = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat-child-2"); shm_child[1] = '#'; if (shmdt(shm_child) != 0) error_exit("shmdt-child-2"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child"); return 0; } else { int shmid; diff --git a/test/at-memory.c b/test/at-memory.c index 73dcbad..52270a2 100644 --- a/test/at-memory.c +++ b/test/at-memory.c @@ -20,6 +20,7 @@ int main() { if (shm_child_copy != shm_child) failure_exit("not-at-requested-memory"); shm_child[1] = '#'; if (shmdt(shm_child) != 0) error_exit("shmdt-child-2"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child"); return 0; } else { int shmid; diff --git a/test/deadlock.c b/test/deadlock.c index f4f4643..ef65b36 100644 --- a/test/deadlock.c +++ b/test/deadlock.c @@ -6,6 +6,7 @@ int main() { int shmid_from_shmget; if ((shmid_from_shmget = shmget(SHMEM_KEY, 30, IPC_CREAT | 0666)) < 0) error_exit("shmget"); if ((shmid_from_shmget = shmget(SHMEM_KEY, 30, IPC_CREAT | 0666)) < 0) error_exit("shmget"); + if (shmctl(shmid_from_shmget, IPC_RMID, 0) == -1) error_exit("shmctl"); return 0; } diff --git a/test/error-codes.c b/test/error-codes.c index b539428..7f1eb0a 100644 --- a/test/error-codes.c +++ b/test/error-codes.c @@ -8,4 +8,5 @@ int main() { if (errno != EINVAL) error_exit("shmat-wrong-errno"); int shmid; if ((shmid = shmget(IPC_PRIVATE, 30, IPC_CREAT | 0666)) < 0) error_exit("shmget"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl"); } diff --git a/test/interactions-shm/Makefile b/test/interactions-shm/Makefile new file mode 100644 index 0000000..33fe610 --- /dev/null +++ b/test/interactions-shm/Makefile @@ -0,0 +1,39 @@ +CFLAGS += -Wall -Wextra -Werror -std=c99 -DTEST -I.. + +OS := $(shell uname -o 2> /dev/null) + +ifeq ($(OS), Android) +CFLAGS += -I../.. -llog +EXTRA_CFILE = ../../shmem.c +cleanup-memory: test ; +else +CFLAGS += -DSYSV_ASHMEM_TEST_SYSTEM +EXTRA_CFILE = +cleanup-memory: test + ../cleanup-shared-memory.sh +endif + +test: testcase-1 testcase-2 testcase-3 testcase-4 + +testcase-1: test-create-shm test-remove shm-available.sh testcase-1.sh + bash ./testcase-1.sh + +testcase-2: test-create-shm test-write test-read test-remove shm-available.sh testcase-2.sh + bash ./testcase-2.sh + +testcase-3: test-create-shm test-create-ipc test-remove shm-available.sh testcase-3.sh + bash ./testcase-3.sh + +testcase-4: test-create-shm test-write test-read test-endless-attachment test-status test-remove shm-available.sh check-attachments.sh + bash ./testcase-4.sh + +test-%: %.c $(EXTRA_CFILE) + $(CC) $(CFLAGS) $+ -o $@ + +clean: + rm -f test-* + +.PHONY: clean \ + test + +.PRECIOUS: test-% diff --git a/test/interactions-shm/check-attachments.sh b/test/interactions-shm/check-attachments.sh new file mode 100755 index 0000000..26d59ee --- /dev/null +++ b/test/interactions-shm/check-attachments.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -e + +ca=$(./test-status) +cr=$(pidof test-endless-attachment | tr ' ' '\n' | wc -l) + +if [ "${ca}" = "${cr}" ]; then + echo "check-attachments - ok" + exit 0 +fi + +echo "check-attachments - no matches (ca:${ca} vs cr:${cr})" +for pid in $(pidof test-endless-attachment); do + kill $pid +done +./test-remove &> /dev/null || true +exit 1 diff --git a/test/interactions-shm/create-ipc.c b/test/interactions-shm/create-ipc.c new file mode 100644 index 0000000..43ce03d --- /dev/null +++ b/test/interactions-shm/create-ipc.c @@ -0,0 +1,8 @@ +#include "utils.h" + +int main() { + int shmid; + if ((shmid = shmget(IPC_PRIVATE, 30, 0666)) < 0) error_exit("shmget"); + printf("%d\n", shmid); + return 0; +} diff --git a/test/interactions-shm/create-shm.c b/test/interactions-shm/create-shm.c new file mode 100644 index 0000000..d5f13da --- /dev/null +++ b/test/interactions-shm/create-shm.c @@ -0,0 +1,9 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + if (shmget(key, 30, IPC_CREAT | IPC_EXCL | 0666) < 0) error_exit("shmget"); + printf("create-shm - ok\n"); + return 0; +} diff --git a/test/interactions-shm/endless-attachment.c b/test/interactions-shm/endless-attachment.c new file mode 100644 index 0000000..110101a --- /dev/null +++ b/test/interactions-shm/endless-attachment.c @@ -0,0 +1,13 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + if (shmat(shmid, NULL, 0) == (void*) -1) error_exit("shmat"); + printf("endless-attachment - ok\n"); + while (1) + continue; + return 0; +} diff --git a/test/interactions-shm/read.c b/test/interactions-shm/read.c new file mode 100644 index 0000000..0de43d1 --- /dev/null +++ b/test/interactions-shm/read.c @@ -0,0 +1,15 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + char *shm; + if ((shm = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat"); + printf("%s\n", shm); + if (strcmp(TEST_TEXT, shm) != 0) error_exit("strcmp"); + if (shmdt(shm) != 0) error_exit("shmdt"); + printf("read - ok\n"); + return 0; +} diff --git a/test/interactions-shm/remove.c b/test/interactions-shm/remove.c new file mode 100644 index 0000000..62ce683 --- /dev/null +++ b/test/interactions-shm/remove.c @@ -0,0 +1,15 @@ +#include "utils.h" + +int main(int argc, char **argv) { + int shmid; + if (argc > 1) { + if ((shmid = atoi(argv[1])) == 0) error_exit("atoi"); + } else { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + } + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl"); + printf("remove - ok\n"); + return 0; +} diff --git a/test/interactions-shm/shm-available.sh b/test/interactions-shm/shm-available.sh new file mode 100755 index 0000000..e7751a1 --- /dev/null +++ b/test/interactions-shm/shm-available.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +status=$(grep -s global-shmem /proc/*/comm | wc -l) +echo "host available: $status" +[ "${status}" = "${1}" ] +exit $? diff --git a/test/interactions-shm/status.c b/test/interactions-shm/status.c new file mode 100644 index 0000000..d14b36a --- /dev/null +++ b/test/interactions-shm/status.c @@ -0,0 +1,12 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + struct shmid_ds buf; + if (shmctl(shmid, IPC_STAT, &buf) == -1) error_exit("shmctl"); + printf("%lu\n", buf.shm_nattch); + return 0; +} diff --git a/test/interactions-shm/testcase-1.sh b/test/interactions-shm/testcase-1.sh new file mode 100644 index 0000000..74653cd --- /dev/null +++ b/test/interactions-shm/testcase-1.sh @@ -0,0 +1,7 @@ +set -e + +./test-create-shm +./shm-available.sh 1 + +./test-remove +./shm-available.sh 0 diff --git a/test/interactions-shm/testcase-2.sh b/test/interactions-shm/testcase-2.sh new file mode 100644 index 0000000..0fc2328 --- /dev/null +++ b/test/interactions-shm/testcase-2.sh @@ -0,0 +1,12 @@ +set -e + +./test-create-shm + +./test-create-shm && exit 1 || true +./shm-available.sh 1 + +./test-write +./test-read + +./test-remove +./shm-available.sh 0 diff --git a/test/interactions-shm/testcase-3.sh b/test/interactions-shm/testcase-3.sh new file mode 100644 index 0000000..5005301 --- /dev/null +++ b/test/interactions-shm/testcase-3.sh @@ -0,0 +1,22 @@ +set -e + +./test-create-shm + +IPC1=$(./test-create-ipc) +echo ${IPC1} +./shm-available.sh 1 +IPC2=$(./test-create-ipc) +echo ${IPC2} +./shm-available.sh 1 +IPC3=$(./test-create-ipc) +echo ${IPC3} +./shm-available.sh 1 + +./test-remove +./shm-available.sh 1 +./test-remove ${IPC1} +./shm-available.sh 1 +./test-remove ${IPC2} +./shm-available.sh 1 +./test-remove ${IPC3} +./shm-available.sh 0 diff --git a/test/interactions-shm/testcase-4.sh b/test/interactions-shm/testcase-4.sh new file mode 100644 index 0000000..08bfc6e --- /dev/null +++ b/test/interactions-shm/testcase-4.sh @@ -0,0 +1,23 @@ +set -e + +./test-create-shm + +./check-attachments.sh + +./test-endless-attachment & +./check-attachments.sh + +./test-endless-attachment & +./check-attachments.sh + +./test-remove +./shm-available.sh 1 + +kill $(pidof -s test-endless-attachment) +./shm-available.sh 1 + +./test-write +./test-read + +kill $(pidof -s test-endless-attachment) +./shm-available.sh 0 diff --git a/test/interactions-shm/write.c b/test/interactions-shm/write.c new file mode 100644 index 0000000..4d7334c --- /dev/null +++ b/test/interactions-shm/write.c @@ -0,0 +1,14 @@ +#include "utils.h" + +int main() { + key_t key; + if ((key = ftok(".", 1)) == -1) error_exit("ftok"); + int shmid; + if ((shmid = shmget(key, 30, 0666)) < 0) error_exit("shmget"); + char *shm; + if ((shm = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat"); + memcpy(shm, TEST_TEXT, sizeof(TEST_TEXT)); + if (shmdt(shm) != 0) error_exit("shmdt"); + printf("write - ok\n"); + return 0; +} diff --git a/test/ipc-private.c b/test/ipc-private.c index 127f94e..0a819c7 100644 --- a/test/ipc-private.c +++ b/test/ipc-private.c @@ -25,6 +25,7 @@ int main() { if ((shm_child = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat-child"); shm_child[1] = '#'; if (shmdt(shm_child) != 0) error_exit("shmdt-child-2"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child"); // Create a new shared memory segment in the child to check that the // parent can access it. @@ -40,6 +41,7 @@ int main() { int dummy; if (read(exit_pipes[0], &dummy, sizeof(int)) != sizeof(int)) error_exit("read-pipe"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child-2"); return 0; } else { int shmid_from_child; diff --git a/test/utils.h b/test/utils.h index a02a982..7f324de 100644 --- a/test/utils.h +++ b/test/utils.h @@ -13,6 +13,8 @@ # include "shm.h" #endif +#define TEST_TEXT "Hello, Test!" + void error_exit(char const* msg) { perror(msg); exit(1); diff --git a/test/with-key.c b/test/with-key.c index de7b3e6..6e91934 100644 --- a/test/with-key.c +++ b/test/with-key.c @@ -36,6 +36,7 @@ int main() { if ((shm_child = shmat(shmid, NULL, 0)) == (char*) -1) error_exit("shmat-child-2"); shm_child[1] = '#'; if (shmdt(shm_child) != 0) error_exit("shmdt-child-2"); + if (shmctl(shmid, IPC_RMID, 0) == -1) error_exit("shmctl-child"); return 0; } else { int shmid;