diff options
Diffstat (limited to 'dpid/main.c')
-rw-r--r-- | dpid/main.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/dpid/main.c b/dpid/main.c new file mode 100644 index 00000000..09c46968 --- /dev/null +++ b/dpid/main.c @@ -0,0 +1,398 @@ +/* + Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au> + + 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 3 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 + */ + +#include <errno.h> +#include <unistd.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <assert.h> +#include "dpid_common.h" +#include "dpid.h" +#include "dpi.h" +#include "dpi_socket_dir.h" +#include "misc_new.h" +#include "../dpip/dpip.h" + +sigset_t mask_sigchld; + + +/* Start a dpi filter plugin after accepting the pending connection + * \Return + * \li Child process ID on success + * \li 0 on failure + */ +static int start_filter_plugin(struct dp dpi_attr) +{ + int newsock, old_stdout=-1, old_stdin=-1; + socklen_t csz; + struct sockaddr_un clnt_addr; + pid_t pid; + + csz = (socklen_t) sizeof(clnt_addr); + + newsock = accept(dpi_attr.socket, (struct sockaddr *) &clnt_addr, &csz); + if (newsock == -1) + ERRMSG("start_plugin", "accept", errno); + + dup2(STDIN_FILENO, old_stdin); + if (dup2(newsock, STDIN_FILENO) == -1) { + ERRMSG("start_plugin", "dup2", errno); + MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path); + exit(1); + } + + dup2(STDOUT_FILENO, old_stdout); + if (dup2(newsock, STDOUT_FILENO) == -1) { + ERRMSG("start_plugin", "dup2", errno); + MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path); + exit(1); + } + if ((pid = fork()) == -1) { + ERRMSG("main", "fork", errno); + return 0; + } + if (pid == 0) { + /* Child, start plugin */ + if (execl(dpi_attr.path, dpi_attr.path, NULL) == -1) { + ERRMSG("start_plugin", "execl", errno); + MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path); + exit(1); + } + } + + /* Parent, Close sockets fix stdio and return pid */ + if (a_Misc_close_fd(newsock) == -1) { + ERRMSG("start_plugin", "close", errno); + MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path); + exit(1); + } + a_Misc_close_fd(STDIN_FILENO); + a_Misc_close_fd(STDOUT_FILENO); + dup2(old_stdin, STDIN_FILENO); + dup2(old_stdout, STDOUT_FILENO); + return pid; +} + +static void start_server_plugin(struct dp dpi_attr) +{ + if (dup2(dpi_attr.socket, STDIN_FILENO) == -1) { + ERRMSG("start_plugin", "dup2", errno); + MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path); + exit(1); + } + if (a_Misc_close_fd(dpi_attr.socket) == -1) { + ERRMSG("start_plugin", "close", errno); + MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path); + exit(1); + } + if (execl(dpi_attr.path, dpi_attr.path, NULL) == -1) { + ERRMSG("start_plugin", "execl", errno); + MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path); + exit(1); + } +} + +/*! + * Read service request from sock + * \Return + * pointer to dynamically allocated request tag + */ +static char *get_request(int sock) +{ + char *req, buf[10]; + size_t buflen; + size_t rqsz; + ssize_t rdln; + + req = NULL; + buf[0] = '\0'; + buflen = sizeof(buf) / sizeof(buf[0]); + + (void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL); + for (rqsz = 0; (rdln = read(sock, buf, buflen)) != 0; rqsz += rdln) { + if (rdln == -1) + break; + req = (char *) realloc(req, rqsz + rdln + 1); + if (rqsz == 0) + req[0] = '\0'; + strncat(req, buf, (size_t) rdln); + } + (void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); + if (rdln == -1) { + ERRMSG("get_request", "read", errno); + } + + return (req); +} + +/*! + * Get value of cmd field in dpi_tag + * \Return + * command code on success, -1 on failure + */ +static int get_command(int sock, char *dpi_tag, struct dp *dpi_attr_list) +{ + char *cmd, *d_cmd; + int COMMAND; + + if (dpi_tag == NULL) { + _ERRMSG("get_command", "dpid tag is NULL", 0); + return (-1); + } + + cmd = a_Dpip_get_attr(dpi_tag, strlen(dpi_tag), "cmd"); + + if (cmd == NULL) { + ERRMSG("get_command", "a_Dpip_get_attr", 0); + MSG_ERR(": dpid failed to parse cmd in %s\n", dpi_tag); + d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", + "DpiError", "Failed to parse request"); + (void) CKD_WRITE(sock, d_cmd); + dFree(d_cmd); + COMMAND = -1; + } else if (strcmp("DpiBye", cmd) == 0) { + COMMAND = BYE_CMD; + } else if (strcmp("check_server", cmd) == 0) { + COMMAND = CHECK_SERVER_CMD; + } else if (strcmp("register_all", cmd) == 0) { + COMMAND = REGISTER_ALL_CMD; + } else if (strcmp("register_service", cmd) == 0) { + COMMAND = REGISTER_SERVICE_CMD; + } else { /* Error unknown command */ + COMMAND = UNKNOWN_CMD; + } + + dFree(cmd); + return (COMMAND); +} + +/* + * Check whether a dpi server is running + */ +int server_is_running(char *server_id) +{ + int i; + + /* Search in the set of running servers */ + for (i = 0; i < numdpis; i++) { + if (!dpi_attr_list[i].filter && dpi_attr_list[i].pid > 1 && + strcmp(dpi_attr_list[i].id, server_id) == 0) + return 1; + } + return 0; +} + + +/* + * Get MAX open FD limit (yes, it's tricky --Jcid). + */ +static int get_open_max(void) +{ +#ifdef OPEN_MAX + return OPEN_MAX; +#else + int ret = sysconf(_SC_OPEN_MAX); + if (ret < 0) + ret = 256; + return ret; +#endif +} + +/*! \todo + * \li Add a dpid_idle_timeout variable to dpidrc + * \bug Infinite loop if plugin crashes before it accepts a connection + */ +int main(void) +{ + int i, n = 0, open_max; + char *dirname = NULL, *sockdir = NULL; + int dpid_idle_timeout = 60 * 60; /* default, in seconds */ + struct timeval select_timeout; + sigset_t mask_none; + fd_set selected_set; + + dpi_attr_list = NULL; + //daemon(0,0); /* Use 0,1 for feedback */ + /* todo: call setsid() ?? */ + + /* Allow read and write access, but only for the user. + * todo: can this cause trouble with umount? */ + umask(0077); + /* todo: make dpid work on any directory. */ + // chdir("/"); + + /* close inherited file descriptors */ + open_max = get_open_max(); + for (i = 3; i < open_max; i++) + a_Misc_close_fd(i); + + /* this sleep used to unmask a race condition */ + // sleep(2); + + dpi_errno = no_errors; + + /* Get list of available dpis */ + numdpis = register_all(&dpi_attr_list); + + /* Get name of socket directory */ + dirname = a_Dpi_sockdir_file(); + if ((sockdir = init_sockdir(dirname)) == NULL) { + ERRMSG("main", "init_sockdir", 0); + MSG_ERR("Failed to create socket directory\n"); + exit(1); + } + + /* Remove any sockets that may have been leftover from a crash */ + cleanup(sockdir); + /* Initialise sockets */ + if ((numsocks = init_srs_socket(sockdir)) == -1) { + switch (dpi_errno) { + case dpid_srs_addrinuse: + MSG_ERR("dpid refuses to start, possibly because:\n"); + MSG_ERR("\t1) An instance of dpid is already running.\n"); + MSG_ERR("\t2) A previous dpid didn't clean up on exit.\n"); + exit(1); + default: + ERRMSG("main", "init_srs_sockets failed", 0); + exit(1); + } + } + numsocks = init_all_dpi_sockets(dpi_attr_list, sockdir); + //est_terminator(); /* Do we still want to clean up on an abnormal exit? */ + est_dpi_sigchld(); + + (void) sigemptyset(&mask_sigchld); + (void) sigaddset(&mask_sigchld, SIGCHLD); + (void) sigemptyset(&mask_none); + (void) sigprocmask(SIG_SETMASK, &mask_none, NULL); + + printf("dpid started\n"); +/* Start main loop */ + while (1) { + do { + (void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL); + if (caught_sigchld) { + handle_sigchld(); + caught_sigchld = 0; + } + (void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); + select_timeout.tv_sec = dpid_idle_timeout; + select_timeout.tv_usec = 0; + selected_set = sock_set; + n = select(FD_SETSIZE, &selected_set, NULL, NULL, &select_timeout); + if (n == 0) { /* select timed out, try to exit */ + /* BUG: This is a workaround for dpid not to exit when the + * downloads server is active. The proper way to handle it is with + * a dpip command that asks the server whether it's busy. + * Note: the cookies server may lose session info too. */ + if (server_is_running("downloads")) + continue; + + stop_active_dpis(dpi_attr_list, numdpis); + cleanup(sockdir); + exit(0); + } + } while (n == -1 && errno == EINTR); + + if (n == -1) { + ERRMSG("main", "select", errno); + exit(1); + } + /* If the service req socket is selected then service the req. */ + if (FD_ISSET(srs, &selected_set)) { + int sock; + socklen_t csz; + struct sockaddr_un clnt_addr; + char *req = NULL; + + --n; + assert(n >= 0); + csz = (socklen_t) sizeof(clnt_addr); + sock = accept(srs, (struct sockaddr *) &clnt_addr, &csz); + if (sock == -1) { + ERRMSG("main", "accept", errno); + MSG_ERR("accept on srs socket failed\n"); + MSG_ERR("service pending connections, and continue\n"); + } else { + int command; + + req = get_request(sock); + command = get_command(sock, req, dpi_attr_list); + switch (command) { + case BYE_CMD: + stop_active_dpis(dpi_attr_list, numdpis); + cleanup(sockdir); + exit(0); + break; + case CHECK_SERVER_CMD: + send_sockpath(sock, req, dpi_attr_list); + break; + case REGISTER_ALL_CMD: + register_all_cmd(sockdir); + break; + case UNKNOWN_CMD: + { + char *d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", + "DpiError", "Unknown command"); + (void) CKD_WRITE(sock, d_cmd); + dFree(d_cmd); + ERRMSG("main", "Unknown command", 0); + MSG_ERR(" for request: %s\n", req); + break; + } + case -1: + _ERRMSG("main", "get_command failed", 0); + break; + } + if (req) + free(req); + a_Misc_close_fd(sock); + } + } + + /* While there's a request on one of the plugin sockets + * find the matching plugin and start it. */ + for (i = 0; n > 0 && i < numdpis; i++) { + if (FD_ISSET(dpi_attr_list[i].socket, &selected_set)) { + --n; + assert(n >= 0); + + if (dpi_attr_list[i].filter) { + /* start a dpi filter plugin and continue watching its socket + * for new connections */ + (void) sigprocmask(SIG_SETMASK, &mask_none, NULL); + start_filter_plugin(dpi_attr_list[i]); + } else { + /* start a dpi server plugin but don't wait for new connections + * on its socket */ + numsocks--; + assert(numsocks >= 0); + FD_CLR(dpi_attr_list[i].socket, &sock_set); + if ((dpi_attr_list[i].pid = fork()) == -1) { + ERRMSG("main", "fork", errno); + /* exit(1); */ + } else if (dpi_attr_list[i].pid == 0) { + (void) sigprocmask(SIG_SETMASK, &mask_none, NULL); + start_server_plugin(dpi_attr_list[i]); + } + } + } + } + } +} |