pyceo/src/util.c

306 lines
7.2 KiB
C

#define _ATFILE_SOURCE
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include "util.h"
#include "strbuf.h"
static int log_stderr = 1;
static int log_maxprio = LOG_DEBUG;
void init_log(const char *ident, int option, int facility, int lstderr) {
openlog(ident, option, facility);
log_stderr = lstderr || isatty(STDERR_FILENO);
}
void log_set_maxprio(int prio) {
log_maxprio = prio;
}
static void errmsg(int prio, const char *prefix, const char *fmt, va_list args) {
struct strbuf msg = STRBUF_INIT;
strbuf_addf(&msg, "%s: ", prefix);
strbuf_vaddf(&msg, fmt, args);
strbuf_addch(&msg, '\n');
syslog(prio, "%s", msg.buf);
if (log_stderr && prio <= log_maxprio)
fputs(msg.buf, stderr);
strbuf_release(&msg);
}
static void errmsgpe(int prio, const char *prefix, const char *fmt, va_list args) {
struct strbuf msg = STRBUF_INIT;
strbuf_addf(&msg, "%s: ", prefix);
strbuf_vaddf(&msg, fmt, args);
strbuf_addf(&msg, ": %s\n", strerror(errno));
syslog(prio, "%s", msg.buf);
if (log_stderr && prio <= log_maxprio)
fputs(msg.buf, stderr);
strbuf_release(&msg);
}
NORETURN static void die(int prio, const char *prefix, const char *msg, va_list args) {
errmsg(prio, prefix, msg, args);
exit(1);
}
NORETURN static void diepe(int prio, const char *prefix, const char *msg, va_list args) {
errmsgpe(prio, prefix, msg, args);
exit(1);
}
NORETURN void fatal(const char *msg, ...) {
va_list args;
va_start(args, msg);
die(LOG_CRIT, "fatal", msg, args);
va_end(args);
}
void error(const char *msg, ...) {
va_list args;
va_start(args, msg);
errmsg(LOG_ERR, "error", msg, args);
va_end(args);
}
void warn(const char *msg, ...) {
va_list args;
va_start(args, msg);
errmsg(LOG_WARNING, "warning", msg, args);
va_end(args);
}
void notice(const char *msg, ...) {
va_list args;
va_start(args, msg);
errmsg(LOG_NOTICE, "notice", msg, args);
va_end(args);
}
void debug(const char *msg, ...) {
va_list args;
va_start(args, msg);
errmsg(LOG_DEBUG, "debug", msg, args);
va_end(args);
}
void logmsg(int priority, const char *msg, ...) {
va_list args;
va_start(args, msg);
vsyslog(priority, msg, args);
va_end(args);
va_start(args, msg);
if (log_stderr && priority <= log_maxprio) {
vfprintf(stderr, msg, args);
fputc('\n', stderr);
}
va_end(args);
}
NORETURN void deny(const char *msg, ...) {
va_list args;
va_start(args, msg);
die(LOG_ERR, "denied", msg, args);
va_end(args);
}
NORETURN void badconf(const char *msg, ...) {
va_list args;
va_start(args, msg);
die(LOG_CRIT, "configuration error", msg, args);
va_end(args);
}
NORETURN void fatalpe(const char *msg, ...) {
va_list args;
va_start(args, msg);
diepe(LOG_CRIT, "fatal", msg, args);
va_end(args);
}
void errorpe(const char *msg, ...) {
va_list args;
va_start(args, msg);
errmsgpe(LOG_ERR, "error", msg, args);
va_end(args);
}
void warnpe(const char *msg, ...) {
va_list args;
va_start(args, msg);
errmsgpe(LOG_WARNING, "warning", msg, args);
va_end(args);
}
int spawnv(const char *path, char *const argv[]) {
int pid, status;
fflush(stdout);
fflush(stderr);
pid = fork();
if (pid < 0)
fatalpe("fork");
else if (pid)
waitpid(pid, &status, 0);
else
exit(execv(path, argv));
return status;
}
int full_write(int fd, const void *buf, size_t count) {
ssize_t total = 0;
while (total < count) {
ssize_t wcount = write(fd, (char *)buf + total, count - total);
if (wcount < 0)
return wcount;
total += wcount;
}
return 0;
}
int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr) {
return spawnvemu(path, argv, envp, output, input, cap_stderr, NULL);
}
int spawnvemu(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr, char *user) {
int pid, wpid, status;
int tochild[2];
int fmchild[2];
if (pipe(tochild))
fatalpe("pipe");
if (pipe(fmchild))
fatalpe("pipe");
fflush(stdout);
fflush(stderr);
pid = fork();
if (pid < 0)
fatalpe("fork");
if (!pid) {
dup2(tochild[0], STDIN_FILENO);
dup2(fmchild[1], STDOUT_FILENO);
if (cap_stderr)
dup2(STDOUT_FILENO, STDERR_FILENO);
close(tochild[0]);
close(tochild[1]);
close(fmchild[0]);
close(fmchild[1]);
if (user) {
struct passwd *pw = getpwnam(user);
if (!pw)
fatalpe("getpwnam: %s", user);
if (initgroups(user, pw->pw_gid))
fatalpe("initgroups: %s", user);
if (setregid(pw->pw_gid, pw->pw_gid))
fatalpe("setregid: %s", user);
if (setreuid(pw->pw_uid, pw->pw_uid))
fatalpe("setreuid");
}
execve(path, argv, envp);
fatalpe("execve");
} else {
close(tochild[0]);
close(fmchild[1]);
full_write(tochild[1], output->buf, output->len);
close(tochild[1]);
if (input)
strbuf_read(input, fmchild[0], 0);
close(fmchild[0]);
}
wpid = waitpid(pid, &status, 0);
if (wpid < 0)
fatalpe("waitpid");
else if (wpid != pid)
fatal("waitpid is broken");
if (WIFEXITED(status) && WEXITSTATUS(status))
notice("child %s exited with status %d", path, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
notice("child %s killed by signal %d", path, WTERMSIG(status));
return status;
}
int spawnv_msg(const char *path, char *const *argv, const struct strbuf *output) {
return spawnvem(path, argv, environ, output, NULL, 0);
}
int check_group(char *username, char *group) {
struct group *grp = getgrnam(group);
char **members;
if (grp)
for (members = grp->gr_mem; *members; members++)
if (!strcmp(username, *members))
return 1;
return 0;
}
FILE *fopenat(DIR *d, const char *path, int flags) {
int dfd = dirfd(d);
if (dfd < 0)
return NULL;
int fd = openat(dfd, path, flags);
if (fd < 0)
return NULL;
return fdopen(fd, flags & O_RDWR ? "r+" :
flags & O_WRONLY ? "w" :
"r");
}
void make_env(char **envp, ...) {
const size_t len = 4096;
size_t used = 0;
int args = 0;
char *buf = xmalloc(len);
va_list ap;
va_start(ap, envp);
char *name, *val;
while ((name = va_arg(ap, char *))) {
val = va_arg(ap, char *);
if (!val)
continue;
int n = snprintf(buf + used, len - used, "%s=%s", name, val);
if (n < 0)
fatalpe("snprintf");
if (n >= len - used)
fatal("environment too big");
envp[args++] = buf + used;
used += n + 1;
}
if (!args)
free(buf);
envp[args] = NULL;
}
void free_env(char **envp) {
free(*envp);
}