#include "status.hpp"
#ifdef NO_SHM
bool status_init(size_t) { return true; }
void status_deinit() {}
bool status_attach() { return true; }
void status_detach() {}
void status_add(unsigned, bool) {}
bool status_get(unsigned) { return true; }
unsigned status_rating(unsigned) { return 100; }
#else
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <assert.h>
typedef struct {
unsigned tries;
unsigned fails;
} status_t;
static const char* shm_fn = "/proxypool";
static volatile status_t* status = NULL;
static size_t nstatus = 0;
static void* status_attach(int fd, size_t size) {
void* p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
LOG_ERRNO("mmap(%s)", shm_fn);
return NULL;
}
close(fd); // After a call to mmap(2) the file descriptor may be closed without affecting the memory mapping
return p;
}
bool status_init(size_t maxid) {
++maxid;
int fd = shm_open(shm_fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if (fd == -1) {
LOG_ERRNO("shm_open(%s)", shm_fn);
return false;
}
if (ftruncate(fd, maxid * sizeof(status_t)) == -1) {
LOG_ERRNO("ftruncate(%s)", shm_fn);
shm_unlink(shm_fn);
close(fd);
return false;
}
status = (volatile status_t*)status_attach(fd, maxid * sizeof(status_t));
if (!status) {
shm_unlink(shm_fn);
close(fd);
return false;
}
nstatus = maxid;
memset((void*)status, 0, nstatus * sizeof(status_t));
return true;
}
void status_deinit() {
if (!nstatus) return;
if (shm_unlink(shm_fn) == -1) {
LOG_ERRNO("shm_unlink(%s)", shm_fn);
}
status_detach();
}
bool status_attach() {
if (!nstatus) {
return false;
}
int fd = shm_open(shm_fn, O_RDWR, 0);
if (fd == -1) {
LOG_ERRNO("shm_open(%s)", shm_fn);
return false;
}
status = (volatile status_t*)status_attach(fd, nstatus * sizeof(status_t));
if (!status) {
close(fd);
return false;
}
return true;
}
void status_detach() {
if (status && munmap((void*)status, nstatus * sizeof(status_t)) == -1) {
LOG_ERRNO("munmap(%s)", shm_fn);
}
status = NULL;
nstatus = 0;
}
void status_add(unsigned id, bool ok) {
if (!nstatus) return;
assert(id < nstatus);
++status[id].tries;
if (!ok) ++status[id].fails;
}
bool status_get(unsigned id) {
if (!nstatus) return true;
assert(id < nstatus);
for (unsigned i=0; i<nstatus; ++i) {
//LOG("%u: %u/%u", i, status[i].fails, status[i].tries);
}
if (status[id].fails <= status[id].tries/2) {
return true;
} else if (rand() % nstatus == 0) { // only one or retry from time to time
return true;
} else {
return false;
}
}
int status_rating(unsigned id) {
if (!nstatus) return -1;
assert(id < nstatus);
if (!status[id].tries) {
return -1;
} else {
return ((status[id].tries - status[id].fails) * 100) / status[id].tries;
}
}
#endif /* !NO_SHM */