#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"
#ifdef __CYGWIN32__
#define NOGDI
#include <windows.h>
#endif
int dcc_mk_tmpdir(const char *path)
{
struct stat buf;
int ret;
if (stat(path, &buf) == -1) {
if (mkdir(path, 0777) == -1) {
return EXIT_IO_ERROR;
}
if ((ret = dcc_add_cleanup(path))) {
rmdir(path);
return ret;
}
} else {
if (S_ISDIR(buf.st_mode)) {
return 0;
} else {
rs_log_error("mkdir '%s' failed: %s", path, strerror(errno));
return EXIT_IO_ERROR;
}
}
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;
}
return 0;
}
#ifndef HAVE_MKDTEMP
static char* mkdtemp(char *pattern)
{
char* path = mktemp(pattern);
if (path == NULL)
return NULL;
if (mkdir(path, 0700) == 0)
return path;
return NULL;
}
#endif
int dcc_get_new_tmpdir(char **tempdir)
{
int ret;
const char *tmp_top;
char *s;
if ((ret = dcc_get_tmp_top(&tmp_top))) {
return ret;
}
if (asprintf(&s, "%s/distccd_XXXXXX", tmp_top) == -1)
return EXIT_OUT_OF_MEMORY;
if ((*tempdir = mkdtemp(s)) == 0) {
return EXIT_IO_ERROR;
}
if ((ret = dcc_add_cleanup(s))) {
rmdir(s);
return ret;
}
return 0;
}
int dcc_get_tmp_top(const char **p_ret)
{
#ifdef __CYGWIN32__
int ret;
char *s = malloc(MAXPATHLEN+1);
int f,ln;
GetTempPath(MAXPATHLEN+1,s);
for (f = 0, ln = strlen(s); f != ln; f++)
if (s[f]=='\\') s[f]='/';
for (f = strlen(s)-1; f > 0 && s[f] == '/'; f--)
s[f]='\0';
if ((ret = dcc_add_cleanup(s))) {
free(s);
return ret;
}
*p_ret = s;
return 0;
#else
const char *d;
char *s;
int l;
static const char *tmp_dir = NULL;
if (!tmp_dir) {
d = getenv("TMPDIR");
if (d && d[0] != '\0') {
l = strlen(d) - 1;
if (d[l] == '/') {
s = strdup(d);
if (s) {
tmp_dir = s;
while (l && s[l] == '/') {
s[l--] = 0;
}
}
} else {
tmp_dir = d;
}
}
if (!tmp_dir) {
tmp_dir = "/tmp";
}
}
*p_ret = tmp_dir;
return 0;
#endif
}
int dcc_mk_tmp_ancestor_dirs(const char *path)
{
char *copy = 0;
char *p;
int ret;
copy = strdup(path);
if (copy == NULL) {
return EXIT_OUT_OF_MEMORY;
}
dcc_truncate_to_dirname(copy);
if (copy[0] == '\0') {
free(copy);
return 0;
}
if ((ret = dcc_mk_tmpdir(copy)) == 0) {
free(copy);
return 0;
}
for (p = copy; *p != '\0'; ++p) {
if (*p == '/' && p != copy) {
*p = '\0';
if ((ret = dcc_mk_tmpdir(copy))) {
free(copy);
return ret;
}
*p = '/';
}
}
ret = dcc_mk_tmpdir(copy);
free(copy);
return ret;
}
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 ((env = getenv("HOME")) == NULL) {
rs_log_warning("HOME is not set; can't find distcc directory");
return EXIT_BAD_ARGUMENTS;
}
if (asprintf(path_ret, "%s/.distcc", env) == -1) {
rs_log_error("asprintf failed");
return EXIT_OUT_OF_MEMORY;
}
ret = dcc_mkdir(*path_ret);
if (ret == 0)
cached = *path_ret;
return ret;
}
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))) {
unlink(s);
free(s);
return ret;
}
*name_ret = s;
return 0;
}