diff options
Diffstat (limited to 'src/rc/rc-plugin.c')
-rw-r--r-- | src/rc/rc-plugin.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/src/rc/rc-plugin.c b/src/rc/rc-plugin.c new file mode 100644 index 00000000..613f049e --- /dev/null +++ b/src/rc/rc-plugin.c @@ -0,0 +1,241 @@ +/* + librc-plugin.c + Simple plugin handler + */ + +/* + * Copyright 2007 Roy Marples + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 <sys/types.h> +#include <sys/wait.h> +#include <dirent.h> +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" +#include "rc-plugin.h" +#include "strlist.h" + +#define RC_PLUGIN_HOOK "rc_plugin_hook" + +bool rc_in_plugin = false; + +typedef struct plugin +{ + char *name; + void *handle; + int (*hook) (rc_hook_t, const char *); + struct plugin *next; +} plugin_t; + +static plugin_t *plugins = NULL; + +#ifndef __FreeBSD__ +dlfunc_t dlfunc (void * __restrict handle, const char * __restrict symbol) +{ + union { + void *d; + dlfunc_t f; + } rv; + + rv.d = dlsym (handle, symbol); + return (rv.f); +} +#endif + +void rc_plugin_load (void) +{ + DIR *dp; + struct dirent *d; + plugin_t *plugin = plugins; + char *p; + void *h; + int (*fptr) (rc_hook_t, const char *); + + /* Don't load plugins if we're in one */ + if (rc_in_plugin) + return; + + /* Ensure some sanity here */ + rc_plugin_unload (); + + if (! (dp = opendir (RC_PLUGINDIR))) + return; + + while ((d = readdir (dp))) { + if (d->d_name[0] == '.') + continue; + + p = rc_strcatpaths (RC_PLUGINDIR, d->d_name, NULL); + h = dlopen (p, RTLD_LAZY); + free (p); + if (! h) { + eerror ("dlopen: %s", dlerror ()); + continue; + } + + fptr = (int (*)(rc_hook_t, const char*)) dlfunc (h, RC_PLUGIN_HOOK); + if (! fptr) { + eerror ("%s: cannot find symbol `%s'", d->d_name, RC_PLUGIN_HOOK); + dlclose (h); + } else { + if (plugin) { + plugin->next = xmalloc (sizeof (plugin_t)); + plugin = plugin->next; + } else + plugin = plugins = xmalloc (sizeof (plugin_t)); + + memset (plugin, 0, sizeof (plugin_t)); + plugin->name = xstrdup (d->d_name); + plugin->handle = h; + plugin->hook = fptr; + } + } + closedir (dp); +} + +int rc_waitpid (pid_t pid) +{ + int status = 0; + pid_t savedpid = pid; + int retval = -1; + + errno = 0; + while ((pid = waitpid (savedpid, &status, 0)) > 0) { + if (pid == savedpid) + retval = WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE; + } + + return (retval); +} + +void rc_plugin_run (rc_hook_t hook, const char *value) +{ + plugin_t *plugin = plugins; + + /* Don't run plugins if we're in one */ + if (rc_in_plugin) + return; + + while (plugin) { + if (plugin->hook) { + int i; + int flags; + int pfd[2]; + pid_t pid; + + /* We create a pipe so that plugins can affect our environment + * vars, which in turn influence our scripts. */ + if (pipe (pfd) == -1) { + eerror ("pipe: %s", strerror (errno)); + return; + } + + /* Stop any scripts from inheriting us. + * This is actually quite important as without this, the splash + * plugin will probably hang when running in silent mode. */ + for (i = 0; i < 2; i++) + if ((flags = fcntl (pfd[i], F_GETFD, 0)) < 0 || + fcntl (pfd[i], F_SETFD, flags | FD_CLOEXEC) < 0) + eerror ("fcntl: %s", strerror (errno)); + + /* We run the plugin in a new process so we never crash + * or otherwise affected by it */ + if ((pid = fork ()) == -1) { + eerror ("fork: %s", strerror (errno)); + return; + } + + if (pid == 0) { + int retval; + + rc_in_plugin = true; + close (pfd[0]); + rc_environ_fd = fdopen (pfd[1], "w"); + retval = plugin->hook (hook, value); + fclose (rc_environ_fd); + rc_environ_fd = NULL; + + /* Just in case the plugin sets this to false */ + rc_in_plugin = true; + exit (retval); + } else { + char *buffer; + char *token; + char *p; + ssize_t nr; + + close (pfd[1]); + buffer = xmalloc (sizeof (char) * RC_LINEBUFFER); + memset (buffer, 0, RC_LINEBUFFER); + + while ((nr = read (pfd[0], buffer, RC_LINEBUFFER)) > 0) { + p = buffer; + while (*p && p - buffer < nr) { + token = strsep (&p, "="); + if (token) { + unsetenv (token); + if (*p) { + setenv (token, p, 1); + p += strlen (p) + 1; + } else + p++; + } + } + } + + free (buffer); + close (pfd[0]); + + rc_waitpid (pid); + } + } + plugin = plugin->next; + } +} + +void rc_plugin_unload (void) +{ + plugin_t *plugin = plugins; + plugin_t *next; + + while (plugin) { + next = plugin->next; + dlclose (plugin->handle); + free (plugin->name); + free (plugin); + plugin = next; + } + plugins = NULL; +} |