Implemented SSL_accept() timeout handling
This commit is contained in:
parent
ec4bd54e53
commit
fe82425de3
@ -425,8 +425,9 @@ int main ()
|
||||
memset (&tcp_bind, 0, STIO_SIZEOF(tcp_bind));
|
||||
stio_sckaddr_initforip4 (&tcp_bind.localaddr, 1235, STIO_NULL);
|
||||
tcp_bind.options = STIO_DEV_SCK_BIND_REUSEADDR | /*STIO_DEV_SCK_BIND_REUSEPORT |*/ STIO_DEV_SCK_BIND_SSL;
|
||||
tcp_bind.certfile = STIO_MT("localhost.crt");
|
||||
tcp_bind.keyfile = STIO_MT("localhost.key");
|
||||
tcp_bind.ssl_certfile = STIO_MT("localhost.crt");
|
||||
tcp_bind.ssl_keyfile = STIO_MT("localhost.key");
|
||||
//stio_inittime (&tcp_bind.ssl_accept_tmout, 0, 1);
|
||||
|
||||
if (stio_dev_sck_bind (tcp[2],&tcp_bind) <= -1)
|
||||
{
|
||||
|
@ -285,6 +285,18 @@ static void tmr_connect_handle (stio_t* stio, const stio_ntime_t* now, stio_tmrj
|
||||
}
|
||||
}
|
||||
|
||||
static void ssl_accept_timedout (stio_t* stio, const stio_ntime_t* now, stio_tmrjob_t* job)
|
||||
{
|
||||
stio_dev_sck_t* rdev = (stio_dev_sck_t*)job->ctx;
|
||||
|
||||
STIO_ASSERT (IS_STATEFUL(rdev));
|
||||
|
||||
if (rdev->state & STIO_DEV_SCK_ACCEPTING_SSL)
|
||||
{
|
||||
stio_dev_sck_halt(rdev);
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================== */
|
||||
|
||||
static int dev_sck_make (stio_dev_t* dev, void* ctx)
|
||||
@ -301,7 +313,7 @@ static int dev_sck_make (stio_dev_t* dev, void* ctx)
|
||||
rdev->on_write = arg->on_write;
|
||||
rdev->on_read = arg->on_read;
|
||||
rdev->type = arg->type;
|
||||
rdev->tmridx_connect = STIO_TMRIDX_INVALID;
|
||||
rdev->tmrjob_index = STIO_TMRIDX_INVALID;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -324,6 +336,8 @@ static int dev_sck_make_client (stio_dev_t* dev, void* ctx)
|
||||
* after a client socket has been created. */
|
||||
|
||||
rdev->sck = *sck;
|
||||
rdev->tmrjob_index = STIO_TMRIDX_INVALID;
|
||||
|
||||
if (stio_makesckasync (rdev->stio, rdev->sck) <= -1) return -1;
|
||||
#if defined(FD_CLOEXEC)
|
||||
{
|
||||
@ -350,16 +364,16 @@ static int dev_sck_kill (stio_dev_t* dev, int force)
|
||||
if (rdev->on_disconnect) rdev->on_disconnect (rdev);
|
||||
}
|
||||
|
||||
if (rdev->tmridx_connect != STIO_TMRIDX_INVALID)
|
||||
if (rdev->tmrjob_index != STIO_TMRIDX_INVALID)
|
||||
{
|
||||
stio_deltmrjob (dev->stio, rdev->tmridx_connect);
|
||||
STIO_ASSERT (rdev->tmridx_connect == STIO_TMRIDX_INVALID);
|
||||
stio_deltmrjob (dev->stio, rdev->tmrjob_index);
|
||||
STIO_ASSERT (rdev->tmrjob_index == STIO_TMRIDX_INVALID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
STIO_ASSERT (rdev->state == 0);
|
||||
STIO_ASSERT (rdev->tmridx_connect == STIO_TMRIDX_INVALID);
|
||||
STIO_ASSERT (rdev->tmrjob_index == STIO_TMRIDX_INVALID);
|
||||
}
|
||||
|
||||
#if defined(USE_SSL)
|
||||
@ -369,10 +383,10 @@ static int dev_sck_kill (stio_dev_t* dev, int force)
|
||||
SSL_free ((SSL*)rdev->ssl);
|
||||
rdev->ssl = STIO_NULL;
|
||||
}
|
||||
if (!(rdev->state & (STIO_DEV_SCK_ACCEPTED | STIO_DEV_SCK_ACCEPTING_SSL)) && rdev->sslctx)
|
||||
if (!(rdev->state & (STIO_DEV_SCK_ACCEPTED | STIO_DEV_SCK_ACCEPTING_SSL)) && rdev->ssl_ctx)
|
||||
{
|
||||
SSL_CTX_free ((SSL_CTX*)rdev->sslctx);
|
||||
rdev->sslctx = STIO_NULL;
|
||||
SSL_CTX_free ((SSL_CTX*)rdev->ssl_ctx);
|
||||
rdev->ssl_ctx = STIO_NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -622,7 +636,7 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
if (bnd->options & STIO_DEV_SCK_BIND_SSL)
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
if (!bnd->certfile || !bnd->keyfile)
|
||||
if (!bnd->ssl_certfile || !bnd->ssl_keyfile)
|
||||
{
|
||||
rdev->stio->errnum = STIO_EINVAL;
|
||||
return -1;
|
||||
@ -635,8 +649,8 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_certificate_file (ssl_ctx, bnd->certfile, SSL_FILETYPE_PEM) == 0 ||
|
||||
SSL_CTX_use_PrivateKey_file (ssl_ctx, bnd->keyfile, SSL_FILETYPE_PEM) == 0 ||
|
||||
if (SSL_CTX_use_certificate_file (ssl_ctx, bnd->ssl_certfile, SSL_FILETYPE_PEM) == 0 ||
|
||||
SSL_CTX_use_PrivateKey_file (ssl_ctx, bnd->ssl_keyfile, SSL_FILETYPE_PEM) == 0 ||
|
||||
SSL_CTX_check_private_key (ssl_ctx) == 0 /*||
|
||||
SSL_CTX_use_certificate_chain_file (ssl_ctx, bnd->chainfile) == 0*/)
|
||||
{
|
||||
@ -645,7 +659,10 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2);
|
||||
SSL_CTX_set_read_ahead (ssl_ctx, 0);
|
||||
|
||||
rdev->ssl_accept_tmout = bnd->ssl_accept_tmout;
|
||||
#else
|
||||
rdev->stio->errnum = STIO_ENOIMPL;
|
||||
return -1;
|
||||
@ -667,7 +684,7 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
rdev->localaddr = bnd->localaddr;
|
||||
|
||||
#if defined(USE_SSL)
|
||||
rdev->sslctx = ssl_ctx;
|
||||
rdev->ssl_ctx = ssl_ctx;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@ -680,7 +697,10 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
stio_scklen_t sl;
|
||||
stio_sckaddr_t localaddr;
|
||||
int x;
|
||||
|
||||
#if defined(USE_SSL)
|
||||
SSL_CTX* ssl_ctx = STIO_NULL;
|
||||
SSL* ssl = STIO_NULL;
|
||||
#endif
|
||||
if (!IS_STATEFUL(rdev))
|
||||
{
|
||||
dev->stio->errnum = STIO_ENOCAPA;
|
||||
@ -695,6 +715,18 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn->options & STIO_DEV_SCK_CONNECT_SSL)
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ssl_ctx)
|
||||
{
|
||||
rdev->stio->errnum = STIO_ESYSERR;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* the socket is already non-blocking */
|
||||
x = connect (rdev->sck, sa, sl);
|
||||
if (x == -1)
|
||||
@ -705,18 +737,18 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
{
|
||||
stio_tmrjob_t tmrjob;
|
||||
|
||||
if (!stio_isnegtime(&conn->tmout))
|
||||
if (stio_ispostime(&conn->tmout))
|
||||
{
|
||||
STIO_MEMSET (&tmrjob, 0, STIO_SIZEOF(tmrjob));
|
||||
tmrjob.ctx = rdev;
|
||||
stio_gettime (&tmrjob.when);
|
||||
stio_addtime (&tmrjob.when, &conn->tmout, &tmrjob.when);
|
||||
tmrjob.handler = tmr_connect_handle;
|
||||
tmrjob.idxptr = &rdev->tmridx_connect;
|
||||
tmrjob.idxptr = &rdev->tmrjob_index;
|
||||
|
||||
STIO_ASSERT (rdev->tmridx_connect == STIO_TMRIDX_INVALID);
|
||||
rdev->tmridx_connect = stio_instmrjob (rdev->stio, &tmrjob);
|
||||
if (rdev->tmridx_connect == STIO_TMRIDX_INVALID)
|
||||
STIO_ASSERT (rdev->tmrjob_index == STIO_TMRIDX_INVALID);
|
||||
rdev->tmrjob_index = stio_instmrjob (rdev->stio, &tmrjob);
|
||||
if (rdev->tmrjob_index == STIO_TMRIDX_INVALID)
|
||||
{
|
||||
stio_dev_watch ((stio_dev_t*)rdev, STIO_DEV_WATCH_UPDATE, STIO_DEV_EVENT_IN);
|
||||
/* event manipulation failure can't be handled properly. so ignore it.
|
||||
@ -729,11 +761,29 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
rdev->remoteaddr = conn->remoteaddr;
|
||||
rdev->on_connect = conn->on_connect;
|
||||
rdev->on_disconnect = conn->on_disconnect;
|
||||
#if defined(USE_SSL)
|
||||
rdev->ssl_ctx = ssl_ctx;
|
||||
#endif
|
||||
|
||||
ssl = SSL_new (ssl_ctx);
|
||||
if (!ssl)
|
||||
{
|
||||
}
|
||||
|
||||
SSL_set_fd (ssl, rdev->sck);
|
||||
|
||||
if (SSL_connect(ssl) <= 0)
|
||||
{
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
rdev->stio->errnum = stio_syserrtoerrnum(errno);
|
||||
#if defined(USE_SSL)
|
||||
if (ssl_ctx) SSL_CTX_free (ssl_ctx);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -742,9 +792,13 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
|
||||
rdev->remoteaddr = conn->remoteaddr;
|
||||
rdev->on_connect = conn->on_connect;
|
||||
rdev->on_disconnect = conn->on_disconnect;
|
||||
#if defined(USE_SSL)
|
||||
rdev->ssl_ctx = ssl_ctx;
|
||||
#endif
|
||||
|
||||
sl = STIO_SIZEOF(localaddr);
|
||||
if (getsockname (rdev->sck, (struct sockaddr*)&localaddr, &sl) == 0) rdev->localaddr = localaddr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -875,10 +929,10 @@ static int dev_evcb_sck_ready_stateful (stio_dev_t* dev, int events)
|
||||
|
||||
if (stio_dev_watch ((stio_dev_t*)rdev, STIO_DEV_WATCH_RENEW, 0) <= -1) return -1;
|
||||
|
||||
if (rdev->tmridx_connect != STIO_TMRIDX_INVALID)
|
||||
if (rdev->tmrjob_index != STIO_TMRIDX_INVALID)
|
||||
{
|
||||
stio_deltmrjob (rdev->stio, rdev->tmridx_connect);
|
||||
STIO_ASSERT (rdev->tmridx_connect == STIO_TMRIDX_INVALID);
|
||||
stio_deltmrjob (rdev->stio, rdev->tmrjob_index);
|
||||
STIO_ASSERT (rdev->tmrjob_index == STIO_TMRIDX_INVALID);
|
||||
}
|
||||
|
||||
addrlen = STIO_SIZEOF(localaddr);
|
||||
@ -946,7 +1000,7 @@ static int dev_evcb_sck_ready_stateful (stio_dev_t* dev, int events)
|
||||
STIO_ASSERT (clidev->sck == clisck);
|
||||
|
||||
clidev->dev_capa |= STIO_DEV_CAPA_IN | STIO_DEV_CAPA_OUT | STIO_DEV_CAPA_STREAM | STIO_DEV_CAPA_OUT_QUEUED;
|
||||
if (rdev->sslctx)
|
||||
if (rdev->ssl_ctx)
|
||||
clidev->state |= STIO_DEV_SCK_ACCEPTING_SSL;
|
||||
else
|
||||
clidev->state |= STIO_DEV_SCK_ACCEPTED;
|
||||
@ -1000,24 +1054,42 @@ static int dev_evcb_sck_ready_stateful (stio_dev_t* dev, int events)
|
||||
clidev->on_write = rdev->on_write;
|
||||
clidev->on_read = rdev->on_read;
|
||||
|
||||
STIO_ASSERT (clidev->tmrjob_index == STIO_TMRIDX_INVALID);
|
||||
|
||||
if (clidev->state & STIO_DEV_SCK_ACCEPTED)
|
||||
{
|
||||
STIO_ASSERT (!(clidev->state & STIO_DEV_SCK_ACCEPTING_SSL));
|
||||
clidev->tmridx_connect = STIO_TMRIDX_INVALID;
|
||||
if (clidev->on_connect(clidev) <= -1) stio_dev_sck_halt (clidev);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
STIO_ASSERT (clidev->state & STIO_DEV_SCK_ACCEPTING_SSL);
|
||||
/* actual SSL acceptance must be completed in the client device */
|
||||
|
||||
/* let the client device know the SSL context to use */
|
||||
clidev->sslctx = rdev->sslctx;
|
||||
clidev->ssl_ctx = rdev->ssl_ctx;
|
||||
|
||||
/* TODO: secure accept timeout handling */
|
||||
/* clidev->tmridx_connect??? */
|
||||
clidev->tmridx_connect = STIO_TMRIDX_INVALID;
|
||||
printf ("TO DO SSL ...............................\n");
|
||||
if (stio_ispostime(&rdev->ssl_accept_tmout))
|
||||
{
|
||||
stio_tmrjob_t tmrjob;
|
||||
|
||||
STIO_MEMSET (&tmrjob, 0, STIO_SIZEOF(tmrjob));
|
||||
tmrjob.ctx = clidev;
|
||||
stio_gettime (&tmrjob.when);
|
||||
stio_addtime (&tmrjob.when, &rdev->ssl_accept_tmout, &tmrjob.when);
|
||||
|
||||
tmrjob.handler = ssl_accept_timedout;
|
||||
tmrjob.idxptr = &clidev->tmrjob_index;
|
||||
|
||||
clidev->tmrjob_index = stio_instmrjob (clidev->stio, &tmrjob);
|
||||
if (clidev->tmrjob_index == STIO_TMRIDX_INVALID)
|
||||
{
|
||||
/* TODO: call a warning callback */
|
||||
printf ("SSL ACCEPT TIMEOUT CAN't BE HONORED....\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0; /* success but don't invoke on_read() */
|
||||
@ -1035,7 +1107,7 @@ printf ("SSL IN ACCPEING>.. %p.......................\n", rdev);
|
||||
SSL* ssl;
|
||||
|
||||
printf ("SSL CREATED.....................\n");
|
||||
ssl = SSL_new (rdev->sslctx);
|
||||
ssl = SSL_new (rdev->ssl_ctx);
|
||||
if (!ssl)
|
||||
{
|
||||
printf ("SSL ERROR 1>..................... %s\n", ERR_reason_error_string(ERR_get_error()));
|
||||
@ -1070,6 +1142,12 @@ printf ("SSL ERROR 3>..................... %s\n", ERR_reason_error_string(err));
|
||||
}
|
||||
|
||||
printf ("SSL ACCEPTED.....................\n");
|
||||
if (rdev->tmrjob_index != STIO_TMRIDX_INVALID)
|
||||
{
|
||||
stio_deltmrjob (rdev->stio, rdev->tmrjob_index);
|
||||
rdev->tmrjob_index = STIO_TMRIDX_INVALID;
|
||||
}
|
||||
|
||||
rdev->state &= ~STIO_DEV_SCK_ACCEPTING_SSL;
|
||||
rdev->state |= STIO_DEV_SCK_ACCEPTED;
|
||||
if (rdev->on_connect(rdev) <= -1) stio_dev_sck_halt (rdev);
|
||||
|
@ -164,13 +164,22 @@ struct stio_dev_sck_bind_t
|
||||
stio_sckaddr_t localaddr;
|
||||
/* TODO: add device name for BIND_TO_DEVICE */
|
||||
|
||||
const stio_mchar_t* certfile;
|
||||
const stio_mchar_t* keyfile;
|
||||
const stio_mchar_t* ssl_certfile;
|
||||
const stio_mchar_t* ssl_keyfile;
|
||||
stio_ntime_t ssl_accept_tmout;
|
||||
};
|
||||
|
||||
enum stio_def_sck_connect_option_t
|
||||
{
|
||||
STIO_DEV_SCK_CONNECT_SSL = (1 << 15)
|
||||
};
|
||||
typedef enum stio_dev_sck_connect_option_t stio_dev_sck_connect_option_t;
|
||||
|
||||
|
||||
typedef struct stio_dev_sck_connect_t stio_dev_sck_connect_t;
|
||||
struct stio_dev_sck_connect_t
|
||||
{
|
||||
int options;
|
||||
stio_sckaddr_t remoteaddr;
|
||||
stio_ntime_t tmout; /* connect timeout */
|
||||
stio_dev_sck_on_connect_t on_connect;
|
||||
@ -188,9 +197,9 @@ struct stio_dev_sck_listen_t
|
||||
typedef struct stio_dev_sck_accept_t stio_dev_sck_accept_t;
|
||||
struct stio_dev_sck_accept_t
|
||||
{
|
||||
stio_syshnd_t sck;
|
||||
stio_syshnd_t sck;
|
||||
/* TODO: add timeout */
|
||||
stio_sckaddr_t remoteaddr;
|
||||
stio_sckaddr_t remoteaddr;
|
||||
};
|
||||
|
||||
struct stio_dev_sck_t
|
||||
@ -200,23 +209,15 @@ struct stio_dev_sck_t
|
||||
stio_dev_sck_type_t type;
|
||||
stio_sckhnd_t sck;
|
||||
|
||||
void* sslctx;
|
||||
void* ssl_ctx;
|
||||
void* ssl;
|
||||
|
||||
stio_dev_sck_on_write_t on_write;
|
||||
stio_dev_sck_on_read_t on_read;
|
||||
stio_ntime_t ssl_accept_tmout;
|
||||
|
||||
int state;
|
||||
|
||||
/* return 0 on succes, -1 on failure.
|
||||
* called on a new tcp device for an accepted client or
|
||||
* on a tcp device conntected to a remote server */
|
||||
stio_dev_sck_on_connect_t on_connect;
|
||||
stio_dev_sck_on_disconnect_t on_disconnect;
|
||||
stio_tmridx_t tmridx_connect;
|
||||
|
||||
/* peer address for a stateful stream socket. valid if one of the
|
||||
/* remote peer address for a stateful stream socket. valid if one of the
|
||||
* followings is set in state:
|
||||
* STIO_DEV_TCP_ACCEPTING_SSL
|
||||
* STIO_DEV_TCP_ACCEPTED
|
||||
* STIO_DEV_TCP_CONNECTED
|
||||
* STIO_DEV_TCP_CONNECTING
|
||||
@ -225,8 +226,25 @@ struct stio_dev_sck_t
|
||||
* a stateless socket */
|
||||
stio_sckaddr_t remoteaddr;
|
||||
|
||||
/* local socket address */
|
||||
stio_sckaddr_t localaddr;
|
||||
|
||||
/* original destination address */
|
||||
stio_sckaddr_t orgdstaddr;
|
||||
|
||||
stio_dev_sck_on_write_t on_write;
|
||||
stio_dev_sck_on_read_t on_read;
|
||||
|
||||
/* return 0 on succes, -1 on failure.
|
||||
* called on a new tcp device for an accepted client or
|
||||
* on a tcp device conntected to a remote server */
|
||||
stio_dev_sck_on_connect_t on_connect;
|
||||
stio_dev_sck_on_disconnect_t on_disconnect;
|
||||
|
||||
/* timer job index for handling
|
||||
* - connect() timeout for a connecting socket.
|
||||
* - SSL_accept() timeout for a socket accepting SSL */
|
||||
stio_tmridx_t tmrjob_index;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -1045,7 +1045,7 @@ enqueue_data:
|
||||
q->olen = len;
|
||||
STIO_MEMCPY (q->ptr, uptr, urem);
|
||||
|
||||
if (tmout && !stio_isnegtime(tmout))
|
||||
if (tmout && stio_ispostime(tmout))
|
||||
{
|
||||
stio_tmrjob_t tmrjob;
|
||||
|
||||
@ -1060,7 +1060,6 @@ enqueue_data:
|
||||
if (q->tmridx == STIO_TMRIDX_INVALID)
|
||||
{
|
||||
STIO_MMGR_FREE (dev->stio->mmgr, q);
|
||||
printf ("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZz222222\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1073,7 +1072,6 @@ printf ("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZz222222\n");
|
||||
{
|
||||
unlink_wq (dev->stio, q);
|
||||
STIO_MMGR_FREE (dev->stio->mmgr, q);
|
||||
printf ("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZz11111\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1082,7 +1080,6 @@ printf ("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZz11111\n");
|
||||
dev->stio->renew_watch = 1;
|
||||
}
|
||||
|
||||
printf ("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZz\n");
|
||||
return 0; /* request pused to a write queue. */
|
||||
}
|
||||
|
||||
|
@ -512,6 +512,9 @@ STIO_EXPORT void stio_dev_halt (
|
||||
/* if time has been normalized properly, nsec must be equal to or
|
||||
* greater than 0. */
|
||||
#define stio_isnegtime(x) ((x)->sec < 0)
|
||||
#define stio_ispostime(x) ((x)->sec > 0 || ((x)->sec == 0 && (x)->nsec > 0))
|
||||
#define stio_iszerotime(x) ((x)->sec == 0 && (x)->nsec == 0)
|
||||
|
||||
|
||||
/**
|
||||
* The stio_gettime() function gets the current time.
|
||||
|
Loading…
Reference in New Issue
Block a user