/* * Proxy variables and functions. * * Copyright 2000-2006 Willy Tarreau * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int listeners; /* # of listeners */ struct proxy *proxy = NULL; /* list of all existing proxies */ /* * This function returns a string containing the type of the proxy in a format * suitable for error messages, from its capabilities. */ const char *proxy_type_str(struct proxy *proxy) { int cap = proxy->cap; if ((cap & PR_CAP_LISTEN) == PR_CAP_LISTEN) return "listener"; else if (cap & PR_CAP_FE) return "frontend"; else if (cap & PR_CAP_BE) return "backend"; else if (cap & PR_CAP_RS) return "ruleset"; else return "proxy"; } /* * this function starts all the proxies. Its return value is composed from * ERR_NONE, ERR_RETRYABLE and ERR_FATAL. Retryable errors will only be printed * if is not zero. */ int start_proxies(int verbose) { struct proxy *curproxy; struct listener *listener; int err = ERR_NONE; int fd, pxerr; for (curproxy = proxy; curproxy != NULL; curproxy = curproxy->next) { if (curproxy->state != PR_STNEW) continue; /* already initialized */ pxerr = 0; for (listener = curproxy->listen; listener != NULL; listener = listener->next) { if (listener->fd != -1) continue; /* already initialized */ if ((fd = socket(listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { if (verbose) Alert("cannot create listening socket for proxy %s. Aborting.\n", curproxy->id); err |= ERR_RETRYABLE; pxerr |= 1; continue; } if (fd >= global.maxsock) { Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n", curproxy->id); close(fd); err |= ERR_FATAL; pxerr |= 1; break; } if ((fcntl(fd, F_SETFL, O_NONBLOCK) == -1) || (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) { Alert("cannot make socket non-blocking for proxy %s. Aborting.\n", curproxy->id); close(fd); err |= ERR_FATAL; pxerr |= 1; break; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) { Alert("cannot do so_reuseaddr for proxy %s. Continuing.\n", curproxy->id); } #ifdef SO_REUSEPORT /* OpenBSD supports this. As it's present in old libc versions of Linux, * it might return an error that we will silently ignore. */ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &one, sizeof(one)); #endif if (bind(fd, (struct sockaddr *)&listener->addr, listener->addr.ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == -1) { if (verbose) Alert("cannot bind socket for proxy %s. Aborting.\n", curproxy->id); close(fd); err |= ERR_RETRYABLE; pxerr |= 1; continue; } if (listen(fd, curproxy->maxconn) == -1) { if (verbose) Alert("cannot listen to socket for proxy %s. Aborting.\n", curproxy->id); close(fd); err |= ERR_RETRYABLE; pxerr |= 1; continue; } /* the socket is ready */ listener->fd = fd; /* the function for the accept() event */ fdtab[fd].cb[DIR_RD].f = &event_accept; fdtab[fd].cb[DIR_WR].f = NULL; /* never called */ fdtab[fd].cb[DIR_RD].b = fdtab[fd].cb[DIR_WR].b = NULL; fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */ fdtab[fd].state = FD_STLISTEN; MY_FD_SET(fd, StaticReadEvent); fd_insert(fd); listeners++; } if (!pxerr) { curproxy->state = PR_STRUN; send_log(curproxy, LOG_NOTICE, "Proxy %s started.\n", curproxy->id); } } return err; } /* * this function enables proxies when there are enough free sessions, * or stops them when the table is full. It is designed to be called from the * select_loop(). It returns the time left before next expiration event * during stop time, TIME_ETERNITY otherwise. */ int maintain_proxies(void) { struct proxy *p; struct listener *l; int tleft; /* time left */ p = proxy; tleft = TIME_ETERNITY; /* infinite time */ /* if there are enough free sessions, we'll activate proxies */ if (actconn < global.maxconn) { while (p) { if (p->feconn < p->maxconn) { if (p->state == PR_STIDLE) { for (l = p->listen; l != NULL; l = l->next) { MY_FD_SET(l->fd, StaticReadEvent); } p->state = PR_STRUN; } } else { if (p->state == PR_STRUN) { for (l = p->listen; l != NULL; l = l->next) { MY_FD_CLR(l->fd, StaticReadEvent); } p->state = PR_STIDLE; } } p = p->next; } } else { /* block all proxies */ while (p) { if (p->state == PR_STRUN) { for (l = p->listen; l != NULL; l = l->next) { MY_FD_CLR(l->fd, StaticReadEvent); } p->state = PR_STIDLE; } p = p->next; } } if (stopping) { p = proxy; while (p) { if (p->state != PR_STSTOPPED) { int t; t = tv_remain2(&now, &p->stop_time); if (t == 0) { Warning("Proxy %s stopped.\n", p->id); send_log(p, LOG_WARNING, "Proxy %s stopped.\n", p->id); for (l = p->listen; l != NULL; l = l->next) { fd_delete(l->fd); listeners--; } p->state = PR_STSTOPPED; } else { tleft = MINTIME(t, tleft); } } p = p->next; } } return tleft; } /* * this function disables health-check servers so that the process will quickly be ignored * by load balancers. Note that if a proxy was already in the PAUSED state, then its grace * time will not be used since it would already not listen anymore to the socket. */ void soft_stop(void) { struct proxy *p; stopping = 1; p = proxy; tv_now(&now); /* else, the old time before select will be used */ while (p) { if (p->state != PR_STSTOPPED) { Warning("Stopping proxy %s in %d ms.\n", p->id, p->grace); send_log(p, LOG_WARNING, "Stopping proxy %s in %d ms.\n", p->id, p->grace); tv_delayfrom(&p->stop_time, &now, p->grace); } p = p->next; } } /* * Linux unbinds the listen socket after a SHUT_RD, and ignores SHUT_WR. * Solaris refuses either shutdown(). * OpenBSD ignores SHUT_RD but closes upon SHUT_WR and refuses to rebind. * So a common validation path involves SHUT_WR && listen && SHUT_RD. * If disabling at least one listener returns an error, then the proxy * state is set to PR_STERROR because we don't know how to resume from this. */ void pause_proxy(struct proxy *p) { struct listener *l; for (l = p->listen; l != NULL; l = l->next) { if (shutdown(l->fd, SHUT_WR) == 0 && listen(l->fd, p->maxconn) == 0 && shutdown(l->fd, SHUT_RD) == 0) { MY_FD_CLR(l->fd, StaticReadEvent); if (p->state != PR_STERROR) p->state = PR_STPAUSED; } else p->state = PR_STERROR; } } /* * This function temporarily disables listening so that another new instance * can start listening. It is designed to be called upon reception of a * SIGTTOU, after which either a SIGUSR1 can be sent to completely stop * the proxy, or a SIGTTIN can be sent to listen again. */ void pause_proxies(void) { int err; struct proxy *p; err = 0; p = proxy; tv_now(&now); /* else, the old time before select will be used */ while (p) { if (p->state != PR_STERROR && p->state != PR_STSTOPPED && p->state != PR_STPAUSED) { Warning("Pausing proxy %s.\n", p->id); send_log(p, LOG_WARNING, "Pausing proxy %s.\n", p->id); pause_proxy(p); if (p->state != PR_STPAUSED) { err |= 1; Warning("Proxy %s failed to enter pause mode.\n", p->id); send_log(p, LOG_WARNING, "Proxy %s failed to enter pause mode.\n", p->id); } } p = p->next; } if (err) { Warning("Some proxies refused to pause, performing soft stop now.\n"); send_log(p, LOG_WARNING, "Some proxies refused to pause, performing soft stop now.\n"); soft_stop(); } } /* * This function reactivates listening. This can be used after a call to * sig_pause(), for example when a new instance has failed starting up. * It is designed to be called upon reception of a SIGTTIN. */ void listen_proxies(void) { struct proxy *p; struct listener *l; p = proxy; tv_now(&now); /* else, the old time before select will be used */ while (p) { if (p->state == PR_STPAUSED) { Warning("Enabling proxy %s.\n", p->id); send_log(p, LOG_WARNING, "Enabling proxy %s.\n", p->id); for (l = p->listen; l != NULL; l = l->next) { if (listen(l->fd, p->maxconn) == 0) { if (actconn < global.maxconn && p->feconn < p->maxconn) { MY_FD_SET(l->fd, StaticReadEvent); p->state = PR_STRUN; } else p->state = PR_STIDLE; } else { int port; if (l->addr.ss_family == AF_INET6) port = ntohs(((struct sockaddr_in6 *)(&l->addr))->sin6_port); else port = ntohs(((struct sockaddr_in *)(&l->addr))->sin_port); Warning("Port %d busy while trying to enable proxy %s.\n", port, p->id); send_log(p, LOG_WARNING, "Port %d busy while trying to enable proxy %s.\n", port, p->id); /* Another port might have been enabled. Let's stop everything. */ pause_proxy(p); break; } } } p = p->next; } } /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * End: */