#include "db_config.h"
#include "db_int.h"
#define REGISTER_FILE "__db.register"
#define PID_EMPTY "X 0\n"
#define PID_FMT "%24lu\n"
#define PID_ISEMPTY(p) (memcmp(p, PID_EMPTY, PID_LEN) == 0)
#define PID_LEN (25)
#define REGISTRY_LOCK(env, pos, nowait) \
__os_fdlock(env, (env)->dbenv->registry, (off_t)(pos), 1, nowait)
#define REGISTRY_UNLOCK(env, pos) \
__os_fdlock(env, (env)->dbenv->registry, (off_t)(pos), 0, 0)
#define REGISTRY_EXCL_LOCK(env, nowait) \
REGISTRY_LOCK(env, 1, nowait)
#define REGISTRY_EXCL_UNLOCK(env) \
REGISTRY_UNLOCK(env, 1)
static int __envreg_add __P((ENV *, int *, u_int32_t));
static int __envreg_pid_compare __P((const void *, const void *));
static int __envreg_create_active_pid __P((ENV *, char *));
#define DB_ENVREG_KILL_ALL 0
int
__envreg_register(env, need_recoveryp, flags)
ENV *env;
int *need_recoveryp;
u_int32_t flags;
{
DB_ENV *dbenv;
pid_t pid;
u_int32_t bytes, mbytes;
int ret;
char *pp;
*need_recoveryp = 0;
dbenv = env->dbenv;
dbenv->thread_id(dbenv, &pid, NULL);
pp = NULL;
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1524",
"%lu: register environment", "%lu"), (u_long)pid);
if ((ret = __db_appname(env,
DB_APP_NONE, REGISTER_FILE, NULL, &pp)) != 0)
goto err;
if ((ret = __os_open(env, pp, 0,
DB_OSO_CREATE, DB_MODE_660, &dbenv->registry)) != 0)
goto err;
if ((ret = REGISTRY_EXCL_LOCK(env, 0)) != 0)
goto err;
if ((ret = __os_ioinfo(
env, pp, dbenv->registry, &mbytes, &bytes, NULL)) != 0)
goto err;
if (mbytes == 0 && bytes == 0) {
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1525",
"%lu: creating %s", "%lu %s"), (u_long)pid, pp);
*need_recoveryp = 1;
}
if ((ret = __envreg_add(env, need_recoveryp, flags)) != 0)
goto err;
if (*need_recoveryp == 0 && (ret = REGISTRY_EXCL_UNLOCK(env)) != 0)
goto err;
if (0) {
err: *need_recoveryp = 0;
if (dbenv->registry != NULL)
(void)__os_closehandle(env, dbenv->registry);
dbenv->registry = NULL;
}
if (pp != NULL)
__os_free(env, pp);
return (ret);
}
static int
__envreg_add(env, need_recoveryp, flags)
ENV *env;
int *need_recoveryp;
u_int32_t flags;
{
DB_ENV *dbenv;
DB_THREAD_INFO *ip;
REGENV * renv;
REGINFO *infop;
pid_t pid;
off_t end, pos, dead;
size_t nr, nw;
u_int lcnt;
u_int32_t bytes, mbytes, orig_flags;
int need_recovery, ret, t_ret;
char *p, buf[PID_LEN + 10], pid_buf[PID_LEN + 10];
dbenv = env->dbenv;
need_recovery = 0;
COMPQUIET(dead, 0);
COMPQUIET(p, NULL);
ip = NULL;
dbenv->thread_id(dbenv, &pid, NULL);
snprintf(pid_buf, sizeof(pid_buf), PID_FMT, (u_long)pid);
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1526",
"%lu: adding self to registry", "%lu"), (u_long)pid);
#if DB_ENVREG_KILL_ALL
if (0) {
kill_all:
if ((ret = __os_seek(env, dbenv->registry, 0, 0, 0)) != 0)
return (ret);
}
#endif
for (lcnt = 0;; ++lcnt) {
if ((ret = __os_read(
env, dbenv->registry, buf, PID_LEN, &nr)) != 0)
return (ret);
if (nr == 0)
break;
if (nr != PID_LEN) {
need_recovery = 1;
break;
}
if (PID_ISEMPTY(buf)) {
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1527",
"%02u: EMPTY", "%02u"), lcnt);
continue;
}
if (memcmp(buf, pid_buf, PID_LEN) == 0) {
__db_errx(env, DB_STR("1528",
"DB_REGISTER limits processes to one open DB_ENV handle per environment"));
return (EINVAL);
}
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER)) {
for (p = buf; *p == ' ';)
++p;
buf[nr - 1] = '\0';
}
#if DB_ENVREG_KILL_ALL
if (need_recovery) {
pid = (pid_t)strtoul(buf, NULL, 10);
(void)kill(pid, SIGKILL);
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1529",
"%02u: %s: KILLED", "%02u %s"), lcnt, p);
continue;
}
#endif
pos = (off_t)lcnt * PID_LEN;
if (REGISTRY_LOCK(env, pos, 1) == 0) {
if ((ret = REGISTRY_UNLOCK(env, pos)) != 0)
return (ret);
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1530",
"%02u: %s: FAILED", "%02u %s"), lcnt, p);
need_recovery = 1;
dead = pos;
#if DB_ENVREG_KILL_ALL
goto kill_all;
#else
break;
#endif
} else
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1531",
"%02u: %s: LOCKED", "%02u %s"), lcnt, p);
}
if (need_recovery) {
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, "%lu: recovery required", (u_long)pid);
if (LF_ISSET(DB_FAILCHK) || LF_ISSET(DB_FAILCHK_ISALIVE)) {
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env,
"%lu: performing failchk", (u_long)pid);
if (LF_ISSET(DB_FAILCHK_ISALIVE))
if ((ret = __envreg_create_active_pid(
env, pid_buf)) != 0)
goto sig_proc;
LF_CLR(DB_CREATE | DB_RECOVER | DB_RECOVER_FATAL);
orig_flags = dbenv->flags;
F_SET(dbenv, DB_ENV_FAILCHK);
if ((ret = __env_attach_regions(
dbenv, flags, orig_flags, 0)) != 0)
goto sig_proc;
if ((t_ret =
__env_set_state(env, &ip, THREAD_FAILCHK)) != 0 &&
ret == 0)
ret = t_ret;
if ((t_ret =
__env_failchk_int(dbenv)) != 0 && ret == 0)
ret = t_ret;
if (LF_ISSET(DB_FAILCHK_ISALIVE)) {
DB_GLOBAL(num_active_pids) = 0;
DB_GLOBAL(size_active_pids) = 0;
__os_free( env, DB_GLOBAL(active_pids));
}
if ((t_ret =
__env_refresh(dbenv, orig_flags, 0)) != 0 &&
ret == 0)
ret = t_ret;
if (ret == 0) {
if ((ret = __os_seek(env, dbenv->registry,
0, 0,(u_int32_t)dead)) != 0 ||
(ret = __os_write(env, dbenv->registry,
PID_EMPTY, PID_LEN, &nw)) != 0)
return (ret);
need_recovery = 0;
goto add;
}
}
sig_proc: if (__env_attach(env, NULL, 0, 0) == 0) {
infop = env->reginfo;
renv = infop->primary;
renv->reg_panic = 1;
renv->panic = 1;
(void)__env_detach(env, 0);
}
__os_yield(env, 0, dbenv->envreg_timeout);
if ((ret = __os_ioinfo(
env, NULL, dbenv->registry, &mbytes, &bytes, NULL)) != 0)
return (ret);
end = (off_t)mbytes * MEGABYTE + bytes;
if ((ret = __os_seek(env, dbenv->registry, 0, 0, 0)) != 0)
return (ret);
for (lcnt = 0; lcnt < ((u_int)end / PID_LEN +
((u_int)end % PID_LEN == 0 ? 0 : 1)); ++lcnt) {
if ((ret = __os_read(
env, dbenv->registry, buf, PID_LEN, &nr)) != 0)
return (ret);
pos = (off_t)lcnt * PID_LEN;
if (pos != dead) {
pid = (pid_t)strtoul(buf, NULL, 10);
DB_EVENT(env, DB_EVENT_REG_ALIVE, &pid);
}
if ((ret = __os_seek(env,
dbenv->registry, 0, 0, (u_int32_t)pos)) != 0 ||
(ret = __os_write(env,
dbenv->registry, PID_EMPTY, PID_LEN, &nw)) != 0)
return (ret);
}
__os_yield(env, 0, dbenv->envreg_timeout);
}
add: if ((ret = __os_seek(env, dbenv->registry, 0, 0, 0)) != 0)
return (ret);
for (lcnt = 0;; ++lcnt) {
if ((ret = __os_read(
env, dbenv->registry, buf, PID_LEN, &nr)) != 0)
return (ret);
if (nr == PID_LEN && !PID_ISEMPTY(buf))
continue;
pos = (off_t)lcnt * PID_LEN;
if (REGISTRY_LOCK(env, pos, 1) == 0) {
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1532",
"%lu: locking slot %02u at offset %lu",
"%lu %02u %lu"), (u_long)pid, lcnt,
(u_long)pos);
if ((ret = __os_seek(env,
dbenv->registry, 0, 0, (u_int32_t)pos)) != 0 ||
(ret = __os_write(env,
dbenv->registry, pid_buf, PID_LEN, &nw)) != 0)
return (ret);
dbenv->registry_off = (u_int32_t)pos;
break;
}
}
if (need_recovery)
*need_recoveryp = 1;
return (ret);
}
int
__envreg_unregister(env, recovery_failed)
ENV *env;
int recovery_failed;
{
DB_ENV *dbenv;
size_t nw;
int ret, t_ret;
dbenv = env->dbenv;
ret = 0;
if (recovery_failed)
goto err;
if ((ret = __os_seek(env,
dbenv->registry, 0, 0, dbenv->registry_off)) != 0 ||
(ret = __os_write(
env, dbenv->registry, PID_EMPTY, PID_LEN, &nw)) != 0)
goto err;
err: if ((t_ret =
__os_closehandle(env, dbenv->registry)) != 0 && ret == 0)
ret = t_ret;
dbenv->registry = NULL;
return (ret);
}
int
__envreg_xunlock(env)
ENV *env;
{
DB_ENV *dbenv;
pid_t pid;
int ret;
dbenv = env->dbenv;
dbenv->thread_id(dbenv, &pid, NULL);
if (FLD_ISSET(dbenv->verbose, DB_VERB_REGISTER))
__db_msg(env, DB_STR_A("1533",
"%lu: recovery completed, unlocking", "%lu"), (u_long)pid);
if ((ret = REGISTRY_EXCL_UNLOCK(env)) == 0)
return (ret);
__db_err(env, ret, DB_STR_A("1534",
"%s: exclusive file unlock", "%s"), REGISTER_FILE);
return (__env_panic(env, ret));
}
static int
__envreg_pid_compare(key, membr)
const void *key;
const void *membr;
{
return ( *(pid_t*)key - *(pid_t*)membr );
}
int
__envreg_isalive(dbenv, pid, tid, flags )
DB_ENV *dbenv;
pid_t pid;
db_threadid_t tid;
u_int32_t flags;
{
DB_THREADID_INIT(tid);
if (!((flags == 0) || (flags == DB_MUTEX_PROCESS_ONLY)))
return (EINVAL);
if (DB_GLOBAL(active_pids) == NULL ||
DB_GLOBAL(num_active_pids) == 0 || dbenv == NULL)
return (0);
if (bsearch(&pid, DB_GLOBAL(active_pids), DB_GLOBAL(num_active_pids),
sizeof(pid_t), __envreg_pid_compare))
return 1;
return (0);
}
static int
__envreg_create_active_pid(env, my_pid)
ENV *env;
char *my_pid;
{
DB_ENV *dbenv;
char buf[PID_LEN + 10];
int ret;
off_t pos;
pid_t pid, *tmparray;
size_t tmpsize, nr;
u_int lcnt;
dbenv = env->dbenv;
pos = 0;
ret = 0;
if ((ret = __os_seek(env, dbenv->registry, 0, 0, 0)) != 0)
return (ret);
for (lcnt = 0;; ++lcnt) {
if ((ret = __os_read(
env, dbenv->registry, buf, PID_LEN, &nr)) != 0)
return (ret);
if (nr == 0 || nr != PID_LEN)
break;
if (PID_ISEMPTY(buf))
continue;
pos = (off_t)lcnt * PID_LEN;
if (REGISTRY_LOCK(env, pos, 1) == 0) {
if ((ret = REGISTRY_UNLOCK(env, pos)) != 0)
return (ret);
} else {
if (DB_GLOBAL(num_active_pids) + 1 >
DB_GLOBAL(size_active_pids)) {
tmpsize =
DB_GLOBAL(size_active_pids) * sizeof(pid_t);
tmpsize = tmpsize>0 ? tmpsize*2 : 512;
if ((ret = __os_malloc
(env, tmpsize, &tmparray )) != 0)
return (ret);
if (DB_GLOBAL(active_pids)) {
memcpy( tmparray,
DB_GLOBAL(active_pids),
DB_GLOBAL(num_active_pids) *
sizeof(pid_t));
__os_free( env, DB_GLOBAL(active_pids));
}
DB_GLOBAL(active_pids) = tmparray;
DB_GLOBAL(size_active_pids) = tmpsize;
if (DB_GLOBAL(num_active_pids) == 0) {
pid = (pid_t)strtoul(my_pid, NULL, 10);
DB_GLOBAL(active_pids)
[DB_GLOBAL(num_active_pids)++] = pid;
}
}
pid = (pid_t)strtoul(buf, NULL, 10);
DB_GLOBAL(active_pids)
[DB_GLOBAL(num_active_pids)++] = pid;
}
}
qsort(DB_GLOBAL(active_pids), DB_GLOBAL(num_active_pids),
sizeof(pid_t), __envreg_pid_compare);
return (ret);
}