The code below is for an example NetIO module, mod_netiolog.c
.
It demonstrates how to use the NetIO API to register custom callbacks,
and illustrates where, in the running of the server, those callbacks are
invoked.
#include "conf.h" #define MOD_NETIOLOG_VERSION "mod_netiolog/1.0" static pr_netio_t *netiolog_netio = NULL; /* NetIO Callbacks */ static void netiolog_abort_cb(pr_netio_stream_t *nstrm) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_abort_cb(): called", nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" : nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]", nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]"); nstrm->strm_flags |= PR_NETIO_SESS_ABORT; } static int netiolog_close_cb(pr_netio_stream_t *nstrm) { int res; log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_close_cb(): called", nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" : nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]", nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]"); res = close(nstrm->strm_fd); nstrm->strm_fd = -1; return res; } /* Note: This callback may not actually ever be called, depending on when * the netio is registered. In this module, for example, the netio * registration occurs in the session_init function. At this point the * control connection has already been opened/established (when the client * first connects to the server), and so this function is not invoked. * However, the other netio callbacks are invoked. This has consequences * for any netio- or stream-specific initialization that needs to occur, * at least for control channels: other control stream callbacks may need * to check that any necessary initialization has occurred, rather than * assuming that such initialization has been taken care of by this open * callback. * * In order to have this callback used, the netio must be registered at * mod_init time, which means that it will be used for all connections * established; this may or may not be desired, depending. */ static pr_netio_stream_t *netiolog_open_cb(pr_netio_stream_t *nstrm, int fd, int mode) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_open_cb(): called", nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" : nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]", nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]"); nstrm->strm_fd = fd; nstrm->strm_mode = mode; return nstrm; } static int netiolog_poll_cb(pr_netio_stream_t *nstrm) { fd_set rfds, wfds; struct timeval tval; log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_poll_cb(): called", nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" : nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]", nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]"); 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 netiolog_postopen_cb(pr_netio_stream_t *nstrm) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_postopen_cb(): called", nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" : nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]", nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]"); return 0; } static int netiolog_read_cb(pr_netio_stream_t *nstrm, char *buf, size_t buflen) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_read_cb(): called", nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" : nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]", nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]"); return read(nstrm->strm_fd, buf, buflen); } static pr_netio_stream_t *netiolog_reopen_cb(pr_netio_stream_t *nstrm, int fd, int mode) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_reopen_cb(): called", nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" : nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]", nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]"); if (nstrm->strm_fd != -1) close(nstrm->strm_fd); nstrm->strm_fd = fd; nstrm->strm_mode = mode; return nstrm; } static int netiolog_write_cb(pr_netio_stream_t *nstrm, char *buf, size_t buflen) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_write_cb(): called", nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" : nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]", nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]"); return write(nstrm->strm_fd, buf, buflen); } /* Helper routines */ static void netiolog_install(void) { pr_netio_t *netio = netiolog_netio ? netiolog_netio : (netiolog_netio = pr_alloc_netio(session.pool ? session.pool : permanent_pool)); int strm_types = PR_NETIO_STRM_CTRL|PR_NETIO_STRM_DATA|PR_NETIO_STRM_OTHR; /* Install this module's NetIO callbacks. */ netio->abort = netiolog_abort_cb; netio->close = netiolog_close_cb; netio->open = netiolog_open_cb; netio->poll = netiolog_poll_cb; netio->postopen = netiolog_postopen_cb; netio->read = netiolog_read_cb; netio->reopen = netiolog_reopen_cb; netio->write = netiolog_write_cb; pr_unregister_netio(strm_types); log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": registering netiolog netio"); if (pr_register_netio(netio, strm_types) < 0) log_pri(LOG_INFO, MOD_NETIOLOG_VERSION ": error registering netio: %s", strerror(errno)); } /* Configuration handlers */ /* usage: NetIOLogEngine "on"|"off"|"daemon"|"sessions" */ MODRET set_netiologengine(cmd_rec *cmd) { int bool = -1; config_rec *c = NULL; CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); if ((bool = get_boolean(cmd, 1)) == -1) { if (!strcmp(cmd->argv[1], "daemon")) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s: applying netio to daemon", cmd->argv[0]); netiolog_install(); } else if (!strcmp(cmd->argv[1], "sessions")) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s: applying netio to sessions", cmd->argv[0]); bool = TRUE; } } else { if (bool == TRUE) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s: applying netio to daemon and sessions", cmd->argv[0]); netiolog_install(); } } c = add_config_param(cmd->argv[0], 1, NULL); c->argv[0] = pcalloc(c->pool, sizeof(unsigned char)); *((unsigned char *) c->argv[0]) = bool; return HANDLED(cmd); } /* Initialization functions */ static void netiolog_exit(void) { /* Be thorough, and clean up after ourselves. */ destroy_pool(netiolog_netio->pool); } static int netiolog_sess_init(void) { unsigned char *sessions = get_param_ptr(main_server->conf, "NetIOLogEngine", FALSE); if (sessions && *sessions == TRUE) { log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": session init: registering netiolog netio"); netiolog_install(); } /* Register our exit handler. */ add_exit_handler(netiolog_exit); return 0; } /* Module API tables */ static conftable netiolog_conftab[] = { { "NetIOLogEngine", set_netiologengine, NULL }, { NULL } }; module netiolog_module = { NULL, NULL, /* Module API version 2.0 */ 0x20, /* Module name */ "netiolog", /* Module configuration handler table */ netiolog_conftab, /* Module command handler table */ NULL, /* Module authentication handler table */ NULL, /* Module initialization function */ NULL, /* Session initialization function */ netiolog_sess_init };