#include "config.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "distcc.h"
#include "trace.h"
#include "util.h"
#include "snprintf.h"
#include "exitcode.h"
int dcc_set_owner(const char *path)
{
int result = 1;
char base[MAXPATHLEN];
int i, lastSlash;
struct stat st;
for (i=0; path[i]!=0; i++) {
base[i] = path[i];
if (path[i] == '/')
lastSlash = i;
}
base[lastSlash] = 0;
if (stat(base, &st) == 0) {
if (chown(path, st.st_uid, st.st_gid) == 0)
result = 0;
}
return result;
}
int dcc_make_tmpfile_path(char **path_ret, int create_directories, const char *file_part, ...)
{
char *path;
const char *tempdir;
int result = 0;
result = dcc_get_tmp_top(&tempdir);
if (result == 0) {
path = (char *)malloc(MAXPATHLEN);
if (!path)
result = -1;
}
if (result == 0) {
int path_len, part_len;
const char *path_part;
va_list va;
strcpy(path, tempdir);
path_len = strlen(path);
va_start(va, file_part);
for (path_part = va_arg(va, const char *); result == 0 && path_part != NULL; path_part = va_arg(va, const char *)) {
part_len = strlen(path_part);
if (path_len + part_len + 2 < MAXPATHLEN) {
if (path_part[0] != '/')
path[path_len++] = '/';
if (create_directories) {
int i;
for (i = 0; result == 0 && i<part_len; i++) {
if (i > 0 && path_part[i] == '/') {
path[path_len] = 0;
result = dcc_mkdir(path);
}
path[path_len++] = path_part[i];
}
path[path_len] = 0;
} else {
strcpy(&path[path_len], path_part);
path_len += part_len;
}
if (path[path_len] == '/') {
path[path_len] = 0;
path_len--;
} else {
if (create_directories) {
result = dcc_mkdir(path);
}
}
} else {
result = -1;
}
}
va_end(va);
if (file_part) {
part_len = strlen(file_part);
if (path_len + part_len + 2 < MAXPATHLEN) {
if (file_part[0] != '/')
path[path_len++] = '/';
strcpy(&path[path_len], file_part);
} else {
result = -1;
}
}
}
if (result != 0 && path) {
free(path);
path = NULL;
}
*path_ret = path;
return result;
}
int dcc_get_tmp_top(const char **p_ret)
{
static const char *d = NULL;
if (d == NULL) {
d = getenv("TMPDIR");
if (!d || d[0] == '\0') {
d = "/tmp/distcc";
if (dcc_mkdir(d) == 0) {
chmod(d, 0777);
} else {
d = "/tmp";
}
}
}
*p_ret = d;
return 0;
}
int dcc_mkdir(const char *path)
{
if ((mkdir(path, 0777) == -1) && (errno != EEXIST)) {
rs_log_error("mkdir %s failed: %s", path, strerror(errno));
return EXIT_IO_ERROR;
}
dcc_set_owner(path);
return 0;
}
int dcc_get_top_dir(char **path_ret)
{
char *env;
static char *cached;
int ret;
if (cached) {
*path_ret = cached;
return 0;
}
if ((env = getenv("DISTCC_DIR"))) {
if ((cached = strdup(env)) == NULL) {
return EXIT_OUT_OF_MEMORY;
} else {
*path_ret = cached;
return 0;
}
}
if (asprintf(path_ret, "/var/tmp/distcc.%d", getuid()) == -1) {
rs_log_error("asprintf failed");
return EXIT_OUT_OF_MEMORY;
}
ret = dcc_mkdir(*path_ret);
if (ret == 0)
cached = *path_ret;
return ret;
}
static int dcc_get_subdir(const char *name,
char **dir_ret)
{
int ret;
char *topdir;
if ((ret = dcc_get_top_dir(&topdir)))
return ret;
if (asprintf(dir_ret, "%s/%s", topdir, name) == -1) {
rs_log_error("asprintf failed");
return EXIT_OUT_OF_MEMORY;
}
return dcc_mkdir(*dir_ret);
}
int dcc_get_lock_dir(char **dir_ret)
{
static char *cached;
int ret;
if (cached) {
*dir_ret = cached;
return 0;
} else {
ret = dcc_get_subdir("lock", dir_ret);
if (ret == 0)
cached = *dir_ret;
return ret;
}
}
int dcc_get_state_dir(char **dir_ret)
{
static char *cached;
int ret;
if (cached) {
*dir_ret = cached;
return 0;
} else {
ret = dcc_get_subdir("state", dir_ret);
if (ret == 0)
cached = *dir_ret;
return ret;
}
}
int dcc_make_tmpnam(const char *prefix,
const char *suffix,
char **name_ret)
{
char *s = NULL;
const char *tempdir;
int ret;
unsigned long random_bits;
int fd;
if ((ret = dcc_get_tmp_top(&tempdir)))
return ret;
if (access(tempdir, W_OK|X_OK) == -1) {
rs_log_error("can't use TMPDIR \"%s\": %s", tempdir, strerror(errno));
return EXIT_IO_ERROR;
}
random_bits = (unsigned long) getpid() << 16;
# if HAVE_GETTIMEOFDAY
{
struct timeval tv;
gettimeofday(&tv, NULL);
random_bits ^= tv.tv_usec << 16;
random_bits ^= tv.tv_sec;
}
# else
random_bits ^= time(NULL);
# endif
#if 0
random_bits = 0;
#endif
do {
free(s);
if (asprintf(&s, "%s/%s_%08lx%s",
tempdir,
prefix,
random_bits & 0xffffffffUL,
suffix) == -1)
return EXIT_OUT_OF_MEMORY;
fd = open(s, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd == -1) {
rs_trace("failed to create %s: %s", s, strerror(errno));
random_bits += 7777;
continue;
}
if (close(fd) == -1) {
rs_log_warning("failed to close %s: %s", s, strerror(errno));
return EXIT_IO_ERROR;
}
break;
} while (1);
if ((ret = dcc_add_cleanup(s))) {
free(s);
return ret;
}
*name_ret = s;
return 0;
}