Add a --debug flag for DEBUG builds
If you compile with: DEBUG=true rake build then all the commands get a --debug flag as an option which will make the server dump crazy amounts of data to stderr.
This commit is contained in:
4
Rakefile
4
Rakefile
@@ -1,8 +1,6 @@
|
|||||||
require 'rake_utils/debian'
|
require 'rake_utils/debian'
|
||||||
include RakeUtils::DSL
|
include RakeUtils::DSL
|
||||||
|
|
||||||
DEBUG = true
|
|
||||||
|
|
||||||
ALL_SOURCES =FileList['src/*']
|
ALL_SOURCES =FileList['src/*']
|
||||||
SOURCES = ALL_SOURCES.select { |c| c =~ /\.c$/ }
|
SOURCES = ALL_SOURCES.select { |c| c =~ /\.c$/ }
|
||||||
OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" )
|
OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" )
|
||||||
@@ -14,7 +12,7 @@ LIBCHECK = "/usr/lib/libcheck.a"
|
|||||||
|
|
||||||
TEST_MODULES = Dir["tests/check_*.c"].map { |n| n[12..-3] }
|
TEST_MODULES = Dir["tests/check_*.c"].map { |n| n[12..-3] }
|
||||||
|
|
||||||
if DEBUG
|
if ENV['DEBUG']
|
||||||
LDFLAGS << ["-g"]
|
LDFLAGS << ["-g"]
|
||||||
CCFLAGS << ["-g -DDEBUG"]
|
CCFLAGS << ["-g -DDEBUG"]
|
||||||
end
|
end
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
static inline char char_with_bit_set(int num) { return 1<<(num%8); }
|
static inline char char_with_bit_set(int num) { return 1<<(num%8); }
|
||||||
|
|
||||||
|
@@ -43,9 +43,9 @@ void exit_err( char *msg )
|
|||||||
}
|
}
|
||||||
|
|
||||||
void params_serve(
|
void params_serve(
|
||||||
struct mode_serve_params* out,
|
struct mode_serve_params* out,
|
||||||
char* s_ip_address,
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_file,
|
char* s_file,
|
||||||
char *s_ctrl_sock,
|
char *s_ctrl_sock,
|
||||||
int acl_entries,
|
int acl_entries,
|
||||||
@@ -53,16 +53,16 @@ void params_serve(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
int parsed;
|
int parsed;
|
||||||
|
|
||||||
out->tcp_backlog = 10; /* does this need to be settable? */
|
out->tcp_backlog = 10; /* does this need to be settable? */
|
||||||
|
|
||||||
if (s_ip_address == NULL)
|
if (s_ip_address == NULL)
|
||||||
SERVER_ERROR("No IP address supplied");
|
SERVER_ERROR("No IP address supplied");
|
||||||
if (s_port == NULL)
|
if (s_port == NULL)
|
||||||
SERVER_ERROR("No port number supplied");
|
SERVER_ERROR("No port number supplied");
|
||||||
if (s_file == NULL)
|
if (s_file == NULL)
|
||||||
SERVER_ERROR("No filename supplied");
|
SERVER_ERROR("No filename supplied");
|
||||||
|
|
||||||
if (parse_ip_to_sockaddr(&out->bind_to.generic, s_ip_address) == 0)
|
if (parse_ip_to_sockaddr(&out->bind_to.generic, s_ip_address) == 0)
|
||||||
SERVER_ERROR("Couldn't parse server address '%s' (use 0 if "
|
SERVER_ERROR("Couldn't parse server address '%s' (use 0 if "
|
||||||
"you want to bind to all IPs)", s_ip_address);
|
"you want to bind to all IPs)", s_ip_address);
|
||||||
@@ -75,12 +75,12 @@ void params_serve(
|
|||||||
parsed = parse_acl(&out->acl, acl_entries, s_acl_entries);
|
parsed = parse_acl(&out->acl, acl_entries, s_acl_entries);
|
||||||
if (parsed != acl_entries)
|
if (parsed != acl_entries)
|
||||||
SERVER_ERROR("Bad ACL entry '%s'", s_acl_entries[parsed]);
|
SERVER_ERROR("Bad ACL entry '%s'", s_acl_entries[parsed]);
|
||||||
|
|
||||||
out->bind_to.v4.sin_port = atoi(s_port);
|
out->bind_to.v4.sin_port = atoi(s_port);
|
||||||
if (out->bind_to.v4.sin_port < 0 || out->bind_to.v4.sin_port > 65535)
|
if (out->bind_to.v4.sin_port < 0 || out->bind_to.v4.sin_port > 65535)
|
||||||
SERVER_ERROR("Port number must be >= 0 and <= 65535");
|
SERVER_ERROR("Port number must be >= 0 and <= 65535");
|
||||||
out->bind_to.v4.sin_port = htobe16(out->bind_to.v4.sin_port);
|
out->bind_to.v4.sin_port = htobe16(out->bind_to.v4.sin_port);
|
||||||
|
|
||||||
out->filename = s_file;
|
out->filename = s_file;
|
||||||
out->filename_incomplete = xmalloc(strlen(s_file)+11);
|
out->filename_incomplete = xmalloc(strlen(s_file)+11);
|
||||||
strcpy(out->filename_incomplete, s_file);
|
strcpy(out->filename_incomplete, s_file);
|
||||||
@@ -104,7 +104,7 @@ void params_serve(
|
|||||||
void params_readwrite(
|
void params_readwrite(
|
||||||
int write_not_read,
|
int write_not_read,
|
||||||
struct mode_readwrite_params* out,
|
struct mode_readwrite_params* out,
|
||||||
char* s_ip_address,
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_from,
|
char* s_from,
|
||||||
char* s_length_or_filename
|
char* s_length_or_filename
|
||||||
@@ -118,11 +118,11 @@ void params_readwrite(
|
|||||||
SERVER_ERROR("No from supplied");
|
SERVER_ERROR("No from supplied");
|
||||||
if (s_length_or_filename == NULL)
|
if (s_length_or_filename == NULL)
|
||||||
SERVER_ERROR("No length supplied");
|
SERVER_ERROR("No length supplied");
|
||||||
|
|
||||||
if (parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address) == 0)
|
if (parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address) == 0)
|
||||||
SERVER_ERROR("Couldn't parse connection address '%s'",
|
SERVER_ERROR("Couldn't parse connection address '%s'",
|
||||||
s_ip_address);
|
s_ip_address);
|
||||||
|
|
||||||
/* FIXME: duplicated from above */
|
/* FIXME: duplicated from above */
|
||||||
out->connect_to.v4.sin_port = atoi(s_port);
|
out->connect_to.v4.sin_port = atoi(s_port);
|
||||||
if (out->connect_to.v4.sin_port < 0 || out->connect_to.v4.sin_port > 65535)
|
if (out->connect_to.v4.sin_port < 0 || out->connect_to.v4.sin_port > 65535)
|
||||||
@@ -130,7 +130,7 @@ void params_readwrite(
|
|||||||
out->connect_to.v4.sin_port = htobe16(out->connect_to.v4.sin_port);
|
out->connect_to.v4.sin_port = htobe16(out->connect_to.v4.sin_port);
|
||||||
|
|
||||||
out->from = atol(s_from);
|
out->from = atol(s_from);
|
||||||
|
|
||||||
if (write_not_read) {
|
if (write_not_read) {
|
||||||
if (s_length_or_filename[0]-48 < 10) {
|
if (s_length_or_filename[0]-48 < 10) {
|
||||||
out->len = atol(s_length_or_filename);
|
out->len = atol(s_length_or_filename);
|
||||||
@@ -180,6 +180,9 @@ void read_serve_param( int c, char **ip_addr, char **ip_port, char **file, char
|
|||||||
case 's':
|
case 's':
|
||||||
*sock = optarg;
|
*sock = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
set_debug(1);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exit_err( serve_help_text );
|
exit_err( serve_help_text );
|
||||||
break;
|
break;
|
||||||
@@ -206,6 +209,9 @@ void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **from, c
|
|||||||
case 'S':
|
case 'S':
|
||||||
*size = optarg;
|
*size = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
set_debug(1);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exit_err( read_help_text );
|
exit_err( read_help_text );
|
||||||
break;
|
break;
|
||||||
@@ -222,6 +228,9 @@ void read_sock_param( int c, char **sock, char *help_text )
|
|||||||
case 's':
|
case 's':
|
||||||
*sock = optarg;
|
*sock = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
set_debug(1);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exit_err( help_text );
|
exit_err( help_text );
|
||||||
break;
|
break;
|
||||||
@@ -249,6 +258,9 @@ void read_mirror_param( int c, char **sock, char **ip_addr, char **ip_port )
|
|||||||
case 'p':
|
case 'p':
|
||||||
*ip_port = optarg;
|
*ip_port = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
set_debug(1);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exit_err( mirror_help_text );
|
exit_err( mirror_help_text );
|
||||||
break;
|
break;
|
||||||
@@ -370,7 +382,7 @@ int mode_acl( int argc, char *argv[] )
|
|||||||
if ( c == -1 ) break;
|
if ( c == -1 ) break;
|
||||||
read_acl_param( c, &sock );
|
read_acl_param( c, &sock );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL == sock ){
|
if ( NULL == sock ){
|
||||||
fprintf( stderr, "--sock is required.\n" );
|
fprintf( stderr, "--sock is required.\n" );
|
||||||
exit_err( acl_help_text );
|
exit_err( acl_help_text );
|
||||||
@@ -418,13 +430,13 @@ int mode_status( int argc, char *argv[] )
|
|||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
char *sock = NULL;
|
char *sock = NULL;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long( argc, argv, status_short_options, status_options, NULL );
|
c = getopt_long( argc, argv, status_short_options, status_options, NULL );
|
||||||
if ( -1 == c ) break;
|
if ( -1 == c ) break;
|
||||||
read_status_param( c, &sock );
|
read_status_param( c, &sock );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL == sock ){
|
if ( NULL == sock ){
|
||||||
fprintf( stderr, "--sock is required.\n" );
|
fprintf( stderr, "--sock is required.\n" );
|
||||||
exit_err( acl_help_text );
|
exit_err( acl_help_text );
|
||||||
@@ -500,6 +512,7 @@ int main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
||||||
error_init();
|
error_init();
|
||||||
|
set_debug(0);
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
exit_err( help_help_text );
|
exit_err( help_help_text );
|
||||||
|
@@ -32,23 +32,36 @@
|
|||||||
#define GETOPT_FROM GETOPT_ARG( OPT_FROM, 'F' )
|
#define GETOPT_FROM GETOPT_ARG( OPT_FROM, 'F' )
|
||||||
#define GETOPT_SIZE GETOPT_ARG( OPT_SIZE, 'S' )
|
#define GETOPT_SIZE GETOPT_ARG( OPT_SIZE, 'S' )
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
# define OPT_DEBUG "debug"
|
||||||
|
# define GETOPT_DEBUG GETOPT_FLAG( OPT_DEBUG, 'd' )
|
||||||
|
# define DEBUG_LINE \
|
||||||
|
"\t--" OPT_DEBUG ",-d\tOutput debug information.\n"
|
||||||
|
#else
|
||||||
|
# define GETOPT_DEBUG {0}
|
||||||
|
# define DEBUG_LINE ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static struct option serve_options[] = {
|
static struct option serve_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
GETOPT_ADDR,
|
GETOPT_ADDR,
|
||||||
GETOPT_PORT,
|
GETOPT_PORT,
|
||||||
GETOPT_FILE,
|
GETOPT_FILE,
|
||||||
GETOPT_SOCK,
|
GETOPT_SOCK,
|
||||||
|
GETOPT_DEBUG,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
static char serve_short_options[] = "hl:p:f:s:";
|
static char serve_short_options[] = "hl:p:f:s:";
|
||||||
static char serve_help_text[] =
|
static char serve_help_text[] =
|
||||||
"Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n"
|
"Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n"
|
||||||
"Serve FILE from ADDR:PORT, with an optional control socket at SOCK.\n\n"
|
"Serve FILE from ADDR:PORT, with an optional control socket at SOCK.\n\n"
|
||||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to serve on.\n"
|
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to serve on.\n"
|
||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to serve on.\n"
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to serve on.\n"
|
||||||
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
|
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
|
||||||
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket to open.\n";
|
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket to open.\n"
|
||||||
|
DEBUG_LINE;
|
||||||
|
|
||||||
static struct option read_options[] = {
|
static struct option read_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
@@ -56,6 +69,7 @@ static struct option read_options[] = {
|
|||||||
GETOPT_PORT,
|
GETOPT_PORT,
|
||||||
GETOPT_FROM,
|
GETOPT_FROM,
|
||||||
GETOPT_SIZE,
|
GETOPT_SIZE,
|
||||||
|
GETOPT_DEBUG,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
static char read_short_options[] = "hl:p:F:S:";
|
static char read_short_options[] = "hl:p:F:S:";
|
||||||
@@ -66,7 +80,8 @@ static char read_help_text[] =
|
|||||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to read from.\n"
|
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to read from.\n"
|
||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to read from.\n"
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to read from.\n"
|
||||||
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to read from.\n"
|
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to read from.\n"
|
||||||
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to read.\n";
|
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to read.\n"
|
||||||
|
DEBUG_LINE;
|
||||||
|
|
||||||
|
|
||||||
static struct option *write_options = read_options;
|
static struct option *write_options = read_options;
|
||||||
@@ -78,11 +93,13 @@ static char write_help_text[] =
|
|||||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to write to.\n"
|
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to write to.\n"
|
||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to write to.\n"
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to write to.\n"
|
||||||
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to write from.\n"
|
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to write from.\n"
|
||||||
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n";
|
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n"
|
||||||
|
DEBUG_LINE;
|
||||||
|
|
||||||
struct option acl_options[] = {
|
struct option acl_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
GETOPT_SOCK,
|
GETOPT_SOCK,
|
||||||
|
GETOPT_DEBUG,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
static char acl_short_options[] = "hs:";
|
static char acl_short_options[] = "hs:";
|
||||||
@@ -90,13 +107,15 @@ static char acl_help_text[] =
|
|||||||
"Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n"
|
"Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n"
|
||||||
"Set the access control list for a server with control socket SOCK.\n\n"
|
"Set the access control list for a server with control socket SOCK.\n\n"
|
||||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||||
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n";
|
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n"
|
||||||
|
DEBUG_LINE;
|
||||||
|
|
||||||
struct option mirror_options[] = {
|
struct option mirror_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
GETOPT_SOCK,
|
GETOPT_SOCK,
|
||||||
GETOPT_ADDR,
|
GETOPT_ADDR,
|
||||||
GETOPT_PORT,
|
GETOPT_PORT,
|
||||||
|
GETOPT_DEBUG,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
static char mirror_short_options[] = "hs:l:p:";
|
static char mirror_short_options[] = "hs:l:p:";
|
||||||
@@ -106,12 +125,14 @@ static char mirror_help_text[] =
|
|||||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||||
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n"
|
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n"
|
||||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to mirror to.\n"
|
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to mirror to.\n"
|
||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n";
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n"
|
||||||
|
DEBUG_LINE;
|
||||||
|
|
||||||
|
|
||||||
struct option status_options[] = {
|
struct option status_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
GETOPT_SOCK,
|
GETOPT_SOCK,
|
||||||
|
GETOPT_DEBUG,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
static char status_short_options[] = "hs:";
|
static char status_short_options[] = "hs:";
|
||||||
@@ -119,9 +140,10 @@ static char status_help_text[] =
|
|||||||
"Usage: flexnbd " CMD_STATUS " <options>\n\n"
|
"Usage: flexnbd " CMD_STATUS " <options>\n\n"
|
||||||
"Get the status for a server with control socket SOCK.\n\n"
|
"Get the status for a server with control socket SOCK.\n\n"
|
||||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||||
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n";
|
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n"
|
||||||
|
DEBUG_LINE;
|
||||||
|
|
||||||
static char help_help_text[] =
|
static char help_help_text[] =
|
||||||
"Usage: flexnbd <cmd> [cmd options]\n\n"
|
"Usage: flexnbd <cmd> [cmd options]\n\n"
|
||||||
"Commands:\n"
|
"Commands:\n"
|
||||||
"\tflexnbd serve\n"
|
"\tflexnbd serve\n"
|
||||||
|
21
src/util.c
21
src/util.c
@@ -10,6 +10,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
static pthread_t main_thread;
|
static pthread_t main_thread;
|
||||||
|
static int global_debug;
|
||||||
|
|
||||||
void error_init()
|
void error_init()
|
||||||
{
|
{
|
||||||
@@ -59,3 +60,23 @@ void* xmalloc(size_t size)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void set_debug(int value) {
|
||||||
|
global_debug = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
# include <sys/times.h>
|
||||||
|
# include <stdarg.h>
|
||||||
|
|
||||||
|
void debug(const char *msg, ...) {
|
||||||
|
va_list argp;
|
||||||
|
|
||||||
|
if ( global_debug ) {
|
||||||
|
fprintf(stderr, "%08x %4d: ", (int) pthread_self(), (int) clock() );
|
||||||
|
fprintf(stderr, msg, argp);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@@ -12,12 +12,9 @@ void* xrealloc(void* ptr, size_t size);
|
|||||||
|
|
||||||
void* xmalloc(size_t size);
|
void* xmalloc(size_t size);
|
||||||
|
|
||||||
#ifndef DEBUG
|
void set_debug(int value);
|
||||||
# define debug(msg, ...)
|
#ifdef DEBUG
|
||||||
#else
|
void debug(const char*msg, ...);
|
||||||
# include <sys/times.h>
|
|
||||||
# define debug(msg, ...) fprintf(stderr, "%08x %4d: " msg "\n" , \
|
|
||||||
(int) pthread_self(), (int) clock(), ##__VA_ARGS__)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CLIENT_ERROR(msg, ...) \
|
#define CLIENT_ERROR(msg, ...) \
|
||||||
|
Reference in New Issue
Block a user