openrc/src/rc-plugin.c

243 lines
5.5 KiB
C

/*
librc-plugin.c
Simple plugin handler
*/
/*
* Copyright 2007 Gentoo Foundation
* 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;
}