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/exports.txt b/exports.txt index 2640f41..5e3ee07 100644 --- a/exports.txt +++ b/exports.txt @@ -4,6 +4,8 @@ libandroid_shmget; libandroid_shmat; libandroid_shmdt; + libandroid_shmat_fd; + libandroid_shmdt_fd; shmctl; shmget; shmat; diff --git a/shm.h b/shm.h index faab787..4201f0a 100644 --- a/shm.h +++ b/shm.h @@ -31,6 +31,9 @@ extern void *shmat(int shmid, void const* shmaddr, int shmflg); #define shmdt libandroid_shmdt extern int shmdt(void const* shmaddr); +extern int libandroid_shmat_fd(int shmid, size_t* out_size); +extern int libandroid_shmdt_fd(int fd); + __END_DECLS #endif diff --git a/shmem.c b/shmem.c index 2f32095..522636e 100644 --- a/shmem.c +++ b/shmem.c @@ -20,8 +20,18 @@ #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 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 + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct { @@ -33,6 +43,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; @@ -45,6 +58,8 @@ static int ashv_local_socket_id = 0; // created for. static int ashv_pid_setup = 0; static pthread_t ashv_listening_thread_id = 0; +static int global_sendsock; +static bool global_conf = false; static int ancil_send_fd(int sock, int fd) { @@ -177,12 +192,94 @@ 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; } +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 (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; + } + + if (send(gsock, &action, sizeof(action), 0) != 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), 0) != sizeof(shmid)) { + DBG("%s: send shmid failed on socket %s: %s", __PRETTY_FUNCTION__, addr->sun_path + 1, strerror(errno)); + goto close; + } + + global_conf = true; + 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_one_action_gsocket(int action, int shmid) +{ + struct sockaddr_un addr; + int gsock = ashv_connect_gsocket(&addr, action, 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_update_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); + } + return -1; +} + +#define FIND_SHMEM \ + int idx = ashv_find_local_index(shmid); \ + if (idx == -1 && (idx = ashv_get_shm_gsocket(shmid)) == -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. @@ -348,13 +790,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) { + ashv_update_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; } @@ -373,12 +824,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) { @@ -387,21 +852,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 (ashv_one_action_gsocket(ASHV_PUT, shmid) == 0 || ashv_fork_function() == 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); @@ -428,29 +891,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 (shmem[idx].global) { \ + ashv_update_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 (shmem[idx].global) { + ashv_send_pid_gsocket(ASHV_AT, shmid); + } + android_shmem_attach_pid(idx, ashv_pid_setup); shmem[idx].addr = mmap((void*) shmaddr, 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)); @@ -470,20 +945,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 (shmem[i].global) { + ashv_update_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 (shmem[i].global) { + ashv_one_action_gsocket(ASHV_RM, shmem[i].id); + } android_shmem_delete(i); } pthread_mutex_unlock(&mutex); return 0; - } } pthread_mutex_unlock(&mutex); @@ -492,51 +976,93 @@ int shmdt(void const* shmaddr) return 0; } -/* Shared memory control operation. */ -int shmctl(int shmid, int cmd, struct shmid_ds *buf) +/* Let PRoot attach shared memory segment to another process. */ +int libandroid_shmat_fd(int shmid, size_t* out_size) { ashv_check_pid(); - if (cmd == 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. */ + int fd; + + pthread_mutex_lock(&mutex); + + INIT_SHMEM(-1) + + if (shmem[idx].global) { + ashv_send_pid_gsocket(ASHV_AT, shmid); + } + + fd = shmem[idx].descriptor; + *out_size = shmem[idx].size; + DBG ("%s: mapped for FD %d ID %d", __PRETTY_FUNCTION__, shmem[idx].descriptor, idx); + pthread_mutex_unlock (&mutex); + + return fd; +} + +/* Let PRoot detach shared memory segment after last process detached. */ +int libandroid_shmdt_fd(int fd) +{ + ashv_check_pid(); + + pthread_mutex_lock(&mutex); + 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].global) { + ashv_update_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 (shmem[i].global) { + ashv_one_action_gsocket(ASHV_RM, shmem[i].id); + } + android_shmem_delete(i); + } pthread_mutex_unlock(&mutex); return 0; } + } + pthread_mutex_unlock(&mutex); - 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); + DBG("%s: invalid fd %d", __PRETTY_FUNCTION__, fd); + /* Could be a remove segment, do not report an error for that. */ + return 0; +} + +/* Shared memory control operation. */ +int shmctl(int shmid, int cmd, struct shmid_ds *buf) +{ + ashv_check_pid(); + + pthread_mutex_lock(&mutex); + + INIT_SHMEM(-1) + + switch (cmd) { + case IPC_RMID: + DBG("%s: IPC_RMID for shmid=%x", __PRETTY_FUNCTION__, shmid); + + if (shmem[idx].global) { + ashv_one_action_gsocket(ASHV_RM, 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(); @@ -545,11 +1071,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..3481f63 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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..ef3c9de --- /dev/null +++ b/test/interactions-shm/Makefile @@ -0,0 +1,39 @@ +CFLAGS += -Wall -Wextra -Werror -std=c99 -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..dccb448 --- /dev/null +++ b/test/interactions-shm/shm-available.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +status=$(grep 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..87931eb --- /dev/null +++ b/test/interactions-shm/testcase-1.sh @@ -0,0 +1,5 @@ +./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..73e156f --- /dev/null +++ b/test/interactions-shm/testcase-2.sh @@ -0,0 +1,10 @@ +./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..f7f4117 --- /dev/null +++ b/test/interactions-shm/testcase-3.sh @@ -0,0 +1,20 @@ +./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..f3fe627 --- /dev/null +++ b/test/interactions-shm/testcase-4.sh @@ -0,0 +1,21 @@ +./test-create-shm + +./check-attachments.sh + +./test-endless-attachment & +./check-attachments.sh + +./test-endless-attachment & +./check-attachments.sh + +./test-remove +./shm-available.sh yes + +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;