#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/file.h>
#include "distcc.h"
#include "trace.h"
#include "util.h"
#include "hosts.h"
#include "lock.h"
#include "exitcode.h"
#include "snprintf.h"
struct dcc_hostdef _dcc_local = {
DCC_MODE_LOCAL,
NULL,
(char *) "localhost",
0,
NULL,
4,
(char *)"localhost",
NULL,
NULL,
DCC_VER_1,
DCC_COMPRESS_NONE,
NULL
};
struct dcc_hostdef *dcc_hostdef_local = &_dcc_local;
static int cpp_lock;
static char cpp_lock_filename[MAXPATHLEN];
int dcc_make_lock_filename(const char *lockname,
const struct dcc_hostdef *host,
int iter,
char **filename_ret)
{
char * buf;
int ret;
char *lockdir;
if ((ret = dcc_get_lock_dir(&lockdir)))
return ret;
if (host->mode == DCC_MODE_LOCAL) {
if (asprintf(&buf, "%s/%s_localhost_%d", lockdir, lockname,
iter) == -1)
return EXIT_OUT_OF_MEMORY;
} else if (host->mode == DCC_MODE_TCP) {
if (asprintf(&buf, "%s/%s_tcp_%s_%d_%d", lockdir, lockname,
host->hostname,
host->port, iter) == -1)
return EXIT_OUT_OF_MEMORY;
} else if (host->mode == DCC_MODE_SSH) {
if (asprintf(&buf, "%s/%s_ssh_%s_%d", lockdir, lockname,
host->hostname, iter) == -1)
return EXIT_OUT_OF_MEMORY;
} else {
rs_log_crit("oops");
return EXIT_PROTOCOL_ERROR;
}
*filename_ret = buf;
return 0;
}
int sys_lock(int fd, int block)
{
#if defined(HAVE_FLOCK)
return flock(fd, LOCK_EX | (block ? 0 : LOCK_NB));
#elif defined(F_SETLK)
struct flock lockparam;
lockparam.l_type = F_WRLCK;
lockparam.l_whence = SEEK_SET;
lockparam.l_start = 0;
lockparam.l_len = 0;
return fcntl(fd, block ? F_SETLKW : F_SETLK, &lockparam);
#elif defined(HAVE_LOCKF)
return lockf(fd, block ? F_LOCK : F_TLOCK, 0);
#else
# error "No supported lock method. Please port this code."
#endif
}
int dcc_unlock(int lock_fd)
{
rs_trace("release lock fd%d", lock_fd);
if (close(lock_fd)) {
rs_log_error("close failed: %s", strerror(errno));
return EXIT_IO_ERROR;
}
return 0;
}
int dcc_open_lockfile(const char *fname, int *plockfd)
{
*plockfd = open(fname, O_WRONLY|O_CREAT, 0666);
if (*plockfd == -1 && errno != EEXIST) {
rs_log_error("failed to creat %s: %s", fname, strerror(errno));
return EXIT_IO_ERROR;
}
dcc_set_owner(fname);
return 0;
}
int dcc_lock_host(const char *lockname,
const struct dcc_hostdef *host,
int slot, int block,
int *lock_fd)
{
char *fname;
int ret;
if ((ret = dcc_make_lock_filename(lockname, host, slot, &fname)))
return ret;
if ((ret = dcc_open_lockfile(fname, lock_fd)) != 0) {
free(fname);
return ret;
}
if (sys_lock(*lock_fd, block) == 0) {
rs_trace("got %s lock on %s slot %d as fd%d", lockname,
host->hostdef_string, slot, *lock_fd);
free(fname);
return 0;
} else {
switch (errno) {
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
case EAGAIN:
case EACCES:
rs_trace("%s is busy", fname);
ret = EXIT_BUSY;
break;
default:
rs_log_error("lock %s failed: %s", fname, strerror(errno));
ret = EXIT_IO_ERROR;
break;
}
dcc_close(*lock_fd);
free(fname);
return ret;
}
}
int dcc_get_cpp_lock()
{
int lock_fd;
char *lockdir;
int i, ncpus, sleepTime = 10000;
if (dcc_get_lock_dir(&lockdir))
return -1;
if (dcc_ncpus(&ncpus))
ncpus = 1;
ncpus++;
for (i=0; i<ncpus; i++) {
sprintf(cpp_lock_filename, "%s/%s_%d", lockdir, "cpp_lock", i);
if (dcc_open_lockfile(cpp_lock_filename, &lock_fd))
lock_fd = -1;
else {
if (sys_lock(lock_fd, 0) != 0) {
rs_trace("someone already has cpp lock: %s (%s)", cpp_lock_filename, strerror(errno));
close(lock_fd);
lock_fd = -1;
} else {
break;
}
}
}
if (lock_fd == -1) {
srandom(getpid());
sprintf(cpp_lock_filename, "%s/%s_%d", lockdir, "cpp_lock", random()%(ncpus));
rs_trace("blocking for cpp lock: %s (%s)", cpp_lock_filename, strerror(errno));
if (dcc_open_lockfile(cpp_lock_filename, &lock_fd))
lock_fd = -1;
else {
if (sys_lock(lock_fd, 1) != 0) {
rs_log_warning("failed to get cpp lock: %s (%s)", cpp_lock_filename, strerror(errno));
close(lock_fd);
lock_fd = -1;
}
}
}
if (lock_fd != -1)
rs_trace("got cpp lock: %s", cpp_lock_filename);
cpp_lock = lock_fd;
return lock_fd;
}
void dcc_unlock_cpp_lock()
{
if (cpp_lock != -1) {
close(cpp_lock);
rs_trace("gave up cpp lock: %s", cpp_lock_filename);
}
}