/*
* ProFTPD - FTP server daemon
* Copyright (c) 2001, 2002, 2003 The ProFTPD Project team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* As a special exemption, the ProFTPD Project team and other respective
* copyright holders give permission to link this program with OpenSSL, and
* distribute the resulting executable, without including the source code for
* OpenSSL in the source distribution.
*/
/* NetIO routines
* $Id: netio.c,v 1.21 2004/10/09 20:46:22 castaglia Exp $
*/
#include "conf.h"
#include <signal.h>
#ifndef IAC
#define IAC 255
#endif
#ifndef DONT
#define DONT 254
#endif
#ifndef DO
#define DO 253
#endif
#ifndef WONT
#define WONT 252
#endif
#ifndef WILL
#define WILL 251
#endif
static pr_netio_t *core_ctrl_netio = NULL, *ctrl_netio = NULL;
static pr_netio_t *core_data_netio = NULL, *data_netio = NULL;
static pr_netio_t *core_othr_netio = NULL, *othr_netio = NULL;
static pr_netio_stream_t *netio_stream_alloc(pool *parent_pool) {
pool *netio_pool = NULL;
pr_netio_stream_t *nstrm = NULL;
if (!parent_pool) {
errno = EINVAL;
return NULL;
}
netio_pool = make_sub_pool(parent_pool);
nstrm = pcalloc(netio_pool, sizeof(pr_netio_stream_t));
nstrm->strm_pool = netio_pool;
nstrm->strm_fd = -1;
nstrm->strm_mode = 0;
nstrm->strm_flags = 0;
nstrm->strm_buf = NULL;
nstrm->strm_data = NULL;
nstrm->strm_errno = 0;
return nstrm;
}
static pr_buffer_t *netio_buffer_alloc(pr_netio_stream_t *nstrm) {
pr_buffer_t *pbuf = NULL;
pbuf = pcalloc(nstrm->strm_pool, sizeof(pr_buffer_t));
/* Allocate a buffer. */
pbuf->buf = pcalloc(nstrm->strm_pool, PR_TUNABLE_BUFFER_SIZE);
pbuf->buflen = PR_TUNABLE_BUFFER_SIZE;
/* Position the offset at the start of the buffer, and set the
* remaining bytes value accordingly.
*/
pbuf->current = pbuf->buf;
pbuf->remaining = PR_TUNABLE_BUFFER_SIZE;
/* Add this buffer to the given stream. */
nstrm->strm_buf = pbuf;
return pbuf;
}
/* Default core NetIO handlers
*/
static void core_netio_abort_cb(pr_netio_stream_t *nstrm) {
nstrm->strm_flags |= PR_NETIO_SESS_ABORT;
}
static int core_netio_close_cb(pr_netio_stream_t *nstrm) {
int res;
res = close(nstrm->strm_fd);
nstrm->strm_fd = -1;
return res;
}
static pr_netio_stream_t *core_netio_open_cb(pr_netio_stream_t *nstrm, int fd,
int mode) {
nstrm->strm_fd = fd;
/* The stream's strm_mode field does not need to be set, as it is set
* by the NetIO layer's open() wrapper function.
*/
return nstrm;
}
static int core_netio_poll_cb(pr_netio_stream_t *nstrm) {
fd_set rfds, wfds;
struct timeval tval;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
if (nstrm->strm_mode == PR_NETIO_IO_RD)
FD_SET(nstrm->strm_fd, &rfds);
else
FD_SET(nstrm->strm_fd, &wfds);
tval.tv_sec = ((nstrm->strm_flags & PR_NETIO_SESS_INTR) ?
nstrm->strm_interval: 60);
tval.tv_usec = 0;
return select(nstrm->strm_fd + 1, &rfds, &wfds, NULL, &tval);
}
static int core_netio_postopen_cb(pr_netio_stream_t *nstrm) {
return 0;
}
static int core_netio_read_cb(pr_netio_stream_t *nstrm, char *buf,
size_t buflen) {
return read(nstrm->strm_fd, buf, buflen);
}
static pr_netio_stream_t *core_netio_reopen_cb(pr_netio_stream_t *nstrm, int fd,
int mode) {
if (nstrm->strm_fd != -1)
close(nstrm->strm_fd);
nstrm->strm_fd = fd;
nstrm->strm_mode = mode;
return nstrm;
}
static int core_netio_shutdown_cb(pr_netio_stream_t *nstrm, int how) {
return shutdown(nstrm->strm_fd, how);
}
static int core_netio_write_cb(pr_netio_stream_t *nstrm, char *buf,
size_t buflen) {
return write(nstrm->strm_fd, buf, buflen);
}
/* NetIO API wrapper functions.
*/
void pr_netio_abort(pr_netio_stream_t *nstrm) {
if (!nstrm) {
errno = EINVAL;
return;
}
if (nstrm->strm_type == PR_NETIO_STRM_CTRL)
ctrl_netio ? ctrl_netio->abort(nstrm) :
core_ctrl_netio->abort(nstrm);
if (nstrm->strm_type == PR_NETIO_STRM_DATA)
data_netio ? data_netio->abort(nstrm) :
core_data_netio->abort(nstrm);
if (nstrm->strm_type == PR_NETIO_STRM_OTHR)
othr_netio ? othr_netio->abort(nstrm) :
core_othr_netio->abort(nstrm);
return;
}
int pr_netio_close(pr_netio_stream_t *nstrm) {
int res = -1;
if (!nstrm) {
errno = EINVAL;
return -1;
}
if (nstrm->strm_type == PR_NETIO_STRM_CTRL) {
res = ctrl_netio ? ctrl_netio->close(nstrm) :
core_ctrl_netio->close(nstrm);
destroy_pool(nstrm->strm_pool);
return res;
}
if (nstrm->strm_type == PR_NETIO_STRM_DATA) {
res = data_netio ? data_netio->close(nstrm) :
core_data_netio->close(nstrm);
destroy_pool(nstrm->strm_pool);
return res;
}
if (nstrm->strm_type == PR_NETIO_STRM_OTHR) {
res = othr_netio ? othr_netio->close(nstrm) :
core_othr_netio->close(nstrm);
destroy_pool(nstrm->strm_pool);
return res;
}
errno = EPERM;
return res;
}
static int netio_lingering_close(pr_netio_stream_t *nstrm, long linger,
int flags) {
int res;
struct timeval tv;
fd_set rs;
time_t when = time(NULL) + linger;
if (!nstrm) {
errno = EINVAL;
return -1;
}
if (!(flags & NETIO_LINGERING_CLOSE_FL_NO_SHUTDOWN))
pr_netio_shutdown(nstrm, 1);
tv.tv_sec = linger;
tv.tv_usec = 0L;
/* Handle timers during reading, once selected for read this
* should mean all buffers have been flushed and the receiving end
* has closed.
*/
while (TRUE) {
run_schedule();
FD_ZERO(&rs);
FD_SET(nstrm->strm_fd, &rs);
res = select(nstrm->strm_fd+1, &rs, NULL, NULL, &tv);
if (res == -1) {
if (errno == EINTR) {
time_t now = time(NULL);
pr_signals_handle();
/* Still here? If the requested lingering interval hasn't passed,
* continue lingering. Reset the timeval struct's fields to
* linger for the interval remaining in the given period of time.
*/
if (now < when) {
tv.tv_sec = when - now;
tv.tv_usec = 0L;
continue;
}
} else {
nstrm->strm_errno = errno;
return -1;
}
}
break;
}
if (nstrm->strm_type == PR_NETIO_STRM_CTRL)
return ctrl_netio ? ctrl_netio->close(nstrm) :
core_ctrl_netio->close(nstrm);
if (nstrm->strm_type == PR_NETIO_STRM_DATA)
return data_netio ? data_netio->close(nstrm) :
core_data_netio->close(nstrm);
if (nstrm->strm_type == PR_NETIO_STRM_OTHR)
return othr_netio ? othr_netio->close(nstrm) :
core_othr_netio->close(nstrm);
errno = EPERM;
return -1;
}
int pr_netio_lingering_abort(pr_netio_stream_t *nstrm, long linger) {
int res;
struct timeval tv;
fd_set rs;
if (!nstrm) {
errno = EINVAL;
return -1;
}
pr_netio_shutdown(nstrm, 1);
/* Wait for just a little while for the shutdown to take effect. */
tv.tv_sec = 0L;
tv.tv_usec = 300000L;
while (TRUE) {
run_schedule();
FD_ZERO(&rs);
FD_SET(nstrm->strm_fd, &rs);
res = select(nstrm->strm_fd+1, &rs, NULL, NULL, &tv);
if (res == -1) {
if (errno == EINTR) {
pr_signals_handle();
/* Linger some more. */
tv.tv_sec = 0L;
tv.tv_usec = 300000L;
continue;
} else {
nstrm->strm_errno = errno;
return -1;
}
}
break;
}
/* Send an appropriate response code down the stream asychronously. */
pr_response_send_async(R_426, "Transfer aborted. Data connection closed.");
/* Now continue with a normal lingering close. */
return netio_lingering_close(nstrm, linger,
NETIO_LINGERING_CLOSE_FL_NO_SHUTDOWN);
}
int pr_netio_lingering_close(pr_netio_stream_t *nstrm, long linger) {
return netio_lingering_close(nstrm, linger, 0);
}
pr_netio_stream_t *pr_netio_open(pool *parent_pool, int strm_type, int fd,
int mode) {
pr_netio_stream_t *nstrm = NULL;
if (!parent_pool) {
errno = EINVAL;
return NULL;
}
/* Create a new stream object, then pass that the NetIO open handler. */
nstrm = netio_stream_alloc(parent_pool);
if (strm_type == PR_NETIO_STRM_CTRL) {
nstrm->strm_type = PR_NETIO_STRM_CTRL;
nstrm->strm_mode = mode;
return ctrl_netio ? ctrl_netio->open(nstrm, fd, mode) :
core_ctrl_netio->open(nstrm, fd, mode);
}
if (strm_type == PR_NETIO_STRM_DATA) {
nstrm->strm_type = PR_NETIO_STRM_DATA;
nstrm->strm_mode = mode;
return data_netio ? data_netio->open(nstrm, fd, mode) :
core_data_netio->open(nstrm, fd, mode);
}
if (strm_type == PR_NETIO_STRM_OTHR) {
nstrm->strm_type = PR_NETIO_STRM_OTHR;
nstrm->strm_mode = mode;
return othr_netio ? othr_netio->open(nstrm, fd, mode) :
core_othr_netio->open(nstrm, fd, mode);
}
destroy_pool(nstrm->strm_pool);
errno = EPERM;
return NULL;
}
pr_netio_stream_t *pr_netio_reopen(pr_netio_stream_t *nstrm, int fd, int mode) {
if (!nstrm) {
errno = EINVAL;
return NULL;
}
if (nstrm->strm_type == PR_NETIO_STRM_CTRL)
return ctrl_netio ? ctrl_netio->reopen(nstrm, fd, mode) :
core_ctrl_netio->reopen(nstrm, fd, mode);
if (nstrm->strm_type == PR_NETIO_STRM_DATA)
return data_netio ? data_netio->reopen(nstrm, fd, mode) :
core_data_netio->reopen(nstrm, fd, mode);
if (nstrm->strm_type == PR_NETIO_STRM_OTHR)
return othr_netio ? othr_netio->reopen(nstrm, fd, mode) :
core_othr_netio->reopen(nstrm, fd, mode);
errno = EPERM;
return NULL;
}
void pr_netio_set_poll_interval(pr_netio_stream_t *nstrm, unsigned int secs) {
if (!nstrm) {
errno = EINVAL;
return;
}
nstrm->strm_flags |= PR_NETIO_SESS_INTR;
nstrm->strm_interval = secs;
}
int pr_netio_poll(pr_netio_stream_t *nstrm) {
int res = 0;
/* Sanity checks. */
if (!nstrm) {
errno = EINVAL;
return -1;
}
if (nstrm->strm_fd == -1) {
errno = EBADF;
return -1;
}
/* Has this stream been aborted? */
if (nstrm->strm_flags & PR_NETIO_SESS_ABORT) {
nstrm->strm_flags &= ~PR_NETIO_SESS_ABORT;
return 1;
}
while (TRUE) {
run_schedule();
pr_signals_handle();
switch (nstrm->strm_type) {
case PR_NETIO_STRM_CTRL:
res = ctrl_netio ? ctrl_netio->poll(nstrm) :
core_ctrl_netio->poll(nstrm);
break;
case PR_NETIO_STRM_DATA:
res = data_netio ? data_netio->poll(nstrm) :
core_data_netio->poll(nstrm);
break;
case PR_NETIO_STRM_OTHR:
res = othr_netio ? othr_netio->poll(nstrm) :
core_othr_netio->poll(nstrm);
break;
}
switch (res) {
case -1:
if (errno == EINTR) {
if (nstrm->strm_flags & PR_NETIO_SESS_ABORT) {
nstrm->strm_flags &= ~PR_NETIO_SESS_ABORT;
return 1;
}
/* Otherwise, restart the call */
pr_signals_handle();
continue;
}
/* Some other error occured */
nstrm->strm_errno = errno;
return -1;
case 0:
/* In case the kernel doesn't support interrupted syscalls. */
if (nstrm->strm_flags & PR_NETIO_SESS_ABORT) {
nstrm->strm_flags &= ~PR_NETIO_SESS_ABORT;
return 1;
}
continue;
default:
return 0;
}
}
/* This will never be reached. */
return -1;
}
int pr_netio_postopen(pr_netio_stream_t *nstrm) {
if (!nstrm) {
errno = EINVAL;
return -1;
}
if (nstrm->strm_type == PR_NETIO_STRM_CTRL)
return ctrl_netio ? ctrl_netio->postopen(nstrm) :
core_ctrl_netio->postopen(nstrm);
if (nstrm->strm_type == PR_NETIO_STRM_DATA)
return data_netio ? data_netio->postopen(nstrm) :
core_data_netio->postopen(nstrm);
if (nstrm->strm_type == PR_NETIO_STRM_OTHR)
return othr_netio ? othr_netio->postopen(nstrm) :
core_othr_netio->postopen(nstrm);
errno = EPERM;
return -1;
}
int pr_netio_printf(pr_netio_stream_t *nstrm, const char *fmt, ...) {
va_list msg;
char buf[PR_RESPONSE_BUFFER_SIZE] = {'\0'};
if (!nstrm) {
errno = EINVAL;
return -1;
}
va_start(msg, fmt);
vsnprintf(buf, sizeof(buf), fmt, msg);
va_end(msg);
buf[sizeof(buf)-1] = '\0';
return pr_netio_write(nstrm, buf, strlen(buf));
}
int pr_netio_printf_async(pr_netio_stream_t *nstrm, char *fmt, ...) {
va_list msg;
char buf[PR_RESPONSE_BUFFER_SIZE] = {'\0'};
if (!nstrm) {
errno = EINVAL;
return -1;
}
va_start(msg, fmt);
vsnprintf(buf, sizeof(buf), fmt, msg);
va_end(msg);
buf[sizeof(buf)-1] = '\0';
return pr_netio_write_async(nstrm, buf, strlen(buf));
}
int pr_netio_write(pr_netio_stream_t *nstrm, char *buf, size_t buflen) {
int bwritten = 0, total = 0;
/* Sanity check */
if (!nstrm) {
errno = EINVAL;
return -1;
}
if (nstrm->strm_fd == -1) {
errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF);
return -1;
}
while (buflen) {
switch (pr_netio_poll(nstrm)) {
case 1:
return -2;
case -1:
return -1;
default:
/* We have to potentially restart here as well, in case we get EINTR. */
do {
pr_signals_handle();
run_schedule();
switch (nstrm->strm_type) {
case PR_NETIO_STRM_CTRL:
bwritten = ctrl_netio ? ctrl_netio->write(nstrm, buf, buflen) :
core_ctrl_netio->write(nstrm, buf, buflen);
break;
case PR_NETIO_STRM_DATA:
bwritten = data_netio ? data_netio->write(nstrm, buf, buflen) :
core_data_netio->write(nstrm, buf, buflen);
break;
case PR_NETIO_STRM_OTHR:
bwritten = othr_netio ? othr_netio->write(nstrm, buf, buflen) :
core_othr_netio->write(nstrm, buf, buflen);
break;
}
} while (bwritten == -1 && errno == EINTR);
break;
}
if (bwritten == -1) {
nstrm->strm_errno = errno;
return -1;
}
buf += bwritten;
total += bwritten;
buflen -= bwritten;
}
return total;
}
int pr_netio_write_async(pr_netio_stream_t *nstrm, char *buf, size_t buflen) {
int flags = 0;
int bwritten = 0, total = 0;
/* Sanity check */
if (!nstrm) {
errno = EINVAL;
return -1;
}
if (nstrm->strm_fd == -1) {
errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF);
return -1;
}
/* Prepare the descriptor for nonblocking IO. */
if ((flags = fcntl(nstrm->strm_fd, F_GETFL)) == -1)
return -1;
if (fcntl(nstrm->strm_fd, F_SETFL, flags|O_NONBLOCK) == -1)
return -1;
while (buflen) {
do {
pr_signals_handle();
switch (nstrm->strm_type) {
case PR_NETIO_STRM_CTRL:
bwritten = ctrl_netio ? ctrl_netio->write(nstrm, buf, buflen) :
core_ctrl_netio->write(nstrm, buf, buflen);
break;
case PR_NETIO_STRM_DATA:
bwritten = data_netio ? data_netio->write(nstrm, buf, buflen) :
core_data_netio->write(nstrm, buf, buflen);
break;
case PR_NETIO_STRM_OTHR:
bwritten = othr_netio ? othr_netio->write(nstrm, buf, buflen) :
core_othr_netio->write(nstrm, buf, buflen);
break;
}
} while (bwritten == -1 && errno == EINTR);
if (bwritten < 0) {
nstrm->strm_errno = errno;
fcntl(nstrm->strm_fd, F_SETFL, flags);
if (nstrm->strm_errno == EWOULDBLOCK)
/* Give up ... */
return total;
return -1;
}
buf += bwritten;
total += bwritten;
buflen -= bwritten;
}
fcntl(nstrm->strm_fd, F_SETFL, flags);
return total;
}
int pr_netio_read(pr_netio_stream_t *nstrm, char *buf, size_t buflen,
int bufmin) {
int bread = 0, total = 0;
/* Sanity check. */
if (!nstrm) {
errno = EINVAL;
return -1;
}
if (nstrm->strm_fd == -1) {
errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF);
return -1;
}
if (bufmin < 1)
bufmin = 1;
if (bufmin > buflen)
bufmin = buflen;
while (bufmin > 0) {
polling:
switch (pr_netio_poll(nstrm)) {
case 1:
return -2;
case -1:
return -1;
default:
do {
pr_signals_handle();
run_schedule();
switch (nstrm->strm_type) {
case PR_NETIO_STRM_CTRL:
bread = ctrl_netio ? ctrl_netio->read(nstrm, buf, buflen) :
core_ctrl_netio->read(nstrm, buf, buflen);
break;
case PR_NETIO_STRM_DATA:
bread = data_netio ? data_netio->read(nstrm, buf, buflen) :
core_data_netio->read(nstrm, buf, buflen);
break;
case PR_NETIO_STRM_OTHR:
bread = othr_netio ? othr_netio->read(nstrm, buf, buflen) :
core_othr_netio->read(nstrm, buf, buflen);
break;
}
#ifdef EAGAIN
if (bread == -1 && errno == EAGAIN)
goto polling;
#endif
} while (bread == -1 && errno == EINTR);
break;
}
if (bread == -1) {
nstrm->strm_errno = errno;
return -1;
}
/* EOF? */
if (bread == 0) {
nstrm->strm_errno = 0;
break;
}
buf += bread;
total += bread;
bufmin -= bread;
buflen -= bread;
}
return total;
}
int pr_netio_shutdown(pr_netio_stream_t *nstrm, int how) {
int res = -1;
if (!nstrm) {
errno = EINVAL;
return -1;
}
if (nstrm->strm_type == PR_NETIO_STRM_CTRL) {
res = ctrl_netio ? ctrl_netio->shutdown(nstrm, how) :
core_ctrl_netio->shutdown(nstrm, how);
return res;
}
if (nstrm->strm_type == PR_NETIO_STRM_DATA) {
res = data_netio ? data_netio->shutdown(nstrm, how) :
core_data_netio->shutdown(nstrm, how);
return res;
}
if (nstrm->strm_type == PR_NETIO_STRM_OTHR) {
res = othr_netio ? othr_netio->shutdown(nstrm, how) :
core_othr_netio->shutdown(nstrm, how);
return res;
}
errno = EPERM;
return res;
}
char *pr_netio_gets(char *buf, size_t buflen, pr_netio_stream_t *nstrm) {
char *bp = buf;
int toread;
pr_buffer_t *pbuf = NULL;
buflen--;
if (nstrm->strm_buf)
pbuf = nstrm->strm_buf;
else
pbuf = netio_buffer_alloc(nstrm);
while (buflen) {
/* Is the buffer empty? */
if (!pbuf->current ||
pbuf->remaining == pbuf->buflen) {
toread = pr_netio_read(nstrm, pbuf->buf,
(buflen < pbuf->buflen ? buflen : pbuf->buflen), 1);
if (toread <= 0) {
if (bp != buf) {
*bp = '\0';
return buf;
} else
return NULL;
}
pbuf->remaining = pbuf->buflen - toread;
pbuf->current = pbuf->buf;
} else
toread = pbuf->buflen - pbuf->remaining;
while (buflen && *pbuf->current != '\n' && toread--) {
if (*pbuf->current & 0x80)
pbuf->current++;
else {
*bp++ = *pbuf->current++;
buflen--;
}
pbuf->remaining++;
}
if (buflen && toread && *pbuf->current == '\n') {
buflen--;
toread--;
*bp++ = *pbuf->current++;
pbuf->remaining++;
break;
}
if (!toread)
pbuf->current = NULL;
}
*bp = '\0';
return buf;
}
char *pr_netio_telnet_gets(char *buf, size_t buflen,
pr_netio_stream_t *in_nstrm, pr_netio_stream_t *out_nstrm) {
char *bp = buf;
unsigned char cp;
static unsigned char mode = 0;
int toread;
pr_buffer_t *pbuf = NULL;
buflen--;
if (in_nstrm->strm_buf)
pbuf = in_nstrm->strm_buf;
else
pbuf = netio_buffer_alloc(in_nstrm);
while (buflen) {
/* Is the buffer empty? */
if (!pbuf->current ||
pbuf->remaining == pbuf->buflen) {
toread = pr_netio_read(in_nstrm, pbuf->buf,
(buflen < pbuf->buflen ? buflen : pbuf->buflen), 1);
if (toread <= 0) {
if (bp != buf) {
*bp = '\0';
return buf;
} else
return NULL;
}
pbuf->remaining = pbuf->buflen - toread;
pbuf->current = pbuf->buf;
} else
toread = pbuf->buflen - pbuf->remaining;
while (buflen && toread > 0 && *pbuf->current != '\n' && toread--) {
cp = *pbuf->current++;
pbuf->remaining++;
switch (mode) {
case IAC:
switch (cp) {
case WILL:
case WONT:
case DO:
case DONT:
mode = cp;
continue;
case IAC:
mode = 0;
break;
default:
/* Ignore */
mode = 0;
continue;
}
break;
case WILL:
case WONT:
pr_netio_printf(out_nstrm, "%c%c%c", IAC, DONT, cp);
mode = 0;
continue;
case DO:
case DONT:
pr_netio_printf(out_nstrm, "%c%c%c", IAC, WONT, cp);
mode = 0;
continue;
default:
if (cp == IAC) {
mode = cp;
continue;
}
break;
}
*bp++ = cp;
buflen--;
}
if (buflen && toread && *pbuf->current == '\n') {
buflen--;
toread--;
*bp++ = *pbuf->current++;
pbuf->remaining++;
break;
}
if (!toread)
pbuf->current = NULL;
}
*bp = '\0';
return buf;
}
int pr_register_netio(pr_netio_t *netio, int strm_types) {
if (!netio) {
pr_netio_t *core_netio = NULL;
/* Only instantiate the core NetIO objects once, reusing the same pointer.
*/
if (!core_ctrl_netio)
core_netio = core_ctrl_netio = pr_alloc_netio(permanent_pool);
if (!core_data_netio)
core_data_netio = core_netio ? core_netio :
(core_netio = pr_alloc_netio(permanent_pool));
if (!core_othr_netio)
core_othr_netio = core_netio ? core_netio :
(core_netio = pr_alloc_netio(permanent_pool));
return 0;
}
if (!netio->abort || !netio->close || !netio->open || !netio->poll ||
!netio->postopen || !netio->read || !netio->reopen ||
!netio->shutdown || !netio->write) {
errno = EINVAL;
return -1;
}
if (strm_types & PR_NETIO_STRM_CTRL)
ctrl_netio = netio;
if (strm_types & PR_NETIO_STRM_DATA)
data_netio = netio;
if (strm_types & PR_NETIO_STRM_OTHR)
othr_netio = netio;
return 0;
}
int pr_unregister_netio(int strm_types) {
if (!strm_types) {
errno = EINVAL;
return -1;
}
/* NOTE: consider using cleanups here in the future? */
if (strm_types & PR_NETIO_STRM_CTRL)
ctrl_netio = NULL;
if (strm_types & PR_NETIO_STRM_DATA)
data_netio = NULL;
if (strm_types & PR_NETIO_STRM_OTHR)
othr_netio = NULL;
return 0;
}
pr_netio_t *pr_alloc_netio(pool *parent_pool) {
pr_netio_t *netio = NULL;
pool *netio_pool = NULL;
if (!parent_pool) {
errno = EINVAL;
return NULL;
}
netio_pool = make_sub_pool(parent_pool);
netio = pcalloc(netio_pool, sizeof(pr_netio_t));
netio->pool = netio_pool;
/* Set the default NetIO handlers to the core handlers. */
netio->abort = core_netio_abort_cb;
netio->close = core_netio_close_cb;
netio->open = core_netio_open_cb;
netio->poll = core_netio_poll_cb;
netio->postopen = core_netio_postopen_cb;
netio->read = core_netio_read_cb;
netio->reopen = core_netio_reopen_cb;
netio->shutdown = core_netio_shutdown_cb;
netio->write = core_netio_write_cb;
return netio;
}
void init_netio(void) {
signal(SIGPIPE, SIG_IGN);
signal(SIGURG, SIG_IGN);
pr_register_netio(NULL, 0);
}
Last Updated: Thu Feb 23 11:07:20 2006
HTML generated by tj's src2html script