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.
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.
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]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * poold - dynamically adjust pool configuration according to load.
47 #include <sys/types.h>
48 #include <sys/ucontext.h>
51 #define POOLD_DEF_CLASSPATH "/usr/lib/pool/JPool.jar"
52 #define POOLD_DEF_LIBPATH "/usr/lib/pool"
53 #define SMF_SVC_INSTANCE "svc:/system/pools/dynamic:default"
55 #define CLASS_FIELD_DESC(class_desc) "L" class_desc ";"
57 #define LEVEL_CLASS_DESC "java/util/logging/Level"
58 #define POOLD_CLASS_DESC "com/sun/solaris/domain/pools/Poold"
59 #define SEVERITY_CLASS_DESC "com/sun/solaris/service/logging/Severity"
60 #define STRING_CLASS_DESC "java/lang/String"
61 #define SYSTEM_CLASS_DESC "java/lang/System"
62 #define LOGGER_CLASS_DESC "java/util/logging/Logger"
66 static const char *pname
;
72 } log_dest
= LD_SYSLOG
;
74 static const char PNAME_FMT
[] = "%s: ";
75 static const char ERRNO_FMT
[] = ": %s";
77 static pthread_mutex_t jvm_lock
= PTHREAD_MUTEX_INITIALIZER
;
78 static JavaVM
*jvm
; /* protected by jvm_lock */
79 static int instance_running
; /* protected by jvm_lock */
80 static int lflag
; /* specifies poold logging mode */
82 static jmethodID log_mid
;
83 static jobject severity_err
;
84 static jobject severity_notice
;
85 static jobject base_log
;
86 static jclass poold_class
;
87 static jobject poold_instance
;
89 static sigset_t hdl_set
;
91 static void pu_notice(const char *fmt
, ...);
92 static void pu_die(const char *fmt
, ...) __NORETURN
;
97 (void) fprintf(stderr
, gettext("Usage:\t%s [-l <level>]\n"), pname
);
103 check_thread_attached(JNIEnv
**env
)
107 ret
= (*jvm
)->GetEnv(jvm
, (void **)env
, JNI_VERSION_1_4
);
109 if (ret
== JNI_EVERSION
) {
111 * Avoid recursively calling
112 * check_thread_attached()
114 if (log_dest
== LD_JAVA
)
115 log_dest
= LD_TERMINAL
;
116 pu_notice(gettext("incorrect JNI version"));
119 if ((*jvm
)->AttachCurrentThreadAsDaemon(jvm
, (void **)env
,
122 * Avoid recursively calling
123 * check_thread_attached()
125 if (log_dest
== LD_JAVA
)
126 log_dest
= LD_TERMINAL
;
127 pu_notice(gettext("thread attach failed"));
134 * Output a message to the designated logging destination.
136 * severity - Specified the severity level when using LD_JAVA logging
137 * fmt - specified the format of the output message
138 * alist - varargs used in the output message
141 pu_output(int severity
, const char *fmt
, va_list alist
)
149 if (pname
!= NULL
&& log_dest
== LD_TERMINAL
)
150 (void) snprintf(line
, sizeof (line
), gettext(PNAME_FMT
), pname
);
152 (void) vsnprintf(line
+ strlen(line
), sizeof (line
) - strlen(line
),
155 if (line
[strlen(line
) - 1] != '\n')
156 (void) snprintf(line
+ strlen(line
), sizeof (line
) -
157 strlen(line
), gettext(ERRNO_FMT
), strerror(err
));
159 line
[strlen(line
) - 1] = 0;
163 (void) fprintf(stderr
, "%s\n", line
);
164 (void) fflush(stderr
);
167 syslog(LOG_ERR
, "%s", line
);
170 if (severity
== LOG_ERR
)
171 jseverity
= severity_err
;
173 jseverity
= severity_notice
;
176 check_thread_attached(&env
);
177 if ((jline
= (*env
)->NewStringUTF(env
, line
)) != NULL
)
178 (*env
)->CallVoidMethod(env
, base_log
, log_mid
,
185 * Notify the user with the supplied message.
189 pu_notice(const char *fmt
, ...)
193 va_start(alist
, fmt
);
194 pu_output(LOG_NOTICE
, fmt
, alist
);
199 * Stop the application executing inside the JVM. Always ensure that jvm_lock
200 * is held before invoking this function.
203 halt_application(void)
206 jmethodID poold_shutdown_mid
;
208 if (jvm
&& instance_running
) {
209 check_thread_attached(&env
);
210 if ((poold_shutdown_mid
= (*env
)->GetMethodID(
211 env
, poold_class
, "shutdown", "()V")) != NULL
) {
212 (*env
)->CallVoidMethod(env
, poold_instance
,
215 if (lflag
&& (*env
)->ExceptionOccurred(env
)) {
216 (*env
)->ExceptionDescribe(env
);
217 pu_notice("could not invoke proper shutdown\n");
220 instance_running
= 0;
225 * Warn the user with the supplied error message, halt the application,
226 * destroy the JVM and then exit the process.
230 pu_die(const char *fmt
, ...)
234 va_start(alist
, fmt
);
235 pu_output(LOG_ERR
, fmt
, alist
);
239 (*jvm
)->DestroyJavaVM(jvm
);
246 * Warn the user with the supplied error message and halt the
247 * application. This function is very similar to pu_die(). However,
248 * this function is designed to be called from the signal handling
249 * routine (handle_sig()) where although we wish to let the user know
250 * that an error has occurred, we do not wish to destroy the JVM or
255 pu_terminate(const char *fmt
, ...)
259 va_start(alist
, fmt
);
260 pu_output(LOG_ERR
, fmt
, alist
);
266 * If SIGHUP is invoked, we should just re-initialize poold. Since
267 * there is no easy way to determine when it's safe to re-initialzie
268 * poold, simply update a dummy property on the system element to
269 * force pool_conf_update() to detect a change.
271 * Both SIGTERM and SIGINT are interpreted as instructions to
276 handle_sig(void *arg
)
278 pool_conf_t
*conf
= NULL
;
281 const char *err_desc
;
282 int keep_handling
= 1;
284 while (keep_handling
) {
286 char buf
[SIG2STR_MAX
];
288 if ((sig
= sigwait(&hdl_set
)) < 0) {
290 * We used forkall() previously to ensure that
291 * all threads started by the JVM are
292 * duplicated in the child. Since forkall()
293 * can cause blocking system calls to be
294 * interrupted, check to see if the errno is
295 * EINTR and if it is wait again.
299 (void) pthread_mutex_lock(&jvm_lock
);
300 pu_terminate("unexpected error: %d\n", errno
);
303 (void) pthread_mutex_lock(&jvm_lock
);
304 (void) sig2str(sig
, buf
);
307 if ((conf
= pool_conf_alloc()) == NULL
) {
308 err_desc
= pool_strerror(pool_error());
311 if (pool_conf_open(conf
, pool_dynamic_location(),
313 err_desc
= pool_strerror(pool_error());
317 if ((val
= pool_value_alloc()) == NULL
) {
318 err_desc
= pool_strerror(pool_error());
321 pe
= pool_conf_to_elem(conf
);
322 pool_value_set_bool(val
, 1);
323 if (pool_put_property(conf
, pe
, "system.poold.sighup",
324 val
) != PO_SUCCESS
) {
325 err_desc
= pool_strerror(pool_error());
326 pool_value_free(val
);
329 pool_value_free(val
);
330 (void) pool_rm_property(conf
, pe
,
331 "system.poold.sighup");
332 if (pool_conf_commit(conf
, 0) != PO_SUCCESS
) {
333 err_desc
= pool_strerror(pool_error());
336 (void) pool_conf_close(conf
);
337 pool_conf_free(conf
);
341 (void) pool_conf_close(conf
);
342 pool_conf_free(conf
);
344 pu_terminate(err_desc
);
350 pu_terminate("terminating due to signal: SIG%s\n", buf
);
354 (void) pthread_mutex_unlock(&jvm_lock
);
362 * Return the name of the process
365 pu_getpname(const char *arg0
)
370 * Guard against '/' at end of command invocation.
373 p
= strrchr(arg0
, '/');
378 if (*(p
+ 1) == '\0') {
392 main(int argc
, char *argv
[])
395 char log_severity
[16] = "";
396 JavaVMInitArgs vm_args
;
397 JavaVMOption vm_opts
[5];
399 const char *classpath
;
402 const char *err_desc
;
404 jmethodID poold_getinstancewcl_mid
;
405 jmethodID poold_run_mid
;
406 jobject log_severity_string
= NULL
;
407 jobject log_severity_obj
= NULL
;
408 jclass severity_class
;
409 jmethodID severity_cons_mid
;
410 jfieldID base_log_fid
;
411 pthread_t hdl_thread
;
414 (void) pthread_mutex_lock(&jvm_lock
);
415 pname
= pu_getpname(argv
[0]);
416 openlog(pname
, 0, LOG_DAEMON
);
419 (void) setlocale(LC_ALL
, "");
420 #if !defined(TEXT_DOMAIN) /* Should be defined with cc -D. */
421 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't. */
423 (void) textdomain(TEXT_DOMAIN
);
426 while ((c
= getopt(argc
, argv
, "l:P")) != EOF
) {
428 case 'l': /* -l option */
430 (void) strlcpy(log_severity
, optarg
,
431 sizeof (log_severity
));
432 log_dest
= LD_TERMINAL
;
443 if (!priv_ineffect(PRIV_SYS_RES_CONFIG
))
444 pu_die(gettext(ERR_PRIVILEGE
), PRIV_SYS_RES_CONFIG
);
447 * In order to avoid problems with arbitrary thread selection
448 * when handling asynchronous signals, dedicate a thread to
449 * look after these signals.
451 if (sigemptyset(&hdl_set
) < 0 ||
452 sigaddset(&hdl_set
, SIGHUP
) < 0 ||
453 sigaddset(&hdl_set
, SIGTERM
) < 0 ||
454 sigaddset(&hdl_set
, SIGINT
) < 0 ||
455 pthread_sigmask(SIG_BLOCK
, &hdl_set
, NULL
) ||
456 pthread_create(&hdl_thread
, NULL
, handle_sig
, NULL
))
457 pu_die(gettext("can't install signal handler"));
460 * If the -l flag is supplied, terminate the SMF service and
461 * run interactively from the command line.
464 char *cmd
= "/usr/sbin/svcadm disable -st " SMF_SVC_INSTANCE
;
466 if (getenv("SMF_FMRI") != NULL
)
467 pu_die("-l option illegal: %s\n", SMF_SVC_INSTANCE
);
469 * Since disabling a service isn't synchronous, use the
470 * synchronous option from svcadm to achieve synchronous
472 * This is not very satisfactory, but since this is only
473 * for use in debugging scenarios, it will do until there
474 * is a C API to synchronously shutdown a service in SMF.
476 if ((p
= popen(cmd
, "w")) == NULL
|| pclose(p
) != 0)
477 pu_die("could not temporarily disable service: %s\n",
481 * Check if we are running as a SMF service. If we
482 * aren't, terminate this process after enabling the
485 if (getenv("SMF_FMRI") == NULL
) {
486 char *cmd
= "/usr/sbin/svcadm enable -s " \
488 if ((p
= popen(cmd
, "w")) == NULL
|| pclose(p
) != 0)
489 pu_die("could not enable "
490 "service: %s\n", SMF_SVC_INSTANCE
);
491 return (E_PO_SUCCESS
);
496 * Establish the classpath and LD_LIBRARY_PATH for native
497 * methods, and get the interpreter going.
499 if ((classpath
= getenv("POOLD_CLASSPATH")) == NULL
) {
500 classpath
= POOLD_DEF_CLASSPATH
;
502 const char *cur
= classpath
;
505 * Check the components to make sure they're absolute
508 while (cur
!= NULL
&& *cur
) {
511 "POOLD_CLASSPATH must contain absolute "
513 cur
= strchr(cur
+ 1, ':');
516 vm_opts
[nopts
].optionString
= malloc(len
= strlen(classpath
) +
517 strlen("-Djava.class.path=") + 1);
518 (void) strlcpy(vm_opts
[nopts
].optionString
, "-Djava.class.path=", len
);
519 (void) strlcat(vm_opts
[nopts
++].optionString
, classpath
, len
);
521 if ((libpath
= getenv("POOLD_LD_LIBRARY_PATH")) == NULL
)
522 libpath
= POOLD_DEF_LIBPATH
;
523 vm_opts
[nopts
].optionString
= malloc(len
= strlen(libpath
) +
524 strlen("-Djava.library.path=") + 1);
525 (void) strlcpy(vm_opts
[nopts
].optionString
, "-Djava.library.path=",
527 (void) strlcat(vm_opts
[nopts
++].optionString
, libpath
, len
);
529 vm_opts
[nopts
++].optionString
= "-Xrs";
530 vm_opts
[nopts
++].optionString
= "-enableassertions";
532 vm_args
.options
= vm_opts
;
533 vm_args
.nOptions
= nopts
;
534 vm_args
.ignoreUnrecognized
= JNI_FALSE
;
535 vm_args
.version
= 0x00010002;
537 if (JNI_CreateJavaVM(&jvm
, (void **)&env
, &vm_args
) < 0)
538 pu_die(gettext("can't create Java VM"));
541 * Locate the Poold class and construct an instance. A side
542 * effect of this is that the poold instance's logHelper will be
543 * initialized, establishing loggers for logging errors from
544 * this point on. (Note, in the event of an unanticipated
545 * exception, poold will invoke die() itself.)
547 err_desc
= gettext("JVM-related error initializing poold\n");
548 if ((poold_class
= (*env
)->FindClass(env
, POOLD_CLASS_DESC
)) == NULL
)
550 if ((poold_getinstancewcl_mid
= (*env
)->GetStaticMethodID(env
,
551 poold_class
, "getInstanceWithConsoleLogging", "("
552 CLASS_FIELD_DESC(SEVERITY_CLASS_DESC
) ")"
553 CLASS_FIELD_DESC(POOLD_CLASS_DESC
))) == NULL
)
555 if ((poold_run_mid
= (*env
)->GetMethodID(env
, poold_class
, "run",
558 if ((severity_class
= (*env
)->FindClass(env
, SEVERITY_CLASS_DESC
))
561 if ((severity_cons_mid
= (*env
)->GetStaticMethodID(env
, severity_class
,
562 "getSeverityWithName", "(" CLASS_FIELD_DESC(STRING_CLASS_DESC
) ")"
563 CLASS_FIELD_DESC(SEVERITY_CLASS_DESC
))) == NULL
)
567 * -l <level> was specified, indicating that messages are to be
568 * logged to the console only.
570 if (strlen(log_severity
) > 0) {
571 if ((log_severity_string
= (*env
)->NewStringUTF(env
,
572 log_severity
)) == NULL
)
574 if ((log_severity_obj
= (*env
)->CallStaticObjectMethod(env
,
575 severity_class
, severity_cons_mid
, log_severity_string
)) ==
577 err_desc
= gettext("invalid level specified\n");
581 log_severity_obj
= NULL
;
583 if ((poold_instance
= (*env
)->CallStaticObjectMethod(env
, poold_class
,
584 poold_getinstancewcl_mid
, log_severity_obj
)) == NULL
)
588 * Grab a global reference to poold for use in our signal
591 poold_instance
= (*env
)->NewGlobalRef(env
, poold_instance
);
594 * Ready LD_JAVA logging.
596 err_desc
= gettext("cannot initialize logging\n");
597 if ((log_severity_string
= (*env
)->NewStringUTF(env
, "err")) == NULL
)
599 if (!(severity_err
= (*env
)->CallStaticObjectMethod(env
, severity_class
,
600 severity_cons_mid
, log_severity_string
)))
602 if (!(severity_err
= (*env
)->NewGlobalRef(env
, severity_err
)))
605 if ((log_severity_string
= (*env
)->NewStringUTF(env
, "notice")) == NULL
)
607 if (!(severity_notice
= (*env
)->CallStaticObjectMethod(env
,
608 severity_class
, severity_cons_mid
, log_severity_string
)))
610 if (!(severity_notice
= (*env
)->NewGlobalRef(env
, severity_notice
)))
613 if (!(base_log_fid
= (*env
)->GetStaticFieldID(env
, poold_class
,
614 "BASE_LOG", CLASS_FIELD_DESC(LOGGER_CLASS_DESC
))))
616 if (!(base_log
= (*env
)->GetStaticObjectField(env
, poold_class
,
619 if (!(base_log
= (*env
)->NewGlobalRef(env
, base_log
)))
621 if (!(log_mid
= (*env
)->GetMethodID(env
, (*env
)->GetObjectClass(env
,
622 base_log
), "log", "(" CLASS_FIELD_DESC(LEVEL_CLASS_DESC
)
623 CLASS_FIELD_DESC(STRING_CLASS_DESC
) ")V")))
628 * If invoked directly and -l is specified, forking is not
635 (void) fclose(stdin
);
636 (void) fclose(stdout
);
637 (void) fclose(stderr
);
640 pu_die(gettext("cannot fork"));
643 return (E_PO_SUCCESS
);
646 instance_running
= 1;
647 (void) pthread_mutex_unlock(&jvm_lock
);
649 (*env
)->CallVoidMethod(env
, poold_instance
, poold_run_mid
);
651 (void) pthread_mutex_lock(&jvm_lock
);
652 if ((*env
)->ExceptionOccurred(env
)) {
656 (*jvm
)->DestroyJavaVM(jvm
);
659 (void) pthread_mutex_unlock(&jvm_lock
);
660 return (E_PO_SUCCESS
);
663 if (lflag
&& (*env
)->ExceptionOccurred(env
))
664 (*env
)->ExceptionDescribe(env
);