/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see .
**/
#define fopen __real_fopen
#define fclose __real_fclose
#define fgets __real_fgets
#define lseek __real_lseek
#define close __real_close
#include
#include
#undef fopen
#undef fclose
#undef fgets
#undef lseek
#undef close
#include "zbxmocktest.h"
#include "zbxmockdata.h"
#include "zbxmockutil.h"
#include "zbxcommon.h"
#define ZBX_MOCK_MAX_FILES 16
void *mock_streams[ZBX_MOCK_MAX_FILES];
static zbx_mock_handle_t fragments;
static FILE *(*fopen_mock_callback)(const char *, const char *) = NULL;
struct zbx_mock_IO_FILE
{
const char *contents;
};
FILE *__wrap_fopen(const char *path, const char *mode);
int __wrap_fclose(FILE *fp);
char *__wrap_fgets(char *s, int size, FILE *stream);
int __wrap_fstat(int __fildes, struct stat *__stat_buf);
int __wrap_connect(int socket, void *addr, socklen_t address_len);
int __wrap_poll(struct pollfd *pds, int nfds, int timeout);
off_t __wrap_lseek(int fd, off_t offset, int whence);
int __wrap_close(int fd);
ssize_t __wrap_read(int fildes, void *buf, size_t nbyte);
int __wrap_open(const char *path, int oflag, ...);
int __wrap_stat(const char *path, struct stat *buf);
int __wrap___xstat(int ver, const char *pathname, struct stat *buf);
#ifdef HAVE_FXSTAT
int __wrap___fxstat(int __ver, int __fildes, struct stat *__stat_buf);
#endif
int __real_open(const char *path, int oflag, ...);
int __real_stat(const char *path, struct stat *buf);
int __real_fstat(int __fildes, struct stat *__stat_buf);
#ifdef HAVE_FXSTAT
int __real___fxstat(int __ver, int __fildes, struct stat *__stat_buf);
#endif
static int is_profiler_path(const char *path)
{
size_t len;
len = strlen(path);
if ((ZBX_CONST_STRLEN(".gcda") < len && 0 == strcmp(path + len - ZBX_CONST_STRLEN(".gcda"), ".gcda")) ||
(ZBX_CONST_STRLEN(".gcno") < len &&
0 == strcmp(path + len - ZBX_CONST_STRLEN(".gcno"), ".gcno")))
{
return SUCCEED;
}
return FAIL;
}
static int is_mock_stream(FILE *stream)
{
int i;
for (i = 0; i < ZBX_MOCK_MAX_FILES && NULL != mock_streams[i]; i++)
{
if (stream == mock_streams[i])
return SUCCEED;
}
return FAIL;
}
FILE *__wrap_fopen(const char *path, const char *mode)
{
zbx_mock_error_t error;
zbx_mock_handle_t file_contents;
const char *contents;
struct zbx_mock_IO_FILE *file = NULL;
/* in case a test needs a custom fopen mock, use callback instead */
if (NULL != fopen_mock_callback)
return (*fopen_mock_callback)(path, mode);
if (SUCCEED == is_profiler_path(path))
return __real_fopen(path, mode);
if (0 != strcmp(mode, "r"))
{
fail_msg("fopen() modes other than \"r\" are not supported.");
}
else if (ZBX_MOCK_NO_PARAMETER == (error = zbx_mock_file(path, &file_contents)))
{
errno = ENOENT; /* No such file or directory */
}
else if (ZBX_MOCK_SUCCESS != error)
{
fail_msg("Error while trying to open path \"%s\" from test case data: %s", path,
zbx_mock_error_string(error));
}
else if (ZBX_MOCK_SUCCESS != (error = zbx_mock_string(file_contents, &contents)))
{
fail_msg("Error while trying to get contents of file \"%s\" from test case data: %s", path,
zbx_mock_error_string(error));
}
else
{
int i;
for (i = 0; i < ZBX_MOCK_MAX_FILES && NULL != mock_streams[i]; i++)
;
if (i < ZBX_MOCK_MAX_FILES)
{
file = zbx_malloc(file, sizeof(struct zbx_mock_IO_FILE));
file->contents = contents;
mock_streams[i] = file;
}
else
errno = EMFILE; /* The per-process limit on the number of open file descriptors has been reached */
}
return (FILE *)file;
}
int __wrap_fclose(FILE *fp)
{
if (SUCCEED != is_mock_stream(fp))
return __real_fclose(fp);
zbx_free(fp);
return 0;
}
char *__wrap_fgets(char *s, int size, FILE *stream)
{
struct zbx_mock_IO_FILE *file = (struct zbx_mock_IO_FILE *)stream;
int length;
const char *newline;
if (SUCCEED != is_mock_stream(stream))
return __real_fgets(s, size, stream);
assert_non_null(s);
assert_true(0 < size);
if ('\0' == *file->contents)
return NULL;
if (size - 1 < (length = strlen(file->contents)))
length = size - 1;
if (NULL != (newline = strchr(file->contents, '\n')) && newline - file->contents + 1 < length)
length = newline - file->contents + 1;
assert_int_equal(length, zbx_snprintf(s, size, "%.*s", length, file->contents));
file->contents += length;
return s;
}
int __wrap_connect(int socket, void *addr, socklen_t address_len)
{
zbx_mock_error_t error;
ZBX_UNUSED(socket);
ZBX_UNUSED(addr);
ZBX_UNUSED(address_len);
if (ZBX_MOCK_SUCCESS != (error = zbx_mock_in_parameter("fragments", &fragments)))
fail_msg("Cannot get fragments handle: %s", zbx_mock_error_string(error));
return 0;
}
int __wrap_poll(struct pollfd *pds, int nfds, int timeout)
{
ZBX_UNUSED(timeout);
for (int i = 0; i < nfds; i++)
pds[i].revents = (POLLIN | POLLOUT);
return nfds;
}
static const char *frag_data = NULL;
static const char *frag_pos = NULL;
static size_t frag_sz = 0;
static int next_fragment(void)
{
zbx_mock_handle_t fragment;
zbx_mock_error_t error;
if (ZBX_MOCK_SUCCESS != zbx_mock_vector_element(fragments, &fragment))
return 0; /* no more data */
if (ZBX_MOCK_SUCCESS != (error = zbx_mock_binary(fragment, &frag_data, &frag_sz)))
fail_msg("Cannot read data '%s'", zbx_mock_error_string(error));
frag_pos = frag_data;
return 1;
}
int __wrap_open(const char *path, int oflag, ...)
{
if (SUCCEED == is_profiler_path(path))
{
va_list args;
int fd;
va_start(args, oflag);
fd = __real_open(path, oflag, va_arg(args, int));
va_end(args);
return fd;
}
fragments = zbx_mock_get_parameter_handle("in.fragments");
next_fragment();
return INT_MAX;
}
/******************************************************************************
* *
* Comments: Note that simply wrapping read function will break any compiled *
* in tool, that would attempt to use read() function. In this case *
* some safeguards must be added to implement pass-through *
* functionality like it's done with open/fxstat etc functions for *
* coverage builds. *
* *
******************************************************************************/
ssize_t __wrap_read(int fildes, void *buf, size_t nbyte)
{
size_t mv_len;
ZBX_UNUSED(fildes);
if (frag_pos >= frag_data + frag_sz)
{
if (1 != next_fragment())
return 0;
}
if ((ssize_t)nbyte < (frag_data + frag_sz) - frag_pos)
mv_len = nbyte;
else
mv_len = (frag_data + frag_sz) - frag_pos;
memcpy(buf, frag_pos, mv_len);
frag_pos += mv_len;
return mv_len;
}
off_t __wrap_lseek(int fd, off_t offset, int whence)
{
const char *new_pos;
if (fd != INT_MAX)
return __real_lseek(fd, offset, whence);
switch(whence)
{
case SEEK_END:
new_pos = (frag_data + frag_sz) + offset;
break;
case SEEK_CUR:
new_pos = frag_pos + offset;
break;
case SEEK_SET:
new_pos = frag_data + offset;
break;
default:
errno = EINVAL;
goto error;
}
if (new_pos < frag_data || new_pos > frag_data + frag_sz)
{
errno = EOVERFLOW;
goto error;
}
frag_pos = new_pos;
return frag_pos - frag_data;
error:
return (off_t)-1;
}
int __wrap_close(int fd)
{
if (fd != INT_MAX) return __real_close(fd);
frag_data = frag_pos = NULL;
frag_sz = 0;
return 0;
}
int __wrap_stat(const char *path, struct stat *buf)
{
zbx_mock_error_t error;
zbx_mock_handle_t handle;
if (SUCCEED == is_profiler_path(path))
return __real_stat(path, buf);
if (ZBX_MOCK_SUCCESS == (error = zbx_mock_file(path, &handle)))
{
buf->st_mode = S_IFMT & S_IFREG;
return 0;
}
if (ZBX_MOCK_NO_PARAMETER != error)
fail_msg("Error during path \"%s\" lookup among files: %s", path, zbx_mock_error_string(error));
if (0) /* directory lookup is not implemented */
{
buf->st_mode = S_IFMT & S_IFDIR;
return 0;
}
errno = ENOENT; /* No such file or directory */
return -1;
}
int __wrap_fstat(int __fildes, struct stat *__stat_buf)
{
if (__fildes != INT_MAX)
return __real_fstat(__fildes, __stat_buf);
__stat_buf->st_size = zbx_mock_get_parameter_uint64("in.file_len");
return 0;
}
int __wrap___xstat(int ver, const char *pathname, struct stat *buf)
{
ZBX_UNUSED(ver);
if (SUCCEED == is_profiler_path(pathname))
return __real_stat(pathname, buf);
return __wrap_stat(pathname, buf);
}
#ifdef HAVE_FXSTAT
int __wrap___fxstat(int __ver, int __fildes, struct stat *__stat_buf)
{
if (__fildes != INT_MAX)
return __real___fxstat(__ver, __fildes, __stat_buf);
__stat_buf->st_size = zbx_mock_get_parameter_uint64("in.file_len");
return 0;
}
#endif
void zbx_set_fopen_mock_callback(FILE *(*fopen_callback)(const char *, const char *))
{
fopen_mock_callback = fopen_callback;
}