summaryrefslogtreecommitdiff
path: root/dpid/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'dpid/main.c')
-rw-r--r--dpid/main.c398
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]);
+ }
+ }
+ }
+ }
+ }
+}