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