/* $Id: context.c,v 1.12 2003/12/11 11:55:06 steve Exp $ */ /*- * Copyright (c) 2001 Steve C. Woodford. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Steve C. Woodford. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "dispatcher.h" #include "context.h" #include "listener.h" #include "server.h" #include "config.h" #include "version.h" static struct config_tokens cf_context_tokens[] = { {"client", 1 | CFG_ARGC_MUST_COMPOUND, cf_client_init}, {"server", 1 | CFG_ARGC_MUST_COMPOUND, cf_server_init}, {NULL, 0, NULL} }; int context_init(struct context **cp, const char *name) { struct context *c; if ((c = calloc(1, sizeof(*c))) == NULL) return (-1); if ((c->c_name = strdup(name)) == NULL) { (void) free(c); return (-1); } TAILQ_INIT(&c->c_clients); c->c_client_count = 0; c->c_total_clients = 0; c->c_server = NULL; dispatcher_add_context(c); *cp = c; return (0); } void context_destroy(struct context *c) { struct client_ctx *cc, *ncc; if (c->c_server != NULL) { dispatcher_del_client(c->c_server); client_destroy(c->c_server); } for (cc = TAILQ_FIRST(&c->c_clients); cc != NULL; cc = ncc) { ncc = TAILQ_NEXT(cc, cc_qent); dispatcher_del_client(cc); client_destroy(cc); } dispatcher_del_context(c); (void) free(c->c_name); (void) free(c); } int context_set_server(struct context *c, struct client_ctx *cc) { c->c_server = cc; cc->cc_ctx = c; dispatcher_add_client(cc); context_server_ioctl(c, CONTEXT_IOCTL_NEW_SERVER, NULL, cc); return (0); } int context_add_client(struct context *c, struct client_ctx *cc) { char buff[128]; int rv; cc->cc_ctx = c; syslog(LOG_NOTICE, "Connection to service \"%s\" from %s", c->c_name, client_getname(cc)); if (client_can_output(cc) && client_show_banner(cc)) { (void) strcpy(buff, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r\n"); if (client_output(cc, buff, strlen(buff), NULL) < 0) return (-1); (void) strcpy(buff, "TITS Version " TITS_VERSION_STRING " \r\n"); if (client_output(cc, buff, strlen(buff), NULL) < 0) return (-1); (void) strcpy(buff, "Copyright 2001, Steve Woodford. All Rights Reserved\r\n"); if (client_output(cc, buff, strlen(buff), NULL) < 0) return (-1); (void) snprintf(buff, sizeof(buff), "Connection to service `%s' from `%s'\r\n", c->c_name, client_getname(cc)); if (client_output(cc, buff, strlen(buff), NULL) < 0) return (-1); (void) sprintf(buff, "There %s currently %d other active connection%s to this service\r\n", (c->c_client_count == 1) ? "is" : "are", c->c_client_count, (c->c_client_count == 1) ? "" : "s"); if (client_output(cc, buff, strlen(buff), NULL) < 0) return (-1); if (cc->cc_options.co_readonly) { (void) strcpy(buff, "Read-only access.\r\n"); if (client_output(cc, buff, strlen(buff), NULL) < 0) return (-1); } (void) strcpy(buff, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\r\n\r\n"); if (client_output(cc, buff, strlen(buff), NULL) < 0) return (-1); } TAILQ_INSERT_TAIL(&c->c_clients, cc, cc_qent); if ((rv = dispatcher_add_client(cc)) == 0 && client_can_output(cc)) c->c_client_count++; c->c_total_clients++; if (c->c_server != NULL) context_server_ioctl(c, CONTEXT_IOCTL_ADD_CLIENT, NULL, cc); return (rv); } int context_del_client(struct context *c, struct client_ctx *cc) { if (cc == c->c_server) return (-1); dispatcher_del_client(cc); TAILQ_REMOVE(&c->c_clients, cc, cc_qent); c->c_total_clients--; if (client_can_output(cc)) { syslog(LOG_NOTICE, "Client %s disconnected from service \"%s\"", client_getname(cc), c->c_name); if (--c->c_client_count < 0) c->c_client_count = 0; } if (c->c_server != NULL) context_server_ioctl(c, CONTEXT_IOCTL_DEL_CLIENT, NULL, cc); (void) client_destroy(cc); return (0); } int context_client_output(struct context *c, const char *buff, size_t len, struct client_ctx *sender) { struct client_ctx *cc, *ncc; for (cc = TAILQ_FIRST(&c->c_clients); cc != NULL; cc = ncc) { ncc = TAILQ_NEXT(cc, cc_qent); if (client_output(cc, buff, len, sender) < 0) context_del_client(c, cc); } return (0); } int context_client_ioctl(struct context *c, int cmd, void *arg, struct client_ctx *sender) { struct client_ctx *cc, *ncc; for (cc = TAILQ_FIRST(&c->c_clients); cc != NULL; cc = ncc) { ncc = TAILQ_NEXT(cc, cc_qent); client_ioctl(cc, cmd, arg, sender); } return (0); } int context_setup_pollfds(struct context *c, struct pollfd *pf) { struct client_ctx *cc; short events; int n; if (c->c_server != NULL && (events = ((client_read_pending(c->c_server) ? POLLRDNORM : 0) | (client_write_pending(c->c_server) ? POLLWRNORM : 0))) != 0) { pf->events = events; pf->revents = 0; pf->fd = client_getfd(c->c_server); pf++; n = 1; } else n = 0; for (cc = TAILQ_FIRST(&c->c_clients); cc != NULL; cc = TAILQ_NEXT(cc, cc_qent)) { if ((events = ((client_read_pending(cc) ? POLLRDNORM : 0) | (client_write_pending(cc) ? POLLWRNORM : 0))) == 0) continue; pf->events = events; pf->revents = 0; pf->fd = client_getfd(cc); pf++, n++; } return (n); } /* ARGSUSED */ const char * cf_context_init(void *cs, char **argv, int argc, int is_compound, void *arg) { struct context *ctx; const char *err; if (argc > 2) return (config_err(cs, "Too many parameters")); if (context_init(&ctx, (argc == 2) ? argv[1] : NULL) < 0) return (config_err(cs, "context_init: %s", strerror(errno))); err = config_parse(cs, cf_context_tokens, (void *)ctx); if (err != NULL) context_destroy(ctx); return (err); }