2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <sys/mman.h>
2N/A#include <fcntl.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <errno.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/auxv.h>
2N/A#include <stdarg.h>
2N/A#include <syslog.h>
2N/A#include <sys/param.h>
2N/A#include <sys/sysmacros.h>
2N/A#include <procfs.h>
2N/A#include <libintl.h>
2N/A#include <locale.h>
2N/A
2N/Aextern int gmatch(const char *s, const char *p);
2N/A
2N/A#pragma init(__mpssmain)
2N/A
2N/Astatic const char *mpssident = "mpss.so.1";
2N/A
2N/A/* environment variables */
2N/A
2N/A#define ENV_MPSSCFGFILE "MPSSCFGFILE"
2N/A#define ENV_MPSSSTACK "MPSSSTACK"
2N/A#define ENV_MPSSHEAP "MPSSHEAP"
2N/A#define ENV_MPSSERRFILE "MPSSERRFILE"
2N/A
2N/A#define MPSSHEAP 0
2N/A#define MPSSSTACK 1
2N/A
2N/A/* config file */
2N/A
2N/A#define DEF_MPSSCFGFILE "/etc/mpss.conf"
2N/A#define MAXLINELEN MAXPATHLEN + 64
2N/A#define CFGDELIMITER ':'
2N/A#define ARGDELIMITER ' '
2N/A
2N/A/*
2N/A * avoid malloc which causes certain applications to crash
2N/A */
2N/Astatic char lbuf[MAXLINELEN];
2N/Astatic char pbuf[MAXPATHLEN];
2N/A
2N/A#ifdef MPSSDEBUG
2N/A#define ENV_MPSSDEBUG "MPSSDEBUG"
2N/A#define MPSSPRINT(x, y) if (mpssdebug & x) (void) fprintf y;
2N/A
2N/Astatic int mpssdebug;
2N/A#else
2N/A#define MPSSPRINT(x, y)
2N/A#endif
2N/A
2N/A#if !defined(TEXT_DOMAIN)
2N/A#define TEXT_DOMAIN "SYS_TEST"
2N/A#endif
2N/A
2N/A/*PRINTFLIKE2*/
2N/Astatic void
2N/Ampsserr(FILE *fp, char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A va_start(ap, fmt);
2N/A if (fp)
2N/A (void) vfprintf(fp, fmt, ap);
2N/A else
2N/A vsyslog(LOG_ERR, fmt, ap);
2N/A va_end(ap);
2N/A}
2N/A
2N/A/*
2N/A * Return the pointer to the fully-resolved path name of the process's
2N/A * executable file obtained from the AT_SUN_EXECNAME aux vector entry.
2N/A */
2N/Astatic const char *
2N/Amygetexecname(void)
2N/A{
2N/A const char *execname = NULL;
2N/A static auxv_t auxb;
2N/A
2N/A /*
2N/A * The first time through, read the initial aux vector that was
2N/A * passed to the process at exec(2). Only do this once.
2N/A */
2N/A int fd = open("/proc/self/auxv", O_RDONLY);
2N/A
2N/A if (fd >= 0) {
2N/A while (read(fd, &auxb, sizeof (auxv_t)) == sizeof (auxv_t)) {
2N/A if (auxb.a_type == AT_SUN_EXECNAME) {
2N/A execname = auxb.a_un.a_ptr;
2N/A break;
2N/A }
2N/A }
2N/A (void) close(fd);
2N/A }
2N/A return (execname);
2N/A}
2N/A
2N/Astatic size_t
2N/Aatosz(char *szstr)
2N/A{
2N/A size_t sz;
2N/A char *endptr, c;
2N/A
2N/A sz = strtoll(szstr, &endptr, 0);
2N/A
2N/A while (c = *endptr++) {
2N/A switch (c) {
2N/A case 't':
2N/A case 'T':
2N/A sz *= 1024;
2N/A /*FALLTHRU*/
2N/A case 'g':
2N/A case 'G':
2N/A sz *= 1024;
2N/A /*FALLTHRU*/
2N/A case 'm':
2N/A case 'M':
2N/A sz *= 1024;
2N/A /*FALLTHRU*/
2N/A case 'k':
2N/A case 'K':
2N/A sz *= 1024;
2N/A default:
2N/A break;
2N/A }
2N/A }
2N/A return (sz);
2N/A
2N/A}
2N/A
2N/A#define PGSZELEM (8 * sizeof (void *))
2N/Astatic size_t pgsz[PGSZELEM];
2N/Astatic int nelem;
2N/A
2N/Astatic int
2N/Apgszok(size_t sz)
2N/A{
2N/A int i;
2N/A
2N/A if (sz == 0)
2N/A return (1);
2N/A
2N/A for (i = 0; i < nelem; i++) {
2N/A if (sz == pgsz[i])
2N/A break;
2N/A }
2N/A
2N/A return (i < nelem);
2N/A}
2N/A
2N/Astatic void
2N/Apgszinit()
2N/A{
2N/A nelem = getpagesizes(NULL, 0);
2N/A
2N/A if (!nelem)
2N/A return;
2N/A
2N/A if (nelem > PGSZELEM)
2N/A nelem = PGSZELEM;
2N/A
2N/A (void) getpagesizes(pgsz, nelem);
2N/A#ifdef MPSSDEBUG
2N/A pgsz[nelem] = 0x800000;
2N/A nelem++;
2N/A#endif
2N/A}
2N/A
2N/A
2N/Astatic int
2N/Apgszset(size_t sz, uint_t flags)
2N/A{
2N/A struct memcntl_mha mpss;
2N/A int rc;
2N/A
2N/A mpss.mha_cmd = (flags == MPSSHEAP) ?
2N/A MHA_MAPSIZE_BSSBRK: MHA_MAPSIZE_STACK;
2N/A mpss.mha_pagesize = sz;
2N/A mpss.mha_flags = 0;
2N/A rc = memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mpss, 0, 0);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * check if exec name matches cfgname found in mpss cfg file.
2N/A */
2N/Astatic int
2N/Afnmatch(const char *execname, char *cfgname, char *cwd)
2N/A{
2N/A const char *ename;
2N/A int rc;
2N/A
2N/A /* cfgname should not have a '/' unless it begins with one */
2N/A if (cfgname[0] == '/') {
2N/A /*
2N/A * if execname does not begin with a '/', prepend the
2N/A * current directory.
2N/A */
2N/A if (execname[0] != '/') {
2N/A ename = (const char *)strcat(cwd, execname);
2N/A } else
2N/A ename = execname;
2N/A } else { /* simple cfg name */
2N/A if (ename = strrchr(execname, '/'))
2N/A /* execname is a path name - get the base name */
2N/A ename++;
2N/A else
2N/A ename = execname;
2N/A }
2N/A rc = gmatch(ename, cfgname);
2N/A MPSSPRINT(2, (stderr, "gmatch: %s %s %s %d\n",
2N/A cfgname, ename, execname, rc));
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * Check if string matches any of exec arguments.
2N/A */
2N/Astatic int
2N/Aargmatch(char *str, FILE *errfp)
2N/A{
2N/A int fd;
2N/A psinfo_t pi;
2N/A int rc = 0;
2N/A int arg;
2N/A char **argv;
2N/A
2N/A fd = open("/proc/self/psinfo", O_RDONLY);
2N/A
2N/A if (fd >= 0) {
2N/A if (read(fd, &pi, sizeof (pi)) == sizeof (pi)) {
2N/A argv = (char **)pi.pr_argv;
2N/A argv++;
2N/A MPSSPRINT(2, (stderr, "argmatch: %s ", str));
2N/A for (arg = 1; arg < pi.pr_argc; arg++, argv++) {
2N/A if (rc = gmatch(*argv, str)) {
2N/A MPSSPRINT(2, (stderr, "%s ", *argv));
2N/A break;
2N/A }
2N/A }
2N/A MPSSPRINT(2, (stderr, "%d\n", rc));
2N/A } else {
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: /proc/self/psinfo read failed [%s]\n"),
2N/A mpssident, strerror(errno));
2N/A }
2N/A (void) close(fd);
2N/A } else {
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: /proc/self/psinfo open failed [%s]\n"),
2N/A mpssident, strerror(errno));
2N/A }
2N/A return (rc);
2N/A}
2N/A
2N/Astatic int
2N/Aempty(char *str)
2N/A{
2N/A char c;
2N/A
2N/A while ((c = *str) == '\n' || c == ' ' || c == '\t')
2N/A str++;
2N/A return (*str == '\0');
2N/A}
2N/A
2N/Avoid
2N/A__mpssmain()
2N/A{
2N/A static size_t heapsz = (size_t)-1, stacksz = (size_t)-1, sz;
2N/A char *cfgfile, *errfile;
2N/A const char *execname;
2N/A char *cwd;
2N/A int cwdlen;
2N/A FILE *fp = NULL, *errfp = NULL;
2N/A char *tok, *tokheap = NULL, *tokstack = NULL, *tokarg;
2N/A char *str, *envheap, *envstack;
2N/A int lineno = 0;
2N/A char *locale;
2N/A
2N/A /*
2N/A * If a private error file is indicated then set the locale
2N/A * for error messages for the duration of this routine.
2N/A * Error messages destined for syslog should not be translated
2N/A * and thus come from the default C locale.
2N/A */
2N/A if ((errfile = getenv(ENV_MPSSERRFILE)) != NULL) {
2N/A errfp = fopen(errfile, "aF");
2N/A if (errfp) {
2N/A locale = setlocale(LC_MESSAGES, "");
2N/A } else {
2N/A mpsserr(NULL, dgettext(TEXT_DOMAIN,
2N/A "%s: cannot open error file: %s [%s]\n"),
2N/A mpssident, errfile, strerror(errno));
2N/A }
2N/A }
2N/A
2N/A#ifdef MPSSDEBUG
2N/A if (str = getenv(ENV_MPSSDEBUG))
2N/A mpssdebug = atosz(str);
2N/A#endif
2N/A
2N/A pgszinit();
2N/A
2N/A if (envstack = getenv(ENV_MPSSSTACK)) {
2N/A sz = atosz(envstack);
2N/A if (pgszok(sz))
2N/A stacksz = sz;
2N/A else
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: invalid stack page size specified:"
2N/A " MPSSSTACK=%s\n"),
2N/A mpssident, envstack);
2N/A }
2N/A
2N/A if (envheap = getenv(ENV_MPSSHEAP)) {
2N/A sz = atosz(envheap);
2N/A if (pgszok(sz))
2N/A heapsz = sz;
2N/A else
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: invalid heap page size specified:"
2N/A " MPSSHEAP=%s\n"),
2N/A mpssident, envheap);
2N/A }
2N/A
2N/A /*
2N/A * Open specified cfg file or default one.
2N/A */
2N/A if (cfgfile = getenv(ENV_MPSSCFGFILE)) {
2N/A fp = fopen(cfgfile, "rF");
2N/A if (!fp) {
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: cannot open configuration file: %s [%s]\n"),
2N/A mpssident, cfgfile, strerror(errno));
2N/A }
2N/A } else {
2N/A cfgfile = DEF_MPSSCFGFILE;
2N/A fp = fopen(cfgfile, "rF");
2N/A }
2N/A
2N/A execname = mygetexecname();
2N/A
2N/A if (fp) {
2N/A
2N/A cwd = getcwd(pbuf, MAXPATHLEN);
2N/A if (!cwd)
2N/A return;
2N/A
2N/A cwd = strcat(cwd, "/");
2N/A cwdlen = strlen(cwd);
2N/A
2N/A while (fgets(lbuf, MAXLINELEN, fp)) {
2N/A lineno++;
2N/A if (empty(lbuf))
2N/A continue;
2N/A /*
2N/A * Make sure line wasn't truncated.
2N/A */
2N/A if (strlen(lbuf) >= MAXLINELEN - 1) {
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: invalid entry, "
2N/A "line too long - cfgfile:"
2N/A " %s, line: %d\n"),
2N/A mpssident, cfgfile, lineno);
2N/A continue;
2N/A }
2N/A /*
2N/A * parse right to left in case delimiter is
2N/A * in name.
2N/A */
2N/A if (!(tokstack = strrchr(lbuf, CFGDELIMITER))) {
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: no delimiters specified - cfgfile:"
2N/A " %s, line: %d\n"),
2N/A mpssident, cfgfile, lineno);
2N/A continue;
2N/A }
2N/A /* found delimiter in lbuf */
2N/A *tokstack++ = '\0';
2N/A /* remove for error message */
2N/A if (str = strrchr(tokstack, '\n'))
2N/A *str = '\0';
2N/A if (!(tokheap = strrchr(lbuf, CFGDELIMITER))) {
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: invalid entry, "
2N/A "missing delimiter - cfgfile: %s,"
2N/A " line: %d\n"),
2N/A mpssident, cfgfile, lineno);
2N/A continue;
2N/A }
2N/A *tokheap++ = '\0';
2N/A
2N/A /* exec-args is optional */
2N/A if (tokarg = strrchr(lbuf, ARGDELIMITER)) {
2N/A *tokarg++ = '\0';
2N/A }
2N/A
2N/A tok = lbuf;
2N/A
2N/A if (!fnmatch(execname, tok, cwd)) {
2N/A tokheap = tokstack = tokarg = NULL;
2N/A cwd[cwdlen] = '\0';
2N/A continue;
2N/A }
2N/A
2N/A if (tokarg &&
2N/A !empty(tokarg) &&
2N/A !argmatch(tokarg, errfp)) {
2N/A tokheap = tokstack = tokarg = NULL;
2N/A cwd[cwdlen] = '\0';
2N/A continue;
2N/A }
2N/A
2N/A /* heap token */
2N/A if (empty(tokheap)) {
2N/A /* empty cfg entry */
2N/A heapsz = (size_t)-1;
2N/A } else {
2N/A sz = atosz(tokheap);
2N/A if (pgszok(sz))
2N/A heapsz = sz;
2N/A else {
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: invalid heap page size"
2N/A " specified (%s) for %s - "
2N/A "cfgfile: %s, line: %d\n"),
2N/A mpssident, tokheap,
2N/A execname, cfgfile,
2N/A lineno);
2N/A heapsz = (size_t)-1;
2N/A }
2N/A }
2N/A
2N/A /* stack token */
2N/A if (empty(tokstack)) {
2N/A stacksz = (size_t)-1;
2N/A break;
2N/A } else {
2N/A sz = atosz(tokstack);
2N/A if (pgszok(sz))
2N/A stacksz = sz;
2N/A else {
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: invalid stack page size"
2N/A " specified (%s) for %s - "
2N/A "cfgfile: %s, line: %d\n"),
2N/A mpssident, tokstack,
2N/A execname, cfgfile, lineno);
2N/A stacksz = (size_t)-1;
2N/A }
2N/A }
2N/A break;
2N/A }
2N/A (void) fclose(fp);
2N/A }
2N/A
2N/A if ((heapsz != (size_t)-1) && (pgszset(heapsz, MPSSHEAP) < 0))
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: memcntl() failed [%s]: heap page size (%s)"
2N/A " for %s not set\n"),
2N/A mpssident, strerror(errno), (tokheap) ? tokheap : envheap,
2N/A execname);
2N/A if ((stacksz != (size_t)-1) && (pgszset(stacksz, MPSSSTACK) < 0))
2N/A mpsserr(errfp, dgettext(TEXT_DOMAIN,
2N/A "%s: memcntl() failed [%s]: stack page size (%s)"
2N/A " for %s not set\n"),
2N/A mpssident, strerror(errno), (tokstack) ? tokstack: envstack,
2N/A execname);
2N/A
2N/A if (errfp) {
2N/A (void) fclose(errfp);
2N/A (void) setlocale(LC_MESSAGES, locale);
2N/A } else {
2N/A /* close log file: no-op if nothing logged to syslog */
2N/A closelog();
2N/A }
2N/A}