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