xref: /illumos-gate/usr/src/cmd/pools/poold/poold.c (revision 4bc0a2ef2b7ba50a7a717e7ddbf31472ad28e358)
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 2005 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 /*
30  * poold - dynamically adjust pool configuration according to load.
31  */
32 #include <errno.h>
33 #include <jni.h>
34 #include <libintl.h>
35 #include <limits.h>
36 #include <link.h>
37 #include <locale.h>
38 #include <poll.h>
39 #include <pool.h>
40 #include <priv.h>
41 #include <pthread.h>
42 #include <signal.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <unistd.h>
48 
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <sys/ucontext.h>
52 #include "utils.h"
53 
54 #define	POOLD_DEF_CLASSPATH	"/usr/lib/pool/JPool.jar"
55 #define	POOLD_DEF_LIBPATH	"/usr/lib/pool"
56 
57 #if defined(sparc)
58 #define	PLAT	"sparc"
59 #else
60 #if defined(i386)
61 #define	PLAT	"i386"
62 #else
63 #error Unrecognized platform.
64 #endif
65 #endif
66 
67 #define	PID_PROPERTY_NAME	"system.poold.pid"
68 
69 #define	CLASS_FIELD_DESC(class_desc)	"L" class_desc ";"
70 
71 #define	LEVEL_CLASS_DESC	"java/util/logging/Level"
72 #define	POOLD_CLASS_DESC	"com/sun/solaris/domain/pools/Poold"
73 #define	SEVERITY_CLASS_DESC	"com/sun/solaris/service/logging/Severity"
74 #define	STRING_CLASS_DESC	"java/lang/String"
75 #define	SYSTEM_CLASS_DESC	"java/lang/System"
76 #define	LOGGER_CLASS_DESC	"java/util/logging/Logger"
77 
78 extern char *optarg;
79 
80 static const char *pname;
81 
82 static enum {
83 	LD_TERMINAL = 1,
84 	LD_SYSLOG,
85 	LD_JAVA
86 } log_dest = LD_SYSLOG;
87 
88 typedef enum {
89 	PGAS_GET_ONLY = 1,
90 	PGAS_GET_AND_SET
91 } pgas_mode_t;
92 
93 static const char PNAME_FMT[] = "%s: ";
94 static const char ERRNO_FMT[] = ": %s";
95 
96 static JavaVM *jvm;
97 static int lflag;
98 
99 static jmethodID log_mid;
100 static jobject severity_err;
101 static jobject severity_notice;
102 static jobject base_log;
103 static jclass poold_class;
104 static jobject poold_instance;
105 static int instance_running;
106 static pthread_mutex_t instance_running_lock = PTHREAD_MUTEX_INITIALIZER;
107 
108 static sigset_t hdl_set;
109 
110 static void pu_die(const char *fmt, ...) __NORETURN;
111 
112 static void
113 usage(void)
114 {
115 	(void) fprintf(stderr, gettext("Usage:\t%s [-l <level>]\n"), pname);
116 
117 	exit(E_USAGE);
118 }
119 
120 static void
121 pu_output(int severity, const char *fmt, va_list alist)
122 {
123 	int err = errno;
124 	char line[255] = "";
125 	jobject jseverity;
126 	jobject jline;
127 	JNIEnv *env;
128 	int detach_required = 0;
129 	if (pname != NULL && log_dest == LD_TERMINAL)
130 		(void) snprintf(line, sizeof (line), gettext(PNAME_FMT), pname);
131 
132 	(void) vsnprintf(line + strlen(line), sizeof (line) - strlen(line),
133 	    fmt, alist);
134 
135 	if (line[strlen(line) - 1] != '\n')
136 		(void) snprintf(line + strlen(line), sizeof (line) -
137 		    strlen(line), gettext(ERRNO_FMT), strerror(err));
138 	else
139 		line[strlen(line) - 1] = 0;
140 
141 	switch (log_dest) {
142 	case LD_TERMINAL:
143 		(void) fprintf(stderr, "%s\n", line);
144 		(void) fflush(stderr);
145 		break;
146 	case LD_SYSLOG:
147 		syslog(LOG_ERR, "%s", line);
148 		break;
149 	case LD_JAVA:
150 		if (severity == LOG_ERR)
151 			jseverity = severity_err;
152 		else
153 			jseverity = severity_notice;
154 
155 		if (jvm) {
156 			(*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2);
157 			if (env == NULL) {
158 				detach_required = 1;
159 				(*jvm)->AttachCurrentThread(jvm,
160 				    (void **)&env, NULL);
161 			}
162 			if ((jline = (*env)->NewStringUTF(env, line)) != NULL)
163 				(*env)->CallVoidMethod(env, base_log, log_mid,
164 				    jseverity, jline);
165 			if (detach_required)
166 				(*jvm)->DetachCurrentThread(jvm);
167 		}
168 	}
169 }
170 
171 /*PRINTFLIKE1*/
172 static void
173 pu_notice(const char *fmt, ...)
174 {
175 	va_list alist;
176 	va_start(alist, fmt);
177 	pu_output(LOG_NOTICE, fmt, alist);
178 	va_end(alist);
179 }
180 
181 /*PRINTFLIKE1*/
182 static void
183 pu_die(const char *fmt, ...)
184 {
185 	va_list alist;
186 	va_start(alist, fmt);
187 	pu_output(LOG_ERR, fmt, alist);
188 	va_end(alist);
189 	exit(E_ERROR);
190 }
191 
192 /*
193  * Update the "system.poold.pid" to reflect this instance of poold only
194  * if the property hasn't been set already to reflect an existing
195  * process, and mode is not set to PGAS_GET_ONLY.  Returns the
196  * property's pre-existing value, or -1 otherwise.
197  */
198 static pid_t
199 poold_get_and_set_pid(pgas_mode_t mode)
200 {
201 	pool_conf_t *conf;
202 	pool_elem_t *pe;
203 	pool_value_t *val;
204 	int64_t ival;
205 
206 	if (!(conf = pool_conf_alloc()))
207 		return ((pid_t)-1);
208 
209 	if (pool_conf_open(conf, pool_dynamic_location(), PO_RDWR) != 0) {
210 		(void) pool_conf_free(conf);
211 		return ((pid_t)-1);
212 	}
213 
214 	pe = pool_conf_to_elem(conf);
215 	if (!(val = pool_value_alloc())) {
216 		(void) pool_conf_close(conf);
217 		return ((pid_t)-1);
218 	}
219 
220 	if (pool_get_property(conf, pe, PID_PROPERTY_NAME, val) == POC_INT) {
221 		if (pool_value_get_int64(val, &ival) != 0) {
222 			(void) pool_value_free(val);
223 			(void) pool_conf_close(conf);
224 			return ((pid_t)-1);
225 		}
226 	} else {
227 		ival = (pid_t)-1;
228 	}
229 
230 	if (mode == PGAS_GET_AND_SET) {
231 		ival = getpid();
232 		pool_value_set_int64(val, ival);
233 		(void) pool_put_property(conf, pe, PID_PROPERTY_NAME, val);
234 		(void) pool_conf_commit(conf, 0);
235 	}
236 
237 	(void) pool_value_free(val);
238 	(void) pool_conf_close(conf);
239 	pool_conf_free(conf);
240 
241 	return ((pid_t)ival);
242 }
243 
244 /*
245  * Reconfigure the JVM by simply updating a dummy property on the
246  * system element to force pool_conf_update() to detect a change.
247  */
248 static void
249 reconfigure()
250 {
251 	JNIEnv *env;
252 	pool_conf_t *conf;
253 	pool_elem_t *pe;
254 	pool_value_t *val;
255 	const char *err_desc;
256 	int detach_required = 0;
257 
258 	if ((conf = pool_conf_alloc()) == NULL) {
259 		err_desc = pool_strerror(pool_error());
260 		goto destroy;
261 	}
262 	if (pool_conf_open(conf, pool_dynamic_location(), PO_RDWR) != 0) {
263 		err_desc = pool_strerror(pool_error());
264 		pool_conf_free(conf);
265 		goto destroy;
266 	}
267 
268 	if ((val = pool_value_alloc()) == NULL) {
269 		err_desc = pool_strerror(pool_error());
270 		(void) pool_conf_close(conf);
271 		pool_conf_free(conf);
272 		goto destroy;
273 	}
274 	pe = pool_conf_to_elem(conf);
275 	pool_value_set_bool(val, 1);
276 	if (pool_put_property(conf, pe, "system.poold.sighup", val) !=
277 	    PO_SUCCESS) {
278 		err_desc = pool_strerror(pool_error());
279 		pool_value_free(val);
280 		(void) pool_conf_close(conf);
281 		pool_conf_free(conf);
282 		goto destroy;
283 	}
284 	pool_value_free(val);
285 	(void) pool_rm_property(conf, pe, "system.poold.sighup");
286 	if (pool_conf_commit(conf, 0) != PO_SUCCESS) {
287 		err_desc = pool_strerror(pool_error());
288 		(void) pool_conf_close(conf);
289 		pool_conf_free(conf);
290 		goto destroy;
291 	}
292 	(void) pool_conf_close(conf);
293 	pool_conf_free(conf);
294 	return;
295 destroy:
296 	if (jvm) {
297 		(*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2);
298 		if (env == NULL) {
299 			detach_required = 1;
300 			(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
301 		}
302 		if (lflag && (*env)->ExceptionOccurred(env))
303 			(*env)->ExceptionDescribe(env);
304 		if (detach_required)
305 			(*jvm)->DetachCurrentThread(jvm);
306 		(*jvm)->DestroyJavaVM(jvm);
307 	}
308 	pu_die(err_desc);
309 }
310 
311 /*
312  * If SIGHUP is invoked, we should just re-initialize poold. Since
313  * there is no easy way to determine when it's safe to re-initialzie
314  * poold, simply update a dummy property on the system element to
315  * force pool_conf_update() to detect a change.
316  *
317  * Both SIGTERM and SIGINT are interpreted as instructions to
318  * shutdown.
319  */
320 /*ARGSUSED*/
321 static void *
322 handle_sig(void *arg)
323 {
324 	for (;;) {
325 		JNIEnv *env;
326 		jmethodID poold_shutdown_mid;
327 		int sig;
328 		char buf[SIG2STR_MAX];
329 		int detach_required = 0;
330 
331 		if ((sig = sigwait(&hdl_set)) < 0)
332 			pu_die("unexpected error: %d\n", errno);
333 		(void) sig2str(sig, buf);
334 		switch (sig) {
335 		case SIGHUP:
336 			reconfigure();
337 			break;
338 		case SIGINT:
339 		case SIGTERM:
340 			(void) pthread_mutex_lock(&instance_running_lock);
341 			if (instance_running) {
342 				(void) pthread_mutex_unlock(
343 				    &instance_running_lock);
344 				(*jvm)->GetEnv(jvm, (void **)&env,
345 				    JNI_VERSION_1_2);
346 				if (env == NULL) {
347 					detach_required = 1;
348 					(*jvm)->AttachCurrentThread(jvm,
349 					    (void **)&env, NULL);
350 				}
351 				pu_notice("terminating due to signal: SIG%s\n",
352 				    buf);
353 				if ((poold_shutdown_mid = (*env)->GetMethodID(
354 				    env, poold_class, "shutdown", "()V")) !=
355 				    NULL) {
356 					(*env)->CallVoidMethod(env,
357 					    poold_instance,
358 					    poold_shutdown_mid);
359 				} else {
360 					(*env)->ExceptionDescribe(env);
361 					pu_die("could not invoke"
362 					    " proper shutdown\n");
363 				}
364 				if (detach_required)
365 					(*jvm)->DetachCurrentThread(jvm);
366 			} else {
367 				(void) pthread_mutex_unlock(
368 				    &instance_running_lock);
369 				pu_die("terminated with signal: SIG%s\n", buf);
370 				/*NOTREACHED*/
371 			}
372 			break;
373 		default:
374 			pu_die("unexpected signal: SIG%s\n", buf);
375 		}
376 	}
377 	/*NOTREACHED*/
378 	return (NULL);
379 }
380 
381 static const char *
382 pu_getpname(const char *arg0)
383 {
384 	char *p;
385 
386 	/*
387 	 * Guard against '/' at end of command invocation.
388 	 */
389 	for (;;) {
390 		p = strrchr(arg0, '/');
391 		if (p == NULL) {
392 			pname = arg0;
393 			break;
394 		} else {
395 			if (*(p + 1) == '\0') {
396 				*p = '\0';
397 				continue;
398 			}
399 
400 			pname = p + 1;
401 			break;
402 		}
403 	}
404 
405 	return (pname);
406 }
407 
408 int
409 main(int argc, char *argv[])
410 {
411 	char c;
412 	char log_severity[16] = "";
413 	pid_t pid;
414 	JavaVMInitArgs vm_args;
415 	JavaVMOption vm_opts[5];
416 	int nopts = 0;
417 	const char *classpath;
418 	const char *libpath;
419 	size_t len;
420 	const char *err_desc;
421 	JNIEnv *env;
422 	jmethodID poold_getinstancewcl_mid;
423 	jmethodID poold_run_mid;
424 	jobject log_severity_string = NULL;
425 	jobject log_severity_obj = NULL;
426 	jclass severity_class;
427 	jmethodID severity_cons_mid;
428 	jfieldID base_log_fid;
429 	int explain_ex = 1;
430 	JavaVM *jvm_tmp;
431 	pthread_t hdl_thread;
432 
433 	pname = pu_getpname(argv[0]);
434 	openlog(pname, 0, LOG_DAEMON);
435 	(void) chdir("/");
436 
437 	(void) setlocale(LC_ALL, "");
438 #if !defined(TEXT_DOMAIN)		/* Should be defined with cc -D. */
439 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't. */
440 #endif
441 	(void) textdomain(TEXT_DOMAIN);
442 
443 	opterr = 0;
444 	while ((c = getopt(argc, argv, "l:P")) != EOF) {
445 		switch (c) {
446 		case 'l':	/* -l option */
447 			lflag++;
448 			(void) strlcpy(log_severity, optarg,
449 			    sizeof (log_severity));
450 			log_dest = LD_TERMINAL;
451 			break;
452 		default:
453 			usage();
454 			/*NOTREACHED*/
455 		}
456 	}
457 
458 	/*
459 	 * Verify no other poold is running.  This condition is checked
460 	 * again later, but should be checked now since it is more
461 	 * serious (i.e.  should be reported before) than a lack of
462 	 * privileges.
463 	 */
464 	if (((pid = poold_get_and_set_pid(PGAS_GET_ONLY)) != (pid_t)-1) &&
465 	    pid != getpid() && kill(pid, 0) == 0)
466 		pu_die(gettext("poold is already active (process %ld)\n"), pid);
467 
468 	/*
469 	 * Check permission
470 	 */
471 	if (!priv_ineffect(PRIV_SYS_RES_CONFIG))
472 		pu_die(gettext(ERR_PRIVILEGE), PRIV_SYS_RES_CONFIG);
473 
474 	/*
475 	 * Establish the classpath and LD_LIBRARY_PATH for native
476 	 * methods, and get the interpreter going.
477 	 */
478 	if ((classpath = getenv("POOLD_CLASSPATH")) == NULL) {
479 		classpath = POOLD_DEF_CLASSPATH;
480 	} else {
481 		const char *cur = classpath;
482 
483 		/*
484 		 * Check the components to make sure they're absolute
485 		 * paths.
486 		 */
487 		while (cur != NULL && *cur) {
488 			if (*cur != '/')
489 				pu_die(gettext(
490 				    "POOLD_CLASSPATH must contain absolute "
491 				    "components\n"));
492 			cur = strchr(cur + 1, ':');
493 		}
494 	}
495 	vm_opts[nopts].optionString = malloc(len = strlen(classpath) +
496 	    strlen("-Djava.class.path=") + 1);
497 	(void) strlcpy(vm_opts[nopts].optionString, "-Djava.class.path=", len);
498 	(void) strlcat(vm_opts[nopts++].optionString, classpath, len);
499 
500 	if ((libpath = getenv("POOLD_LD_LIBRARY_PATH")) == NULL)
501 		libpath = POOLD_DEF_LIBPATH;
502 	vm_opts[nopts].optionString = malloc(len = strlen(libpath) +
503 	    strlen("-Djava.library.path=") + 1);
504 	(void) strlcpy(vm_opts[nopts].optionString, "-Djava.library.path=",
505 	    len);
506 	(void) strlcat(vm_opts[nopts++].optionString, libpath, len);
507 
508 	vm_opts[nopts++].optionString = "-Xrs";
509 	vm_opts[nopts++].optionString = "-enableassertions";
510 
511 	vm_args.options = vm_opts;
512 	vm_args.nOptions = nopts;
513 	vm_args.ignoreUnrecognized = JNI_FALSE;
514 	vm_args.version = 0x00010002;
515 
516 	/*
517 	 * XXX - Forking after the VM is created is desirable to
518 	 * guarantee reporting of errors, but cannot be done (see
519 	 * 4919246).
520 	 *
521 	 * If invoked by libpool(3LIB), it's set the system.poold.pid
522 	 * property and forked already.  If invoked directly and -l is
523 	 * specified, forking is not desired.
524 	 */
525 	if (!lflag && pid != getpid())
526 		switch (fork()) {
527 		case 0:
528 			(void) setsid();
529 			(void) fclose(stdin);
530 			(void) fclose(stdout);
531 			(void) fclose(stderr);
532 			break;
533 		case -1:
534 			pu_die(gettext("cannot fork"));
535 			/*NOTREACHED*/
536 		default:
537 			return (E_PO_SUCCESS);
538 		}
539 
540 	/*
541 	 * In order to avoid problems with arbitrary thread selection
542 	 * when handling asynchronous signals, dedicate a thread to
543 	 * look after these signals.
544 	 */
545 	if (sigemptyset(&hdl_set) < 0 ||
546 	    sigaddset(&hdl_set, SIGHUP) < 0 ||
547 	    sigaddset(&hdl_set, SIGTERM) < 0 ||
548 	    sigaddset(&hdl_set, SIGINT) < 0 ||
549 	    pthread_sigmask(SIG_BLOCK, &hdl_set, NULL) ||
550 	    pthread_create(&hdl_thread, NULL, handle_sig, NULL))
551 		pu_die(gettext("can't install signal handler"));
552 
553 	/*
554 	 * Use jvm_tmp when creating the jvm to prevent race
555 	 * conditions with signal handlers. As soon as the call
556 	 * returns, assign the global jvm to jvm_tmp.
557 	 */
558 	if (JNI_CreateJavaVM(&jvm_tmp, (void **)&env, &vm_args) < 0)
559 		pu_die(gettext("can't create Java VM"));
560 	jvm = jvm_tmp;
561 
562 	/*
563 	 * Locate the Poold class and construct an instance.  A side
564 	 * effect of this is that the poold instance's logHelper will be
565 	 * initialized, establishing loggers for logging errors from
566 	 * this point on.  (Note, in the event of an unanticipated
567 	 * exception, poold will invoke die() itself.)
568 	 */
569 	err_desc = gettext("JVM-related error initializing poold\n");
570 	if ((poold_class = (*env)->FindClass(env, POOLD_CLASS_DESC)) == NULL)
571 		goto destroy;
572 	if ((poold_getinstancewcl_mid = (*env)->GetStaticMethodID(env,
573 	    poold_class, "getInstanceWithConsoleLogging", "("
574 	    CLASS_FIELD_DESC(SEVERITY_CLASS_DESC) ")"
575 	    CLASS_FIELD_DESC(POOLD_CLASS_DESC))) == NULL)
576 		goto destroy;
577 	if ((poold_run_mid = (*env)->GetMethodID(env, poold_class, "run",
578 	    "()V")) == NULL)
579 		goto destroy;
580 	if ((severity_class = (*env)->FindClass(env, SEVERITY_CLASS_DESC))
581 	    == NULL)
582 		goto destroy;
583 	if ((severity_cons_mid = (*env)->GetStaticMethodID(env, severity_class,
584 	    "getSeverityWithName", "(" CLASS_FIELD_DESC(STRING_CLASS_DESC) ")"
585 	    CLASS_FIELD_DESC(SEVERITY_CLASS_DESC))) == NULL)
586 		goto destroy;
587 
588 	/*
589 	 * -l <level> was specified, indicating that messages are to be
590 	 * logged to the console only.
591 	 */
592 	if (strlen(log_severity) > 0) {
593 		if ((log_severity_string = (*env)->NewStringUTF(env,
594 		    log_severity)) == NULL)
595 			goto destroy;
596 		if ((log_severity_obj = (*env)->CallStaticObjectMethod(env,
597 		    severity_class, severity_cons_mid, log_severity_string)) ==
598 		    NULL) {
599 			err_desc = gettext("invalid level specified\n");
600 			explain_ex = 0;
601 			goto destroy;
602 		}
603 	} else
604 		log_severity_obj = NULL;
605 
606 	if ((poold_instance = (*env)->CallStaticObjectMethod(env, poold_class,
607 	    poold_getinstancewcl_mid, log_severity_obj)) == NULL)
608 		goto destroy;
609 
610 	/*
611 	 * Grab a global reference to poold for use in our signal
612 	 * handlers.
613 	 */
614 	poold_instance = (*env)->NewGlobalRef(env, poold_instance);
615 
616 	/*
617 	 * Ready LD_JAVA logging.
618 	 */
619 	err_desc = gettext("cannot initialize logging\n");
620 	if ((log_severity_string = (*env)->NewStringUTF(env, "err")) == NULL)
621 		goto destroy;
622 	if (!(severity_err = (*env)->CallStaticObjectMethod(env, severity_class,
623 	    severity_cons_mid, log_severity_string)))
624 		goto destroy;
625 	if (!(severity_err = (*env)->NewGlobalRef(env, severity_err)))
626 		goto destroy;
627 
628 	if ((log_severity_string = (*env)->NewStringUTF(env, "notice")) == NULL)
629 		goto destroy;
630 	if (!(severity_notice = (*env)->CallStaticObjectMethod(env,
631 	    severity_class, severity_cons_mid, log_severity_string)))
632 		goto destroy;
633 	if (!(severity_notice = (*env)->NewGlobalRef(env, severity_notice)))
634 		goto destroy;
635 
636 	if (!(base_log_fid = (*env)->GetStaticFieldID(env, poold_class,
637 	    "BASE_LOG", CLASS_FIELD_DESC(LOGGER_CLASS_DESC))))
638 		goto destroy;
639 	if (!(base_log = (*env)->GetStaticObjectField(env, poold_class,
640 	    base_log_fid)))
641 		goto destroy;
642 	if (!(base_log = (*env)->NewGlobalRef(env, base_log)))
643 		goto destroy;
644 	if (!(log_mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env,
645 	    base_log), "log", "(" CLASS_FIELD_DESC(LEVEL_CLASS_DESC)
646 	    CLASS_FIELD_DESC(STRING_CLASS_DESC) ")V")))
647 		goto destroy;
648 	log_dest = LD_JAVA;
649 
650 	/*
651 	 * Now we're ready to start poold.  Store our pid in the pools
652 	 * configuration to mark that an instance of poold is active,
653 	 * then invoke Poold.run(), which does not normally return.
654 	 *
655 	 * Note that the ignoreUpdates variable in Poold is used to
656 	 * allow Poold to ignore the pools configuration update that
657 	 * this change triggers. If this code is ever modified to
658 	 * remove or modify this logic, then the Poold class must also
659 	 * be modified to keep the actions synchronized.
660 	 */
661 
662 	if (((pid = poold_get_and_set_pid(PGAS_GET_AND_SET)) != (pid_t)-1) &&
663 	    pid != getpid() && kill(pid, 0) == 0)
664 		pu_die(gettext("poold is already active (process %ld)\n"), pid);
665 
666 	(void) pthread_mutex_lock(&instance_running_lock);
667 	instance_running = 1;
668 	(void) pthread_mutex_unlock(&instance_running_lock);
669 	(*env)->CallVoidMethod(env, poold_instance, poold_run_mid);
670 
671 	if ((*env)->ExceptionOccurred(env))
672 		goto destroy;
673 
674 	(*jvm)->DestroyJavaVM(jvm);
675 	return (E_PO_SUCCESS);
676 
677 destroy:
678 	if (lflag && explain_ex && (*env)->ExceptionOccurred(env))
679 		(*env)->ExceptionDescribe(env);
680 	(*jvm)->DestroyJavaVM(jvm);
681 	pu_die(err_desc);
682 }
683