Library functions are now static, RPC return value tuning, and cleanup

This commit is contained in:
Joseph Henry 2015-12-14 09:06:58 -08:00
parent 99afc74021
commit f558b088e6
5 changed files with 52 additions and 119 deletions

View File

@ -101,14 +101,14 @@ netcon: $(OBJS)
# Build netcon/liblwip.so which must be placed in ZT home for zerotier-netcon-service to work # Build netcon/liblwip.so which must be placed in ZT home for zerotier-netcon-service to work
cd netcon ; make -f make-liblwip.mk cd netcon ; make -f make-liblwip.mk
# Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility # Use gcc not clang to build standalone intercept library since gcc is typically used for libc and we want to ensure maximal ABI compatibility
cd netcon ; gcc -g -O2 -Wall -std=c99 -fPIC -DVERBOSE -DDEBUG_RPC -DCHECKS -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -nostdlib -shared -o ../libzerotierintercept.so Intercept.c -ldl cd netcon ; gcc -g -O2 -Wall -std=c99 -fPIC -DDEBUG_RPC -DCHECKS -D_GNU_SOURCE -DNETCON_INTERCEPT -I. -nostdlib -shared -o ../libzerotierintercept.so Intercept.c -ldl
ln -sf zerotier-netcon-service zerotier-cli ln -sf zerotier-netcon-service zerotier-cli
ln -sf zerotier-netcon-service zerotier-idtool ln -sf zerotier-netcon-service zerotier-idtool
install-intercept: #install-intercept:
cp libzerotierintercept.so /lib/libzerotierintercept.so # cp libzerotierintercept.so /lib/libzerotierintercept.so
ln -sf /lib/libzerotierintercept.so /lib/libzerotierintercept # ln -sf /lib/libzerotierintercept.so /lib/libzerotierintercept
/usr/bin/install -c netcon/zerotier-intercept /usr/bin # /usr/bin/install -c netcon/zerotier-intercept /usr/bin
#uninstall-intercept: #uninstall-intercept:
# rm -r /lib/libzerotierintercept.so # rm -r /lib/libzerotierintercept.so

View File

@ -62,7 +62,6 @@
/* Global Declarations */ /* Global Declarations */
static int (*realconnect)(CONNECT_SIG); static int (*realconnect)(CONNECT_SIG);
static int (*realselect)(SELECT_SIG);
static int (*realbind)(BIND_SIG); static int (*realbind)(BIND_SIG);
static int (*realaccept)(ACCEPT_SIG); static int (*realaccept)(ACCEPT_SIG);
static int (*reallisten)(LISTEN_SIG); static int (*reallisten)(LISTEN_SIG);
@ -80,7 +79,6 @@ static int (*realgetsockname)(GETSOCKNAME_SIG);
/* Exported Function Prototypes */ /* Exported Function Prototypes */
void my_init(void); void my_init(void);
int connect(CONNECT_SIG); int connect(CONNECT_SIG);
int select(SELECT_SIG);
int bind(BIND_SIG); int bind(BIND_SIG);
int accept(ACCEPT_SIG); int accept(ACCEPT_SIG);
int listen(LISTEN_SIG); int listen(LISTEN_SIG);
@ -95,18 +93,15 @@ int dup2(DUP2_SIG);
int dup3(DUP3_SIG); int dup3(DUP3_SIG);
int getsockname(GETSOCKNAME_SIG); int getsockname(GETSOCKNAME_SIG);
int connect_to_service(void); static int init_service_connection();
int init_service_connection(); static void load_symbols(void);
void load_symbols(void); static void set_up_intercept();
void set_up_intercept();
#define SERVICE_CONNECT_ATTEMPTS 30 #define SERVICE_CONNECT_ATTEMPTS 30
#define RPC_FD 1023 #define RPC_FD 1023
ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd);
/* threading */
static pthread_mutex_t lock; static pthread_mutex_t lock;
static ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd);
void handle_error(char *name, char *info, int err) void handle_error(char *name, char *info, int err)
{ {
@ -121,8 +116,6 @@ void handle_error(char *name, char *info, int err)
#endif #endif
} }
static unsigned long rpc_count = 0;
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
------------------- Intercept<--->Service Comm mechanisms----------------------- ------------------- Intercept<--->Service Comm mechanisms-----------------------
------------------------------------------------------------------------------*/ ------------------------------------------------------------------------------*/
@ -138,7 +131,7 @@ static int instance_count = 0;
/* /*
* Check for forking * Check for forking
*/ */
void checkpid() static void checkpid()
{ {
/* Do noting if not configured (sanity check -- should never get here in this case) */ /* Do noting if not configured (sanity check -- should never get here in this case) */
if (!getenv(ZT_NC_NWID_ENV)) if (!getenv(ZT_NC_NWID_ENV))
@ -156,7 +149,7 @@ void checkpid()
/* /*
* Reads a return value from the service and sets errno (if applicable) * Reads a return value from the service and sets errno (if applicable)
*/ */
int get_retval() static int get_retval()
{ {
dwr(MSG_DEBUG,"get_retval()\n"); dwr(MSG_DEBUG,"get_retval()\n");
if(fdret_sock >= 0) { if(fdret_sock >= 0) {
@ -176,7 +169,7 @@ int get_retval()
} }
/* Reads a new file descriptor from the service */ /* Reads a new file descriptor from the service */
int get_new_fd(int oversock) static int get_new_fd(int oversock)
{ {
char buf[BUF_SZ]; char buf[BUF_SZ];
int newfd; int newfd;
@ -189,11 +182,12 @@ int get_new_fd(int oversock)
return -1; return -1;
} }
#ifdef VERBOSE
static unsigned long rpc_count = 0;
#endif
int send_cmd(int rpc_fd, char *cmd) /* Sends an RPC command to the service */
static int send_cmd(int rpc_fd, char *cmd)
{ {
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
char metabuf[BUF_SZ]; // portion of buffer which contains RPC metadata for debugging char metabuf[BUF_SZ]; // portion of buffer which contains RPC metadata for debugging
@ -259,7 +253,7 @@ int send_cmd(int rpc_fd, char *cmd)
need to know if this is a regular AF_LOCAL socket or an end of a socketpair need to know if this is a regular AF_LOCAL socket or an end of a socketpair
that the service uses. We don't want to keep state in the intercept, so that the service uses. We don't want to keep state in the intercept, so
we simply ask the service via an RPC */ we simply ask the service via an RPC */
int is_mapped_to_service(int sockfd) static int is_mapped_to_service(int sockfd)
{ {
dwr(MSG_DEBUG,"is_mapped_to_service()\n"); dwr(MSG_DEBUG,"is_mapped_to_service()\n");
char cmd[BUF_SZ]; char cmd[BUF_SZ];
@ -274,7 +268,7 @@ int is_mapped_to_service(int sockfd)
------------------------------------------------------------------------------*/ ------------------------------------------------------------------------------*/
/* Sets up the connection pipes and sockets to the service */ /* Sets up the connection pipes and sockets to the service */
int init_service_connection() static int init_service_connection()
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
int tfd = -1, attempts = 0, conn_err = -1; int tfd = -1, attempts = 0, conn_err = -1;
@ -285,7 +279,6 @@ int init_service_connection()
if ((!network_id)||(strlen(network_id) != 16)) if ((!network_id)||(strlen(network_id) != 16))
return -1; return -1;
snprintf(af_sock_name,sizeof(af_sock_name),"/tmp/.ztnc_%s",network_id); snprintf(af_sock_name,sizeof(af_sock_name),"/tmp/.ztnc_%s",network_id);
instance_count++; instance_count++;
dwr(MSG_DEBUG,"init_service_connection()\n"); dwr(MSG_DEBUG,"init_service_connection()\n");
@ -293,11 +286,8 @@ int init_service_connection()
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, af_sock_name, sizeof(addr.sun_path)-1); strncpy(addr.sun_path, af_sock_name, sizeof(addr.sun_path)-1);
if ( (tfd = realsocket(AF_UNIX, SOCK_STREAM, 0)) == -1) { if((tfd = realsocket(AF_UNIX, SOCK_STREAM, 0)) == -1)
return -1; return -1;
/*perror("socket error");
exit(-1);*/
}
while(conn_err < 0 && attempts < SERVICE_CONNECT_ATTEMPTS) { while(conn_err < 0 && attempts < SERVICE_CONNECT_ATTEMPTS) {
conn_err = realconnect(tfd, (struct sockaddr*)&addr, sizeof(addr)); conn_err = realconnect(tfd, (struct sockaddr*)&addr, sizeof(addr));
@ -315,7 +305,6 @@ int init_service_connection()
} }
attempts++; attempts++;
} }
return -1; return -1;
} }
@ -323,14 +312,13 @@ int init_service_connection()
------------------------ ctors and dtors (and friends)------------------------- ------------------------ ctors and dtors (and friends)-------------------------
------------------------------------------------------------------------------*/ ------------------------------------------------------------------------------*/
void my_dest(void) __attribute__ ((destructor)); static void my_dest(void) __attribute__ ((destructor));
void my_dest(void) { static void my_dest(void) {
dwr(MSG_DEBUG,"closing connections to service...\n"); dwr(MSG_DEBUG,"closing connections to service...\n");
//close(fdret_sock);
pthread_mutex_destroy(&lock); pthread_mutex_destroy(&lock);
} }
void load_symbols(void) static void load_symbols(void)
{ {
if(thispid == getpid()) { if(thispid == getpid()) {
dwr(MSG_DEBUG,"detected duplicate call to global constructor (pid=%d).\n", thispid); dwr(MSG_DEBUG,"detected duplicate call to global constructor (pid=%d).\n", thispid);
@ -343,7 +331,6 @@ void load_symbols(void)
reallisten = dlsym(RTLD_NEXT, "listen"); reallisten = dlsym(RTLD_NEXT, "listen");
realsocket = dlsym(RTLD_NEXT, "socket"); realsocket = dlsym(RTLD_NEXT, "socket");
realbind = dlsym(RTLD_NEXT, "bind"); realbind = dlsym(RTLD_NEXT, "bind");
realselect = dlsym(RTLD_NEXT, "select");
realsetsockopt = dlsym(RTLD_NEXT, "setsockopt"); realsetsockopt = dlsym(RTLD_NEXT, "setsockopt");
realgetsockopt = dlsym(RTLD_NEXT, "getsockopt"); realgetsockopt = dlsym(RTLD_NEXT, "getsockopt");
realaccept4 = dlsym(RTLD_NEXT, "accept4"); realaccept4 = dlsym(RTLD_NEXT, "accept4");
@ -356,19 +343,17 @@ void load_symbols(void)
} }
/* Private Function Prototypes */ /* Private Function Prototypes */
void _init(void) __attribute__ ((constructor)); static void _init(void) __attribute__ ((constructor));
void _init(void) { set_up_intercept(); } static void _init(void) { set_up_intercept(); }
/* get symbols and initialize mutexes */ /* get symbols and initialize mutexes */
void set_up_intercept() static void set_up_intercept()
{ {
/* If ZT_NC_NWID_ENV is not set, do nothing -- not configured */ /* If ZT_NC_NWID_ENV is not set, do nothing -- not configured */
if (!getenv(ZT_NC_NWID_ENV)) if (!getenv(ZT_NC_NWID_ENV))
return; return;
/* Hook/intercept Posix net API symbols */ /* Hook/intercept Posix net API symbols */
load_symbols(); load_symbols();
if(pthread_mutex_init(&lock, NULL) != 0) { if(pthread_mutex_init(&lock, NULL) != 0) {
dwr(MSG_ERROR, "error while initializing service call mutex\n"); dwr(MSG_ERROR, "error while initializing service call mutex\n");
} }
@ -617,22 +602,6 @@ int connect(CONNECT_SIG)
return send_cmd(fdret_sock, cmd); return send_cmd(fdret_sock, cmd);
} }
/*------------------------------------------------------------------------------
---------------------------------- select() ------------------------------------
------------------------------------------------------------------------------*/
/* int n, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout */
int select(SELECT_SIG)
{
if(realselect == NULL){
dwr(MSG_ERROR, "select(): SYMBOL NOT FOUND.\n");
return -1;
}
/* dwr(MSG_DEBUG,"select():\n"); */
return realselect(n, readfds, writefds, exceptfds, timeout);
}
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
------------------------------------ bind() ------------------------------------ ------------------------------------ bind() ------------------------------------
------------------------------------------------------------------------------*/ ------------------------------------------------------------------------------*/
@ -691,7 +660,6 @@ int bind(BIND_SIG)
return send_cmd(fdret_sock, cmd); return send_cmd(fdret_sock, cmd);
} }
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
----------------------------------- accept4() ---------------------------------- ----------------------------------- accept4() ----------------------------------
------------------------------------------------------------------------------*/ ------------------------------------------------------------------------------*/
@ -790,10 +758,6 @@ int accept(ACCEPT_SIG)
/* TODO: also get address info */ /* TODO: also get address info */
char cmd[BUF_SZ]; char cmd[BUF_SZ];
if(realaccept == NULL) {
handle_error("accept", "Unresolved symbol [accept]", -1);
return -1;
}
/* The following line is required for libuv/nodejs to accept connections properly, /* The following line is required for libuv/nodejs to accept connections properly,
however, this has the side effect of causing certain webservers to max out the CPU however, this has the side effect of causing certain webservers to max out the CPU
@ -803,8 +767,7 @@ int accept(ACCEPT_SIG)
if(new_conn_socket > 0) if(new_conn_socket > 0)
{ {
//new_conn_socket = get_new_fd(fdret_sock); dwr(MSG_DEBUG, "accept(): RX: fd = (%d) over (%d)\n", new_conn_socket, fdret_sock);
dwr(MSG_DEBUG, " accept(): RX: fd = (%d) over (%d)\n", new_conn_socket, fdret_sock);
/* Send our local-fd number back to service so it can complete its mapping table */ /* Send our local-fd number back to service so it can complete its mapping table */
memset(cmd, '\0', BUF_SZ); memset(cmd, '\0', BUF_SZ);
cmd[0] = RPC_MAP; cmd[0] = RPC_MAP;
@ -820,19 +783,11 @@ int accept(ACCEPT_SIG)
} }
errno = ERR_OK; errno = ERR_OK;
dwr(MSG_DEBUG,"accept()=%d\n", new_conn_socket); dwr(MSG_DEBUG,"accept()=%d\n", new_conn_socket);
handle_error("accept", "", new_conn_socket);
return new_conn_socket; /* OK */ return new_conn_socket; /* OK */
} }
errno = EAGAIN; /* necessary? */ errno = EAGAIN; /* necessary? */
handle_error("accept", "EAGAIN - Error reading signal byte from service", -1); handle_error("accept", "EAGAIN - Error reading signal byte from service", -1);
return -EAGAIN; return -EAGAIN;
/* Prevents libuv in nodejs from accepting properly (it looks for a -EAGAIN) */
/*
errno = EBADF;
handle_error("accept", "EBADF - Error reading signal byte from service", -1);
return -1;
*/
} }
@ -840,8 +795,7 @@ int accept(ACCEPT_SIG)
------------------------------------- listen()---------------------------------- ------------------------------------- listen()----------------------------------
------------------------------------------------------------------------------*/ ------------------------------------------------------------------------------*/
/* int sockfd, int backlog /* int sockfd, int backlog */
listen() intercept function */
int listen(LISTEN_SIG) int listen(LISTEN_SIG)
{ {
if(reallisten == NULL){ if(reallisten == NULL){
@ -913,20 +867,6 @@ int clone(CLONE_SIG)
return err; return err;
} }
/*------------------------------------------------------------------------------
-------------------------------------- poll()-----------------------------------
------------------------------------------------------------------------------*/
/* struct pollfd *fds, nfds_t nfds, int timeout */
/*
int poll(POLL_SIG)
{
dwr(MSG_DEBUG,"poll()\n");
return realpoll(fds, nfds, timeout);
}
*/
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
------------------------------------- close()----------------------------------- ------------------------------------- close()-----------------------------------
------------------------------------------------------------------------------*/ ------------------------------------------------------------------------------*/
@ -1001,10 +941,10 @@ int dup3(DUP3_SIG)
int getsockname(GETSOCKNAME_SIG) int getsockname(GETSOCKNAME_SIG)
{ {
if (realgetsockname == NULL) { if (realgetsockname == NULL) {
dwr(MSG_ERROR, "getsockname(): SYMBOL NOT FOUND.\n"); dwr(MSG_ERROR, "getsockname(): SYMBOL NOT FOUND. \n");
return -1; return -1;
} }
return realgetsockname(sockfd, addr, addrlen); /* return realgetsockname(sockfd, addr, addrlen); */
/* assemble command */ /* assemble command */
char cmd[BUF_SZ]; char cmd[BUF_SZ];
struct getsockname_st rpc_st; struct getsockname_st rpc_st;
@ -1018,7 +958,7 @@ return realgetsockname(sockfd, addr, addrlen);
char addrbuf[sizeof(struct sockaddr)]; char addrbuf[sizeof(struct sockaddr)];
memset(addrbuf, '\0', sizeof(struct sockaddr)); memset(addrbuf, '\0', sizeof(struct sockaddr));
read(fdret_sock, &addrbuf, sizeof(struct sockaddr)); // read address from service read(fdret_sock, &addrbuf, sizeof(struct sockaddr)); /* read address from service */
memcpy(addr, addrbuf, sizeof(struct sockaddr)); memcpy(addr, addrbuf, sizeof(struct sockaddr));
*addrlen = sizeof(struct sockaddr); *addrlen = sizeof(struct sockaddr);
@ -1034,7 +974,6 @@ return realgetsockname(sockfd, addr, addrlen);
int port = connaddr->sin_port; int port = connaddr->sin_port;
dwr(MSG_ERROR, " handle_getsockname(): returning address: %d.%d.%d.%d: %d\n", d[0],d[1],d[2],d[3], port); dwr(MSG_ERROR, " handle_getsockname(): returning address: %d.%d.%d.%d: %d\n", d[0],d[1],d[2],d[3], port);
return 0; return 0;
} }

View File

@ -46,17 +46,15 @@
#define RPC_UNDEFINED 0 #define RPC_UNDEFINED 0
#define RPC_CONNECT 1 #define RPC_CONNECT 1
#define RPC_CONNECT_SOCKARG 2 #define RPC_CONNECT_SOCKARG 2
#define RPC_SELECT 3 #define RPC_CLOSE 3
#define RPC_POLL 4 #define RPC_READ 4
#define RPC_CLOSE 5 #define RPC_WRITE 5
#define RPC_READ 6 #define RPC_BIND 6
#define RPC_WRITE 7 #define RPC_ACCEPT 7
#define RPC_BIND 8 #define RPC_LISTEN 8
#define RPC_ACCEPT 9 #define RPC_SOCKET 9
#define RPC_LISTEN 10 #define RPC_SHUTDOWN 10
#define RPC_SOCKET 11 #define RPC_GETSOCKNAME 11
#define RPC_SHUTDOWN 12
#define RPC_GETSOCKNAME 13
/* Administration RPC codes */ /* Administration RPC codes */
#define RPC_MAP 20 /* Give the service the value we "see" for the new buffer fd */ #define RPC_MAP 20 /* Give the service the value we "see" for the new buffer fd */
@ -183,9 +181,7 @@ struct getsockname_st
socklen_t addrlen; socklen_t addrlen;
}; };
#define CONNECT_SOCKARG struct sockaddr * #define CONNECT_SOCKARG struct sockaddr *
#define SELECT_SIG int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout
#define IOCTL_SIG int __fd, unsigned long int __request, ... #define IOCTL_SIG int __fd, unsigned long int __request, ...
#define FCNTL_SIG int __fd, int __cmd, ... #define FCNTL_SIG int __fd, int __cmd, ...
#define DAEMON_SIG int nochdir, int noclose #define DAEMON_SIG int nochdir, int noclose
@ -193,9 +189,7 @@ struct getsockname_st
#define GETSOCKOPT_SIG int sockfd, int level, int optname, void *optval, socklen_t *optlen #define GETSOCKOPT_SIG int sockfd, int level, int optname, void *optval, socklen_t *optlen
#define SYSCALL_SIG long number, ... #define SYSCALL_SIG long number, ...
#define CLONE_SIG int (*fn)(void *), void *child_stack, int flags, void *arg, ... #define CLONE_SIG int (*fn)(void *), void *child_stack, int flags, void *arg, ...
#define POLL_SIG struct pollfd *fds, nfds_t nfds, int timeout
#define GETSOCKNAME_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen #define GETSOCKNAME_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen
#define DUP2_SIG int oldfd, int newfd #define DUP2_SIG int oldfd, int newfd
#define DUP3_SIG int oldfd, int newfd, int flags #define DUP3_SIG int oldfd, int newfd, int flags

View File

@ -581,7 +581,6 @@ void NetconEthernetTap::threadMain()
} }
} }
} }
//dwr(4, " tap_thread(): tcp_conns = %d, rpc_socks = %d\n", tcp_connections.size(), rpc_sockets.size());
for(size_t i=0, associated = 0; i<rpc_sockets.size(); i++, associated = 0) { for(size_t i=0, associated = 0; i<rpc_sockets.size(); i++, associated = 0) {
for(size_t j=0; j<tcp_connections.size(); j++) { for(size_t j=0; j<tcp_connections.size(); j++) {
if (tcp_connections[j]->rpcSock == rpc_sockets[i]) if (tcp_connections[j]->rpcSock == rpc_sockets[i])
@ -898,7 +897,7 @@ err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf
if(p == NULL) { if(p == NULL) {
if(l->conn && !l->conn->listening) { if(l->conn && !l->conn->listening) {
dwr(MSG_INFO, " nc_recved(): closing connection\n"); dwr(MSG_INFO, " nc_recved(): closing connection\n");
// l->tap->closeConnection(l->conn); l->tap->closeConnection(l->conn);
return ERR_ABRT; return ERR_ABRT;
} }
else { else {

View File

@ -39,12 +39,13 @@
#include <fcntl.h> #include <fcntl.h>
#define DEBUG_LEVEL 3 #define DEBUG_LEVEL 0
#define MSG_ERROR 0 // Errors #define MSG_WARNING 0
#define MSG_INFO 1 // Information which is generally useful to any user #define MSG_ERROR 1 // Errors
#define MSG_DEBUG 2 // Information which is only useful to someone debugging #define MSG_INFO 2 // Information which is generally useful to any user
#define MSG_DEBUG_EXTRA 3 // If nothing in your world makes sense #define MSG_DEBUG 3 // Information which is only useful to someone debugging
#define MSG_DEBUG_EXTRA 4 // If nothing in your world makes sense
#ifdef NETCON_INTERCEPT #ifdef NETCON_INTERCEPT
@ -103,10 +104,10 @@ void print_addr(struct sockaddr *addr)
} }
#endif #endif
ssize_t sock_fd_write(int sock, int fd); static ssize_t sock_fd_write(int sock, int fd);
ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); static ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd);
ssize_t sock_fd_write(int sock, int fd) static ssize_t sock_fd_write(int sock, int fd)
{ {
ssize_t size; ssize_t size;
struct msghdr msg; struct msghdr msg;
@ -147,7 +148,7 @@ ssize_t sock_fd_write(int sock, int fd)
return size; return size;
} }
ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd) static ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd)
{ {
ssize_t size; ssize_t size;
if (fd) { if (fd) {