1
1
Files
zyxel-vmg8825_b50b-cfw/package/public-zyxel/libzos/libzos-1.0/zos_api.c
T
2026-04-17 18:33:03 +02:00

960 lines
21 KiB
C
Executable File

/*!
* @file zos_api.c
* OS porting layer
*
* @author CP Wang
* @date 2017-12-15 19:26:21
* @copyright Copyright 2017 Zyxel Communications Corp. All Rights Reserved.
*/
//==============================================================================
#include <stdio.h>
#include <stdlib.h> // system()
#include <stdarg.h> // va_list()
#include <sys/types.h>
#include <sys/wait.h> // waitpid()
#include <string.h> // strchr()
#include <errno.h>
#include <time.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
#include <utime.h>
#include <sys/time.h>
#include <fcntl.h>
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h> // for syscall(), fork(), execvp(), _exit()
#include <sys/syscall.h> // for syscall()
#include "zos_api.h"
#include "zlog_api.h"
//==============================================================================
#define ZOS_CMD_MAX_LEN 1024
#ifdef ZCFG_MEMORY_LEAK_DETECT
#include <pthread.h>
#include <json/json.h>
struct json_object *ZcfgAllocLineRecordJobj = NULL;
struct json_object *ZcfgAllocAddrRecordJobj = NULL;
pthread_mutex_t recordDataMutex = PTHREAD_MUTEX_INITIALIZER;
// remove this file data in leakage information.
#undef json_object_new_object
#undef json_object_new_string
#undef json_object_new_array
#endif
int default_umask = S_IWGRP | S_IWOTH;
//==============================================================================
/*
copy from
build_dir\target-arm_v7-a_uClibc-0.9.32_eabi\zcfg_be-2.0\libzcfg_be\libzcfg_be_common\zcfg_be_common.c
*/
/*
* zcfg_system():
* Used to prevent command injection
* usage likes system(), but does not support sepcial characters except & "
* & must be seperate and can not be appended to the parameters
* e.g.
* Correct case : zcfg_system("ping 8.8.8.8 &") -> excute "ping 8.8.8.8" in background
* Wrong case : zcfg_system("ping 8.8.8.8&") -> 8.8.8.8& will be treate as a argement
*
* zcfg_system("ping \"8.8.8.8 4.4.4.4\"") -> 8.8.8.8 4.4.4.4 will be treate as a argement since they are enclosed in double quotation marks
* zcfg_system("ping 8.8.8.8 > /tmp/result) -> not support redirect (>)
*
* plan to support redirect (>) in future.
*/
static int _zcfg_system(const char *cmd)
{
pid_t pid, pid2;
char *argv[128] = {NULL};
char buf[ZOS_CMD_MAX_LEN+1] = {0};
int i = 0, j = 0;
char *p = NULL;
int status=0;
int background = 0;
#ifdef ABUU_CUSTOMIZATION
int preindex = 0, argvindex = 0;
#endif
strncpy(buf, cmd, ZOS_CMD_MAX_LEN);
while(i<ZOS_CMD_MAX_LEN && cmd[i]!='\0')
{
if(buf[i] != ' '){
if(buf[i] == '\"'){
argv[j++] = &buf[++i];
p = strchr(&buf[i], '\"');
if(p != NULL){
i = p - buf;
buf[i] = '\0';
}
else
return -1;
}
#ifdef ABUU_CUSTOMIZATION
else if (buf[i] == '&') {
// word & ls. word &ls
argv[j++] = "&";
buf[--i] = '\0';
break;
}
#endif
else{
argv[j++] = &buf[i];
#ifdef ABUU_CUSTOMIZATION
p = strchr(&buf[i], ';');
if (p != NULL) {
i = p - buf;
//printf("buf[i]: %c remove\n",buf[i]);
buf[i] = '\0';
}
p = strchr(&buf[i], '|');
if (p != NULL) {
i = p - buf;
//printf("buf[i]: %c remove\n",buf[i]);
buf[i] = '\0';
}
preindex = i;
argvindex = j - 1;
#endif
p = strchr(&buf[i], ' ');
if(p != NULL){
i = p - buf;
buf[i] = '\0';
}
else {
#ifdef ABUU_CUSTOMIZATION
// word&ls
p = strchr(argv[argvindex], '&');
if (p != NULL) {
i = p-argv[argvindex];
buf[preindex+i] = '\0';
}
#endif
break;
}
#ifdef ABUU_CUSTOMIZATION
// word& ls
p = strchr(argv[argvindex], '&');
if (p != NULL) {
i = p-argv[argvindex];
buf[preindex+i] = '\0';
break;
}
#endif
}
}
i++;
}
/*printf("[www] ");
for (int tmp = 0; tmp < j; tmp++) {
printf("%s ",argv[tmp]);
}
printf("\n");*/
if(*argv[j-1] == '&'){
background = 1;
argv[j-1] = NULL;
}
if((pid = fork()) < 0){
status = -1;
}
else if(pid == 0){
if(!background){
execvp(argv[0], argv);
_exit(127);
}
else{
if((pid2 = fork()) < 0){
_exit(-1);
}
else if(pid2 == 0){
execvp(argv[0], argv);
_exit(127);
}
else{
_exit(0);
}
}
}
else if(pid >0){
if(waitpid(pid, &status, 0)<0){
if (errno != EINTR) {
status = -1;
}
}
}
return status;
} // _zcfg_system
//==============================================================================
/*!
* system command
*
* @param [in] b_linux if true then use linux system() else use _zcfg_system()
* @param [in] format string format of command
* @param [in] ... arguments required by the format
*
* @return ZOS_SUCCESS - command executed successful
* ZOS_FAIL - fail to execute command
*/
bool zos_system(
bool b_linux,
const char *format,
...
)
{
char cmd[ZOS_CMD_MAX_LEN + 1] = {0};
va_list ap;
int r;
memset(cmd, 0, sizeof(cmd));
if (format == NULL)
{
ZLOG_ERROR("format == NULL");
return ZOS_FAIL;
}
va_start(ap, format);
r = vsnprintf(cmd, ZOS_CMD_MAX_LEN, format, ap);
va_end(ap);
if (r < 0 || r > ZOS_CMD_MAX_LEN)
{
ZLOG_ERROR("fail to vsnprintf()");
return ZOS_FAIL;
}
if (b_linux)
{
if (system(cmd) == -1)
{
ZLOG_ERROR("fail to system(%s)", cmd);
return ZOS_FAIL;
}
}
else
{
if (_zcfg_system(cmd) == -1)
{
ZLOG_ERROR("fail to _zcfg_system(%s)", cmd);
return ZOS_FAIL;
}
}
return ZOS_SUCCESS;
} // zos_system
/*!
* work as snprintf(), avoid the issue from man page
* "However, the standards explicitly note that the results are undefined
* if source and destination buffers overlap when calling sprintf(),
* snprintf(), vsprintf(), and vsnprintf()."
*
* @param [in] buf string buffer to print to
* @param [in[ size maximum size of the string
* @param [in] format string format of command
* @param [in] ... arguments required by the format
*
* @return >= 0 - successful, the number of characters printed
* < 0 - failed
*/
int zos_snprintf(
char *buf,
size_t size,
const char *format,
...
)
{
char *tmp_buf;
va_list arg;
int ret = -1;
if (buf == NULL)
{
ZLOG_ERROR("buf == NULL");
return ret;
}
if (format == NULL)
{
ZLOG_ERROR("format == NULL");
return ret;
}
tmp_buf = ZOS_MALLOC(size);
if (tmp_buf == NULL)
{
ZLOG_ERROR("fail to allocate memory");
return ret;
}
va_start(arg, format);
ret = vsnprintf(tmp_buf, size, format, arg);
va_end(arg);
if (ret >= 0)
{
memset(buf, 0, size);
if (ret > 0)
{
strcpy(buf, tmp_buf);
}
}
ZOS_FREE(tmp_buf);
return ret;
} // zos_snprintf
/*!
* get my pid which is shown in top command.
*
* @return uint32_t my pid
*/
uint32_t zos_pid_get()
{
return (uint32_t)(syscall(__NR_gettid));
}
/*!
* get monotonic time in milli-second.
*
* @return uint32_t milli-second
* 0: failed
*/
uint32_t zos_mtime_get()
{
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
{
ZLOG_ERROR("fail to get monotonic time");
return 0;
}
return tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
}
//==============================================================================
#ifdef ZCFG_MEMORY_LEAK_DETECT
void dumpZcfgAllocRecord()
{
dumpAllocRecord();
dumpZcfgAllocFrontendRecord();
}
void dumpAllocRecord()
{
int totalSize = 0;
int size = 0;
if(ZcfgAllocAddrRecordJobj != NULL){
#if 1 //hide ZcfgAllocAddrRecordJobj
printf("============================================================\n");
printf("libzos Alloc Address Record\n");
printf("%s\n", json_object_to_json_string(ZcfgAllocAddrRecordJobj));
printf("============================================================\n\n");
#endif
json_object_object_foreach(ZcfgAllocAddrRecordJobj, key, val){
//printf("KEY = %s\n", key);
size = json_object_get_int(json_object_object_get(json_object_object_get(ZcfgAllocAddrRecordJobj, key), "Size"));
//printf("LeakSize = %d\n", leakSize);
totalSize += size;
}
}
else
printf("libzos ZcfgAllocAddrRecordJobj is not init.\n");
if(ZcfgAllocLineRecordJobj != NULL){
printf("============================================================\n");
printf("libzos Alloc File Line Record\n");
printf("%s\n", json_object_to_json_string(ZcfgAllocLineRecordJobj));
printf("============================================================\n\n");
printf("TotalSize = %d\n\n", totalSize);
}
else
printf("libzos ZcfgAllocLineRecordJobj is not init.\n");
}
#endif
#ifdef ZCFG_MEMORY_LEAK_DETECT
void recordAllocData(char *pointerAddr, char *fileLine, int size)
{
int allocCnt = 0;
struct json_object *fileLineArray = NULL;
struct json_object *pointerAddrObj = NULL;
if(ZcfgAllocAddrRecordJobj == NULL)
ZcfgAllocAddrRecordJobj = json_object_new_object();
if(ZcfgAllocLineRecordJobj == NULL)
ZcfgAllocLineRecordJobj = json_object_new_object();
if(ZcfgAllocAddrRecordJobj != NULL){
pointerAddrObj = json_object_object_get(ZcfgAllocAddrRecordJobj, pointerAddr);
if(pointerAddrObj == NULL){
pointerAddrObj = json_object_new_object();
fileLineArray = json_object_new_array();
json_object_array_add(fileLineArray, json_object_new_string(fileLine));
json_object_object_add(pointerAddrObj, "FileLine", fileLineArray);
json_object_object_add(pointerAddrObj, "Size", json_object_new_int(size));
json_object_object_add(ZcfgAllocAddrRecordJobj, pointerAddr, pointerAddrObj);
}
else{
fileLineArray = json_object_object_get(pointerAddrObj, "FileLine");
json_object_array_add(fileLineArray, json_object_new_string(fileLine));
}
}
if(ZcfgAllocLineRecordJobj != NULL){
allocCnt = json_object_get_int(json_object_object_get(ZcfgAllocLineRecordJobj, fileLine));
allocCnt++;
json_object_object_add(ZcfgAllocLineRecordJobj, fileLine, json_object_new_int(allocCnt));
}
}
#endif
#ifdef ZCFG_MEMORY_LEAK_DETECT
void* _ZOS_MALLOC(int _size_, const char *fileName, int line)
{
char fileLine[ZCFG_ALLOC_RECORD_LEN] = {0};
char pointerAddr[ZCFG_ALLOC_RECORD_LEN] = {0};
void *_p_ = NULL;
_p_ = calloc(1, (size_t)(_size_));
if (_p_ == NULL)
{
ZLOG_ERROR("fail to calloc memory, size = %zu", (size_t)(_size_));
}
else
{
sprintf(pointerAddr, "%p", _p_);
sprintf(fileLine, "%s:%d", fileName, line);
pthread_mutex_lock(&recordDataMutex);
recordAllocData(pointerAddr, fileLine, _size_);
pthread_mutex_unlock(&recordDataMutex);
}
return _p_;
}
#endif
#ifdef ZCFG_MEMORY_LEAK_DETECT
void recordZcfgAllocData(void *addr, const char *fileName, int line, int size)
{
char fileLine[ZCFG_ALLOC_RECORD_LEN] = {0};
char pointerAddr[ZCFG_ALLOC_RECORD_LEN] = {0};
if(addr == NULL)
return;
sprintf(pointerAddr, "%p", addr);
sprintf(fileLine, "%s:%d", fileName, line);
pthread_mutex_lock(&recordDataMutex);
recordAllocData(pointerAddr, fileLine, size);
pthread_mutex_unlock(&recordDataMutex);
}
#endif
#ifdef ZCFG_MEMORY_LEAK_DETECT
void _ZOS_FREE(void* obj)
{
char pointerAddr[ZCFG_ALLOC_RECORD_LEN] = {0};
struct json_object *pointerAddrObj = NULL;
struct json_object *fileLineArray = NULL;
const char *fileLine = NULL;
int allocCnt = 0;
if(obj == NULL){
ZLOG_DEBUG("Free NULL pointer");
return;
}
pthread_mutex_lock(&recordDataMutex);
if(ZcfgAllocAddrRecordJobj != NULL){
sprintf(pointerAddr, "%p", obj);
pointerAddrObj = json_object_object_get(ZcfgAllocAddrRecordJobj, pointerAddr);
fileLineArray = json_object_object_get(pointerAddrObj, "FileLine");
if(fileLineArray != NULL){
int len = json_object_array_length(fileLineArray);
for(int i=0;i<len;i++){
fileLine = json_object_get_string(json_object_array_get_idx(fileLineArray, i));
if(fileLine != NULL && *fileLine != '\0' && ZcfgAllocLineRecordJobj != NULL){
allocCnt = json_object_get_int(json_object_object_get(ZcfgAllocLineRecordJobj, fileLine));
allocCnt --;
if(allocCnt > 0)
json_object_object_add(ZcfgAllocLineRecordJobj, fileLine, json_object_new_int(allocCnt));
else
json_object_object_del(ZcfgAllocLineRecordJobj, fileLine);
}
}
}
json_object_object_del(ZcfgAllocAddrRecordJobj, pointerAddr);
}
pthread_mutex_unlock(&recordDataMutex);
free(obj);
}
#endif
/*!
* The same function to strdup, but able to trace memory allocation record
* Need use macro ZOS_STRDUP, but not _zos_strdup
*/
char *_zos_strdup(const char *str, const char *fileName, int line)
{
int len = 0;
char *dupStr = NULL;
len = strlen(str) + 1;
dupStr = (char *)ZOS_MALLOC(len);
memcpy(dupStr, str, len);
#ifdef ZCFG_MEMORY_LEAK_DETECT
recordZcfgAllocData(dupStr, fileName, line, 0);
#endif
return dupStr;
}
/*!
*
* Appends a copy of the source string to the destination string.
* The terminating null character in destination is overwritten by the first character of source,
* and a null-character is included at the end of the new string formed by the concatenation of both in destination.
* Destination and source shall not overlap.
*
* @param[in] char *destination Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.
* @param[in] const char *source C string to be appended. This should not overlap destination.
* @param[in] bufferSize Size of the destination array.
* @return ZOS_SUCCESS: 0, ZOS_FAIL: -1
*/
int zos_strcat(char *destination, const char *source, size_t bufferSize)
{
if(destination == NULL || source == NULL || bufferSize == 0)
return ZOS_FAIL;
while (*destination != '\0') {
destination++;
bufferSize--;
if(bufferSize <= 1)
return ZOS_FAIL;
}
while (bufferSize > 1) {
*destination = *source;
if(*source == '\0')
return ZOS_SUCCESS;
bufferSize--;
destination++;
source++;
}
*destination = '\0';
if(*source != '\0')
return ZOS_FAIL;
else
return ZOS_SUCCESS;
}
/*!
* Move or copy the source to destination.
* Both source and destination should be files.
* If the source file is a symbolic link, COPY will copy the file it points to,
* and COPY_LINK will copy the symbolic link itself.
*
* @param[in] const char *dst Destination file name.
* @param[in] const char *src Source file name.
* @param[in] file_action action MOVE, COPY or COPY_LINK.
* @return ZOS_SUCCESS: 0, ZOS_FAIL: -1
*/
int zos_copyFile(char *dst, char *src, file_action action)
{
FILE *fp_src = NULL;
FILE *fp_dst = NULL;
char *buffer = NULL;
size_t readNum = 0;
size_t fileSize = 0;
struct stat srcState, srcLinkState, dstState;
int srcIsDir = 0;
int dstIsDir = 0;
if( (action != MOVE) && (action != COPY) && (action != COPY_LINK) )
{
printf("Action should be MOVE, COPY, or COPY_LINK\n");
goto fail4;
}
if(access(src, F_OK))
{
printf("Source does not exist\n");
goto fail4;
}
stat(src, &srcState);
srcIsDir = S_ISDIR(srcState.st_mode);
if(access(dst, F_OK))
{
dstIsDir = 0;
}
else
{
stat(dst, &dstState);
dstIsDir = S_ISDIR(dstState.st_mode);
}
if(srcIsDir || dstIsDir)
{
printf("Both source and destination should be files\n");
goto fail4;
}
//Link destination to the file that source points to
lstat(src, &srcLinkState);
if(S_ISLNK(srcLinkState.st_mode))
{
if(action == MOVE || action == COPY_LINK)
{
char linkBuf[100] = {'\0'};
if(readlink(src, linkBuf, sizeof(linkBuf)) == -1)
{
printf("Read the contents of the link file error\n");
goto fail4;
}
if(access(dst, F_OK) == 0)
{
remove(dst);
}
symlink(linkBuf, dst);
if(action == MOVE)
{
remove(src);
}
chmod(dst, srcLinkState.st_mode);
struct timeval times[2];
times[0].tv_sec = srcLinkState.st_atime;
times[0].tv_usec = 0;
times[1].tv_sec = srcLinkState.st_mtime;
times[1].tv_usec = 0;
lutimes(dst, times);
return ZOS_SUCCESS;
}
}
fp_src = fopen(src, "rb");
if (fp_src == NULL)
{
printf("Open source file error\n");
goto fail4;
}
//Get file size of source
fileSize = srcState.st_size;
printf("file size of source = %d\n", fileSize);
//Allocate memory for read and write
buffer = (char *)malloc(sizeof(char) * BUFFER_SIZE);
if (buffer == NULL)
{
printf("Allocate memory error\n");
goto fail3;
}
//Read source file into buffer and copy to destination file
fp_dst = fopen(dst, "wb");
if (fp_dst == NULL)
{
printf("Open destination file error\n");
goto fail2;
}
int writeSize = BUFFER_SIZE;
long int remain = fileSize;
while(remain > 0)
{
if(remain < BUFFER_SIZE)
{
writeSize = remain;
}
readNum = fread(buffer, sizeof(char), writeSize, fp_src);
if(readNum != writeSize)
{
printf("Read source file error\n");
goto fail1;
}
if (fwrite(buffer, sizeof(char), readNum, fp_dst) != readNum)
{
printf("Write file error\n");
goto fail1;
}
remain -= readNum/* BUFFER_SIZE */;
}
chmod(dst, srcState.st_mode & ~default_umask);
zos_fclose(fp_src, NORMAL);
if(action == MOVE)
{
chmod(dst, srcState.st_mode);
struct utimbuf dstFileTime;
dstFileTime.modtime = srcState.st_mtime;
dstFileTime.actime = srcState.st_atime;
utime(dst, &dstFileTime);
remove(src);
}
zos_fclose(fp_dst, SYNC);
free(buffer);
return ZOS_SUCCESS;
fail1:
zos_fclose(fp_dst, SYNC);
fail2:
free(buffer);
fail3:
zos_fclose(fp_src, NORMAL);
fail4:
return ZOS_FAIL;
}
/*!
* Remove the directory.
*
* @param[in] const char *dirPath Path of the directory.
* @return ZOS_SUCCESS: 0, ZOS_FAIL: -1
*/
int zos_removeDir(const char *dirPath)
{
DIR *dir = NULL;
struct dirent *dp = NULL;
char *buffer = NULL;
size_t pathLenth = 0;
struct stat bufferState;
int getBufferState = 0;
int ret = 0;
dir = opendir(dirPath);
if(dir == NULL)
{
printf("Open directory error\n");
goto fail2;
}
while( (dp = readdir(dir)) != NULL )
{
if(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
//Allocate memory for lenth of path
pathLenth = strlen(dirPath) + strlen(dp->d_name) + 2;
buffer = (char *)malloc(pathLenth);
if (buffer == NULL)
{
printf("Allocate memory error\n");
goto fail1;
}
snprintf(buffer, pathLenth, "%s/%s", dirPath, dp->d_name);
getBufferState = lstat(buffer, &bufferState);
if(!getBufferState)
{
if(S_ISDIR(bufferState.st_mode))
{
ret = zos_removeDir(buffer);
}
else
{
ret = remove(buffer);
}
}
free(buffer);
}
closedir(dir);
if (!ret)
{
ret = rmdir(dirPath);
}
return ret;
fail1:
closedir(dir);
fail2:
return ZOS_FAIL;
}
/*!
* Create a directory.
*
* @param[in] const char *dirPath Path of the directory.
* @return ZOS_SUCCESS: 0, ZOS_FAIL: -1
*/
int zos_mkdir(const char *dirPath)
{
char *pathBuffer = NULL;
int i = 0;
int len = 0;
struct stat pathState;
pathBuffer = (char *)calloc(sizeof(char), strlen(dirPath) + 2);
if (pathBuffer == NULL)
{
printf("Allocate memory error\n");
goto fail2;
}
strncpy(pathBuffer, dirPath, strlen(dirPath) + 1);
len = strlen(pathBuffer);
if(pathBuffer[len-1] != '/')
{
strcat(pathBuffer, "/");
len++;
}
for(i=1;i<len;i++)
{
if(pathBuffer[i] == '/')
{
pathBuffer[i] = '\0';
lstat(pathBuffer, &pathState);
if(access(pathBuffer, F_OK) == -1)
{
if(mkdir(pathBuffer, 0777 & ~default_umask) == -1)
{
printf("Create the directory error: %s\n", strerror(errno));
goto fail1;
}
}
else
{
if(!S_ISDIR(pathState.st_mode))
{
printf("Error: File %s exists\n", pathBuffer);
goto fail1;
}
}
pathBuffer[i] = '/';
}
}
free(pathBuffer);
return ZOS_SUCCESS;
fail1:
free(pathBuffer);
fail2:
return ZOS_FAIL;
}
/*!
* Create a file.
*
* @param[in] char *fileName Name of the file.
* @return ZOS_SUCCESS: 0, ZOS_FAIL: -1
*/
int zos_touchFile(char *fileName)
{
if(fileName == NULL)
{
return ZOS_FAIL;
}
//If the file name exists, update its timestamp
if(access(fileName, F_OK) == 0)
{
utime(fileName, NULL);
return ZOS_SUCCESS;
}
else
{
FILE *file = fopen(fileName, "wb");
if(file == NULL)
{
printf("Open file error\n");
return ZOS_FAIL;
}
zos_fclose(file, SYNC);
}
return ZOS_SUCCESS;
}
/*!
* If the type is SYNC, ensure the buffered data are written into the file and disk then do fclose.
* If the type is NORMAL, just do fclose.
*
* @param[in] FILE *fp Pointer to a FILE structure.
* @param[in] int type SYNC or NORMAL.
* @return ZOS_SUCCESS: 0, ZOS_FAIL: -1
*/
int zos_fclose(FILE *fp, int type)
{
if(fp == NULL)
{
printf("FILE pointer is NULL\n");
return ZOS_FAIL;
}
if( (type != SYNC) && (type != NORMAL) )
{
printf("type should be SYNC or NORMAL\n");
return ZOS_FAIL;
}
if(type == SYNC)
{
int fd = -1;
int flag = 0;
fd = fileno(fp);
if(fd == -1)
{
printf("Get file descriptor error\n");
return ZOS_FAIL;
}
flag = fcntl(fd, F_GETFL);
if( (flag & O_ACCMODE) != O_RDONLY )
{
if( (flag & O_NONBLOCK) == 0 )
{
fflush(fp);
}
fsync(fd);
}
}
fclose(fp);
return ZOS_SUCCESS;
}