Files
sudo/lib/util/sudo_debug.c
Todd C. Miller 5270ebf1f2 When registering with the debug subsystem, the caller now passes
in an arrary of ints that gets filled in with the subsytem IDs to
be used in debug_decl.
2014-10-23 06:36:50 -06:00

846 lines
23 KiB
C

/*
* Copyright (c) 2011-2014 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/param.h> /* for howmany() on Linux */
#ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h> /* for howmany() on Solaris */
#endif
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#define DEFAULT_TEXT_DOMAIN "sudo"
#include "sudo_gettext.h" /* must be included before sudo_compat.h */
#include "sudo_compat.h"
#include "sudo_alloc.h"
#include "sudo_fatal.h"
#include "sudo_plugin.h"
#include "sudo_conf.h"
#include "sudo_debug.h"
#include "sudo_util.h"
/*
* The debug priorities and subsystems are currently hard-coded.
* In the future we might consider allowing plugins to register their
* own subsystems and provide direct access to the debugging API.
*/
/* Note: this must match the order in sudo_debug.h */
static const char *const sudo_debug_priorities[] = {
"crit",
"err",
"warn",
"notice",
"diag",
"info",
"trace",
"debug",
NULL
};
/* Note: this must match the order in sudo_debug.h */
static const char *const sudo_debug_default_subsystems[] = {
"args",
"conv",
"edit",
"event",
"exec",
"hooks",
"main",
"netif",
"pcomm",
"plugin",
"pty",
"selinux",
"util",
"utmp",
NULL
};
#define NUM_DEF_SUBSYSTEMS (sizeof(sudo_debug_default_subsystems) / sizeof(sudo_debug_default_subsystems[0]) - 1)
/*
* For multiple programs/plugins there is a per-program instance
* and one or more outputs (files).
*/
struct sudo_debug_output {
SLIST_ENTRY(sudo_debug_output) entries;
char *filename;
int *settings;
int fd;
};
SLIST_HEAD(sudo_debug_output_list, sudo_debug_output);
struct sudo_debug_instance {
char *program;
const char *const *subsystems;
const int *subsystem_ids;
unsigned int max_subsystem;
struct sudo_debug_output_list outputs;
};
/* Support up to 10 instances. */
#define SUDO_DEBUG_INSTANCE_MAX 10
static struct sudo_debug_instance *sudo_debug_instances[SUDO_DEBUG_INSTANCE_MAX];
static int sudo_debug_num_instances;
static char sudo_debug_pidstr[(((sizeof(int) * 8) + 2) / 3) + 3];
static size_t sudo_debug_pidlen;
static fd_set sudo_debug_fdset; /* XXX - make dynamic */
static int sudo_debug_max_fd = -1;
/* Default instance index to use for common utility functions. */
static int sudo_debug_default_instance = -1;
/*
* Create a new output file for the specified debug instance.
*/
static struct sudo_debug_output *
sudo_debug_new_output(struct sudo_debug_instance *instance,
struct sudo_debug_file *debug_file)
{
char *buf, *cp, *subsys, *pri;
struct sudo_debug_output *output;
unsigned int i, j;
/* Create new output for the instance. */
/* XXX - reuse fd for existing filename? */
output = sudo_emalloc(sizeof(*output));
output->settings = sudo_emallocarray(instance->max_subsystem + 1, sizeof(int));
output->filename = sudo_estrdup(debug_file->debug_file);
output->fd = -1;
/* Init per-subsystems settings to -1 since 0 is a valid priority. */
for (i = 0; i <= instance->max_subsystem; i++)
output->settings[i] = -1;
/* Open debug file. */
output->fd = open(output->filename, O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR);
if (output->fd == -1) {
/* Create debug file as needed and set group ownership. */
if (errno == ENOENT) {
output->fd = open(output->filename, O_WRONLY|O_APPEND|O_CREAT,
S_IRUSR|S_IWUSR);
}
if (output->fd == -1)
return NULL;
ignore_result(fchown(output->fd, (uid_t)-1, 0));
}
(void)fcntl(output->fd, F_SETFD, FD_CLOEXEC);
/* XXX - realloc sudo_debug_fdset as needed (or use other bitmap). */
if (output->fd < FD_SETSIZE) {
FD_SET(output->fd, &sudo_debug_fdset);
if (output->fd > sudo_debug_max_fd)
sudo_debug_max_fd = output->fd;
}
/* Parse Debug conf string. */
if ((buf = strdup(debug_file->debug_flags)) == NULL) {
/* XXX - free output on error or make non-destructive */
return NULL;
}
for ((cp = strtok(buf, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
/* Should be in the form subsys@pri. */
subsys = cp;
if ((pri = strchr(cp, '@')) == NULL)
continue;
*pri++ = '\0';
/* Look up priority and subsystem, fill in sudo_debug_settings[]. */
for (i = 0; sudo_debug_priorities[i] != NULL; i++) {
if (strcasecmp(pri, sudo_debug_priorities[i]) == 0) {
for (j = 0; instance->subsystems[j] != NULL; j++) {
if (strcasecmp(subsys, "all") == 0) {
const int idx = SUDO_DEBUG_SUBSYS(instance->subsystem_ids[j]);
output->settings[idx] = i;
continue;
}
if (strcasecmp(subsys, instance->subsystems[j]) == 0) {
const int idx = SUDO_DEBUG_SUBSYS(instance->subsystem_ids[j]);
output->settings[idx] = i;
break;
}
}
break;
}
}
}
free(buf);
return output;
}
/*
* Register a program/plugin with the debug framework,
* parses settings string from sudo.conf and opens debugfile.
* If subsystem names are specified they override the default values.
* NOTE: subsystems must not be freed by caller unless deregistered.
* Returns instance index on success or -1 if cannot open debugfile.
*/
int
sudo_debug_register(const char *program, const char *const subsystems[],
int ids[], struct sudo_conf_debug_file_list *debug_files)
{
struct sudo_debug_instance *instance = NULL;
struct sudo_debug_output *output;
struct sudo_debug_file *debug_file;
int idx, free_idx = -1;
if (debug_files == NULL)
return -1;
/* Use default subsystem names if none are provided. */
if (subsystems == NULL) {
subsystems = sudo_debug_default_subsystems;
} else if (ids == NULL) {
/* If subsystems are specified we must have ids[] too. */
return -1;
}
/* Search for existing instance. */
for (idx = 0; idx < sudo_debug_num_instances; idx++) {
if (sudo_debug_instances[idx] == NULL) {
free_idx = idx;
continue;
}
if (sudo_debug_instances[idx]->subsystems == subsystems &&
strcmp(sudo_debug_instances[idx]->program, program) == 0) {
instance = sudo_debug_instances[idx];
break;
}
}
if (instance == NULL) {
unsigned int i, j, max_id = NUM_DEF_SUBSYSTEMS - 1;
/* Fill in subsystem name -> id mapping. */
for (i = 0; subsystems[i] != NULL; i++) {
/* Check default subsystems. */
for (j = 0; j < NUM_DEF_SUBSYSTEMS; j++) {
if (strcmp(subsystems[i], sudo_debug_default_subsystems[j]) == 0)
break;
}
if (j == NUM_DEF_SUBSYSTEMS)
j = ++max_id;
ids[i] = ((j + 1) << 16);
}
if (free_idx != -1)
idx = free_idx;
if (idx == SUDO_DEBUG_INSTANCE_MAX) {
/* XXX - realloc? */
sudo_warnx_nodebug("too many debug instances (max %d)", SUDO_DEBUG_INSTANCE_MAX);
return -1;
}
if (idx != sudo_debug_num_instances && idx != free_idx) {
sudo_warnx_nodebug("%s: instance number mismatch: expected %d, got %u", __func__, idx, sudo_debug_num_instances);
return -1;
}
instance = sudo_emalloc(sizeof(*instance));
instance->program = sudo_estrdup(program);
instance->subsystems = subsystems;
instance->subsystem_ids = ids;
instance->max_subsystem = max_id;
SLIST_INIT(&instance->outputs);
sudo_debug_instances[idx] = instance;
if (idx == sudo_debug_num_instances)
sudo_debug_num_instances++;
} else {
/* Check for matching instance but different ids[]. */
if (ids != NULL && instance->subsystem_ids != ids) {
unsigned int i;
for (i = 0; subsystems[i] != NULL; i++)
ids[i] = instance->subsystem_ids[i];
}
}
TAILQ_FOREACH(debug_file, debug_files, entries) {
output = sudo_debug_new_output(instance, debug_file);
if (output != NULL)
SLIST_INSERT_HEAD(&instance->outputs, output, entries);
}
/* Stash the pid string so we only have to format it once. */
if (sudo_debug_pidlen == 0) {
(void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
(int)getpid());
sudo_debug_pidlen = strlen(sudo_debug_pidstr);
}
/* Convert index to instance. */
return SUDO_DEBUG_MKINSTANCE(idx);
}
/*
* De-register the specified instance from the debug subsystem
* and free up any associated data structures.
*/
int
sudo_debug_deregister(int instance_id)
{
struct sudo_debug_instance *instance;
struct sudo_debug_output *output, *next;
int idx;
idx = SUDO_DEBUG_INSTANCE(instance_id);
if (idx < 0 || idx >= sudo_debug_num_instances) {
sudo_warnx_nodebug("%s: invalid instance number %d, max %u",
__func__, idx + 1, sudo_debug_num_instances);
return -1;
}
/* Reset default instance as needed. */
if (sudo_debug_default_instance == idx)
sudo_debug_default_instance = -1;
instance = sudo_debug_instances[idx];
if (instance == NULL)
return -1; /* already deregistered */
/* Free up instance data, note that subsystems[] is owned by caller. */
sudo_debug_instances[idx] = NULL;
SLIST_FOREACH_SAFE(output, &instance->outputs, entries, next) {
close(output->fd);
sudo_efree(output->filename);
sudo_efree(output->settings);
sudo_efree(output);
}
sudo_efree(instance->program);
sudo_efree(instance);
if (idx == sudo_debug_num_instances - 1)
sudo_debug_num_instances--;
return 0;
}
int
sudo_debug_get_instance(const char *program)
{
size_t proglen;
int idx;
/* Treat "sudoedit" as an alias for "sudo". */
proglen = strlen(program);
if (proglen > 4 && strcmp(program + proglen - 4, "edit") == 0)
proglen -= 4;
for (idx = 0; idx < sudo_debug_num_instances; idx++) {
if (sudo_debug_instances[idx] == NULL)
continue;
if (strncmp(sudo_debug_instances[idx]->program, program, proglen) == 0
&& sudo_debug_instances[idx]->program[proglen] == '\0')
return SUDO_DEBUG_MKINSTANCE(idx);
}
return SUDO_DEBUG_INSTANCE_INITIALIZER;
}
int
sudo_debug_set_output_fd(int level, int ofd, int nfd)
{
struct sudo_debug_instance *instance;
struct sudo_debug_output *output;
int idx;
idx = SUDO_DEBUG_INSTANCE(level);
if (idx < 0 || idx >= sudo_debug_num_instances) {
sudo_warnx_nodebug("%s: invalid instance number %d, max %u",
__func__, idx + 1, sudo_debug_num_instances);
return -1;
}
instance = sudo_debug_instances[idx];
SLIST_FOREACH(output, &instance->outputs, entries) {
if (output->fd == ofd)
output->fd = nfd;
}
return 0;
}
pid_t
sudo_debug_fork(void)
{
pid_t pid;
if ((pid = fork()) == 0) {
(void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
(int)getpid());
sudo_debug_pidlen = strlen(sudo_debug_pidstr);
}
return pid;
}
void
sudo_debug_enter(const char *func, const char *file, int line,
int subsys)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"-> %s @ %s:%d", func, file, line);
}
void
sudo_debug_exit(const char *func, const char *file, int line,
int subsys)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d", func, file, line);
}
void
sudo_debug_exit_int(const char *func, const char *file, int line,
int subsys, int rval)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %d", func, file, line, rval);
}
void
sudo_debug_exit_long(const char *func, const char *file, int line,
int subsys, long rval)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %ld", func, file, line, rval);
}
void
sudo_debug_exit_size_t(const char *func, const char *file, int line,
int subsys, size_t rval)
{
/* XXX - should use %zu but our snprintf.c doesn't support it */
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %lu", func, file, line, (unsigned long)rval);
}
/* We use int, not bool, here for functions that return -1 on error. */
void
sudo_debug_exit_bool(const char *func, const char *file, int line,
int subsys, int rval)
{
if (rval == true || rval == false) {
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %s", func, file, line, rval ? "true" : "false");
} else {
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %d", func, file, line, rval);
}
}
void
sudo_debug_exit_str(const char *func, const char *file, int line,
int subsys, const char *rval)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %s", func, file, line, rval ? rval : "(null)");
}
void
sudo_debug_exit_str_masked(const char *func, const char *file, int line,
int subsys, const char *rval)
{
static const char stars[] = "********************************************************************************";
int len = rval ? strlen(rval) : sizeof("(null)") - 1;
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %.*s", func, file, line, len, rval ? stars : "(null)");
}
void
sudo_debug_exit_ptr(const char *func, const char *file, int line,
int subsys, const void *rval)
{
sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
"<- %s @ %s:%d := %p", func, file, line, rval);
}
void
sudo_debug_write2(int fd, const char *func, const char *file, int lineno,
const char *str, int len, int errnum)
{
char *timestr, numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
time_t now;
struct iovec iov[12];
int iovcnt = 3;
/* Prepend program name and pid with a trailing space. */
iov[1].iov_base = (char *)getprogname();
iov[1].iov_len = strlen(iov[1].iov_base);
iov[2].iov_base = sudo_debug_pidstr;
iov[2].iov_len = sudo_debug_pidlen;
/* Add string, trimming any trailing newlines. */
while (len > 0 && str[len - 1] == '\n')
len--;
if (len > 0) {
iov[iovcnt].iov_base = (char *)str;
iov[iovcnt].iov_len = len;
iovcnt++;
}
/* Append error string if errno is specified. */
if (errnum) {
if (len > 0) {
iov[iovcnt].iov_base = ": ";
iov[iovcnt].iov_len = 2;
iovcnt++;
}
iov[iovcnt].iov_base = strerror(errnum);
iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base);
iovcnt++;
}
/* If function, file and lineno are specified, append them. */
if (func != NULL && file != NULL && lineno != 0) {
iov[iovcnt].iov_base = " @ ";
iov[iovcnt].iov_len = 3;
iovcnt++;
iov[iovcnt].iov_base = (char *)func;
iov[iovcnt].iov_len = strlen(func);
iovcnt++;
iov[iovcnt].iov_base = "() ";
iov[iovcnt].iov_len = 3;
iovcnt++;
iov[iovcnt].iov_base = (char *)file;
iov[iovcnt].iov_len = strlen(file);
iovcnt++;
(void)snprintf(numbuf, sizeof(numbuf), ":%d", lineno);
iov[iovcnt].iov_base = numbuf;
iov[iovcnt].iov_len = strlen(numbuf);
iovcnt++;
}
/* Append newline. */
iov[iovcnt].iov_base = "\n";
iov[iovcnt].iov_len = 1;
iovcnt++;
/* Do timestamp last due to ctime's static buffer. */
time(&now);
timestr = ctime(&now) + 4;
timestr[15] = ' '; /* replace year with a space */
timestr[16] = '\0';
iov[0].iov_base = timestr;
iov[0].iov_len = 16;
/* Write message in a single syscall */
ignore_result(writev(fd, iov, iovcnt));
}
void
sudo_debug_vprintf2(const char *func, const char *file, int lineno, int level,
const char *fmt, va_list ap)
{
int buflen, idx, pri, saved_errno = errno;
unsigned int subsys;
char static_buf[1024], *buf = static_buf;
struct sudo_debug_instance *instance;
struct sudo_debug_output *output;
va_list ap2;
if (sudo_debug_num_instances == 0)
goto out;
/* Extract instance index, priority and subsystem from level. */
idx = SUDO_DEBUG_INSTANCE(level);
pri = SUDO_DEBUG_PRI(level);
subsys = SUDO_DEBUG_SUBSYS(level);
/* Find matching instance. */
if (idx < 0) {
/* Check for default instance, else we are not initialized. */
if (sudo_debug_default_instance < 0 ||
SUDO_DEBUG_MKINSTANCE(idx) != SUDO_DEBUG_INSTANCE_DEFAULT) {
goto out;
}
idx = sudo_debug_default_instance;
} else if (idx >= sudo_debug_num_instances) {
sudo_warnx_nodebug("%s: invalid instance number %d, max %u",
__func__, idx + 1, sudo_debug_num_instances);
goto out;
}
instance = sudo_debug_instances[idx];
if (instance == NULL) {
sudo_warnx_nodebug("%s: unregistered instance index %d", __func__, idx);
goto out;
}
SLIST_FOREACH(output, &instance->outputs, entries) {
/* Make sure we want debug info at this level. */
if (subsys <= instance->max_subsystem && output->settings[subsys] >= pri) {
va_copy(ap2, ap);
buflen = fmt ? vsnprintf(static_buf, sizeof(static_buf), fmt, ap) : 0;
if (buflen >= (int)sizeof(static_buf)) {
/* Not enough room in static buf, allocate dynamically. */
buflen = vasprintf(&buf, fmt, ap2);
}
if (buflen != -1) {
int errcode = ISSET(level, SUDO_DEBUG_ERRNO) ? saved_errno : 0;
if (ISSET(level, SUDO_DEBUG_LINENO))
sudo_debug_write2(output->fd, func, file, lineno, buf, buflen, errcode);
else
sudo_debug_write2(output->fd, NULL, NULL, 0, buf, buflen, errcode);
if (buf != static_buf) {
sudo_efree(buf);
buf = static_buf;
}
}
va_end(ap2);
}
}
out:
errno = saved_errno;
}
#ifdef NO_VARIADIC_MACROS
void
sudo_debug_printf_nvm(int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
sudo_debug_vprintf2(NULL, NULL, 0, pri, fmt, ap);
va_end(ap);
}
#endif /* NO_VARIADIC_MACROS */
void
sudo_debug_printf2(const char *func, const char *file, int lineno, int level,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
sudo_debug_vprintf2(func, file, lineno, level, fmt, ap);
va_end(ap);
}
#define EXEC_PREFIX "exec "
void
sudo_debug_execve2(int level, const char *path, char *const argv[], char *const envp[])
{
int buflen, idx, pri, saved_errno = errno;
unsigned int subsys;
struct sudo_debug_instance *instance;
struct sudo_debug_output *output;
char * const *av;
char *buf, *cp;
size_t plen;
if (sudo_debug_num_instances == 0)
goto out;
/* Extract instance index, priority and subsystem from level. */
idx = SUDO_DEBUG_INSTANCE(level);
pri = SUDO_DEBUG_PRI(level);
subsys = SUDO_DEBUG_SUBSYS(level);
/* Find matching instance. */
if (idx < 0) {
/* Check for default instance, else we are not initialized. */
if (sudo_debug_default_instance < 0 ||
SUDO_DEBUG_MKINSTANCE(idx) != SUDO_DEBUG_INSTANCE_DEFAULT) {
goto out;
}
idx = sudo_debug_default_instance;
} else if (idx >= sudo_debug_num_instances) {
sudo_warnx_nodebug("%s: invalid instance number %d, max %u",
__func__, idx + 1, sudo_debug_num_instances);
goto out;
}
instance = sudo_debug_instances[idx];
if (instance == NULL) {
sudo_warnx_nodebug("%s: unregistered instance index %d", __func__, idx);
goto out;
}
if (subsys > instance->max_subsystem)
goto out;
/* XXX - use static buffer if possible */
SLIST_FOREACH(output, &instance->outputs, entries) {
bool log_envp = false;
/* Make sure we want debug info at this level. */
if (output->settings[subsys] < pri)
continue;
/* Log envp for debug level "debug". */
if (output->settings[subsys] >= SUDO_DEBUG_DEBUG - 1 && envp[0] != NULL)
log_envp = true;
/* Alloc and build up buffer. */
plen = strlen(path);
buflen = sizeof(EXEC_PREFIX) -1 + plen;
if (argv[0] != NULL) {
buflen += sizeof(" []") - 1;
for (av = argv; *av; av++)
buflen += strlen(*av) + 1;
buflen--;
}
if (log_envp) {
buflen += sizeof(" []") - 1;
for (av = envp; *av; av++)
buflen += strlen(*av) + 1;
buflen--;
}
buf = malloc(buflen + 1);
if (buf == NULL)
goto out;
/* Copy prefix and command. */
memcpy(buf, EXEC_PREFIX, sizeof(EXEC_PREFIX) - 1);
cp = buf + sizeof(EXEC_PREFIX) - 1;
memcpy(cp, path, plen);
cp += plen;
/* Copy argv. */
if (argv[0] != NULL) {
*cp++ = ' ';
*cp++ = '[';
for (av = argv; *av; av++) {
size_t avlen = strlen(*av);
memcpy(cp, *av, avlen);
cp += avlen;
*cp++ = ' ';
}
cp[-1] = ']';
}
if (log_envp) {
*cp++ = ' ';
*cp++ = '[';
for (av = envp; *av; av++) {
size_t avlen = strlen(*av);
memcpy(cp, *av, avlen);
cp += avlen;
*cp++ = ' ';
}
cp[-1] = ']';
}
*cp = '\0';
sudo_debug_write(output->fd, buf, buflen, 0);
free(buf);
}
out:
errno = saved_errno;
}
/*
* Returns the default instance or SUDO_DEBUG_INSTANCE_INITIALIZER
* if no default instance is set.
*/
int
sudo_debug_get_default_instance(void)
{
return SUDO_DEBUG_MKINSTANCE(sudo_debug_default_instance);
}
/*
* Sets a new default instance, returning the old one.
* Note that the old instance may be SUDO_DEBUG_INSTANCE_INITIALIZER
* of this is the only instance.
*/
int
sudo_debug_set_default_instance(int inst)
{
const int idx = SUDO_DEBUG_INSTANCE(inst);
const int old_idx = sudo_debug_default_instance;
if (idx >= -1 && idx < sudo_debug_num_instances)
sudo_debug_default_instance = idx;
return SUDO_DEBUG_MKINSTANCE(old_idx);
}
/*
* Replace the ofd with nfd in all outputs if present.
* Also updates sudo_debug_fdset.
*/
void
sudo_debug_update_fd(int ofd, int nfd)
{
int idx;
if (ofd <= sudo_debug_max_fd && FD_ISSET(ofd, &sudo_debug_fdset)) {
/* Update sudo_debug_fdset. */
FD_CLR(ofd, &sudo_debug_fdset);
FD_SET(nfd, &sudo_debug_fdset);
/* Update the outputs. */
for (idx = 0; idx < sudo_debug_num_instances; idx++) {
struct sudo_debug_instance *instance;
struct sudo_debug_output *output;
instance = sudo_debug_instances[idx];
if (instance == NULL)
continue;
SLIST_FOREACH(output, &instance->outputs, entries) {
if (output->fd == ofd)
output->fd = nfd;
}
}
}
}
/*
* Returns the highest debug output fd or -1 if no debug files open.
* Fills in fdsetp with the value of sudo_debug_fdset.
*/
int
sudo_debug_get_fds(fd_set **fdsetp)
{
*fdsetp = &sudo_debug_fdset;
return sudo_debug_max_fd;
}