| 1 | #include <sys/sem.h> |
| 2 | #include <stdarg.h> |
| 3 | #include <endian.h> |
| 4 | #include "syscall.h" |
| 5 | #include "ipc.h" |
| 6 | |
| 7 | #if __BYTE_ORDER != __BIG_ENDIAN |
| 8 | #undef SYSCALL_IPC_BROKEN_MODE |
| 9 | #endif |
| 10 | |
| 11 | union semun { |
| 12 | 	int val; |
| 13 | 	struct semid_ds *buf; |
| 14 | 	unsigned short *array; |
| 15 | }; |
| 16 | |
| 17 | int semctl(int id, int num, int cmd, ...) |
| 18 | { |
| 19 | 	union semun arg = {0}; |
| 20 | 	va_list ap; |
| 21 | 	switch (cmd & ~IPC_TIME64) { |
| 22 | 	case SETVAL: case GETALL: case SETALL: case IPC_SET: |
| 23 | 	case IPC_INFO: case SEM_INFO: |
| 24 | 	case IPC_STAT & ~IPC_TIME64: |
| 25 | 	case SEM_STAT & ~IPC_TIME64: |
| 26 | 	case SEM_STAT_ANY & ~IPC_TIME64: |
| 27 | 		va_start(ap, cmd); |
| 28 | 		arg = va_arg(ap, union semun); |
| 29 | 		va_end(ap); |
| 30 | 	} |
| 31 | #if IPC_TIME64 |
| 32 | 	struct semid_ds out, *orig; |
| 33 | 	if (cmd&IPC_TIME64) { |
| 34 | 		out = (struct semid_ds){0}; |
| 35 | 		orig = arg.buf; |
| 36 | 		arg.buf = &out; |
| 37 | 	} |
| 38 | #endif |
| 39 | #ifdef SYSCALL_IPC_BROKEN_MODE |
| 40 | 	struct semid_ds tmp; |
| 41 | 	if (cmd == IPC_SET) { |
| 42 | 		tmp = *arg.buf; |
| 43 | 		tmp.sem_perm.mode *= 0x10000U; |
| 44 | 		arg.buf = &tmp; |
| 45 | 	} |
| 46 | #endif |
| 47 | #ifndef SYS_ipc |
| 48 | 	int r = __syscall(SYS_semctl, id, num, IPC_CMD(cmd), arg.buf); |
| 49 | #else |
| 50 | 	int r = __syscall(SYS_ipc, IPCOP_semctl, id, num, IPC_CMD(cmd), &arg.buf); |
| 51 | #endif |
| 52 | #ifdef SYSCALL_IPC_BROKEN_MODE |
| 53 | 	if (r >= 0) switch (cmd | IPC_TIME64) { |
| 54 | 	case IPC_STAT: |
| 55 | 	case SEM_STAT: |
| 56 | 	case SEM_STAT_ANY: |
| 57 | 		arg.buf->sem_perm.mode >>= 16; |
| 58 | 	} |
| 59 | #endif |
| 60 | #if IPC_TIME64 |
| 61 | 	if (r >= 0 && (cmd&IPC_TIME64)) { |
| 62 | 		arg.buf = orig; |
| 63 | 		*arg.buf = out; |
| 64 | 		IPC_HILO(arg.buf, sem_otime); |
| 65 | 		IPC_HILO(arg.buf, sem_ctime); |
| 66 | 	} |
| 67 | #endif |
| 68 | 	return __syscall_ret(r); |
| 69 | } |