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
mpsserr(FILE * fp,char * fmt,...)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 *
mygetexecname(void)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
atosz(char * szstr)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
pgszok(size_t sz)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
pgszinit()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
pgszset(size_t sz,uint_t flags)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
fnmatch(const char * execname,char * cfgname,char * cwd)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
argmatch(char * str,FILE * errfp)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
empty(char * str)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
__mpssmain()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