[BACK]Return to exec.c CVS log [TXT][DIR] Up to [local] / acopm / src

File: [local] / acopm / src / exec.c (download)

Revision 1.1.1.1 (vendor branch), Sat May 8 15:42:17 2021 UTC (3 years, 2 months ago) by bountyht
Branch: alphachat, MAIN
CVS Tags: start, HEAD
Changes since 1.1: +0 -0 lines

Initial import

/*
 * Copyright (C) 2017 Aaron M. D. Jones <aaronmdjones@gmail.com>
 *
 * 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.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 "acopm-common.h"

#include <assert.h> /* assert() */
#include <errno.h> /* errno */
#include <fcntl.h> /* open(), O_* */
#include <inttypes.h> /* PRIu16 */
#include <string.h> /* strerror() */
#include <unistd.h> /* close(), dup2(), execve(), fork(), STD*_FILENO */

#include <sys/stat.h> /* S_IWUSR, S_IRUSR */

#include <event2/event.h> /* struct event*, event_*() */

#include "exec.h" /* decls for own functions */
#include "vars.h" /* global variable definitions */

#include "proxy/client.h" /* enum acopm_proxy_type */
#include "utils/log.h" /* acopm_log_*() */

#define ARGV_MAX        2    /* program, plus null terminator */
#define ENVP_MAX        13   /* environment vars (max 7), plus 5 custom vars, plus null terminator */
#define EVAR_MIN        6    /* minimum length of environment var=value */

static int null_fd = -1;
static int log_fd = -1;

static inline bool acopm_attr_inline
acopm_exec_dup_one(FILE *const restrict file, const int from, const int guard, const int to)
{
	if (from != to && guard != to)
	{
		(void) fflush(file);
		(void) close(to);

		if (dup2(from, to) == -1)
			return false;
	}

	return true;
}

static bool
acopm_exec_dup_all(void)
{
	if (! acopm_exec_dup_one(stdin, null_fd, log_fd, STDIN_FILENO))
		return false;

	if (! acopm_exec_dup_one(stdout, log_fd, null_fd, STDOUT_FILENO))
		return false;

	if (! acopm_exec_dup_one(stderr, log_fd, null_fd, STDERR_FILENO))
		return false;

	if (null_fd >= 3 && close(null_fd) == -1)
		return false;

	if (log_fd >= 3 && log_fd != null_fd && close(log_fd) == -1)
		return false;

	return true;
}

static bool
acopm_exec_add_env(char **const restrict envp, const size_t max,
                   const char *const restrict name, const char *restrict value)
{
	if (! value && ! (value = getenv(name)))
		return true;

	if (! *value)
		return true;

	char var[ACOPM_EXE_EVAR_MAX];

	if (snprintf(var, sizeof var, "%s=%s", name, value) < EVAR_MIN)
		return false;

	char *const dup = strdup(var);

	if (! dup)
		return false;

	for (size_t x = 0; x < max; x++)
	{
		if (! envp[x])
		{
			envp[x] = dup;
			return true;
		}
	}

	(void) free(dup);
	return false;
}

bool
acopm_exec_init(void)
{
	assert(null_fd == -1);
	assert(log_fd == -1);

	(void) acopm_log_debug("%s: initialising execution subsystem", __func__);

	if ((null_fd = open(NULL_PATH, O_RDWR | O_NOCTTY)) == -1)
	{
		(void) acopm_log_error("%s: open('%s'): %s", __func__, NULL_PATH, strerror(errno));
		return false;
	}

	if (! *proxy_hit_log)
	{
		log_fd = null_fd;
	}
	else if ((log_fd = open(proxy_hit_log, O_APPEND | O_CREAT | O_NOCTTY, S_IWUSR | S_IRUSR)) == -1)
	{
		(void) acopm_log_error("%s: open('%s'): %s", __func__, proxy_hit_log, strerror(errno));
		return false;
	}

	return true;
}

void
acopm_exec_free(void)
{
	if (null_fd == -1 && log_fd == -1)
		return;

	(void) acopm_log_debug("%s: destroying execution subsystem", __func__);

	if (null_fd != -1)
		(void) close(null_fd);

	if (log_fd != -1 && log_fd != null_fd)
		(void) close(log_fd);

	null_fd = -1;
	log_fd = -1;
}

void
acopm_exec_proxy_program(const char *const restrict address, const uint16_t port, const enum acopm_proxy_type type)
{
	(void) acopm_log_info("Executing external program for proxy hit");
	(void) acopm_log_flush();

	assert(address != NULL);
	assert(*address != 0x00);
	assert(port != 0);
	assert(type != ACOPM_PROXY_TYPE_NONE);

	char port_str[8];
	(void) snprintf(port_str, sizeof port_str, "%" PRIu16, port);

	assert(*port_str != 0x00);

	const char *const type_str = acopm_proxy_type_str(type);

	assert(type_str != NULL);
	assert(*type_str != 0x00);

	const pid_t pid = fork();

	if (pid == -1)
	{
		(void) acopm_log_error("%s: fork: %s", __func__, strerror(errno));
		return;
	}
	else if (pid != 0)
		return;

	char *argv[ARGV_MAX], *envp[ENVP_MAX];
	(void) memset(argv, 0x00, sizeof argv);
	(void) memset(envp, 0x00, sizeof envp);

	if (! acopm_exec_dup_all())
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "HOME", NULL))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "PATH", NULL))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "SHELL", NULL))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "USER", NULL))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "ADDRESS", address))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "PORT", port_str))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "TYPE", type_str))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "CONFIG1", proxy_hit_var1))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "CONFIG2", proxy_hit_var2))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "CONFIG3", proxy_hit_var3))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "CONFIG4", proxy_hit_var4))
		goto end;
	if (! acopm_exec_add_env(envp, ENVP_MAX, "CONFIG5", proxy_hit_var5))
		goto end;

	argv[0] = proxy_hit_exe;

	(void) execve(proxy_hit_exe, argv, envp);

end:
	exit(EXIT_FAILURE);
}