diff options
author | takeshi_hoshina <takeshi_hoshina@mail.toyota.co.jp> | 2020-10-27 11:16:21 +0900 |
---|---|---|
committer | takeshi_hoshina <takeshi_hoshina@mail.toyota.co.jp> | 2020-10-27 11:16:21 +0900 |
commit | 947c78887e791596d4a5ec2d1079f8b1a049628b (patch) | |
tree | 3981e88eb8764d7180722f8466f36b756dc005af /nsframework/common_library/client/src | |
parent | 706ad73eb02caf8532deaf5d38995bd258725cb8 (diff) |
basesystem 0.1sandbox/ToshikazuOhiwa/basesystem
Diffstat (limited to 'nsframework/common_library/client/src')
-rw-r--r-- | nsframework/common_library/client/src/cl_cgroup.c | 260 | ||||
-rw-r--r-- | nsframework/common_library/client/src/cl_lock.c | 221 | ||||
-rw-r--r-- | nsframework/common_library/client/src/cl_monitor.c | 442 | ||||
-rw-r--r-- | nsframework/common_library/client/src/cl_process.c | 1198 | ||||
-rw-r--r-- | nsframework/common_library/client/src/cl_region.c | 295 | ||||
-rw-r--r-- | nsframework/common_library/client/src/cl_sem.c | 67 |
6 files changed, 2483 insertions, 0 deletions
diff --git a/nsframework/common_library/client/src/cl_cgroup.c b/nsframework/common_library/client/src/cl_cgroup.c new file mode 100644 index 00000000..812ccaaa --- /dev/null +++ b/nsframework/common_library/client/src/cl_cgroup.c @@ -0,0 +1,260 @@ +/* + * @copyright Copyright (c) 2016-2020 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <sys/types.h> +#include "cl_cgroup.h" + +#define CL_CGROOT "/sys/fs/cgroup/" + +static char *cl_cgroup_base(cl_cgroup_t cgroup) { + switch (cgroup) { // LCOV_EXCL_BR_LINE 200: internal interface,code make sure + case CL_CGROUP_MEMORY: + return CL_CGROOT "/memory"; + case CL_CGROUP_CPU: + return CL_CGROOT "/cpu"; + } + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return NULL;// LCOV_EXCL_LINE 200:internal interface,code make sure nerver run +} + +static char *cl_cgroup_create_path(cl_cgroup_t cgroup, const char *cgroup_name, const char *controler) { + char *path = malloc(FILENAME_MAX); + + if (path == NULL) { // LCOV_EXCL_BR_LINE 5:fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + errno = ENOMEM; // LCOV_EXCL_LINE 5:fail safe for libc malloc + goto error; // LCOV_EXCL_LINE 5:fail safe for libc malloc + } + + if (cgroup != CL_CGROUP_MEMORY && cgroup != CL_CGROUP_CPU) { // LCOV_EXCL_BR_LINE 200: internal interface,code make sure + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + errno = EINVAL; // LCOV_EXCL_LINE 200: internal interface,code make sure + goto error; // LCOV_EXCL_LINE 200: internal interface,code make sure + } + + if (cgroup_name == NULL) { // LCOV_EXCL_BR_LINE 200: internal interface,code make sure + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + errno = EINVAL; // LCOV_EXCL_LINE 200: internal interface,code make sure + goto error; // LCOV_EXCL_LINE 200: internal interface,code make sure + } + + if (controler) { + snprintf(path, FILENAME_MAX, "%s/%s/%s", cl_cgroup_base(cgroup), cgroup_name, controler); + } else { + snprintf(path, FILENAME_MAX, "%s/%s", cl_cgroup_base(cgroup), cgroup_name); + } + + return path; + +error: + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + free(path); // LCOV_EXCL_LINE 200: internal interface,code make sure + return NULL; // LCOV_EXCL_LINE 200: internal interface,code make sure +} + +int cl_cgroup_make(cl_cgroup_t cgroup, const char *cgroup_name) { + int r = -1; + char *path = cl_cgroup_create_path(cgroup, cgroup_name, NULL); + + if (path == NULL) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + goto exit; // LCOV_EXCL_LINE 6: double check + } + + if (mkdir(path, 0777) < 0) { // LCOV_EXCL_BR_LINE 5:fail safe for libc mkdir + goto exit; + } + + r = 0; + +exit: + if (path) {// LCOV_EXCL_BR_LINE 6: double check + free(path); + } + return r; +} + +int cl_cgroup_remove(cl_cgroup_t cgroup, const char *cgroup_name) { + int r = -1; + char *path = cl_cgroup_create_path(cgroup, cgroup_name, NULL); + + if (path == NULL) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + goto exit; // LCOV_EXCL_LINE 6: double check + } + + if (rmdir(path) < 0) { // LCOV_EXCL_BR_LINE 5:fail safe for libc rmdir + goto exit; + } + + r = 0; + +exit: + if (path) { // LCOV_EXCL_BR_LINE 6: double check + free(path); + } + return r; +} + +int cl_cgroup_exist(cl_cgroup_t cgroup, const char *cgroup_name) { + int r = -1; + char *path = cl_cgroup_create_path(cgroup, cgroup_name, NULL); + + if (path == NULL) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + goto exit; // LCOV_EXCL_LINE 6: double check + } + + r = access(path, F_OK); + +exit: + if (path) { // LCOV_EXCL_BR_LINE 6: double check + free(path); + } + return r; +} + +int cl_cgroup_open(cl_cgroup_t cgroup, const char *cgroup_name, const char *controler, int flags) { + int r = -1; + char *path = cl_cgroup_create_path(cgroup, cgroup_name, controler); + int fd = -1; + + if (path == NULL) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + goto exit; // LCOV_EXCL_LINE 6: double check + } + + if (controler == NULL) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + errno = EINVAL; // LCOV_EXCL_LINE 6: double check + goto exit; // LCOV_EXCL_LINE 6: double check + } + + if ((fd = open(path, flags | O_CLOEXEC)) < 0) { // LCOV_EXCL_BR_LINE 5:fail safe for libc open + goto exit; + } + + r = fd; + +exit: + if (path) { // LCOV_EXCL_BR_LINE 6: double check + free(path); + } + return r; +} + +int cl_cgroup_set_string(cl_cgroup_t cgroup, const char *cgroup_name, const char *controler, const char *string) { + int r = -1; + int fd = -1; + + if (controler == NULL || string == NULL) { // LCOV_EXCL_BR_LINE 6: double check + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + errno = EINVAL;// LCOV_EXCL_LINE 6: double check + goto exit;// LCOV_EXCL_LINE 6: double check + } + + fd = cl_cgroup_open(cgroup, cgroup_name, controler, O_WRONLY); + if (fd < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc funtion open + goto exit; + } + + if (write(fd, string, strlen(string)) < 0) { // LCOV_EXCL_BR_LINE 5:fail safe for libc write + goto exit; + } + + r = 0; + +exit: + if (fd >= 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc funtion open + int save_err = errno; + close(fd); + errno = save_err; + } + return r; +} + +int cl_cgroup_set_num(cl_cgroup_t cgroup, const char *cgroup_name, const char *controler, int64_t value) { + char num_str[22]; + + snprintf(num_str, sizeof(num_str), "%" PRId64, value); + + return cl_cgroup_set_string(cgroup, cgroup_name, controler, num_str); +} + +int cl_cgroup_get_string(cl_cgroup_t cgroup, const char *cgroup_name, const char *controler, char *string, size_t length) { // LCOV_EXCL_START 8: dead code // NOLINT (readability/nolint) + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + int r = -1; + int fd = -1; + + if (controler == NULL || string == NULL) { + errno = EINVAL; + goto exit; + } + + fd = cl_cgroup_open(cgroup, cgroup_name, controler, O_RDONLY); + if (fd < 0) { + goto exit; + } + + if (read(fd, string, length) < 0) { + goto exit; + } + + r = 0; + +exit: + if (fd >= 0) { + int save_err = errno; + close(fd); + errno = save_err; + } + return r; +} +// LCOV_EXCL_STOP + +int64_t cl_cgroup_get_num(cl_cgroup_t cgroup, const char *cgroup_name, const char *controler) { // LCOV_EXCL_START 8: dead code // NOLINT (readability/nolint) + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + int r; + char num_str[22]; + + if ((r = cl_cgroup_get_string(cgroup, cgroup_name, controler, num_str, sizeof(num_str))) < 0) { + return r; + } + + return atoll(num_str); +} +// LCOV_EXCL_STOP diff --git a/nsframework/common_library/client/src/cl_lock.c b/nsframework/common_library/client/src/cl_lock.c new file mode 100644 index 00000000..6add2a5a --- /dev/null +++ b/nsframework/common_library/client/src/cl_lock.c @@ -0,0 +1,221 @@ +/* + * @copyright Copyright (c) 2016-2020 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #include <stdio.h> +// #include <stdlib.h> +#include <errno.h> + +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <pthread.h> + +#include <native_service/cl_lock.h> +#include <native_service/cl_lockid.h> +#include "cl_lock_internal.h" +#include "cl_error.h" + +static int shm_id = -1; + +/* + * Lock file is consists of slots(4KB) + * The slot layout is: + * 0 ~ 4Byte : field of PID + * 4Byte ~ 28Byte : field of pthread_mutex_t + */ + + +static int cl_LockfileInit(void *base) { + int i; + /* + int j; + */ + void *addr; + pthread_mutexattr_t attr; + + if (pthread_mutexattr_init(&attr) != 0) { // LCOV_EXCL_BR_LINE 5:fail safe for libc pthread_mutexattr_init + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5:fail safe for libc pthread_mutexattr_init + } + if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) != 0) { // LCOV_EXCL_BR_LINE 5:fail safe for libc pthread_mutexattr_setpshared + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5:fail safe for libc pthread_mutexattr_setpshared + } +// if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP) != 0) { +// return -1; +// } + if (pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST) != 0) { + return -1; + } + + for (i = 0; i < LID_NUM; i++) { + /* + addr = SLOT_SIZE * 1 + base; + for (j = 0; j < 1024; j++) { + *(int*)(addr + j * sizeof(int)) = j; + } + */ + addr = SLOT_SIZE * i + (char *)base + sizeof(int); + pthread_mutex_init((pthread_mutex_t *)addr, &attr); + + } + if (pthread_mutexattr_destroy(&attr) != 0) { // LCOV_EXCL_BR_LINE 5:fail safe for libc pthread_mutexattr_destroy + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5:fail safe for libc pthread_mutexattr_destroy + } + return 0; +} + +/* + * Initialize in the system + * This function will generate the Lock file, and initialize pthread_mutex_t for all slots + */ +int CL_LockSystemInit(void) { + int fd = -1; + int ret = 0; + void *base = MAP_FAILED; + + fd = shm_open(LOCKFILE_NAME, O_CREAT | O_EXCL | O_RDWR, (S_IRWXG | S_IRWXO | S_IRWXU)); + if (fd < 0) { + ret = -1; + goto exit; + } + if (ftruncate(fd, LOCKFILE_SIZE) != 0) { // LCOV_EXCL_BR_LINE 5:fail safe for libc ftruncate + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + ret = -1; // LCOV_EXCL_LINE 5:fail safe for libc ftruncate + goto exit; // LCOV_EXCL_LINE 5:fail safe for libc ftruncate + } + + base = mmap(NULL, LOCKFILE_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { // LCOV_EXCL_BR_LINE 5:fail safe for libc mmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + ret = -1; // LCOV_EXCL_LINE 5:fail safe for libc mmap + goto exit; // LCOV_EXCL_LINE 5:fail safe for libc mmap + } + + if (cl_LockfileInit(base) < 0) { // LCOV_EXCL_BR_LINE 11:out branch + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + ret = -1; // LCOV_EXCL_LINE 5:fail safe for libc + goto exit; // LCOV_EXCL_LINE 5:fail safe for libc + } + +exit: + if (fd >= 0) { + close(fd); + } + if (base != MAP_FAILED) { + munmap(base, LOCKFILE_SIZE); + } + return ret; +} + +void CL_LockSystemFin_debug(void) { + if (shm_id >= 0) { + close(shm_id); + } + shm_unlink(LOCKFILE_NAME); + shm_id = -1; +} + +/* + * Initialize in the process + * Open the Lock file + */ +int CL_LockProcessInit(void) { + if (shm_id < 0) { + shm_id = shm_open(LOCKFILE_NAME, O_RDWR, (S_IRWXG | S_IRWXO | S_IRWXU)); + } + if (shm_id < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function shm_open + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5: fail safe for glibc function shm_open + } + return 0; +} + +void *CL_LockMap(int lid) { + if ((lid < 0) || (lid >= LID_NUM)) { + errno = EINVAL; + return MAP_FAILED; + } + return mmap(NULL, SLOT_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, shm_id, (off_t)(lid * SLOT_SIZE)); +} + +int CL_LockUnmap(void *addr) { + return munmap(addr, SLOT_SIZE); +} + +int CL_LockGet(void *addr) { + int ret; + + if ((addr == NULL) || (addr == MAP_FAILED)) { + ret = EINVAL; + } else { + CL_DBG_PRINT("@@@@@ %s Start: pid = %d\n", __func__, *(int *)addr); + // LCOV_EXCL_BR_START 5:fail safe for libc pthread_mutex_lock + if ((ret = pthread_mutex_lock((pthread_mutex_t*)((char *)addr + sizeof(int)))) == 0) { + // LCOV_EXCL_BR_STOP + *(int *)addr = (int)getpid(); + } else if (ret == EOWNERDEAD) { + if ((ret = pthread_mutex_consistent((pthread_mutex_t *)((char *)addr + sizeof(int)))) == 0) { + *(int *)addr = (int)getpid(); + } + } + CL_DBG_PRINT("@@@@@ %s End: pid = %d\n", __func__, *(int *)addr); + } + return ret; +} + +int CL_LockNowait(void *addr) { + int ret; + + if ((addr == NULL) || (addr == MAP_FAILED)) { + ret = EINVAL; + } else { + CL_DBG_PRINT("@@@@@ %s Start: pid = %d\n", __func__, *(int *)addr); + if ((ret = pthread_mutex_trylock((pthread_mutex_t*)((char *)addr + sizeof(int)))) == 0) { + *(int *)addr = (int)getpid(); + } else if (ret == EOWNERDEAD) { + if ((ret = pthread_mutex_consistent((pthread_mutex_t *)((char *)addr + sizeof(int)))) == 0) { + *(int *)addr = (int)getpid(); + } + } + CL_DBG_PRINT("@@@@@ %s End: pid = %d\n", __func__, *(int *)addr); + } + return ret; +} + + +int CL_LockRelease(void *addr) { + int ret; + if ((addr == NULL) || (addr == MAP_FAILED)) { + ret = EINVAL; + } else { + CL_DBG_PRINT("@@@@@ %s : pid = %d\n", __func__, *(int *)addr); + *(int *)addr = (int)0; + ret = pthread_mutex_unlock((pthread_mutex_t*)((char *)addr + sizeof(int))); + } + return ret; +} + + + diff --git a/nsframework/common_library/client/src/cl_monitor.c b/nsframework/common_library/client/src/cl_monitor.c new file mode 100644 index 00000000..26d8e2e5 --- /dev/null +++ b/nsframework/common_library/client/src/cl_monitor.c @@ -0,0 +1,442 @@ +/* + * @copyright Copyright (c) 2016-2020 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> +#include <semaphore.h> + +#include "cl_error.h" +#include <native_service/cl_region.h> +#include <native_service/cl_monitor.h> + +#define CL_MONITOR_SHM_NAME "/cl_monitor" + +#define CL_MONITOR_ID_REAL_GENERIC_START (0) +#define CL_MONITOR_ID_REAL_GENERIC_END (1023) +#define CL_MONITOR_ID_REAL_RPC_START (49952) +#define CL_MONITOR_ID_REAL_RPC_END (59999) + +#define CL_MONITOR_ID_FLAT_GENERIC_START (0) +#define CL_MONITOR_ID_FLAT_GENERIC_END \ + (CL_MONITOR_ID_FLAT_GENERIC_START + (CL_MONITOR_ID_REAL_GENERIC_END - CL_MONITOR_ID_REAL_GENERIC_START)) +#define CL_MONITOR_ID_FLAT_RPC_START (CL_MONITOR_ID_FLAT_GENERIC_END + 1) +#define CL_MONITOR_ID_FLAT_RPC_END \ + (CL_MONITOR_ID_FLAT_RPC_START + (CL_MONITOR_ID_REAL_RPC_END - CL_MONITOR_ID_REAL_RPC_START)) +#define CL_MONITOR_ENTRY_NUM (CL_MONITOR_ID_FLAT_RPC_END + 1) + +#define CL_MONITOR_BITMAP_LENGTH ((CL_MONITOR_ENTRY_NUM + 63) / 64) // aligned + +#define CL_ALIGN(x, a) (((x) + (a - 1)) / (a) * (a)) + +struct cl_monitor_header { + char signature[4]; + uint64_t bitmap[CL_MONITOR_BITMAP_LENGTH]; + sem_t sem; +}; + +#define CL_MONITOR_OBJECT_SIZE \ + CL_ALIGN((sizeof(struct cl_monitor_header) + (sizeof(CL_MonitorEntry_t) * CL_MONITOR_ENTRY_NUM)), 4096) + +#define CL_MONITOR_SET_BIT(a, i) (a[i / 64] |= (1ULL << (i % 64))) +#define CL_MONITOR_CLEAR_BIT(a, i) (a[i / 64] &= ~(1ULL << (i % 64))) + +static struct cl_monitor_header *cl_monitor_obj; +static CL_MonitorEntry_t *cl_monitor_entry_head; + +int CL_MonitorInit(CL_MonitorInit_t init_type) { + int fd; + int oflag; + mode_t mode; + + if (init_type != CL_MONITOR_INIT_SYSTEM && init_type != CL_MONITOR_INIT_USER) { + errno = EINVAL; + return -1; + } + + if (cl_monitor_obj != NULL) { + return 0; + } + + if (init_type == CL_MONITOR_INIT_SYSTEM) { + oflag = O_RDWR | O_CREAT | O_EXCL; + mode = 0666; + } else { + oflag = O_RDWR; + mode = 0; + } + + if ((fd = shm_open(CL_MONITOR_SHM_NAME, oflag, mode)) == -1) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function shm_open + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1;// LCOV_EXCL_LINE 5: fail safe for glibc function shm_open + } + + if (init_type == CL_MONITOR_INIT_SYSTEM) { + if (ftruncate(fd, CL_MONITOR_OBJECT_SIZE) == -1) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function ftruncate + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5: fail safe for glibc function ftruncate + } + } else { + struct stat st_buf; + if (fstat(fd, &st_buf) == -1) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function fstat + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5: fail safe for glibc function fstat + } + + if (st_buf.st_size != CL_MONITOR_OBJECT_SIZE) { + errno = EAGAIN; + return -1; + } + } + + // LCOV_EXCL_BR_START 5: fail safe for glibc function mmap + if ((cl_monitor_obj = mmap(NULL, CL_MONITOR_OBJECT_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { + // LCOV_EXCL_BR_STOP + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5: fail safe for glibc function mmap + } + + if (init_type == CL_MONITOR_INIT_SYSTEM) { // LCOV_EXCL_BR_LINE 11: out branch + memcpy(cl_monitor_obj->signature, "CLAM", 4); + memset(cl_monitor_obj->bitmap, 0, sizeof(cl_monitor_obj->bitmap)); + sem_init(&cl_monitor_obj->sem, 1, 1); + } + cl_monitor_entry_head = (CL_MonitorEntry_t *)(((char *)cl_monitor_obj) + sizeof(struct cl_monitor_header)); + + return 0; +} + +static inline int cl_monitor_check_type_id(CL_MonitorType_t type, uint32_t id, int *offset) { + if (type == CL_MONITOR_TYPE_GENERIC) { + if (id > CL_MONITOR_ID_REAL_GENERIC_END) { + errno = ENOENT; + return -1; + } + *offset = (int)((id - CL_MONITOR_ID_REAL_GENERIC_START) + CL_MONITOR_ID_FLAT_GENERIC_START); + } else if (type == CL_MONITOR_TYPE_RPC) { + if (id < CL_MONITOR_ID_REAL_RPC_START || id > CL_MONITOR_ID_REAL_RPC_END) { + errno = ENOENT; + return -1; + } + *offset = (int)((id - CL_MONITOR_ID_REAL_RPC_START) + CL_MONITOR_ID_FLAT_RPC_START); + } else { + errno = ENOENT; + return -1; + } + + return 0; +} + +static inline int cl_monitor_sem_wait(sem_t *sem) { + int ret; + +retry: + ret = sem_wait(sem); + if (ret == -1) { // LCOV_EXCL_BR_LINE 5: fail safe for libc sem_wait + // LCOV_EXCL_START 5: fail safe for libc sem_wait + + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + if (errno == EINTR) { + goto retry; + } + CL_PERROR("sem_wait"); + } + // LCOV_EXCL_STOP + + return ret; +} + +static inline int cl_monitor_sem_post(sem_t *sem) { + int ret; + + if ((ret = sem_post(sem)) == -1) { // LCOV_EXCL_BR_LINE 5: fail safe for libc sem_post + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + CL_PERROR("sem_post"); // LCOV_EXCL_LINE 5: fail safe for libc sem_post + } + + return ret; +} + +int CL_MonitorSetEntry(CL_MonitorType_t type, uint32_t id, CL_MonitorState_t state, uint32_t timeout, + uint32_t user_data) { + int offset; + CL_MonitorEntry_t *e; + struct timespec ts; + + if (cl_monitor_obj == NULL) { + errno = ENOENT; + return -1; + } + + if (cl_monitor_check_type_id(type, id, &offset) == -1) { + return -1; + } + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { // LCOV_EXCL_BR_LINE 5: fail safe for libc clock_gettime + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5: fail safe for libc clock_gettime + } + + e = cl_monitor_entry_head + offset; + cl_monitor_sem_wait(&cl_monitor_obj->sem); + + if (state == CL_MONITOR_STATE_SLEEP) { + memset(e, 0, sizeof(CL_MonitorEntry_t)); + CL_MONITOR_CLEAR_BIT(cl_monitor_obj->bitmap, offset); + } else { + e->pid = (uint16_t)getpid(); + e->type = type; + e->state = state; + +// e->timeout = (uint32_t)ts.tv_sec + timeout; + e->timeout = ts.tv_sec + timeout; + e->id = id; + e->user_data = user_data; + CL_MONITOR_SET_BIT(cl_monitor_obj->bitmap, offset); + } + + cl_monitor_sem_post(&cl_monitor_obj->sem); + + return 0; +} + +int CL_MonitorGetEntry(CL_MonitorType_t type, uint32_t id, CL_MonitorEntry_t *entry) { + int offset; + CL_MonitorEntry_t *e; + + if (cl_monitor_obj == NULL) { + errno = ENOENT; + return -1; + } + + if (entry == NULL) { + errno = EINVAL; + return -1; + } + + if (cl_monitor_check_type_id(type, id, &offset) == -1) { + return -1; + } + + e = cl_monitor_entry_head + offset; + cl_monitor_sem_wait(&cl_monitor_obj->sem); + + memcpy(entry, e, sizeof(CL_MonitorEntry_t)); + + cl_monitor_sem_post(&cl_monitor_obj->sem); + + return 0; +} + +int CL_MonitorSearchInit(CL_MonitorSearch_t *serch) { + if (serch == NULL) { + errno = EINVAL; + return -1; + } + + serch->entry_list = NULL; + serch->entry_num = 0; + return 0; +} + +int CL_MonitorSearchDestroy(CL_MonitorSearch_t *serch) { + if (serch == NULL) { + errno = EINVAL; + return -1; + } + + free(serch->entry_list); + serch->entry_num = 0; + return 0; +} + +static inline int cl_monitor_popcnt64(uint64_t x) { + uint64_t n; + + n = (x >> 1) & 0x7777777777777777ULL; + x = x - n; + n = (n >> 1) & 0x7777777777777777ULL; + x = x - n; + n = (n >> 1) & 0x7777777777777777ULL; + x = x - n; + x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; + x = x * 0x0101010101010101ULL; + return (int)(x >> 56); +} + +typedef struct cl_monitor_offset_s cl_monitor_offset_t; +struct cl_monitor_offset_s { + cl_monitor_offset_t *next; + int offset; +}; + +int CL_MonitorSearchTimeout(CL_MonitorSearch_t *search) { + int i; + int offset; + CL_MonitorEntry_t *e; + struct timespec ts; + int timeout_entry_num = 0; + cl_region_t *r; + cl_monitor_offset_t *o, *head = NULL, *tail = NULL; + + if (cl_monitor_obj == NULL) { + errno = ENOENT; + return -1; + } + + if (search == NULL) { + errno = EINVAL; + return -1; + } + + if ((r = CL_RegionCreate(CL_REGION_DEFAULT_SIZE)) == NULL) { + errno = ENOMEM; + return -1; + } + + free(search->entry_list); + search->entry_list = NULL; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {// LCOV_EXCL_BR_LINE 5: fail safe for libc clock_gettime + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1;// LCOV_EXCL_LINE 5: fail safe for libc clock_gettime + } + + cl_monitor_sem_wait(&cl_monitor_obj->sem); + + for (i = 0; i < CL_MONITOR_BITMAP_LENGTH; i++) { + if (cl_monitor_obj->bitmap[i] != 0) { + uint64_t bits, mrb1; // most right bit 1 + + bits = cl_monitor_obj->bitmap[i]; + while (bits) { + mrb1 = bits & (-bits); + offset = i * 64 + cl_monitor_popcnt64(mrb1 - 1); + e = cl_monitor_entry_head + offset; + + if (e->timeout <= ts.tv_sec) { + timeout_entry_num++; + if ((o = CL_RegionAlloc(r, cl_monitor_offset_t, 1)) == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for mmap + // LCOV_EXCL_START 5: fail safe for libc mmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + errno = ENOMEM; + timeout_entry_num = -1; + goto exit; + // LCOV_EXCL_STOP + } + + o->offset = offset; + o->next = NULL; + + if (head == NULL) { + head = tail = o; + } else { + tail->next = o; + tail = o; + } + } + bits &= ~mrb1; + } + } + } + + if (timeout_entry_num) { + CL_MonitorEntry_t *src, *dst; + dst = malloc(sizeof(CL_MonitorEntry_t) * (size_t)timeout_entry_num); + if (dst == NULL) { // LCOV_EXCL_LINE 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + errno = ENOMEM; // LCOV_EXCL_LINE 5: fail safe for libc malloc + timeout_entry_num = -1; // LCOV_EXCL_LINE 5: fail safe for libc malloc + goto exit; // LCOV_EXCL_LINE 5: fail safe for libc malloc + } + + search->entry_list = dst; + o = head; + for (i = 0; i < timeout_entry_num; i++) { + src = cl_monitor_entry_head + o->offset; + memcpy(dst, src, sizeof(CL_MonitorEntry_t)); + + o = o->next; + dst++; + } + } + +exit: + cl_monitor_sem_post(&cl_monitor_obj->sem); + + CL_RegionDestroy(r); + search->entry_num = (timeout_entry_num == -1) ? 0 : timeout_entry_num; // LCOV_EXCL_BR_LINE 11: out branch + return timeout_entry_num; +} + +int cl_monitor_cleanup(int pid) { + int ret = 0; + int i; + int offset; + CL_MonitorEntry_t *e; + + if (cl_monitor_obj == NULL) { + errno = ENOENT; + return -1; + } + + if (pid <= 0) { // LCOV_EXCL_LINE 5: fail safe for glibc function waitid + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + errno = EINVAL;// LCOV_EXCL_LINE 5: fail safe for glibc function waitid + return -1;// LCOV_EXCL_LINE 5: fail safe for glibc function waitid + } + + cl_monitor_sem_wait(&cl_monitor_obj->sem); + + for (i = 0; i < CL_MONITOR_BITMAP_LENGTH; i++) { + if (cl_monitor_obj->bitmap[i] != 0) { + uint64_t bits, mrb1; // most right bit 1 + + bits = cl_monitor_obj->bitmap[i]; + while (bits) { + mrb1 = bits & (-bits); + offset = i * 64 + cl_monitor_popcnt64(mrb1 - 1); + + e = cl_monitor_entry_head + offset; + if (e->pid == pid) { + memset(e, 0, sizeof(CL_MonitorEntry_t)); + CL_MONITOR_CLEAR_BIT(cl_monitor_obj->bitmap, offset); + ret++; + } + + bits &= ~mrb1; + } + } + } + + cl_monitor_sem_post(&cl_monitor_obj->sem); + + return ret; +} diff --git a/nsframework/common_library/client/src/cl_process.c b/nsframework/common_library/client/src/cl_process.c new file mode 100644 index 00000000..5c9f5a4b --- /dev/null +++ b/nsframework/common_library/client/src/cl_process.c @@ -0,0 +1,1198 @@ +/* + * @copyright Copyright (c) 2016-2020 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <dirent.h> +#include <fcntl.h> +#include <sched.h> +#include <semaphore.h> +#include <pthread.h> +#include <limits.h> +#include <sys/prctl.h> +#include <sys/signalfd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/syscall.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <native_service/cl_process.h> +#include "cl_process_internal.h" +#include "cl_error.h" +#include "cl_cgroup.h" +#include "cl_monitor_internal.h" + +#define assert_static(e) \ + do { \ + enum { assert_static__ = 1/(e) }; \ + } while (0) + +struct linux_dirent64 { + ino64_t d_ino; + off64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[]; +}; + +/* + * Initialize the process + */ +int CL_ProcessInit(void) { + int sfd = -1; + char *name; + sigset_t mask; + + assert_static(sizeof(CL_ProcessAttr_t) == sizeof(CL_ProcessAttrInternal_t)); + + name = getenv(CL_PROCESS_NAME_ENV); + if (name != NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc getenv + if (prctl(PR_SET_NAME, name) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc getenv + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + CL_PERROR("prctl"); // LCOV_EXCL_LINE 5: fail safe for libc getenv + goto exit; // LCOV_EXCL_LINE 5: fail safe for libc getenv + } + } + + if (sigemptyset(&mask) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc sigemptyset + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + CL_PERROR("sigemptyset"); // LCOV_EXCL_LINE 5: fail safe for libc sigemptyset + goto exit; // LCOV_EXCL_LINE 5: fail safe for libc sigemptyset + } + if (sigaddset(&mask, SIGCHLD) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc sigaddset + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + CL_PERROR("sigaddset"); // LCOV_EXCL_LINE 5: fail safe for libc sigaddset + goto exit; // LCOV_EXCL_LINE 5: fail safe for libc sigaddset + } + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc sigprocmask + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + CL_PERROR("sigprocmask"); // LCOV_EXCL_LINE 5: fail safe for libc sigprocmask + goto exit; // LCOV_EXCL_LINE 5: fail safe for libc sigprocmask + } + + if ((sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC)) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc signalfd + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + CL_PERROR("signalfd"); // LCOV_EXCL_LINE 5: fail safe for libc signalfd + goto exit; // LCOV_EXCL_LINE 5: fail safe for libc signalfd + } + +exit: + return sfd; +} + +/* + * start of the functions to be executed from fork to exec + */ +static void cl_process_pf_err(char no) { + char errorstr[17] = "err:post_fork:0\n"; + + errorstr[14] = no; + while (1) { + if (write(STDERR_FILENO, errorstr, 16) == -1 && errno == EINTR) { // LCOV_EXCL_BR_LINE 5: fail safe for libc write + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + continue; // LCOV_EXCL_LINE 5: fail safe for libc write + } + break; + } +} + +static inline int cl_process_pf_set_schedule(const CL_ProcessAttrInternal_t *ia) { + int ret = -1; + struct sched_param param; + int policy; + + switch (ia->sched_policy) { + case CL_PROCESS_SCHED_POLICY_RR: + policy = SCHED_RR; + param.sched_priority = ia->sched_priority; + break; + case CL_PROCESS_SCHED_POLICY_FIFO: + param.sched_priority = ia->sched_priority; + policy = SCHED_FIFO; + break; + default: + param.sched_priority = 0; + policy = SCHED_OTHER; + break; + } + if (sched_setscheduler(getpid(), policy, ¶m) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function sched_setscheduler + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + cl_process_pf_err('3');// LCOV_EXCL_LINE 5: fail safe for glibc function sched_setscheduler + goto exit;// LCOV_EXCL_LINE 5: fail safe for glibc function sched_setscheduler + } + if (ia->sched_policy == CL_PROCESS_SCHED_POLICY_OTHER) { + if (setpriority(PRIO_PROCESS, (__id_t)getpid(), ia->sched_priority) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function setpriority + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + cl_process_pf_err('4');// LCOV_EXCL_LINE 5: fail safe for glibc function setpriority + goto exit;// LCOV_EXCL_LINE 5: fail safe for glibc function setpriority + } + } + ret = 0; + +exit: + return ret; +} + +static inline int cl_process_pf_check_close_fd(int check_fd, int dirfd, int hold_fds_num, const int *hold_fds) { + int i; + + if (check_fd < 3 || check_fd == dirfd) { + return 0; + } + + for (i = 0; i < hold_fds_num; i++) { + if (check_fd == *(hold_fds + i)) { + return 0; + } + } + + return 1; +} + +static inline int cl_process_pf_fname2fd(const char *s) { + int result = 0; + for (; *s != '\0'; s++) { + result = result * 10 + (*s - '0'); + } + return result; +} + +static inline int cl_process_pf_cleanup_fds(const CL_ProcessAttrInternal_t *ia) { + int ret = -1; + int dfd; +#define BUF_SIZE 128 + char buf[BUF_SIZE]; + struct linux_dirent64 *d; + int hold_fds_num, i; + + for (i = 0; i < CL_PROCESSS_ATTR_HOLD_FDS_NUM; i++) { + if (ia->hold_fds[i] == 0) { + break; + } + } + hold_fds_num = i; + + if ((dfd = open("/proc/self/fd/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function open + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + cl_process_pf_err('8');// LCOV_EXCL_LINE 5: fail safe for glibc function open + goto exit;// LCOV_EXCL_LINE 5: fail safe for glibc function open + } + + while (1) { + long nread; + int fd; + int bpos; + + nread = syscall(SYS_getdents64, dfd, buf, sizeof(buf)); + if (nread == -1) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function syscall + // LCOV_EXCL_START 5: fail safe for glibc function syscall + + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + cl_process_pf_err('9'); + close(dfd); + goto exit; + } + // LCOV_EXCL_STOP + + if (nread == 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function syscall + break; + } + + for (bpos = 0; bpos < nread;) { + d = (struct linux_dirent64 *)(buf + bpos); + fd = cl_process_pf_fname2fd(d->d_name); + if (cl_process_pf_check_close_fd(fd, dfd, hold_fds_num, ia->hold_fds)) { + close(fd); + } + bpos += d->d_reclen; + } + } + close(dfd); + ret = 0; + +exit: + return ret; +} + +static void cl_process_post_fork(const char *file, char *const argv[], char *const envp[], + const CL_ProcessAttrInternal_t *ia) { + int intfy_fd; + char intfy_fname[32] = "/tmp/intfy_00000"; + pid_t pid = getpid(); + + intfy_fname[11] = (char)(intfy_fname[11] + (pid / 10000)); + intfy_fname[12] = (char)(intfy_fname[12] + ((pid / 1000) % 10)); + intfy_fname[13] = (char)(intfy_fname[13] + ((pid / 100) % 10)); + intfy_fname[14] = (char)(intfy_fname[14] + ((pid / 10) % 10)); + intfy_fname[15] = (char)(intfy_fname[15] + (pid % 10)); + + unlink(intfy_fname); + if ((intfy_fd = open(intfy_fname, O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function open + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + cl_process_pf_err('0'); // LCOV_EXCL_LINE 5:libc fail safe for open + goto exit; // LCOV_EXCL_LINE 5:libc fail safe for open + } + if (close(intfy_fd) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function close + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + cl_process_pf_err('1'); // LCOV_EXCL_LINE 5:libc fail safe for close + goto exit; // LCOV_EXCL_LINE 5:libc fail safe for close + } + + // Process group + if (ia->create_group) { + if (setpgid(getpid(), getpid()) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function setpgid + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + cl_process_pf_err('2'); // LCOV_EXCL_LINE 5: fail safe for glibc function setpgid + goto exit; // LCOV_EXCL_LINE 5: fail safe for glibc function setpgid + } + } + + // Scheduling Policy/Priority + if (cl_process_pf_set_schedule(ia) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function sched_setscheduler + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + goto exit; // LCOV_EXCL_LINE 5: fail safe for glibc function sched_setscheduler + } + + // UID/GID + if (ia->gid) { + if (setgid(ia->gid) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function setgid + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + cl_process_pf_err('5'); // LCOV_EXCL_LINE 5: fail safe for glibc function setgid + goto exit; // LCOV_EXCL_LINE 5: fail safe for glibc function setgid + } + } + + if (ia->uid) { + uid_t uid = (ia->uid == UINT_MAX) ? 0 : ia->uid; + if (setuid(uid) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function setuid + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + cl_process_pf_err('6');// LCOV_EXCL_LINE 5: fail safe for glibc function setuid + goto exit;// LCOV_EXCL_LINE 5: fail safe for glibc function setuid + } + } + + // stack size + if (ia->stack_size) { + struct rlimit rlim; + getrlimit(RLIMIT_STACK, &rlim); + rlim.rlim_cur = (rlim_t)ia->stack_size; + if (setrlimit(RLIMIT_STACK, &rlim) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function setrlimit + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + cl_process_pf_err('7');// LCOV_EXCL_LINE 5: fail safe for glibc function setrlimit + } + } + + // cleanup fds + if (ia->disable_close_fds == 0) { + if (cl_process_pf_cleanup_fds(ia) < 0) { + goto exit; + } + } + + // exec + if (execve(file, argv, envp) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function execve + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + cl_process_pf_err('A');// LCOV_EXCL_LINE 5: fail safe for glibc function execve + } + +exit: + return; +} +/* + * end of the functions to be executed from fork to exec + */ + +static int cl_process_pre_fork(char *const envp[], char ***new_env, const CL_ProcessAttrInternal_t *ia) { + int i; + size_t envlen = 0; + char **e; + char *b; + int new_env_count; + size_t name_env_len = strlen(CL_PROCESS_NAME_ENV); + int last_name_env_pos = -1; + + if (envp == NULL) { + e = environ; + } else { + e = (char **)envp; + } + + for (i = 0; e[i] != NULL; i++) { + if (strncmp(e[i], CL_PROCESS_NAME_ENV, name_env_len) == 0) { + last_name_env_pos = i; + } else { + envlen += strlen(e[i]) + 1; + } + } + + if (ia->name[0] != 0) { + envlen += strlen(CL_PROCESS_NAME_ENV) + 1 + strlen(ia->name) + 1; + i++; + } + + if ((b = malloc(envlen)) == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + CL_ERR_PRINT("malloc fail"); // LCOV_EXCL_LINE 5: fail safe for libc malloc + errno = EFAULT; // LCOV_EXCL_LINE 5: fail safe for libc malloc + goto error; // LCOV_EXCL_LINE 5: fail safe for libc malloc + } + memset(b, 0, envlen); + + if ((*new_env = malloc(sizeof(char *) * (size_t)(i + 1))) == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc malloc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + free(b); // LCOV_EXCL_LINE 5: fail safe for libc malloc + CL_ERR_PRINT("malloc fail"); // LCOV_EXCL_LINE 5: fail safe for libc malloc + errno = EFAULT; // LCOV_EXCL_LINE 5: fail safe for libc malloc + goto error; // LCOV_EXCL_LINE 5: fail safe for libc malloc + } + memset(*new_env, 0, sizeof(char *) * (size_t)(i + 1)); + + new_env_count = 0; + for (i = 0; e[i] != NULL; i++) { + if (i == last_name_env_pos) { + continue; + } + strcpy(b, e[i]); + *(*new_env + i) = b; + b += strlen(e[i]) + 1; + new_env_count++; + } + + if (ia->name[0] != 0) { + *(*new_env + new_env_count) = b; + b += sprintf(b, "%s=%s", CL_PROCESS_NAME_ENV, ia->name); + b++; + new_env_count++; + } + + return 0; + +error: + return -1; +} + +/* + * Create process + */ +int CL_ProcessCreate(const char *file, char *const argv[], char *const envp[], const CL_ProcessAttr_t *attr) { + const CL_ProcessAttrInternal_t *ia = (const CL_ProcessAttrInternal_t *)attr; + pid_t childpid; + char intfy_fname[32]; + char **new_env; + int ret; + int retry; + int i = 0; + + if (file == NULL || argv == NULL || attr == NULL) { + errno = EINVAL; + return -1; + } + + if (cl_process_pre_fork(envp, &new_env, ia) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc function malloc + return -1; + } + + childpid = fork(); + switch (childpid) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function fork + case 0: /* child process */ + cl_process_post_fork(file, argv, new_env, ia); + _exit(CL_PROCESS_EXIT_INTERNAL); + break; + + case -1: /* error */ + goto exit; + + default: /* parent process */ + break; + } + + if (ia->cpu_assign != 0) { + cpu_set_t set; + CPU_ZERO(&set); + for (i = 0; i < (sizeof(ia->cpu_assign) * 8); i++) { + if ((ia->cpu_assign >> i) & 0x1) { + CPU_SET(i, &set); + } + } + // LCOV_EXCL_BR_START 5: fail safe for libc sched_setaffinity + if (sched_setaffinity(childpid, sizeof(set), &set) < 0) { + // LCOV_EXCL_BR_STOP + int last_errno = errno; + CL_PERROR("sched_setaffinity"); + errno = last_errno; + childpid = -1; + goto exit; + } + } + + ret = snprintf(intfy_fname, sizeof(intfy_fname), CL_INTFY_FILENAME_FORMAT, childpid); + if (ret < 0 || ret > sizeof(intfy_fname)) {// LCOV_EXCL_BR_LINE 5: fail safe for libc snprintf + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + CL_ERR_PRINT("snprintf fail:%d", ret); // LCOV_EXCL_LINE 5: fail safe for libc snprintf + childpid = -1; // LCOV_EXCL_LINE 5: fail safe for libc snprintf + goto exit; // LCOV_EXCL_LINE 5: fail safe for libc snprintf + } + + if (ia->cgroup_name[0] != '\0') { + if (cl_cgroup_exist(CL_CGROUP_CPU, ia->cgroup_name) == 0) { + if (cl_cgroup_set_num(CL_CGROUP_CPU, ia->cgroup_name, "tasks", childpid) < 0) { + int last_errno = errno; + CL_PERROR("cl_cgroup_set_num"); + errno = last_errno; + childpid = -1; + goto exit; + } + } + // LCOV_EXCL_BR_START 5: fail safe for glibc function access + if (cl_cgroup_exist(CL_CGROUP_MEMORY, ia->cgroup_name) == 0) { + // LCOV_EXCL_BR_STOP + if (cl_cgroup_set_num(CL_CGROUP_MEMORY, ia->cgroup_name, "tasks", childpid) < 0) { + int last_errno = errno; + CL_PERROR("cl_cgroup_set_num"); + errno = last_errno; + childpid = -1; + goto exit; + } + } + } + + for (retry = 100; retry >= 0; retry--) { + if (access(intfy_fname, F_OK) == 0) { // LCOV_EXCL_BR_LINE 5: fail safe for glibc function access + break; + } + usleep(100); + } + +exit: + free(new_env[0]); + free(new_env); + + return childpid; +} + +/* + * Initialize process attribute + */ +int CL_ProcessCreateAttrInit(CL_ProcessAttr_t *attr) { + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + memset(attr, 0, sizeof(CL_ProcessAttr_t)); + return 0; +} + +/* + * Set process attribute (process name) + */ +int CL_ProcessCreateAttrSetName(CL_ProcessAttr_t *attr, const char *name) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL || name == NULL) { + errno = EINVAL; + return -1; + } + + strncpy(ia->name, name, 16); + ia->name[15] = 0; + + return 0; +} + +/* + * Set process attribute (user ID) + */ +int CL_ProcessCreateAttrSetUid(CL_ProcessAttr_t *attr, uid_t uid) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + ia->uid = uid; + + return 0; +} + +/* + * Set process attribute (group ID) + */ +int CL_ProcessCreateAttrSetGid(CL_ProcessAttr_t *attr, gid_t gid) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + ia->gid = gid; + + return 0; +} + +/* + * Set process attribute (schedule policy and priority) + */ +int CL_ProcessCreateAttrSetSchedule(CL_ProcessAttr_t *attr, CL_ProcessSchedPolicy_t policy, int priority) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + if (policy == CL_PROCESS_SCHED_POLICY_RR || policy == CL_PROCESS_SCHED_POLICY_FIFO) { + if (priority < 1 || priority > 99) { + errno = EINVAL; + return -1; + } + } else if (policy == CL_PROCESS_SCHED_POLICY_OTHER) { + if (priority < -20 || priority > 19) { + CL_ERR_PRINT("Invlid SCHED_OTHER priority:%d", priority); + } + } else { + errno = EINVAL; + return -1; + } + + ia->sched_policy = policy; + ia->sched_priority = priority; + + return 0; +} + +/* + * Set process attribute (process group) + */ +int CL_ProcessCreateAttrSetGroup(CL_ProcessAttr_t *attr, int create) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + if (create < 0 || create > 1) { + errno = EINVAL; + return -1; + } + + ia->create_group = create; + + return 0; +} + +static int32_t CL_CpuAssignMsbCpu(int cpu_assign) { + int32_t i; + int32_t ret = 0; + + for (i = 0; i < (sizeof(cpu_assign) * 8); i++) { + if ((cpu_assign >> i) & 0x1) + ret = i; + } + + return ret; +} + +/** + * Set process attribute (CPU Assign) + */ +int CL_ProcessCreateAttrSetCpuAssign(CL_ProcessAttr_t *attr, int cpu_assign) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + if (cpu_assign < 0) { + errno = EINVAL; + return -1; + } + + if (CL_CpuAssignMsbCpu(cpu_assign) >= sysconf(_SC_NPROCESSORS_CONF)) { + errno = EINVAL; + return -1; + } + + ia->cpu_assign = cpu_assign; + + return 0; +} + +/* + * Set process attribute (Stack Size) + */ +int CL_ProcessCreateAttrSetStackSize(CL_ProcessAttr_t *attr, int stack_size) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + ia->stack_size = stack_size; + + return 0; +} + +/* + * Set process attribute (FD to maintain) + */ +int CL_ProcessCreateAttrSetHoldFds(CL_ProcessAttr_t *attr, int hold_fds[]) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL || hold_fds == NULL) { + errno = EINVAL; + return -1; + } + + memcpy(ia->hold_fds, hold_fds, sizeof(ia->hold_fds)); + + return 0; +} + +/* + * Set process attribute (to suspend forced FD close) + */ +int CL_ProcessCreateAttrSetDisableCloseFds(CL_ProcessAttr_t *attr) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + ia->disable_close_fds = 1; + + return 0; +} + +/* + * Set process attribute (Cgroup) + */ +int CL_ProcessCreateAttrSetCgroup(CL_ProcessAttr_t *attr, const char *cgroup_name) { + CL_ProcessAttrInternal_t *ia = (CL_ProcessAttrInternal_t *)attr; + + if (attr == NULL || cgroup_name == NULL) { + errno = EINVAL; + return -1; + } + + if (strlen(cgroup_name) >= sizeof(ia->cgroup_name)) { + errno = EINVAL; + return -1; + } + + strcpy(ia->cgroup_name, cgroup_name); + + return 0; +} + +/* + * Kill process + */ +int CL_ProcessTerminate(pid_t pid) { + return kill(pid, SIGKILL); +} + +/* + * Kill process group + */ +int CL_ProcessTerminateGroup(pid_t pid) { + return killpg(pid, SIGKILL); +} + +/** + * Forced process termination + */ +int CL_ProcessAbort(pid_t pid) { + return kill(pid, SIGABRT); +} + +/** + * Forced process group termination + */ +int CL_ProcessAbortGroup(pid_t pid) { + return killpg(pid, SIGABRT); +} + +/** + * Euthanize process group + */ +int CL_ProcessEuthanizeGroup(pid_t pid) { + usleep(10 * 1000); + return killpg(pid, SIGKILL); +} + +/* + * Collect child process + */ +int CL_ProcessCleanup(int sigchld_fd, CL_ProcessCleanupInfo_t *cleanup_info) { + struct signalfd_siginfo fdsi; + ssize_t s; + siginfo_t info; + int ret; + char intfy_fname[32]; + + if (cleanup_info == NULL) { + errno = EINVAL; + return -1; + } + + while (1) { + s = read(sigchld_fd, &fdsi, sizeof(struct signalfd_siginfo)); + if (s != sizeof(struct signalfd_siginfo)) { + if (s == -1 && errno == EINTR) { + continue; + } + break; + } + } + + info.si_pid = 0; + while (1) { + ret = waitid(P_ALL, 0, &info, WEXITED | WNOHANG); + if (ret == -1) { + return -1; + } else if (ret == 0 && info.si_pid == 0) { + errno = ECHILD; + return -1; + } + break; + } + + cleanup_info->pid = info.si_pid; + cleanup_info->code = info.si_code; + cleanup_info->status = info.si_status; + + cl_monitor_cleanup(info.si_pid); + + ret = snprintf(intfy_fname, sizeof(intfy_fname), CL_INTFY_FILENAME_FORMAT, info.si_pid); + + if (ret < 0 || ret > sizeof(intfy_fname)) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function snprintf + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + CL_ERR_PRINT("snprintf fail:%d\n", ret);// LCOV_EXCL_LINE 5: fail safe for glibc function snprintf + goto exit;// LCOV_EXCL_LINE 5: fail safe for glibc function snprintf + } + if (unlink(intfy_fname) < 0) { + CL_PERROR("unlink"); + } + +exit: + info.si_pid = 0; + ret = waitid(P_ALL, 0, &info, WEXITED | WNOWAIT | WNOHANG); + if (ret == 0 && info.si_pid != 0) { + return 1; + } else if (ret == -1 && errno != ECHILD) { + return -1; + } + return 0; +} + +typedef struct { + sem_t sem; + CL_ThreadAttrInternal_t *ia; + void *(*start_routine)(void *); + void *start_arg; +} create_thread_arg_t; + +static void *thread_start_func(void *arg) { + create_thread_arg_t *p = (create_thread_arg_t *)arg; + void *(*start_routine)(void *) = p->start_routine; + void *start_arg = p->start_arg; + + prctl(PR_SET_NAME, p->ia->name != 0 ? p->ia->name : NULL); + + if (sem_post(&p->sem) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for libc sem_post + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + CL_PERROR("sem_post"); // LCOV_EXCL_LINE 5: fail safe for libc sem_post + } + + return (*start_routine)(start_arg); +} + +/* + * Create thread + */ +int CL_ThreadCreate(pthread_t *thread, pthread_attr_t *attr, CL_ThreadAttr_t *cl_attr, void *(*start_routine)(void *), + void *arg) { + int ret; + create_thread_arg_t cr_arg = { .ia = (CL_ThreadAttrInternal_t *)cl_attr, + .start_routine = start_routine, + .start_arg = arg + }; + + if (thread == NULL || cl_attr == NULL || start_routine == NULL) { + errno = EINVAL; + return -1; + } + + if (sem_init(&cr_arg.sem, 0, 0) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc sem_init + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5: fail safe for libc sem_init + } + + // LCOV_EXCL_BR_START 5: fail safe for libc pthread_create + if ((ret = pthread_create(thread, attr, thread_start_func, &cr_arg)) != 0) { + // LCOV_EXCL_BR_STOP + + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + errno = ret; // LCOV_EXCL_LINE 5: fail safe for libc pthread_create + return -1; // LCOV_EXCL_LINE 5: fail safe for libc pthread_create + } + + if (sem_wait(&cr_arg.sem) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for libc sem_wait + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1; // LCOV_EXCL_LINE 5: fail safe for libc sem_wait + } + + return 0; +} + +/* + * Initialize CommonLibrary extension thread attribute + */ +int CL_ThreadCreateAttrInit(CL_ThreadAttr_t *attr) { + assert_static(sizeof(CL_ThreadAttr_t) == sizeof(CL_ThreadAttrInternal_t)); + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + memset(attr, 0, sizeof(CL_ThreadAttr_t)); + return 0; +} + +/* + * Set CommonLibrary extension thread attribute (thread name) + */ +int CL_ThreadCreateAttrSetName(CL_ThreadAttr_t *attr, const char *name) { + CL_ThreadAttrInternal_t *ia = (CL_ThreadAttrInternal_t *)attr; + + if (attr == NULL || name == NULL) { + errno = EINVAL; + return -1; + } + + strncpy(ia->name, name, 16); + ia->name[15] = 0; + + return 0; +} + +/* + * Create Cgroup + */ +int CL_ProcessCreateCgroupCreate(const char *cgroup_name, CL_ProcessCreateCgroupAttr_t *attr) { + CL_ProcessCreateCgroupAttrInternal_t *ia = (CL_ProcessCreateCgroupAttrInternal_t *)attr; + CL_ProcessCreateCgroupAttrInternal_t cmp; + + if (cgroup_name == NULL || attr == NULL) { + errno = EINVAL; + return -1; + } + + memset(&cmp, 0, sizeof(CL_ProcessCreateCgroupAttrInternal_t)); + if (memcmp(ia, &cmp, sizeof(CL_ProcessCreateCgroupAttrInternal_t)) == 0) { + errno = EINVAL; + return -1; + } + + if (ia->rt_runtime_us || ia->cfs_quota_us || ia->cpu_shares) { + // LCOV_EXCL_BR_START 5: fail safe for glibc function mkdir + if (cl_cgroup_make(CL_CGROUP_CPU, cgroup_name) < 0) { + // LCOV_EXCL_BR_STOP + return -1; + } + + if (ia->rt_runtime_us) { + // LCOV_EXCL_BR_START 5: fail safe for glibc function write + if (cl_cgroup_set_num(CL_CGROUP_CPU, cgroup_name, "cpu.rt_runtime_us", ia->rt_runtime_us) < 0) { + // LCOV_EXCL_BR_STOP + + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1;// LCOV_EXCL_LINE 5: fail safe for glibc function write + } + } + + if (ia->cfs_quota_us) { + // LCOV_EXCL_BR_START 5: fail safe for glibc function write + if (cl_cgroup_set_num(CL_CGROUP_CPU, cgroup_name, "cpu.cfs_quota_us", ia->cfs_quota_us) < 0) { + // LCOV_EXCL_BR_STOP + + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1;// LCOV_EXCL_LINE 5: fail safe for glibc function write + } + } + + if (ia->cpu_shares) { + // LCOV_EXCL_BR_START 5: fail safe for glibc function write + if (cl_cgroup_set_num(CL_CGROUP_CPU, cgroup_name, "cpu.shares", ia->cpu_shares) < 0) { + // LCOV_EXCL_BR_STOP + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return -1;// LCOV_EXCL_LINE 5: fail safe for glibc function write + } + } + } + + if (ia->memory_limit || ia->usage_in_bytes || ia->event_fd) { + // LCOV_EXCL_BR_START 5: fail safe for glibc function mkdir + if (cl_cgroup_make(CL_CGROUP_MEMORY, cgroup_name) < 0) { + // LCOV_EXCL_BR_STOP + return -1; + } + + if (ia->memory_limit) { + // LCOV_EXCL_BR_START 5: fail safe for glibc function write + if (cl_cgroup_set_num(CL_CGROUP_MEMORY, cgroup_name, "memory.limit_in_bytes", ia->memory_limit) < 0) { + // LCOV_EXCL_BR_STOP + return -1; + } + + if (cl_cgroup_set_num(CL_CGROUP_MEMORY, cgroup_name, "memory.memsw.limit_in_bytes", ia->memory_limit) < 0) { + return -1; + } + + } + + if (ia->usage_in_bytes || ia->event_fd) { + int checkfd; + char setstr[64]; + + // LCOV_EXCL_BR_START 5: fail safe for glibc function open + if ( (checkfd = cl_cgroup_open(CL_CGROUP_MEMORY, cgroup_name, "memory.memsw.usage_in_bytes", O_RDONLY)) < 0) { + // LCOV_EXCL_BR_STOP + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return -1; // LCOV_EXCL_LINE 5: fail safe for glibc function open + } + + snprintf(setstr, sizeof(setstr), "%d %d %d", ia->event_fd, checkfd, ia->usage_in_bytes); + // LCOV_EXCL_BR_START 5: fail safe for glibc function write + if (cl_cgroup_set_string(CL_CGROUP_MEMORY, cgroup_name, "cgroup.event_control", setstr) < 0) { + // LCOV_EXCL_BR_STOP + + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + + return -1;// LCOV_EXCL_LINE 5: fail safe for glibc function write + } + } + } + + return 0; +} + +/* + * Initialize Cgroup attribute + */ +int CL_ProcessCreateCgroupAttrInit(CL_ProcessCreateCgroupAttr_t *attr) { + assert_static(sizeof(CL_ProcessCreateCgroupAttr_t) == sizeof(CL_ProcessCreateCgroupAttrInternal_t)); + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + memset(attr, 0, sizeof(CL_ProcessCreateCgroupAttr_t)); + return 0; +} + +/* + * Set Cgroup attribute (RT Throttling) + */ +int CL_ProcessCreateCgroupAttrSetRtThrottling(CL_ProcessCreateCgroupAttr_t *attr, int runtime_us) { + CL_ProcessCreateCgroupAttrInternal_t *ia = (CL_ProcessCreateCgroupAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + ia->rt_runtime_us = runtime_us; + + return 0; +} + +/* + * Set Cgroup attribute (CFS Bandwidth Control) + */ +int CL_ProcessCreateCgroupAttrSetCfsBandwidthControl(CL_ProcessCreateCgroupAttr_t *attr, int cfs_quota_us) { + CL_ProcessCreateCgroupAttrInternal_t *ia = (CL_ProcessCreateCgroupAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + ia->cfs_quota_us = cfs_quota_us; + + return 0; +} + +/* + * Set Cgroup attribute (CPU Shares) + */ +int CL_ProcessCreateCgroupAttrSetCpuShares(CL_ProcessCreateCgroupAttr_t *attr, int cpu_shares) { + CL_ProcessCreateCgroupAttrInternal_t *ia = (CL_ProcessCreateCgroupAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + ia->cpu_shares = cpu_shares; + + return 0; +} + +/* + * Set Cgroup attribute (Memory Limit) + */ +int CL_ProcessCreateCgroupAttrSetMemoryLimit(CL_ProcessCreateCgroupAttr_t *attr, int memory_limit) { + CL_ProcessCreateCgroupAttrInternal_t *ia = (CL_ProcessCreateCgroupAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + ia->memory_limit = memory_limit; + + return 0; +} + +/* + * Set Cgroup attribute (Memory Usage Notification) + */ +int CL_ProcessCreateCgroupAttrSetMemoryUsageNotification(CL_ProcessCreateCgroupAttr_t *attr, int usage_in_bytes, + int event_fd) { + CL_ProcessCreateCgroupAttrInternal_t *ia = (CL_ProcessCreateCgroupAttrInternal_t *)attr; + + if (attr == NULL) { + errno = EINVAL; + return -1; + } + + if (event_fd < 0) { + errno = EINVAL; + return -1; + } + + ia->usage_in_bytes = usage_in_bytes; + ia->event_fd = event_fd; + + return 0; +} + +/* + * Delete Cgroup + */ +int CL_ProcessCreateCgroupDelete(const char *cgroup_name) { + int mem_ret = 1, mem_err; + int cpu_ret = 1, cpu_err; + + if (cgroup_name == NULL) { + errno = EINVAL; + return -1; + } + // LCOV_EXCL_BR_START 5: fail safe for glibc function access + if (cl_cgroup_exist(CL_CGROUP_MEMORY, cgroup_name) == 0) { + // LCOV_EXCL_BR_STOP + mem_ret = cl_cgroup_remove(CL_CGROUP_MEMORY, cgroup_name); + mem_err = errno; + } + + // LCOV_EXCL_BR_START 5: fail safe for glibc function access + if (cl_cgroup_exist(CL_CGROUP_CPU, cgroup_name) == 0) { + // LCOV_EXCL_BR_STOP + cpu_ret = cl_cgroup_remove(CL_CGROUP_CPU, cgroup_name); + cpu_err = errno; + } + + if (mem_ret == 1 && cpu_ret == 1) { + errno = ENOENT; + return -1; + } else if (mem_ret < 0) { + errno = mem_err; + return -1; + } else if (cpu_ret < 0) { + errno = cpu_err; + return -1; + } + + return 0; +} + +/* + * Move process to the Cgroup + */ +int CL_ProcessCreateCgroupClassify(const char *cgroup_name, pid_t pid) { + int mem_ret = 1, mem_err; + int cpu_ret = 1, cpu_err; + + if (cgroup_name == NULL) { + errno = EINVAL; + return -1; + } + + if (cl_cgroup_exist(CL_CGROUP_MEMORY, cgroup_name) == 0) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function access + mem_ret = cl_cgroup_set_num(CL_CGROUP_MEMORY, cgroup_name, "tasks", pid); + mem_err = errno; + } + + if (cl_cgroup_exist(CL_CGROUP_CPU, cgroup_name) == 0) {// LCOV_EXCL_BR_LINE 5: fail safe for glibc function access + cpu_ret = cl_cgroup_set_num(CL_CGROUP_CPU, cgroup_name, "tasks", pid); + cpu_err = errno; + } + + if (mem_ret == 1 && cpu_ret == 1) { + errno = ENOENT; + return -1; + } else if (mem_ret < 0) { + errno = mem_err; + return -1; + } else if (cpu_ret < 0) { + errno = cpu_err; + return -1; + } + + return 0; +} + +/* vim:set ts=8 sts=2 sw=2: */ diff --git a/nsframework/common_library/client/src/cl_region.c b/nsframework/common_library/client/src/cl_region.c new file mode 100644 index 00000000..0f6d470c --- /dev/null +++ b/nsframework/common_library/client/src/cl_region.c @@ -0,0 +1,295 @@ +/* + * @copyright Copyright (c) 2016-2020 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file cl_region.c + * @brief region manage + * + */ + +#include <stdio.h> +#include <sys/mman.h> +#include <native_service/cl_region.h> +#include "cl_error.h" + +#define cl_align_ptr(p, a) \ + (uint8_t *) (((long)(p) + ((long)a - 1)) & ~((long)a - 1)) + +static void *cl_region_anonmmap(size_t size); +static void *cl_region_expand(cl_region_t *region, size_t size, size_t align_size); +static void *cl_region_alloc_large(cl_region_t *region, size_t size); + +cl_region_t * +CL_RegionCreate(size_t size) { + cl_region_t *r; + long pagesize; + + pagesize = sysconf(_SC_PAGE_SIZE); + size = ((size + (size_t)pagesize - 1) / (size_t)pagesize) * (size_t)pagesize; + + r = cl_region_anonmmap(size); + if (r == NULL) { + return NULL; + } + + r->d.last = (uint8_t *) r + sizeof(cl_region_t); + r->d.end = (uint8_t *) r + size; + r->d.next = NULL; + r->d.failed = 0; + + size = size - sizeof(cl_region_t); + r->max = (size < (pagesize - 1)) ? size : (size_t)(pagesize - 1); + + r->current = r; + r->large = NULL; + r->cleanup = NULL; + + return r; +} + + +void +CL_RegionDestroy(cl_region_t *region) { + cl_region_t *r, *n; + cl_region_large_t *l; + cl_region_cleanup_t *c; + + for (c = region->cleanup; c; c = c->next) { + if (c->handler) { + CL_DBG_PRINT("run cleanup: %p\n", c->handler); + c->handler(c->data); + } + } + + for (l = region->large; l; l = l->next) { + + CL_DBG_PRINT("free: %p\n", l->alloc); + + if (l->alloc) { // LCOV_EXCL_BR_LINE 6: double check, mmap in cl_monitor.c + if (munmap(l->alloc, l->size) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc munmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + CL_PERROR("munmap"); // LCOV_EXCL_LINE 5: fail safe for libc munmap + } + } + } + + for (r = region, n = region->d.next; /* void */; r = n, n = n->d.next) { + if (munmap(r, (size_t)(r->d.end - (uint8_t *)r)) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for libc munmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + CL_PERROR("munmap"); // LCOV_EXCL_LINE 5: fail safe for libc munmap + } + + if (n == NULL) { + break; + } + } +} + + +void * +cl_region_alloc(cl_region_t *region, size_t size, size_t align_size) { + uint8_t *m; + volatile uint8_t *old_last; + cl_region_t *r; + + if (size <= region->max) { + + r = region->current; + + do { + retry: + old_last = r->d.last; + m = cl_align_ptr(old_last, align_size); + if ((size_t)(r->d.end - m) >= size) { + if (false == __sync_bool_compare_and_swap(&r->d.last, old_last, m + size)) { + goto retry; + } + return m; + } + + r = r->d.next; + + } while (r); + + return cl_region_expand(region, size, align_size); + } + + return cl_region_alloc_large(region, size); +} + + +static void * +cl_region_anonmmap(size_t size) { + uint8_t *p; + + p = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) { /* pgr0203 */ + CL_PERROR("mmap"); + return NULL; + } + + return p; +} + + +static void * +cl_region_expand(cl_region_t *region, size_t size, size_t align_size) { + uint8_t *m; + size_t psize; + cl_region_t *r, *new, *current; + + psize = (size_t)(region->d.end - (uint8_t *)region); + + m = cl_region_anonmmap(psize); + if (m == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc mmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return NULL;// LCOV_EXCL_LINE 5: fail safe for libc mmap + } + + new = (cl_region_t *)m; + + new->d.end = m + psize; + new->d.next = NULL; + new->d.failed = 0; + + m += sizeof(cl_region_data_t); + m = cl_align_ptr(m, align_size); + new->d.last = m + size; + + current = region->current; + + for (r = current; r->d.next; r = r->d.next) { /* pgr0689 */ + if (r->d.failed++ > 4) { + current = r->d.next; + } + } + + while (false == __sync_bool_compare_and_swap(&r->d.next, NULL, new)) { + for (r = r->d.next; r->d.next; r = r->d.next) {} /* pgr0689 */ + } + + region->current = current ? current : new; + + return m; +} + + +static void * +cl_region_alloc_large(cl_region_t *region, size_t size) { + void *p; + int n; + long pagesize; + cl_region_large_t *large; + + pagesize = sysconf(_SC_PAGE_SIZE); + size = ((size + (size_t)pagesize - 1) / (size_t)pagesize) * (size_t)pagesize; + + p = cl_region_anonmmap(size); + + if (p == NULL) { // LCOV_EXCL_BR_LINE 5: fail safe for libc mmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return NULL;// LCOV_EXCL_LINE 5: fail safe for libc mmap + } + + n = 0; + + for (large = region->large; large; large = large->next) { + if (large->alloc == NULL) { + if (false == __sync_bool_compare_and_swap(&large->alloc, NULL, p)) { + break; + } + large->size = size; + return p; + } + + if (n++ > 3) { + break; + } + } + + large = CL_RegionAlloc(region, cl_region_large_t, 1); + if (large == NULL) { // LCOV_EXCL_START 5: fail safe for libc mmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + if (munmap(p, size) < 0) { + CL_PERROR("munmap"); + } + return NULL; + } // LCOV_EXCL_STOP + + large->alloc = p; + large->size = size; + large->next = region->large; + while (false == __sync_bool_compare_and_swap(®ion->large, large->next, large)) { // LCOV_EXCL_BR_LINE 100: race condition // NOLINT (readability/nolint) + // LCOV_EXCL_START 100: race condition + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + large->next = region->large; + } // LCOV_EXCL_STOP + + return p; +} + + +bool +CL_RegionFree(cl_region_t *region, void *p) { + cl_region_large_t *l; + + for (l = region->large; l; l = l->next) { + if (p == l->alloc) { + CL_DBG_PRINT("free: %p\n", l->alloc); + if (munmap(l->alloc, l->size) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for libc munmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + CL_PERROR("munmap");// LCOV_EXCL_LINE 5: fail safe for libc munmap + } + l->alloc = NULL; + return true; + } + } + + return false; +} + + +cl_region_cleanup_t * +cl_region_cleanup_add(cl_region_t *region, size_t size, size_t align_size) { + cl_region_cleanup_t *c; + + c = CL_RegionAlloc(region, cl_region_cleanup_t, 1); + if (c == NULL) { // LCOV_EXCL_START 5: fail safe for libc mmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return NULL; + } // LCOV_EXCL_STOP + + if (size) { + c->data = cl_region_alloc(region, size, align_size); + if (c->data == NULL) { // LCOV_EXCL_START 5: fail safe for libc mmap + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + return NULL; + } // LCOV_EXCL_STOP + } else { + c->data = NULL; + } + + c->handler = NULL; + c->next = region->cleanup; + + region->cleanup = c; + + CL_DBG_PRINT("add cleanup: %p\n", c); + + return c; +} + +/* vim:set ts=8 sw=2 sts=2: */ diff --git a/nsframework/common_library/client/src/cl_sem.c b/nsframework/common_library/client/src/cl_sem.c new file mode 100644 index 00000000..911daff4 --- /dev/null +++ b/nsframework/common_library/client/src/cl_sem.c @@ -0,0 +1,67 @@ +/* + * @copyright Copyright (c) 2016-2020 TOYOTA MOTOR CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <semaphore.h> +#include <time.h> + +#include <native_service/cl_sem.h> +#include "cl_error.h" + +#define CL_MILLI_TO_NANO(time) ((time) * 1000000U) +#define CL_SEC_TO_NANO(time) ((time) * 1000000000U) +#define CL_NANO_TO_SEC(time) ((time) / 1000000000U) + +static void cl_TimeoutCalc(struct timespec *ts, unsigned int timeout) { + unsigned long long nsec; + + clock_gettime(CLOCK_REALTIME, ts); + nsec = (unsigned long long)ts->tv_nsec + (CL_MILLI_TO_NANO((unsigned long long)timeout)); + ts->tv_sec = ts->tv_sec + (time_t)CL_NANO_TO_SEC(nsec); + ts->tv_nsec = (__syscall_slong_t)(nsec - CL_SEC_TO_NANO((unsigned long long)CL_NANO_TO_SEC(nsec))); +} + +int CL_SemWait(sem_t *semid, unsigned int timeout) { + struct timespec ts, tmp; + int ret; + + while (1) { + cl_TimeoutCalc(&ts, timeout); + if ((ret = sem_timedwait(semid, &ts)) != 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc + // LCOV_EXCL_START 5: fail safe for libc + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + if (errno == ETIMEDOUT) { + /* Check the time considering the case where the time has been changed by clock_settime/settimeofday */ + /* If the current time is much larger than the timeout specified time, + the system assumes that the time has changed and re-executes the command. + (A threshold of 100 is appropriate) */ + clock_gettime(CLOCK_REALTIME, &tmp); + if (difftime(tmp.tv_sec, ts.tv_sec) > 100.0) { + CL_DBG_PRINT("detect clock changed\n"); + continue; + } + } + } + // LCOV_EXCL_STOP 5: fail safe for libc + break; + AGL_ASSERT_NOT_TESTED(); // LCOV_EXCL_LINE 200: test assert + } // LCOV_EXCL_LINE 10: end line + + return ret; +} + |