/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Just in case we're not in a build environment, make sure that
* TEXT_DOMAIN gets set to something.
*/
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
/*
* setup utility
*/
#include "meta_set_prv.h"
#include <sys/resource.h>
#include <syslog.h>
/* globals */
char *myname = "";
FILE *metalogfp = NULL;
int metasyslog = 0;
uint_t verbosity = 0;
hrtime_t start_time = 0;
sigset_t allsigs;
/* locals */
static int rb_signal_handling = FALSE;
static int rb_signal_caught = FALSE;
static int rb_signal_which = 0;
static size_t metansig = 0;
static struct sigaction *metahandlers = NULL;
#ifdef _DEBUG_MALLOC_INC
static ulong_t malloc_histid_begin;
static ulong_t malloc_histid_end;
static ulong_t malloc_inuse_begin;
static ulong_t malloc_inuse_end;
#endif /* _DEBUG_MALLOC_INC */
/* forwards */
static void md_catcher(int sig);
/*
* push/pop signal handlers
*/
static int
md_pushsig(
unsigned sig,
void (*handler)(int sig),
md_error_t *ep
)
{
struct sigaction newhandler;
/* expand vector as neccessary */
if (sig >= metansig) {
if (metahandlers == NULL) {
metahandlers = Zalloc(
(sig + 1) * sizeof (metahandlers[0]));
} else {
metahandlers = Realloc(metahandlers,
((sig + 1) * sizeof (metahandlers[0])));
(void) memset(&metahandlers[metansig], 0,
((sig - metansig) * sizeof (metahandlers[0])));
}
metansig = sig;
}
/* We need to have a seperate stack to handle rollback properly */
newhandler.sa_flags = 0;
if (sigfillset(&newhandler.sa_mask) < 0)
return (mdsyserror(ep, errno,
"sigfillset(&newhandler.sa_mask)"));
newhandler.sa_handler = handler;
/* push handler */
if (sigaction(sig, &newhandler, &metahandlers[sig]) < 0)
return (mdsyserror(ep, errno, "sigaction(&newhandler)"));
/* return success */
return (0);
}
static int
md_popsig(
unsigned sig,
md_error_t *ep
)
{
/* can't pop what isn't pushed */
assert(sig <= metansig);
assert(metahandlers[sig].sa_handler != md_catcher);
/* pop handler */
if (sigaction(sig, &metahandlers[sig], NULL) < 0)
return (mdsyserror(ep, errno, "sigaction(&metahandlers)"));
/* return success */
return (0);
}
char *
meta_lock_name(
set_t setno
)
{
char lockname[30];
if (setno == MD_LOCAL_SET)
return (strdup(METALOCK));
(void) snprintf(lockname, sizeof (lockname), "%s.%ld", METALOCK, setno);
return (strdup(lockname));
}
#define META_LOCK_FD(sp) ((sp)->lockfd)
#define META_LOCK_NAME(sp) (meta_lock_name((sp)->setno))
/*
* open lock
*/
static int
meta_lock_open(
mdsetname_t *sp,
md_error_t *ep
)
{
int lockfd = META_LOCK_FD(sp);
char *lockname = META_LOCK_NAME(sp);
/* check for already open */
if (lockfd >= 0)
goto success;
assert(lockfd == MD_NO_LOCK);
/* open and/or create lock file */
if ((lockfd = open(lockname, O_WRONLY, 0)) < 0) {
if (errno == EROFS) {
lockfd = MD_NO_LOCK;
goto success;
}
if (errno != ENOENT) {
(void) mdsyserror(ep, errno, lockname);
goto failure;
}
if ((lockfd = open(lockname, (O_WRONLY|O_CREAT),
0644)) < 0) {
(void) mdsyserror(ep, errno, lockname);
goto failure;
}
if (fchmod(lockfd, 0644) != 0) {
(void) mdsyserror(ep, errno, lockname);
goto failure;
}
}
/* return success */
success:
if (lockname != NULL)
free(lockname);
META_LOCK_FD(sp) = lockfd;
return (0);
/* flag failure */
failure:
if (lockname != NULL)
free(lockname);
if (lockfd >= 0)
(void) close(lockfd);
return (-1);
}
static int
meta_lock_close(
mdsetname_t *sp,
md_error_t *ep
)
{
int retval = 0;
if (close(META_LOCK_FD(sp)) != 0) {
if (ep != NULL) {
char *lockname = META_LOCK_NAME(sp);
(void) mdsyserror(ep, errno, lockname);
if (lockname != NULL)
free(lockname);
}
retval = -1;
}
META_LOCK_FD(sp) = MD_NO_LOCK;
return (retval);
}
/*
* unlock
*/
int
meta_unlock(
mdsetname_t *sp,
md_error_t *ep
)
{
int lockfd = META_LOCK_FD(sp);
/* ignore read-only filesystem */
if (lockfd == MD_NO_LOCK)
return (0);
assert(lockfd >= 0);
/* unlock and discard */
if (lockf(lockfd, F_ULOCK, 0) != 0) {
(void) mdsyserror(ep, errno, METALOCK);
(void) meta_lock_close(sp, NULL);
return (-1);
}
return (meta_lock_close(sp, ep));
}
/*
* lock
*/
int
meta_lock(
mdsetname_t *sp,
int print_status,
md_error_t *ep
)
{
int lockfd;
char *lockname = NULL;
/* open lock file */
if (meta_lock_open(sp, ep) != 0) {
assert(META_LOCK_FD(sp) == MD_NO_LOCK);
goto failure;
}
/* ignore read-only filesystem */
if ((lockfd = META_LOCK_FD(sp)) == MD_NO_LOCK)
goto success;
assert(lockfd >= 0);
lockname = META_LOCK_NAME(sp);
/* grab lock */
if (lockf(lockfd, F_TLOCK, 0) != 0) {
if ((errno != EACCES) && (errno != EAGAIN)) {
(void) mdsyserror(ep, errno, lockname);
goto failure;
}
if (print_status)
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
"%s: waiting on %s\n"),
myname, lockname);
if (lockf(lockfd, F_LOCK, 0) != 0) {
(void) mdsyserror(ep, errno, lockname);
goto failure;
}
}
/* return success */
success:
if (lockname != NULL)
free(lockname);
return (0);
/* flag failure */
failure:
if (lockname != NULL)
free(lockname);
if (lockfd >= 0)
(void) meta_lock_close(sp, ep);
return (-1);
}
int
meta_lock_nowait(
mdsetname_t *sp,
md_error_t *ep
)
{
int lockfd;
char *lockname = NULL;
/* open lock file */
if (meta_lock_open(sp, ep) != 0) {
assert(META_LOCK_FD(sp) == MD_NO_LOCK);
goto failure;
}
/* ignore read-only filesystem */
if ((lockfd = META_LOCK_FD(sp)) == MD_NO_LOCK)
goto success;
assert(lockfd >= 0);
lockname = META_LOCK_NAME(sp);
/* grab lock */
if (lockf(lockfd, F_TLOCK, 0) != 0) {
if ((errno != EACCES) && (errno != EAGAIN)) {
(void) mdsyserror(ep, errno, lockname);
goto failure;
}
(void) mdsyserror(ep, EAGAIN, lockname);
goto failure;
}
/* return success */
success:
if (lockname != NULL)
free(lockname);
return (0);
/* flag failure */
failure:
if (lockname != NULL)
free(lockname);
if (lockfd >= 0)
(void) meta_lock_close(sp, ep);
return (-1);
}
/*
* lock status
*/
int
meta_lock_status(
mdsetname_t *sp,
md_error_t *ep
)
{
int lockfd;
/* open lock file */
if (meta_lock_open(sp, ep) != 0) {
assert(META_LOCK_FD(sp) == MD_NO_LOCK);
return (-1);
}
lockfd = META_LOCK_FD(sp);
/* ignore read-only filesystem */
if (lockfd == MD_NO_LOCK)
return (0);
assert(lockfd >= 0);
/* test lock */
if (lockf(lockfd, F_TEST, 0) != 0) {
char *lockname = META_LOCK_NAME(sp);
(void) mdsyserror(ep, errno, lockname);
if (lockname != NULL)
free(lockname);
return (-1);
}
return (0);
}
/*
* setup for syslog daemon output
*/
static void
md_syslog(
char *name /* name of program */
)
{
if ((name == NULL) || (*name == '\0'))
name = "md";
openlog(name, LOG_CONS, LOG_DAEMON);
metasyslog = 1;
}
/*
* daemonize: put in background
*/
int
md_daemonize(
mdsetname_t *sp,
md_error_t *ep
)
{
char *p;
struct rlimit rlim;
pid_t pid;
int i;
/* debug */
if (((p = getenv("MD_DEBUG")) != NULL) &&
(strstr(p, "NODAEMON") != NULL)) {
return (0); /* do nothing */
}
/* get number of file descriptors */
if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
return (mdsyserror(ep, errno, "getrlimit(RLIMIT_NOFILE)"));
}
/* fork and kill parent */
if ((pid = fork()) == -1)
return (mdsyserror(ep, errno, "fork"));
else if (pid != 0)
return (pid);
/*
* We need to close the admin device and reset the specialfd to force
* the child process to reopen it, since we are going to close all
* descriptors from 3 up to RLIMIT_NOFILE in the child.
*/
if (close_admin(ep) != 0)
return (-1);
/* close RPC connections */
metarpccloseall();
/* drop lock */
if (meta_unlock(sp, ep) != 0)
return (-1);
if (rlim.rlim_cur != RLIM_INFINITY) {
/*
* close all but stdout, stderr, and metalogfp
*/
for (i = 0; (i < rlim.rlim_cur); ++i) {
if ((i == fileno(stdout)) ||
(i == fileno(stderr)) ||
((metalogfp != NULL) &&
(i == fileno(metalogfp)))) {
continue;
}
(void) close(i);
}
}
/* put in own process group */
if (setsid() == -1)
return (mdsyserror(ep, errno, "setsid"));
/* setup syslog */
md_syslog(myname);
/* return success */
return (0);
}
/*
* flush and sync fp
*/
static void
flushfp(
FILE *fp
)
{
(void) fflush(fp);
(void) fsync(fileno(fp));
}
/*
* reset and exit utility
*/
void
md_exit(
mdsetname_t *sp,
int eval
)
{
md_error_t status = mdnullerror;
md_error_t *ep = &status;
/* close RPC connections */
metarpccloseall();
if (sp != NULL) {
if (meta_unlock(sp, ep) != 0) {
mde_perror(ep, "");
mdclrerror(ep);
if (eval == 0)
eval = 1;
}
}
/* flush name caches */
#ifdef DEBUG
metaflushnames(1);
#endif /* DEBUG */
/* log exit */
if (metalogfp != NULL) {
md_logpfx(metalogfp);
(void) fprintf(metalogfp, dgettext(TEXT_DOMAIN,
"exiting with %d\n"), eval);
flushfp(metalogfp);
(void) fclose(metalogfp);
metalogfp = NULL;
}
if ((metasyslog) && (eval != 0)) {
syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
"exiting with %d\n"), eval);
closelog();
metasyslog = 0;
}
/* check arena, print malloc usage */
#ifdef _DEBUG_MALLOC_INC
(void) malloc_chain_check(1);
{
char *p;
if (((p = getenv("MD_DEBUG")) != NULL) &&
(strstr(p, "MALLOC") != NULL)) {
malloc_inuse_end = malloc_inuse(&malloc_histid_end);
(void) fprintf(stderr, "%s: end malloc_inuse %lu\n",
myname, malloc_inuse_end);
if (malloc_inuse_end != malloc_inuse_begin) {
malloc_list(fileno(stderr),
malloc_histid_begin, malloc_histid_end);
}
}
}
#endif /* _DEBUG_MALLOC_INC */
/* exit with value */
exit(eval);
}
/*
* signal catcher
*/
static void
md_catcher(
int sig
)
{
char buf[128];
char *msg;
md_error_t status = mdnullerror;
md_error_t *ep = &status;
struct sigaction defhandler;
/* log signal */
if ((msg = strsignal(sig)) == NULL) {
(void) snprintf(buf, sizeof (buf),
dgettext(TEXT_DOMAIN, "unknown signal %d"), sig);
msg = buf;
}
md_eprintf("%s\n", msg);
/*
* In roll_back crtical section handling, the first instance of a user
* generated signal is caught, a flag is set to allow preemption at a
* "convenient" point and md_catcher returns. If the user continues
* generate the signal, the second instance will invoke the default
* handler and exit.
*/
if (rb_signal_handling == TRUE) {
if (sig != SIGABRT && sig != SIGBUS && sig != SIGSEGV) {
if (rb_signal_caught == FALSE) {
rb_signal_caught = TRUE;
rb_signal_which = sig;
return;
}
}
}
/* let default handler do it's thing */
if (md_popsig(sig, ep) != 0) {
mde_perror(ep, "");
mdclrerror(ep);
defhandler.sa_flags = 0;
if (sigfillset(&defhandler.sa_mask) < 0) {
(void) mdsyserror(ep, errno,
"sigfillset(&defhandler.sa_mask)");
mde_perror(ep, "");
md_exit(NULL, 1);
}
defhandler.sa_handler = SIG_DFL;
if (sigaction(sig, &defhandler, NULL) < 0) {
(void) mdsyserror(ep, errno, "sigaction(&defhandler)");
mde_perror(ep, "");
md_exit(NULL, 1);
}
}
md_post_sig(sig);
}
void
md_post_sig(int sig)
{
if (kill(getpid(), sig) != 0) {
md_perror("kill(getpid())");
md_exit(NULL, -sig);
}
}
int
md_got_sig(void)
{
return (rb_signal_caught);
}
int
md_which_sig(void)
{
return (rb_signal_which);
}
void
md_rb_sig_handling_on(void)
{
rb_signal_handling = TRUE;
}
void
md_rb_sig_handling_off(int sig_seen, int sig)
{
rb_signal_handling = FALSE;
rb_signal_caught = FALSE;
rb_signal_which = 0;
if (sig_seen)
md_post_sig(sig);
}
/*
* setup metaclust variables
*/
void
setup_mc_log(
uint_t level
)
{
/* initialise externals */
verbosity = level;
start_time = gethrtime();
}
/*
* initilize utility
*/
int
md_init(
int argc,
char *argv[],
int dosyslog,
int doadmin,
md_error_t *ep
)
{
int ret = 0;
/* initialize everything but the signals */
if ((ret = md_init_nosig(argc, argv, dosyslog,
doadmin, ep)) != 0)
return (ret);
if (sigfillset(&allsigs) < 0)
return (mdsyserror(ep, errno, "sigfillset(&allsigs)"));
/* catch common signals */
if ((md_pushsig(SIGHUP, md_catcher, ep) != 0) ||
(md_pushsig(SIGINT, md_catcher, ep) != 0) ||
(md_pushsig(SIGQUIT, md_catcher, ep) != 0) ||
(md_pushsig(SIGABRT, md_catcher, ep) != 0) ||
(md_pushsig(SIGBUS, md_catcher, ep) != 0) ||
(md_pushsig(SIGSEGV, md_catcher, ep) != 0) ||
(md_pushsig(SIGPIPE, md_catcher, ep) != 0) ||
(md_pushsig(SIGTERM, md_catcher, ep) != 0)) {
return (-1);
}
/* return success */
return (0);
}
/*
* initilize utility without setting up sighandlers
* setting up signal handlers in libmeta can affect others
* programs that link with libmeta but have their own handlers
*/
int
md_init_nosig(
int argc,
char *argv[],
int dosyslog,
int doadmin,
md_error_t *ep
)
{
/* setup myname */
if ((myname = strrchr(argv[0], '/')) != NULL)
++myname;
else
myname = argv[0];
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
/* print malloc usage */
#ifdef _DEBUG_MALLOC_INC
{
char *p;
if (((p = getenv("MD_DEBUG")) != NULL) &&
(strstr(p, "MALLOC") != NULL)) {
malloc_inuse_begin =
malloc_inuse(&malloc_histid_begin);
(void) fprintf(stderr, "%s: begin malloc_inuse %lu\n",
myname, malloc_inuse_begin);
}
}
#endif /* _DEBUG_MALLOC_INC */
/* open syslog */
if (dosyslog)
md_syslog(myname);
/* log command */
if (getenv(METALOGENV) != NULL) {
if ((metalogfp = fopen(METALOG, "a")) != NULL) {
int i;
(void) fchmod(fileno(metalogfp), 0664);
md_logpfx(metalogfp);
for (i = 1; (i < argc); ++i)
(void) fprintf(metalogfp, " %s", argv[i]);
(void) fprintf(metalogfp, "\n");
flushfp(metalogfp);
}
}
/* make sure we can open the admin device before we do anything else */
if (doadmin)
if (open_admin(ep) < 0)
return (-1);
/* flush name caches */
metaflushnames(1);
/* return success */
return (0);
}
/*
* (re)initilize daemon
*/
int
md_init_daemon(
char *name,
md_error_t *ep
)
{
static int already = 0;
int dosyslog = 1;
int doadmin = 1;
/* setup */
if (! already) {
if (md_init(1, &name, dosyslog, doadmin, ep) != 0)
return (-1);
already = 1;
}
/* return success */
return (0);
}
/*
* Roll back functions for handling sync and async cleanup.
*/
int
procsigs(int block, sigset_t *oldsigs, md_error_t *ep)
{
if (block == TRUE) {
if (sigprocmask(SIG_BLOCK, &allsigs, oldsigs) < 0) {
(void) mdsyserror(ep, errno, "sigprocmask(SIG_BLOCK)");
return (-1);
}
} else {
if (sigprocmask(SIG_SETMASK, oldsigs, NULL) < 0) {
(void) mdsyserror(ep, errno,
"sigprocmask(SIG_SETMASK)");
return (-1);
}
}
return (0);
}
#ifdef DEBUG
int
rb_test(
int rbt_sel_tpt,
char *rbt_sel_tag,
md_error_t *ep
)
{
char *rbt_env_tpt = getenv("META_RBT_TPT");
char *rbt_env_tag = getenv("META_RBT_TAG");
int sig = 0;
int rbt_int_tpt;
int rbt_tag_match = 1;
sigset_t curmask;
md_error_t xep = mdnullerror;
if (rbt_env_tpt) {
rbt_int_tpt = atoi(rbt_env_tpt);
if (rbt_int_tpt < 0) {
sig = 1;
rbt_int_tpt = -1 * rbt_int_tpt;
}
assert(rbt_sel_tpt != 0);
if (rbt_int_tpt == 0)
return (0);
if (rbt_env_tag && rbt_sel_tag)
if (strcmp(rbt_env_tag, rbt_sel_tag) != 0)
rbt_tag_match = 0;
if (rbt_int_tpt == rbt_sel_tpt && rbt_tag_match) {
md_eprintf(
"******************** RB_TEST(%s, %d, sig=%s)\n",
rbt_sel_tag, rbt_sel_tpt,
(sig != 0) ? "True" : "False");
if (sig) {
md_eprintf("********** sigsuspend()\n");
if (sigprocmask(NULL, NULL, &curmask) < 0) {
(void) mdsyserror(&xep, errno, NULL);
mde_perror(&xep, "sigprocmask(GET)");
md_exit(NULL, 1);
}
if (sigsuspend(&curmask) < 0) {
(void) mdsyserror(&xep, errno, NULL);
mde_perror(&xep,
"sigsuspend(&curmask)");
md_exit(NULL, 1);
}
if (md_got_sig())
return (-1);
}
(void) mderror(ep, MDE_TESTERROR,
"********** rb_test()");
md_eprintf("******************** rollback\n");
return (-1);
}
}
return (0);
}
#else
/* ARGSUSED */
int
rb_test(
int rbt_sel_tpt,
char *rbt_sel_tag,
md_error_t *ep
)
{
(void) mderror(ep, MDE_TESTERROR, "******** rb_test:Not supported\n");
return (-1);
}
#endif /* DEBUG */