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