1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <strings.h> 32 #include <sys/mman.h> 33 #include <fcntl.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <errno.h> 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/auxv.h> 40 #include <stdarg.h> 41 #include <syslog.h> 42 #include <sys/param.h> 43 #include <sys/sysmacros.h> 44 #include <procfs.h> 45 #include <libintl.h> 46 #include <locale.h> 47 48 extern int gmatch(const char *s, const char *p); 49 50 #pragma init(__mpssmain) 51 52 static const char *mpssident = "mpss.so.1"; 53 54 /* environment variables */ 55 56 #define ENV_MPSSCFGFILE "MPSSCFGFILE" 57 #define ENV_MPSSSTACK "MPSSSTACK" 58 #define ENV_MPSSHEAP "MPSSHEAP" 59 #define ENV_MPSSERRFILE "MPSSERRFILE" 60 61 #define MPSSHEAP 0 62 #define MPSSSTACK 1 63 64 /* config file */ 65 66 #define DEF_MPSSCFGFILE "/etc/mpss.conf" 67 #define MAXLINELEN MAXPATHLEN + 64 68 #define CFGDELIMITER ':' 69 #define ARGDELIMITER ' ' 70 71 /* 72 * avoid malloc which causes certain applications to crash 73 */ 74 static char lbuf[MAXLINELEN]; 75 static char pbuf[MAXPATHLEN]; 76 77 #ifdef MPSSDEBUG 78 #define ENV_MPSSDEBUG "MPSSDEBUG" 79 #define MPSSPRINT(x, y) if (mpssdebug & x) (void) fprintf y; 80 81 static int mpssdebug; 82 #else 83 #define MPSSPRINT(x, y) 84 #endif 85 86 #if !defined(TEXT_DOMAIN) 87 #define TEXT_DOMAIN "SYS_TEST" 88 #endif 89 90 /*PRINTFLIKE2*/ 91 static void 92 mpsserr(FILE *fp, char *fmt, ...) 93 { 94 va_list ap; 95 va_start(ap, fmt); 96 if (fp) 97 (void) vfprintf(fp, fmt, ap); 98 else 99 vsyslog(LOG_ERR, fmt, ap); 100 va_end(ap); 101 } 102 103 /* 104 * Return the pointer to the fully-resolved path name of the process's 105 * executable file obtained from the AT_SUN_EXECNAME aux vector entry. 106 */ 107 static const char * 108 mygetexecname(void) 109 { 110 const char *execname = NULL; 111 static auxv_t auxb; 112 113 /* 114 * The first time through, read the initial aux vector that was 115 * passed to the process at exec(2). Only do this once. 116 */ 117 int fd = open("/proc/self/auxv", O_RDONLY); 118 119 if (fd >= 0) { 120 while (read(fd, &auxb, sizeof (auxv_t)) == sizeof (auxv_t)) { 121 if (auxb.a_type == AT_SUN_EXECNAME) { 122 execname = auxb.a_un.a_ptr; 123 break; 124 } 125 } 126 (void) close(fd); 127 } 128 return (execname); 129 } 130 131 static size_t 132 atosz(char *szstr) 133 { 134 size_t sz; 135 char *endptr, c; 136 137 sz = strtoll(szstr, &endptr, 0); 138 139 while (c = *endptr++) { 140 switch (c) { 141 case 't': 142 case 'T': 143 sz *= 1024; 144 /*FALLTHRU*/ 145 case 'g': 146 case 'G': 147 sz *= 1024; 148 /*FALLTHRU*/ 149 case 'm': 150 case 'M': 151 sz *= 1024; 152 /*FALLTHRU*/ 153 case 'k': 154 case 'K': 155 sz *= 1024; 156 default: 157 break; 158 } 159 } 160 return (sz); 161 162 } 163 164 #define PGSZELEM (8 * sizeof (void *)) 165 static size_t pgsz[PGSZELEM]; 166 static int nelem; 167 168 static int 169 pgszok(size_t sz) 170 { 171 int i; 172 173 if (sz == 0) 174 return (1); 175 176 for (i = 0; i < nelem; i++) { 177 if (sz == pgsz[i]) 178 break; 179 } 180 181 return (i < nelem); 182 } 183 184 static void 185 pgszinit() 186 { 187 nelem = getpagesizes(NULL, 0); 188 189 if (!nelem) 190 return; 191 192 if (nelem > PGSZELEM) 193 nelem = PGSZELEM; 194 195 (void) getpagesizes(pgsz, nelem); 196 #ifdef MPSSDEBUG 197 pgsz[nelem] = 0x800000; 198 nelem++; 199 #endif 200 } 201 202 203 static int 204 pgszset(size_t sz, uint_t flags) 205 { 206 struct memcntl_mha mpss; 207 int rc; 208 209 mpss.mha_cmd = (flags == MPSSHEAP) ? 210 MHA_MAPSIZE_BSSBRK: MHA_MAPSIZE_STACK; 211 mpss.mha_pagesize = sz; 212 mpss.mha_flags = 0; 213 rc = memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mpss, 0, 0); 214 215 return (rc); 216 } 217 218 /* 219 * check if exec name matches cfgname found in mpss cfg file. 220 */ 221 static int 222 fnmatch(const char *execname, char *cfgname, char *cwd) 223 { 224 const char *ename; 225 int rc; 226 227 /* cfgname should not have a '/' unless it begins with one */ 228 if (cfgname[0] == '/') { 229 /* 230 * if execname does not begin with a '/', prepend the 231 * current directory. 232 */ 233 if (execname[0] != '/') { 234 ename = (const char *)strcat(cwd, execname); 235 } else 236 ename = execname; 237 } else { /* simple cfg name */ 238 if (ename = strrchr(execname, '/')) 239 /* execname is a path name - get the base name */ 240 ename++; 241 else 242 ename = execname; 243 } 244 rc = gmatch(ename, cfgname); 245 MPSSPRINT(2, (stderr, "gmatch: %s %s %s %d\n", 246 cfgname, ename, execname, rc)); 247 248 return (rc); 249 } 250 251 /* 252 * Check if string matches any of exec arguments. 253 */ 254 static int 255 argmatch(char *str, FILE *errfp) 256 { 257 int fd; 258 psinfo_t pi; 259 int rc = 0; 260 int arg; 261 char **argv; 262 263 fd = open("/proc/self/psinfo", O_RDONLY); 264 265 if (fd >= 0) { 266 if (read(fd, &pi, sizeof (pi)) == sizeof (pi)) { 267 argv = (char **)pi.pr_argv; 268 argv++; 269 MPSSPRINT(2, (stderr, "argmatch: %s ", str)); 270 for (arg = 1; arg < pi.pr_argc; arg++, argv++) { 271 if (rc = gmatch(*argv, str)) { 272 MPSSPRINT(2, (stderr, "%s ", *argv)); 273 break; 274 } 275 } 276 MPSSPRINT(2, (stderr, "%d\n", rc)); 277 } else { 278 mpsserr(errfp, dgettext(TEXT_DOMAIN, 279 "%s: /proc/self/psinfo read failed [%s]\n"), 280 mpssident, strerror(errno)); 281 } 282 (void) close(fd); 283 } else { 284 mpsserr(errfp, dgettext(TEXT_DOMAIN, 285 "%s: /proc/self/psinfo open failed [%s]\n"), 286 mpssident, strerror(errno)); 287 } 288 return (rc); 289 } 290 291 static int 292 empty(char *str) 293 { 294 char c; 295 296 while ((c = *str) == '\n' || c == ' ' || c == '\t') 297 str++; 298 return (*str == '\0'); 299 } 300 301 void 302 __mpssmain() 303 { 304 static size_t heapsz = (size_t)-1, stacksz = (size_t)-1, sz; 305 char *cfgfile, *errfile; 306 const char *execname; 307 char *cwd; 308 int cwdlen; 309 FILE *fp = NULL, *errfp = NULL; 310 char *tok, *tokheap = NULL, *tokstack = NULL, *tokarg; 311 char *str, *envheap, *envstack; 312 int lineno = 0; 313 char *locale; 314 315 /* 316 * If a private error file is indicated then set the locale 317 * for error messages for the duration of this routine. 318 * Error messages destined for syslog should not be translated 319 * and thus come from the default C locale. 320 */ 321 if ((errfile = getenv(ENV_MPSSERRFILE)) != NULL) { 322 errfp = fopen(errfile, "a"); 323 if (errfp) { 324 locale = setlocale(LC_MESSAGES, ""); 325 } else { 326 mpsserr(NULL, dgettext(TEXT_DOMAIN, 327 "%s: cannot open error file: %s [%s]\n"), 328 mpssident, errfile, strerror(errno)); 329 } 330 } 331 332 #ifdef MPSSDEBUG 333 if (str = getenv(ENV_MPSSDEBUG)) 334 mpssdebug = atosz(str); 335 #endif 336 337 pgszinit(); 338 339 if (envstack = getenv(ENV_MPSSSTACK)) { 340 sz = atosz(envstack); 341 if (pgszok(sz)) 342 stacksz = sz; 343 else 344 mpsserr(errfp, dgettext(TEXT_DOMAIN, 345 "%s: invalid stack page size specified:" 346 " MPSSSTACK=%s\n"), 347 mpssident, envstack); 348 } 349 350 if (envheap = getenv(ENV_MPSSHEAP)) { 351 sz = atosz(envheap); 352 if (pgszok(sz)) 353 heapsz = sz; 354 else 355 mpsserr(errfp, dgettext(TEXT_DOMAIN, 356 "%s: invalid heap page size specified:" 357 " MPSSHEAP=%s\n"), 358 mpssident, envheap); 359 } 360 361 /* 362 * Open specified cfg file or default one. 363 */ 364 if (cfgfile = getenv(ENV_MPSSCFGFILE)) { 365 fp = fopen(cfgfile, "r"); 366 if (!fp) { 367 mpsserr(errfp, dgettext(TEXT_DOMAIN, 368 "%s: cannot open configuration file: %s [%s]\n"), 369 mpssident, cfgfile, strerror(errno)); 370 } 371 } else { 372 cfgfile = DEF_MPSSCFGFILE; 373 fp = fopen(cfgfile, "r"); 374 } 375 376 execname = mygetexecname(); 377 378 if (fp) { 379 380 cwd = getcwd(pbuf, MAXPATHLEN); 381 if (!cwd) 382 return; 383 384 cwd = strcat(cwd, "/"); 385 cwdlen = strlen(cwd); 386 387 while (fgets(lbuf, MAXLINELEN, fp)) { 388 lineno++; 389 if (empty(lbuf)) 390 continue; 391 /* 392 * Make sure line wasn't truncated. 393 */ 394 if (strlen(lbuf) >= MAXLINELEN - 1) { 395 mpsserr(errfp, dgettext(TEXT_DOMAIN, 396 "%s: invalid entry, " 397 "line too long - cfgfile:" 398 " %s, line: %d\n"), 399 mpssident, cfgfile, lineno); 400 continue; 401 } 402 /* 403 * parse right to left in case delimiter is 404 * in name. 405 */ 406 if (!(tokstack = strrchr(lbuf, CFGDELIMITER))) { 407 mpsserr(errfp, dgettext(TEXT_DOMAIN, 408 "%s: no delimiters specified - cfgfile:" 409 " %s, line: %d\n"), 410 mpssident, cfgfile, lineno); 411 continue; 412 } 413 /* found delimiter in lbuf */ 414 *tokstack++ = '\0'; 415 /* remove for error message */ 416 if (str = strrchr(tokstack, '\n')) 417 *str = '\0'; 418 if (!(tokheap = strrchr(lbuf, CFGDELIMITER))) { 419 mpsserr(errfp, dgettext(TEXT_DOMAIN, 420 "%s: invalid entry, " 421 "missing delimiter - cfgfile: %s," 422 " line: %d\n"), 423 mpssident, cfgfile, lineno); 424 continue; 425 } 426 *tokheap++ = '\0'; 427 428 /* exec-args is optional */ 429 if (tokarg = strrchr(lbuf, ARGDELIMITER)) { 430 *tokarg++ = '\0'; 431 } 432 433 tok = lbuf; 434 435 if (!fnmatch(execname, tok, cwd)) { 436 tokheap = tokstack = tokarg = NULL; 437 cwd[cwdlen] = '\0'; 438 continue; 439 } 440 441 if (tokarg && 442 !empty(tokarg) && 443 !argmatch(tokarg, errfp)) { 444 tokheap = tokstack = tokarg = NULL; 445 cwd[cwdlen] = '\0'; 446 continue; 447 } 448 449 /* heap token */ 450 if (empty(tokheap)) { 451 /* empty cfg entry */ 452 heapsz = (size_t)-1; 453 } else { 454 sz = atosz(tokheap); 455 if (pgszok(sz)) 456 heapsz = sz; 457 else { 458 mpsserr(errfp, dgettext(TEXT_DOMAIN, 459 "%s: invalid heap page size" 460 " specified (%s) for %s - " 461 "cfgfile: %s, line: %d\n"), 462 mpssident, tokheap, 463 execname, cfgfile, 464 lineno); 465 heapsz = (size_t)-1; 466 } 467 } 468 469 /* stack token */ 470 if (empty(tokstack)) { 471 stacksz = (size_t)-1; 472 break; 473 } else { 474 sz = atosz(tokstack); 475 if (pgszok(sz)) 476 stacksz = sz; 477 else { 478 mpsserr(errfp, dgettext(TEXT_DOMAIN, 479 "%s: invalid stack page size" 480 " specified (%s) for %s - " 481 "cfgfile: %s, line: %d\n"), 482 mpssident, tokstack, 483 execname, cfgfile, lineno); 484 stacksz = (size_t)-1; 485 } 486 } 487 break; 488 } 489 (void) fclose(fp); 490 } 491 492 if ((heapsz != (size_t)-1) && (pgszset(heapsz, MPSSHEAP) < 0)) 493 mpsserr(errfp, dgettext(TEXT_DOMAIN, 494 "%s: memcntl() failed [%s]: heap page size (%s)" 495 " for %s not set\n"), 496 mpssident, strerror(errno), (tokheap) ? tokheap : envheap, 497 execname); 498 if ((stacksz != (size_t)-1) && (pgszset(stacksz, MPSSSTACK) < 0)) 499 mpsserr(errfp, dgettext(TEXT_DOMAIN, 500 "%s: memcntl() failed [%s]: stack page size (%s)" 501 " for %s not set\n"), 502 mpssident, strerror(errno), (tokstack) ? tokstack: envstack, 503 execname); 504 505 if (errfp) { 506 (void) fclose(errfp); 507 (void) setlocale(LC_MESSAGES, locale); 508 } else { 509 /* close log file: no-op if nothing logged to syslog */ 510 closelog(); 511 } 512 } 513