diff options
author | jcid <devnull@localhost> | 2007-10-07 00:36:34 +0200 |
---|---|---|
committer | jcid <devnull@localhost> | 2007-10-07 00:36:34 +0200 |
commit | 93715c46a99c96d6c866968312691ec9ab0f6a03 (patch) | |
tree | 573f19ec6aa740844f53a7c0eb7114f04096bf64 /dpid |
Initial revision
Diffstat (limited to 'dpid')
-rw-r--r-- | dpid/Makefile.am | 26 | ||||
-rw-r--r-- | dpid/TODO | 32 | ||||
-rw-r--r-- | dpid/dpi.c | 97 | ||||
-rw-r--r-- | dpid/dpi.h | 54 | ||||
-rw-r--r-- | dpid/dpi_service.c | 114 | ||||
-rw-r--r-- | dpid/dpi_service.h | 20 | ||||
-rw-r--r-- | dpid/dpi_socket_dir.c | 128 | ||||
-rw-r--r-- | dpid/dpi_socket_dir.h | 17 | ||||
-rw-r--r-- | dpid/dpid.c | 734 | ||||
-rw-r--r-- | dpid/dpid.h | 103 | ||||
-rw-r--r-- | dpid/dpid_common.c | 43 | ||||
-rw-r--r-- | dpid/dpid_common.h | 61 | ||||
-rw-r--r-- | dpid/dpidc | 31 | ||||
-rw-r--r-- | dpid/main.c | 398 | ||||
-rw-r--r-- | dpid/misc_new.c | 172 | ||||
-rw-r--r-- | dpid/misc_new.h | 12 |
16 files changed, 2042 insertions, 0 deletions
diff --git a/dpid/Makefile.am b/dpid/Makefile.am new file mode 100644 index 00000000..cd15afd4 --- /dev/null +++ b/dpid/Makefile.am @@ -0,0 +1,26 @@ +AM_CPPFLAGS=-DDPIDRC_SYS='"$(sysconfdir)/dpidrc"' + +bin_PROGRAMS = dpid +dpid_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a + +EXTRA_DIST = dpidc +bin_SCRIPTS = dpidc + +dpid_SOURCES = \ + dpi.h \ + dpi_service.h \ + dpi_socket_dir.h \ + dpid.h \ + dpid_common.h \ + misc_new.h \ + dpi.c \ + dpi_service.c \ + dpi_socket_dir.c \ + dpid.c \ + dpid_common.c \ + main.c \ + misc_new.c + +install-data-local : + $(mkinstalldirs) $(DESTDIR)$(sysconfdir) + echo dpi_dir=$(libdir)/dillo/dpi > $(DESTDIR)$(sysconfdir)/dpidrc diff --git a/dpid/TODO b/dpid/TODO new file mode 100644 index 00000000..c9967535 --- /dev/null +++ b/dpid/TODO @@ -0,0 +1,32 @@ +Todo List + + File dpi_service.c + This module should be removed because its original functions + have been removed or modified. Put these functions in dpid.c + + File dpi_service.h + This module should be removed because its original functions + have been removed or modified. Put these functions in dpid.c + + Add other file types, but first we need to add files associated + with a dpi to the design. + + Global SRS_NAME + Should read this from dillorc + + File dpid_common.h + The dpid error codes will be used in the next patch + + Global main() + + add new plugins when they become available + __________________________________________________________ + +Remove global variables. + +Use a singly linked list for dpi_attr_list + +Allow dpis to be registered one at a time + +Only run dpis listed in users dillorc + +MAYBE Have dpid accept all connections to dpis (fixes inf loop if dpi crashes before accept) diff --git a/dpid/dpi.c b/dpid/dpi.c new file mode 100644 index 00000000..45e5b49d --- /dev/null +++ b/dpid/dpi.c @@ -0,0 +1,97 @@ +/* + 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 + */ + +/*! \file + * Access functions for ~/.dillo/dpi_socket_dir. + * The most useful function for dillo is a_Dpi_srs, it returns + * the full path to the dpid service request socket. + */ + +#include <errno.h> +#include "dpid_common.h" +#include "dpi.h" +#include "misc_new.h" + +/*! \Return + * Returns path to the dpi_socket_dir file + * Use dFree to free memory + */ +char *a_Dpi_sockdir_file(void) +{ + char *dpi_socket_dir, *dirfile_path = "/.dillo/dpi_socket_dir"; + + dpi_socket_dir = dStrconcat(dGethomedir(), dirfile_path, NULL); + return dpi_socket_dir; +} + +/*! Read socket directory path from ~/.dillo/dpi_socket_dir + * \Return + * socket directory path or NULL if the dpi_socket_dir file does not exist. + * \Note + * This function exits if ~/.dillo does not exist or + * if the dpi_socket_dir file cannot be opened for a + * reason other than it does not exist. + */ + +char *a_Dpi_rd_dpi_socket_dir(char *dirname) +{ + FILE *dir; + char *sockdir = NULL, *rcpath; + + rcpath = dStrconcat(dGethomedir(), "/.dillo", NULL); + + /* If .dillo does not exist it is an unrecoverable error */ + if (access(rcpath, F_OK) == -1) { + ERRMSG("a_Dpi_rd_dpi_socket_dir", "access", errno); + MSG_ERR(" - %s\n", rcpath); + exit(1); + } + dFree(rcpath); + + if ((dir = fopen(dirname, "r")) != NULL) { + sockdir = dGetline(dir); + fclose(dir); + } else if (errno == ENOENT) { + ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno); + MSG_ERR(" - %s\n", dirname); + } else if (errno != ENOENT) { + ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno); + MSG_ERR(" - %s\n", dirname); + exit(1); + } + + return sockdir; +} + +/*! + * \Modifies + * srs_name + * \Return + * The service request socket name with its complete path. + */ +char *a_Dpi_srs(void) +{ + char *dirfile_path, *sockdir, *srs_name; + + dirfile_path = a_Dpi_sockdir_file(); + sockdir = dStrstrip(a_Dpi_rd_dpi_socket_dir(dirfile_path)); + srs_name = dStrconcat(sockdir, "/", "dpid.srs", NULL); + dFree(sockdir); + dFree(dirfile_path); + return (srs_name); +} diff --git a/dpid/dpi.h b/dpid/dpi.h new file mode 100644 index 00000000..d97fdc6d --- /dev/null +++ b/dpid/dpi.h @@ -0,0 +1,54 @@ +/*! \file + * Access functions for ~/.dillo/dpi_socket_dir. + * The most useful function for dillo is a_Dpi_srs, it returns + * the full path to the dpid service request socket. + */ + +#ifndef DPI_H +#define DPI_H + +#include <config.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +/* Check the Unix98 goodie */ +#ifndef socklen_t + #define socklen_t uint32_t +#endif + +/* Some systems may not have this one... */ +#ifndef AF_LOCAL + #define AF_LOCAL AF_UNIX +#endif + +/* This one is tricky, some sources state it should include the byte + * for the terminating NULL, and others say it shouldn't. + * The other way is to only use this one when a native SUN_LEN is not present, + * but as dillo has used this for a long time successfully, here it goes. + */ +# define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) + +/*! + * dpi commands + */ +enum { + UNKNOWN_CMD, + BYE_CMD, /* "DpiBye" */ + CHECK_SERVER_CMD, /* "check_server" */ + REGISTER_ALL_CMD, /* "register_all" */ + REGISTER_SERVICE_CMD /* "register_service" */ +}; + + +char *a_Dpi_sockdir_file(void); + +char *a_Dpi_rd_dpi_socket_dir(char *dirname); + +char *a_Dpi_srs(void); + +#endif diff --git a/dpid/dpi_service.c b/dpid/dpi_service.c new file mode 100644 index 00000000..07cdad8e --- /dev/null +++ b/dpid/dpi_service.c @@ -0,0 +1,114 @@ +/* + 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 + */ + +/*! \file + * \todo + * This module should be removed because its original functions + * have been removed or modified. + * Put these functions in dpid.c + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include "dpid_common.h" +#include "dpid.h" +#include "../dpip/dpip.h" + +#ifdef TEST +#include "testdat.h" +#endif + +/* exported functions */ +char *get_dpi_dir(char *dpidrc); + + +/*! Get dpi directory path from dpidrc + * \Return + * dpi directory on success, NULL on failure + * \Important + * The dpi_dir definition in dpidrc must have no leading white space. + */ +char *get_dpi_dir(char *dpidrc) +{ + FILE *In; + int len; + char *rcline = NULL, *value = NULL, *p; + + if ((In = fopen(dpidrc, "r")) == NULL) { + ERRMSG("dpi_dir", "fopen", errno); + MSG_ERR(" - %s\n", dpidrc); + return (NULL); + } + + while ((rcline = dGetline(In)) != NULL) { + if (strncmp(rcline, "dpi_dir", 7) == 0) + break; + dFree(rcline); + } + fclose(In); + + if (!rcline) { + ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0); + MSG_ERR("Put your dillo plugins path in %s\n", dpidrc); + MSG_ERR("eg. dpi_dir=/usr/local/lib/dillo/dpi "); + MSG_ERR("with no leading spaces.\n"); + value = NULL; + } else { + len = (int) strlen(rcline); + if (len && rcline[len - 1] == '\n') + rcline[len - 1] = 0; + + if ((p = strchr(rcline, '='))) { + while (*++p == ' '); + value = dStrdup(p); + } else { + ERRMSG("dpi_dir", "strchr", 0); + MSG_ERR(" - '=' not found in %s\n", rcline); + value = NULL; + } + } + + dFree(rcline); + return (value); +} + +/*! Send the list of available dpi IDs to a client + * \Return + * 1 on success, -1 on failure. + * +static int send_service_list(int sock, struct dp *dpi_attr_list, int srv_num) +{ + int i; + char *buf; + ssize_t wlen = 0; + + for (i = 0; i < srv_num && wlen != -1; i++) { + d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", + "send_data", dpi_attr_list[i].id); + wlen = write(sock, d_cmd, strlen(d_cmd)); + dFree(d_cmd); + } + if (wlen == -1) { + ERRMSG("send_service_list", "write", errno); + return (-1); + } + return (1); +} + */ diff --git a/dpid/dpi_service.h b/dpid/dpi_service.h new file mode 100644 index 00000000..af7679b3 --- /dev/null +++ b/dpid/dpi_service.h @@ -0,0 +1,20 @@ +/*! \file + * \todo + * This module should be removed because its original functions + * have been removed or modified. + * Put these functions in dpid.c + */ + +#ifndef DPI_SERVICE_H +#define DPI_SERVICE_H + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "dpid.h" + +char *get_dpi_dir(char *dpidrc); + +int send_service_list(int sock, struct dp *dpi_attr_list, int srv_num); + +#endif diff --git a/dpid/dpi_socket_dir.c b/dpid/dpi_socket_dir.c new file mode 100644 index 00000000..c18faf0d --- /dev/null +++ b/dpid/dpi_socket_dir.c @@ -0,0 +1,128 @@ +/* + 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 + */ + +/*! \file + * Create a per user temporary directory for dpi sockets + */ + +#include <errno.h> +#include "dpid_common.h" +#include "dpi.h" +#include "misc_new.h" +#include "dpi_socket_dir.h" /* for function prototypes */ + +/*! Save socket directory name in ~/.dillo/dpi_socket_dir + * \Return + * \li 1 on success + * \li -1 on failure + */ +int w_dpi_socket_dir(char *dirname, char *sockdir) +{ + FILE *dir; + + if ((dir = fopen(dirname, "w")) == NULL) { + ERRMSG("w_dpi_socket_dir", "fopen", errno); + return (-1); + } + fprintf(dir, "%s", sockdir); + fclose(dir); + return (1); +} + +/*! Test that socket directory exists and is a directory + * \Return + * \li 1 on success + * \li 0 on failure + * \bug Does not check permissions or that it's a symbolic link + * to another directory. + */ +int tst_dir(char *dir) +{ + char *dirtest; + int ret = 0; + + /* test for a directory */ + dirtest = dStrconcat(dir, "/", NULL); + if (access(dirtest, F_OK) == -1) { + ERRMSG("tst_dir", "access", errno); + MSG_ERR(" - %s\n", dirtest); + } else { + ret = 1; + } + dFree(dirtest); + + return ret; +} + +/*! Create socket directory + * \Return + * \li Socket directory path on success + * \li NULL on failure + */ +char *mk_sockdir(void) +{ + char *template; + + template = dStrconcat("/tmp/", getlogin(), "-", "XXXXXX", NULL); + if (a_Misc_mkdtemp(template) == NULL) { + ERRMSG("mk_sockdir", "a_Misc_mkdtemp", 0); + MSG_ERR(" - %s\n", template); + dFree(template); + return (NULL); + } + return template; +} + +/*! Create socket directory if it does not exist and save its name in + * ~/.dillo/dpi_socket_dir. + * \Return + * \li Socket directory name on success + * \li NULL on failure. + */ +char *init_sockdir(char *dpi_socket_dir) +{ + char *sockdir = NULL; + int dir_ok = 0; + + if ((sockdir = a_Dpi_rd_dpi_socket_dir(dpi_socket_dir)) == NULL) { + MSG_ERR("init_sockdir: The dpi_socket_dir file %s does not exist\n", + sockdir); + } else { + if ((dir_ok = tst_dir(sockdir)) == 1) { + MSG_ERR("init_sockdir: The socket directory %s exists and is OK\n", + sockdir); + } else { + MSG_ERR("init_sockdir: The socket directory %s does not exist " + "or is not a directory\n", sockdir); + dFree(sockdir); + } + } + if (!dir_ok) { + sockdir = mk_sockdir(); + if (sockdir == NULL) { + ERRMSG("init_sockdir", "mk_sockdir", 0); + MSG_ERR(" - Failed to create dpi socket directory\n"); + } else if ((w_dpi_socket_dir(dpi_socket_dir, sockdir)) == -1) { + ERRMSG("init_sockdir", "w_dpi_socket_dir", 0); + MSG_ERR(" - failed to save %s\n", sockdir); + dFree(sockdir); + sockdir = NULL; + } + } + return (sockdir); +} diff --git a/dpid/dpi_socket_dir.h b/dpid/dpi_socket_dir.h new file mode 100644 index 00000000..87719212 --- /dev/null +++ b/dpid/dpi_socket_dir.h @@ -0,0 +1,17 @@ +/*! \file + * Create a per user temporary directory for dpi sockets + */ + +#ifndef DPI_SOCKET_DIR_H +#define DPI_SOCKET_DIR_H + + +int w_dpi_socket_dir(char *dirname, char *sockdir); + +int tst_dir(char *dir); + +char *mk_sockdir(void); + +char *init_sockdir(char *dpi_socket_dir); + +#endif diff --git a/dpid/dpid.c b/dpid/dpid.c new file mode 100644 index 00000000..9fe2e74d --- /dev/null +++ b/dpid/dpid.c @@ -0,0 +1,734 @@ +/* + 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 + */ + +/*! \file + * Main functions to set-up dpi information and to initialise sockets + */ +#include <errno.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include "dpid_common.h" +#include "dpid.h" +#include "dpi.h" +#include "dpi_socket_dir.h" +#include "dpi_service.h" +#include "misc_new.h" + +#include "../dpip/dpip.h" + +#define QUEUE 5 + +volatile sig_atomic_t caught_sigchld = 0; + +/*! Return the basename of a filename + */ +char *get_basename(char *filename) +{ + char *p; + + if (filename && (p = strrchr(filename, '/'))) { + filename = p + 1; + } + return filename; +} + +/*! Close and remove the sockets in the + * given dpi attribute list + */ +void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis) +{ + int i; + + for (i = 0; i < numdpis; i++) { + a_Misc_close_fd(dpi_attr_list[i].socket); + (void) unlink(dpi_attr_list[i].sockpath); + } +} + +/*! Close and remove inactive dpi sockets + * \Return + * Number of active dpis. + */ +int rm_inactive_dpi_sockets(struct dp *dpi_attr_list, int numdpis) +{ + int i, active = 0; + + for (i = 0; i < numdpis; i++) { + if (dpi_attr_list[i].pid == 1) { + a_Misc_close_fd(dpi_attr_list[i].socket); + (void) unlink(dpi_attr_list[i].sockpath); + } else + active++; + } + return (active); +} + +/*! Remove sockets + */ +void cleanup(char *socket_dir) +{ + DIR *dir; + struct dirent *dir_entry = NULL; + char *sockpath; + + dir = opendir(socket_dir); + if (dir == NULL) { + ERRMSG("cleanup", "opendir", errno); + return; + } + while ( (dir_entry = readdir(dir)) != NULL ) { + if (dir_entry->d_name[0] == '.') + continue; + sockpath = dStrconcat(socket_dir, "/", dir_entry->d_name, NULL); + unlink(sockpath); + dFree(sockpath); + } + closedir(dir); +} + +/*! Free memory used to describe + * a set of dpi attributes + */ +void free_dpi_attr(struct dp *dpi_attr) +{ + if (dpi_attr->id != NULL) { + dFree(dpi_attr->id); + dpi_attr->id = NULL; + } + if (dpi_attr->path != NULL) { + dFree(dpi_attr->path); + dpi_attr->path = NULL; + } + if (dpi_attr->sockpath != NULL) { + dFree(dpi_attr->sockpath); + dpi_attr->sockpath = NULL; + } +} + +/*! Free memory used by the plugin list + */ +void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis) +{ + int i; + struct dp *dpi_attr_list = *dpi_attr_list_ptr; + + if (dpi_attr_list == NULL) + return; + + for (i = 0; i < numdpis; i++) + free_dpi_attr(dpi_attr_list + i); + + dFree(dpi_attr_list); + *dpi_attr_list_ptr = NULL; +} + +/*! \todo + * Remove terminator and est_terminator unless we really want to clean up + * on abnormal exit. + */ +#if 0 +/*! Signal handler for SIGINT, SIGQUIT, and SIGTERM. Calls cleanup + */ +void terminator(int sig) +{ + (void) signal(SIGCHLD, SIG_DFL); + cleanup(); + (void) signal(sig, SIG_DFL); + (void) raise(sig); + _exit(0); +} + +/*! Establish handler for termination signals + * and register cleanup with atexit */ +void est_terminator(void) +{ + struct sigaction act; + sigset_t block; + + (void) sigemptyset(&block); + (void) sigaddset(&block, SIGINT); + (void) sigaddset(&block, SIGQUIT); + (void) sigaddset(&block, SIGTERM); + (void) sigaddset(&block, SIGSEGV); + + act.sa_handler = terminator; + act.sa_mask = block; + act.sa_flags = 0; + + if (sigaction(SIGINT, &act, NULL) || + sigaction(SIGQUIT, &act, NULL) || + sigaction(SIGTERM, &act, NULL) || sigaction(SIGSEGV, &act, NULL)) { + ERRMSG("est_terminator", "sigaction", errno); + exit(1); + } + + if (atexit(cleanup) != 0) { + ERRMSG("est_terminator", "atexit", 0); + MSG_ERR("Hey! atexit failed, how did that happen?\n"); + exit(1); + } +} +#endif + +/*! Identify a given file + * Currently there is only one file type associated with dpis. + * More file types will be added as needed + */ +enum file_type get_file_type(char *file_name) +{ + char *dot = strrchr(file_name, '.'); + + if (dot && !strcmp(dot, ".dpi")) + return DPI_FILE; /* Any filename ending in ".dpi" */ + else { + MSG_ERR("get_file_type: Unknown file type for %s\n", file_name); + return UNKNOWN_FILE; + } +} + +/*! Scans a service directory in dpi_dir and fills dpi_attr + * \Note + * Caller must allocate memory for dpi_attr. + * \Return + * \li 0 on success + * \li -1 on failure + * \todo + * Add other file types, but first we need to add files associated with a dpi + * to the design. + */ +int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr) +{ + char *service_dir = NULL; + struct stat statinfo; + enum file_type ftype; + int retval = -1; + DIR *dir_stream; + struct dirent *dir_entry = NULL; + + service_dir = dStrconcat(dpi_dir, "/", service, NULL); + if (stat(service_dir, &statinfo) == -1) { + ERRMSG("get_dpi_attr", "stat", errno); + MSG_ERR("file=%s\n", service_dir); + } else if ((dir_stream = opendir(service_dir)) == NULL) { + ERRMSG("get_dpi_attr", "opendir", errno); + } else { + /* Scan the directory loking for dpi files. + * (currently there's only the dpi program, but in the future + * there may also be helper scripts.) */ + while ( (dir_entry = readdir(dir_stream)) != NULL) { + if (dir_entry->d_name[0] == '.') + continue; + + ftype = get_file_type(dir_entry->d_name); + switch (ftype) { + case DPI_FILE: + dpi_attr->path = + dStrconcat(service_dir, "/", dir_entry->d_name, NULL); + dpi_attr->id = dStrdup(service); + dpi_attr->sockpath = NULL; + dpi_attr->pid = 1; + if (strstr(dpi_attr->path, ".filter") != NULL) + dpi_attr->filter = 1; + else + dpi_attr->filter = 0; + retval = 0; + break; + default: + break; + } + } + closedir(dir_stream); + + if (retval != 0) + MSG_ERR("get_dpi_attr: No dpi plug-in in %s/%s\n", + dpi_dir, service); + } + dFree(service_dir); + return retval; +} + +/*! Register a service + * Retrieves attributes for "service" and stores them + * in dpi_attr. It looks for "service" in ~/.dillo/dpi + * first, and then in the system wide dpi directory. + * Caller must allocate memory for dpi_attr. + * \Return + * \li 0 on success + * \li -1 on failure + */ +int register_service(struct dp *dpi_attr, char *service) +{ + char *user_dpi_dir, *dpidrc, *user_service_dir, *dir = NULL; + int retval = -1; + + user_dpi_dir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL); + user_service_dir = + dStrconcat(dGethomedir(), "/", dotDILLO_DPI, "/", service, NULL); + + dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL); + if (access(dpidrc, F_OK) == -1) { + if (access(DPIDRC_SYS, F_OK) == -1) { + ERRMSG("register_service", "Error ", 0); + MSG_ERR("\n - There is no %s or %s file\n", dpidrc, + DPIDRC_SYS); + dFree(user_dpi_dir); + dFree(user_service_dir); + dFree(dpidrc); + return(-1); + } + dFree(dpidrc); + dpidrc = dStrdup(DPIDRC_SYS); + } + + /* Check home dir for dpis */ + if (access(user_service_dir, F_OK) == 0) { + get_dpi_attr(user_dpi_dir, service, dpi_attr); + retval = 0; + } else { /* Check system wide dpis */ + if ((dir = get_dpi_dir(dpidrc)) != NULL) { + if (access(dir, F_OK) == 0) { + get_dpi_attr(dir, service, dpi_attr); + retval = 0; + } else { + ERRMSG("register_service", "get_dpi_attr failed", 0); + } + } else { + ERRMSG("register_service", "dpi_dir: Error getting dpi dir.", 0); + } + } + dFree(user_dpi_dir); + dFree(user_service_dir); + dFree(dpidrc); + dFree(dir); + return (retval); +} + +/*! + * Create dpi directory for available + * plugins and create plugin list. + * \Return + * \li Returns number of available plugins on success + * \li -1 on failure + */ +int register_all(struct dp **attlist) +{ + DIR *user_dir_stream, *sys_dir_stream; + char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL; + char *basename=NULL; + struct dirent *user_dirent, *sys_dirent; + int j, st, not_in_user_list; + int snum, usr_srv_num; + size_t dp_sz = sizeof(struct dp); + + if (*attlist != NULL) { + ERRMSG("register_all", "attlist parameter should be NULL", 0); + return -1; + } + + user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL); + if (access(user_dpidir, F_OK) == -1) { + /* no dpis in user's space */ + dFree(user_dpidir); + user_dpidir = NULL; + } + dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL); + if (access(dpidrc, F_OK) == -1) { + dFree(dpidrc); + dpidrc = dStrdup(DPIDRC_SYS); + if (access(dpidrc, F_OK) == -1) { + dFree(dpidrc); + dpidrc = NULL; + } + } + if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL) + sys_dpidir = NULL; + dFree(dpidrc); + + if (!user_dpidir && !sys_dpidir) { + ERRMSG("register_all", "Fatal error ", 0); + MSG_ERR("\n - Can't find the directory for dpis.\n"); + exit(1); + } + + /* Get list of services in user's .dillo/dpi directory */ + snum = usr_srv_num = 0; + if (user_dpidir && (user_dir_stream = opendir(user_dpidir)) != NULL) { + while ((user_dirent = readdir(user_dir_stream)) != NULL) { + if (user_dirent->d_name[0] == '.') + continue; + *attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz); + st=get_dpi_attr(user_dpidir, user_dirent->d_name, &(*attlist)[snum]); + if (st == 0) + snum++; + } + usr_srv_num = snum; + closedir(user_dir_stream); + } + if (sys_dpidir && (sys_dir_stream = opendir(sys_dpidir)) != NULL) { + /* if system service is not in user list then add it */ + while ((sys_dirent = readdir(sys_dir_stream)) != NULL) { + if (sys_dirent->d_name[0] == '.') + continue; + not_in_user_list = 1; + for (j = 0; j < usr_srv_num; j++) { + basename = get_basename((*attlist)[j].path); + if (strcmp(sys_dirent->d_name, basename) == 0) { + not_in_user_list = 0; + break; + } + } + if (not_in_user_list) { + *attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz); + st=get_dpi_attr(sys_dpidir, sys_dirent->d_name, &(*attlist)[snum]); + if (st == 0) + snum++; + } + } + closedir(sys_dir_stream); + } + + dFree(sys_dpidir); + dFree(user_dpidir); + + /* todo: do we consider snum == 0 an error? + * (if so, we should return -1 ) */ + return (snum); +} + +/*! Initialise the service request socket + * \Return: + * \li Number of sockets (1 == success) + * \li -1 on failure + */ +int init_srs_socket(char *sockdir) +{ + int retval = -1; + struct sockaddr_un srs_sa; + size_t sun_path_len; + socklen_t addr_sz; + + srs_name = dStrconcat(sockdir, "/", SRS_NAME, NULL); + FD_ZERO(&sock_set); + + /* Initialise srs, service request socket on startup */ + if ((srs = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) { + ERRMSG("init_srs_socket", "socket", errno); + return (retval); /* avoids nesting ifs too deeply */ + } + /* Set srs to close on exec */ + fcntl(srs, F_SETFD, FD_CLOEXEC | fcntl(srs, F_GETFD)); + + srs_sa.sun_family = AF_LOCAL; + + sun_path_len = sizeof(srs_sa.sun_path); + if (strlen(srs_name) > sun_path_len) { + ERRMSG("init_srs_socket", "srs_name is too long", 0); + MSG_ERR("\n - it should be <= %lu chars", (ulong_t)sun_path_len); + MSG_ERR("\n - srs_name = %s\n", srs_name); + return(retval); + } + strncpy(srs_sa.sun_path, srs_name, sun_path_len); + addr_sz = (socklen_t) D_SUN_LEN(&srs_sa); + + if ((bind(srs, (struct sockaddr *) &srs_sa, addr_sz)) == -1) { + if (errno == EADDRINUSE) { + ERRMSG("init_srs_socket", "bind", errno); + MSG_ERR("srs_sa.sun_path = %s\n", srs_sa.sun_path); + dpi_errno = dpid_srs_addrinuse; + } else { + ERRMSG("init_srs_socket", "bind", errno); + MSG_ERR("srs_sa.sun_path = %s\n", srs_sa.sun_path); + } + } else if (chmod(srs_sa.sun_path, S_IRUSR | S_IWUSR) == -1) { + ERRMSG("init_srs_socket", "chmod", errno); + MSG_ERR("srs_sa.sun_path = %s\n", srs_sa.sun_path); + } else if (listen(srs, QUEUE) == -1) { + ERRMSG("init_srs_socket", "listen", errno); + } else { + retval = 1; + } + + FD_SET(srs, &sock_set); + return (retval); +} + +/*! Initialise a single dpi socket + * \Return + * \li 1 on success + * \li -1 on failure + */ +int init_dpi_socket(struct dp *dpi_attr, char *sockdir) +{ + int caught_error = 0, s; + char *dpi_nm; /* pointer to basename in dpi_attr->path */ + struct sockaddr_un sa; + size_t sp_len; + socklen_t addr_sz; + size_t sock_buflen = 8192; + + sp_len = sizeof(sa.sun_path); + if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) { + ERRMSG("init_all_dpi_sockets", "socket", errno); + return (-1); /* avoids nested ifs */ + } + /* Set the socket FD to close on exec */ + fcntl(s, F_SETFD, FD_CLOEXEC | fcntl(s, F_GETFD)); + + /* set some buffering to increase the transfer's speed */ + setsockopt(s, SOL_SOCKET, SO_SNDBUF, + &sock_buflen, (socklen_t)sizeof(sock_buflen)); + + dpi_attr->socket = s; + dpi_attr->sa.sun_family = AF_LOCAL; + dpi_nm = get_basename(dpi_attr->path); + + dpi_attr->sockpath = dStrconcat(sockdir, "/", dpi_nm, NULL); + if (strlen(dpi_attr->sockpath) > sp_len) { + ERRMSG("init_all_dpi_sockets", "socket path is too long", 0); + MSG_ERR("\n - it should be <= %lu chars", (ulong_t)sp_len); + MSG_ERR("\n - socket path = %s\n", dpi_attr->sockpath); + return(-1); + } + strncpy(dpi_attr->sa.sun_path, dpi_attr->sockpath, sp_len); + addr_sz = (socklen_t) D_SUN_LEN(&dpi_attr->sa); + + if ((bind(s, (struct sockaddr *) &dpi_attr->sa, addr_sz)) == -1) { + ERRMSG("init_all_dpi_sockets", "bind", errno); + MSG_ERR("%s\n", dpi_attr->sa.sun_path); + caught_error = 1; + } else if (chmod(dpi_attr->sa.sun_path, S_IRUSR | S_IWUSR) == -1) { + ERRMSG("init_all_dpi_sockets", "chmod", errno); + MSG_ERR("%s\n", dpi_attr->sa.sun_path); + caught_error = 1; + } else if (listen(s, QUEUE) == -1) { + ERRMSG("init_all_dpi_sockets", "listen", errno); + caught_error = 1; + } + + if (caught_error) { + return (-1); + } else { + FD_SET(s, &sock_set); + return (1); + } +} + +/*! Setup sockets for the plugins and add them to + * to the set of sockets (sock_set) watched by select. + * \Return + * \li Number of sockets on success + * \li -1 on failure + * \Modifies + * dpi_attr_list.sa, dpi_attr_list.socket, numsocks, sock_set, srs + * \Uses + * numdpis, srs, srs_name + */ +int init_all_dpi_sockets(struct dp *dpi_attr_list, char *sockdir) +{ + int i; + struct sockaddr_un sa; + size_t sp_len; + + sp_len = sizeof(sa.sun_path); + + /* Initialise sockets for each dpi */ + for (i = 0; i < numdpis; i++) { + if (init_dpi_socket(dpi_attr_list + i, sockdir) == -1) + return (-1); + numsocks++; + } + + return (numsocks); +} + +/*! SIGCHLD handler + */ +void dpi_sigchld(int sig) +{ + caught_sigchld = 1; +} + +/*! Called by main loop when caught_sigchld == 1 */ +void handle_sigchld(void) +{ + // pid_t pid; + int i, status; //, num_active; + + /* For all of the dpis in the current list + * add the ones that have exited to the set of sockets being + * watched by 'select'. + */ + for (i = 0; i < numdpis; i++) { + if (waitpid(dpi_attr_list[i].pid, &status, WNOHANG) > 0) { + dpi_attr_list[i].pid = 1; + FD_SET(dpi_attr_list[i].socket, &sock_set); + numsocks++; + } + } + + /* Wait for any old dpis that have exited */ + while (waitpid(-1, &status, WNOHANG) > 0) + ; +} + +/*! Establish SIGCHLD handler */ +void est_dpi_sigchld(void) +{ + struct sigaction sigact; + sigset_t set; + + (void) sigemptyset(&set); + sigact.sa_handler = dpi_sigchld; + sigact.sa_mask = set; + sigact.sa_flags = SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sigact, NULL) == -1) { + ERRMSG("est_dpi_sigchld", "sigaction", errno); + exit(1); + } +} + +/*! Send DpiBye command to all active non-filter dpis + */ +void stop_active_dpis(struct dp *dpi_attr_list, int numdpis) +{ + static char *DpiBye_cmd = NULL; + int i, dpi_socket; + struct sockaddr_un dpi_addr; + struct sockaddr_un sa; + size_t sun_path_len, addr_len; + + if (!DpiBye_cmd) + DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye"); + + sun_path_len = sizeof(sa.sun_path); + addr_len = sizeof(dpi_addr); + + dpi_addr.sun_family = AF_LOCAL; + + for (i = 0; i < numdpis; i++) { + /* Skip inactive dpis and filters */ + if (dpi_attr_list[i].pid == 1 || dpi_attr_list[i].filter) + continue; + + if ((dpi_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) { + ERRMSG("stop_active_dpis", "socket", errno); + } + if (strlen(dpi_attr_list[i].sockpath) > sun_path_len) { + ERRMSG("stop_active_dpis", "socket path is too long", 0); + MSG_ERR("\n - it should be <= %lu chars",(ulong_t)sun_path_len); + MSG_ERR("\n - socket path = %s\n", dpi_attr_list[i].sockpath); + } + strncpy(dpi_addr.sun_path, dpi_attr_list[i].sockpath, sun_path_len); + if (connect(dpi_socket, (struct sockaddr *) &dpi_addr, addr_len) == -1) { + ERRMSG("stop_active_dpis", "connect", errno); + MSG_ERR("%s\n", dpi_addr.sun_path); + } + (void) write(dpi_socket, DpiBye_cmd, strlen(DpiBye_cmd)); + a_Misc_close_fd(dpi_socket); + } +} + +/*! Removes dpis in dpi_attr_list from the + * set of sockets watched by select and + * closes their sockets. + */ +void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis) +{ + int i; + + for (i = 0; i < numdpis; i++) { + FD_CLR(dpi_attr_list[i].socket, &sock_set); + a_Misc_close_fd(dpi_attr_list[i].socket); + } +} + +/*! Registers available dpis and stops active non-filter dpis. + * Called when dpid receives + * cmd='register' service='all' + * command + * \Return + * Number of available dpis + */ +int register_all_cmd(char *sockdir) +{ + stop_active_dpis(dpi_attr_list, numdpis); + rm_dpi_sockets(dpi_attr_list, numdpis); + free_plugin_list(&dpi_attr_list, numdpis); + numdpis = 0; + numsocks = 1; /* the srs socket */ + FD_ZERO(&sock_set); + FD_SET(srs, &sock_set); + numdpis = register_all(&dpi_attr_list); + numsocks = init_all_dpi_sockets(dpi_attr_list, sockdir); + return (numdpis); +} + +/*! + * Get value of msg field from dpi_tag + * \Return + * message on success, NULL on failure + */ +char *get_message(int sock, char *dpi_tag) +{ + char *msg, *d_cmd; + + msg = a_Dpip_get_attr(dpi_tag, strlen(dpi_tag), "msg"); + if (msg == NULL) { + ERRMSG("get_message", "failed to parse msg", 0); + d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", + "DpiError", "Failed to parse request"); + (void) CKD_WRITE(sock, d_cmd); + dFree(d_cmd); + } + return (msg); +} + +/*! + * Send socket path that matches dpi_id to client + */ +void send_sockpath(int sock, char *dpi_tag, struct dp *dpi_attr_list) +{ + int i; + char *dpi_id; + char *d_cmd; + + dReturn_if_fail((dpi_id = get_message(sock, dpi_tag)) != NULL); + + for (i = 0; i < numdpis; i++) + if (strstr(dpi_attr_list[i].id, dpi_id)) + break; + if (i < numdpis) { + /* found */ + if (access(dpi_attr_list[i].path, F_OK) == -1) { + ERRMSG("send_sockpath", "access", errno); + MSG_ERR(" - %s\n", dpi_attr_list[i].sockpath); + d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", + "DpiError", "Plugin currently unavailable"); + (void) CKD_WRITE(sock, d_cmd); + dFree(d_cmd); + } else { + d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", + "send_data", dpi_attr_list[i].sockpath); + (void) CKD_WRITE(sock, d_cmd); + dFree(d_cmd); + } + } + + dFree(dpi_id); +} diff --git a/dpid/dpid.h b/dpid/dpid.h new file mode 100644 index 00000000..2a41f538 --- /dev/null +++ b/dpid/dpid.h @@ -0,0 +1,103 @@ +/*! \file + * Main functions to set-up dpi information and to initialise sockets + */ + +#ifndef DPID_H +#define DPID_H + +#include <assert.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <errno.h> + +#include "d_size.h" + + +#define PATH_LEN 50 +#define CMDLEN 20 +#define MSGLEN 50 +/*! \todo: Should read this from dillorc */ +#define SRS_NAME "dpid.srs" +char *srs_name; + +/*! dpid service request socket */ +int srs; + +/*! plugin state information + */ +struct dp { + char *id; + char *path; + char *sockpath; + int socket; + struct sockaddr_un sa; + pid_t pid; + int filter; +}; + +/*! Number of available plugins */ +int numdpis; + +/*! Number of sockets being watched */ +int numsocks; + +/*! State information for each plugin. */ +struct dp *dpi_attr_list; + +/*! Set of sockets watched for connections */ +fd_set sock_set; + +/*! Set to 1 by the SIGCHLD handler dpi_sigchld */ +extern volatile sig_atomic_t caught_sigchld; + +void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis); + +int rm_inactive_dpi_sockets(struct dp *dpi_attr_list, int numdpis); + +void cleanup(char *socket_dir); + +void free_dpi_attr(struct dp *dpi_attr); + +void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis); + +enum file_type get_file_type(char *file_name); + +int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr); + +int register_service(struct dp *dpi_attr, char *service); + +int register_all(struct dp **attlist); + +int init_srs_socket(char *sockdir); + +int init_dpi_socket(struct dp *dpi_attr, char *sockdir); + +int init_all_dpi_sockets(struct dp *dpi_attr_list, char *sockdir); + +void dpi_sigchld(int sig); + +void handle_sigchld(void); + +void est_dpi_sigchld(void); + +void stop_active_dpis(struct dp *dpi_attr_list, int numdpis); + +void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis); + +int register_all_cmd(char *sockdir); + +char *get_message(int sock, char *dpi_tag); + +void send_sockpath(int sock, char * dpi_tag, struct dp *dpi_attr_list); + +#endif diff --git a/dpid/dpid_common.c b/dpid/dpid_common.c new file mode 100644 index 00000000..a04d9c4f --- /dev/null +++ b/dpid/dpid_common.c @@ -0,0 +1,43 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "dpid_common.h" + +/* + * Send a verbose error message. + */ +void errmsg(char *caller, char *called, int errornum, char *file, int line) +{ + MSG_ERR("%s:%d: %s: %s\n", file, line, caller, called); + if (errornum > 0) + MSG_ERR("%s\n", dStrerror(errornum)); +} + +/*! Selector function for scandir + * Do not scan files starting with '.' + */ +int no_dotfiles(const struct dirent *filedat) +{ + if (filedat->d_name[0] == '.') + return 0; + else + return 1; +} + +/*! + * Provides an error checked write command. + * Call this via the CKD_WRITE macro + * \return write return value + */ +ssize_t ckd_write(int fd, char *msg, char *file, int line) +{ + ssize_t ret; + + do { + ret = write(fd, msg, strlen(msg)); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + MSG_ERR("%s:%d: write: %s\n", file, line, dStrerror(errno)); + } + return (ret); +} diff --git a/dpid/dpid_common.h b/dpid/dpid_common.h new file mode 100644 index 00000000..4311a8a8 --- /dev/null +++ b/dpid/dpid_common.h @@ -0,0 +1,61 @@ +#ifndef DPID_COMMON_H +#define DPID_COMMON_H + +/*! \file + * Declares common functions, global variables, and types. + * + * \todo + * The dpid error codes will be used in + * the next patch + */ + +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <dirent.h> + +#include "../dlib/dlib.h" + +/* + * Debugging macros + */ +#define _MSG(...) +#define MSG(...) printf("[dpid]: " __VA_ARGS__) +#define _MSG_ERR(...) +#define MSG_ERR(...) fprintf(stderr, "[dpid]: " __VA_ARGS__) + +#define dotDILLO_DPI ".dillo/dpi" +#define dotDILLO_DPIDRC ".dillo/dpidrc" +#define ERRMSG(CALLER, CALLED, ERR)\ + errmsg(CALLER, CALLED, ERR, __FILE__, __LINE__) +#define _ERRMSG(CALLER, CALLED, ERR) + + +/*! + * Macro for calling the ckd_write function + */ +#define CKD_WRITE(fd, msg) ckd_write(fd, msg, __FILE__, __LINE__) + + +/*! Error codes for dpid */ +enum { + no_errors, + dpid_srs_addrinuse /* dpid service request socket address already in use */ +} dpi_errno; + +/*! Intended for identifying dillo plugins + * and related files + */ +enum file_type { + DPI_FILE, /*! Any file name containing .dpi */ + UNKNOWN_FILE +}; + + +void errmsg(char *caller, char *called, int errornum, char *file, int line); + +int no_dotfiles(const struct dirent *filedat); + +ssize_t ckd_write(int fd, char *msg, char *file, int line); + +#endif diff --git a/dpid/dpidc b/dpid/dpidc new file mode 100644 index 00000000..88b887cb --- /dev/null +++ b/dpid/dpidc @@ -0,0 +1,31 @@ +#!/usr/bin/perl -w +# Author: Ferdi Franceschini +# +# dpid control program +# Currently allows +# register: Tells dpid to register all available dpis +# stop: Stops dpid. + +use strict; +use IO::Socket::UNIX; + +# Get socket directory name +open(DSD, "<$ENV{HOME}/.dillo/dpi_socket_dir"); +my $dir = <DSD>; +close(DSD); + +my $socket = IO::Socket::UNIX->new(Peer => "$dir/dpid.srs", Type => SOCK_STREAM, Timeout => 1000 ) or die "new: $@"; + +$socket->autoflush(1); + +my %dpi_command = ( + "register" => "<dpi cmd='register_all' '>", + "stop" => "<dpi cmd='DpiBye' '>", + ); + +if ( exists($dpi_command{$ARGV[0]}) ) { + print $socket $dpi_command{$ARGV[0]}; +} else { + close($socket); + print "Usage: dpidc register|stop\n"; +} 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]); + } + } + } + } + } +} diff --git a/dpid/misc_new.c b/dpid/misc_new.c new file mode 100644 index 00000000..038e1fa6 --- /dev/null +++ b/dpid/misc_new.c @@ -0,0 +1,172 @@ +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include "d_size.h" +#include "misc_new.h" +#include "dpid_common.h" + +#include "misc_new.h" /* for function prototypes */ + + +/* + * Close a FD handling EINTR. + */ +int a_Misc_close_fd(int fd) +{ + int st; + + do { + st = close(fd); + } while (st < 0 && errno == EINTR); + return st; +} + +/*! Reads a dpi tag from a socket + * \li Continues after a signal interrupt + * \Return + * Dstr pointer to tag on success, NULL on failure + * \important Caller is responsible for freeing the returned Dstr * + */ +Dstr *a_Misc_rdtag(int socket) +{ + char c = '\0'; + ssize_t rdlen; + Dstr *tag; + + tag = dStr_sized_new(64); + + errno = 0; + + do { + rdlen = read(socket, &c, 1); + if (rdlen == -1 && errno != EINTR) + break; + dStr_append_c(tag, c); + } while (c != '>'); + + if (rdlen == -1) { + perror("a_Misc_rdtag"); + dStr_free(tag, TRUE); + return (NULL); + } + return (tag); +} + +/*! + * Read a dpi tag from sock + * \return + * pointer to dynamically allocated request tag + */ +char *a_Misc_readtag(int sock) +{ + char *tag, c, buf[10]; + size_t buflen, i; + size_t taglen = 0, tagmem = 10; + ssize_t rdln = 1; + + tag = NULL; + buf[0] = '\0'; + buflen = sizeof(buf) / sizeof(buf[0]); + // new start + tag = (char *) dMalloc(tagmem + 1); + for (i = 0; (rdln = read(sock, &c, 1)) != 0; i++) { + if (i == tagmem) { + tagmem += tagmem; + tag = (char *) dRealloc(tag, tagmem + 1); + } + tag[i] = c; + taglen += rdln; + if (c == '>') { + tag[i + 1] = '\0'; + break; + } + } + // new end + if (rdln == -1) { + ERRMSG("a_Misc_readtag", "read", errno); + } + + return (tag); +} + +/*! Reads a dpi tag from a socket without hanging on read. + * \li Continues after a signal interrupt + * \Return + * \li 1 on success + * \li 0 if input is not available within timeout microseconds. + * \li -1 on failure + * \important Caller is responsible for freeing the returned Dstr * + */ +/* Is this useful? +int a_Misc_nohang_rdtag(int socket, int timeout, Dstr **tag) +{ + int n_fd; + fd_set sock_set, select_set; + struct timeval tout; + + FD_ZERO(&sock_set); + FD_SET(socket, &sock_set); + + errno = 0; + do { + select_set = sock_set; + tout.tv_sec = 0; + tout.tv_usec = timeout; + n_fd = select(socket + 1, &select_set, NULL, NULL, &tout); + } while (n_fd == -1 && errno == EINTR); + + if (n_fd == -1) { + MSG_ERR("%s:%d: a_Misc_nohang_rdtag: %s\n", + __FILE__, __LINE__, dStrerror(errno)); + return(-1); + } + if (n_fd == 0) { + return(0); + } else { + *tag = a_Misc_rdtag(socket); + return(1); + } +} +*/ + +/* + * Alternative to mkdtemp(). + * Not as strong as mkdtemp, but enough for creating a directory. + * (adapted from dietlibc) + */ +char *a_Misc_mkdtemp(char *template) +{ + char *tmp = template + strlen(template) - 6; + int i; + unsigned int random; + + if (tmp < template) + goto error; + for (i = 0; i < 6; ++i) + if (tmp[i] != 'X') { + error: + errno = EINVAL; + return 0; + } + srand((uint_t)(time(0) ^ getpid())); + for (;;) { + random = (unsigned) rand(); + for (i = 0; i < 6; ++i) { + int hexdigit = (random >> (i * 5)) & 0x1f; + + tmp[i] = hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0'; + } + if (mkdir(template, 0700) == 0) + break; + if (errno == EEXIST) + continue; + return 0; + } + return template; +} diff --git a/dpid/misc_new.h b/dpid/misc_new.h new file mode 100644 index 00000000..94e10676 --- /dev/null +++ b/dpid/misc_new.h @@ -0,0 +1,12 @@ +#ifndef MISC_NEW_H +#define MISC_NEW_H + +#include "../dlib/dlib.h" + + +int a_Misc_close_fd(int fd); +Dstr *a_Misc_rdtag(int socket); +char *a_Misc_readtag(int sock); +char *a_Misc_mkdtemp(char *template); + +#endif |